diff options
| author | Nico Weber <nicolasweber@gmx.de> | 2014-09-06 01:25:55 +0000 |
|---|---|---|
| committer | Nico Weber <nicolasweber@gmx.de> | 2014-09-06 01:25:55 +0000 |
| commit | 728894340f2a95eef5330b60410fb2abb158137b (patch) | |
| tree | 88e99b74e1c5c9bd024dc17d9b63f31c1f5b5c83 /clang/lib | |
| parent | aaa0b81a4e9273da01cb8350996f18c04fdd9871 (diff) | |
| download | bcm5719-llvm-728894340f2a95eef5330b60410fb2abb158137b.tar.gz bcm5719-llvm-728894340f2a95eef5330b60410fb2abb158137b.zip | |
Add -Wunused-local-typedef, a warning that finds unused local typedefs.
The warning warns on TypedefNameDecls -- typedefs and C++11 using aliases --
that are !isReferenced(). Since the isReferenced() bit on TypedefNameDecls
wasn't used for anything before this warning it wasn't always set correctly,
so this patch also adds a few missing MarkAnyDeclReferenced() calls in
various places for TypedefNameDecls.
This is made a bit complicated due to local typedefs possibly being used only
after their local scope has closed. Consider:
template <class T>
void template_fun(T t) {
typename T::Foo s3foo; // YYY
(void)s3foo;
}
void template_fun_user() {
struct Local {
typedef int Foo; // XXX
} p;
template_fun(p);
}
Here the typedef in XXX is only used at end-of-translation unit, when YYY in
template_fun() gets instantiated. To handle this, typedefs that are unused when
their scope exits are added to a set of potentially unused typedefs, and that
set gets checked at end-of-TU. Typedefs that are still unused at that point then
get warned on. There's also serialization code for this set, so that the
warning works with precompiled headers and modules. For modules, the warning
is emitted when the module is built, for precompiled headers each time the
header gets used.
Finally, consider a function using C++14 auto return types to return a local
type defined in a header:
auto f() {
struct S { typedef int a; };
return S();
}
Here, the typedef escapes its local scope and could be used by only some
translation units including the header. To not warn on this, add a
RecursiveASTVisitor that marks all delcs on local types returned from auto
functions as referenced. (Except if it's a function with internal linkage, or
the decls are private and the local type has no friends -- in these cases, it
_is_ safe to warn.)
Several of the included testcases (most of the interesting ones) were provided
by Richard Smith.
(gcc's spelling -Wunused-local-typedefs is supported as an alias for this
warning.)
llvm-svn: 217298
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/Sema/MultiplexExternalSemaSource.cpp | 6 | ||||
| -rw-r--r-- | clang/lib/Sema/Sema.cpp | 19 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaCXXScopeSpec.cpp | 3 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 42 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaStmt.cpp | 40 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaStmtAsm.cpp | 5 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 1 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 5 | ||||
| -rw-r--r-- | clang/lib/Serialization/ASTReader.cpp | 18 | ||||
| -rw-r--r-- | clang/lib/Serialization/ASTWriter.cpp | 10 |
10 files changed, 143 insertions, 6 deletions
diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp index 97237dbf097..449ddf43114 100644 --- a/clang/lib/Sema/MultiplexExternalSemaSource.cpp +++ b/clang/lib/Sema/MultiplexExternalSemaSource.cpp @@ -242,6 +242,12 @@ void MultiplexExternalSemaSource::ReadDynamicClasses( Sources[i]->ReadDynamicClasses(Decls); } +void MultiplexExternalSemaSource::ReadUnusedLocalTypedefNameCandidates( + llvm::SmallSetVector<const TypedefNameDecl *, 4> &Decls) { + for(size_t i = 0; i < Sources.size(); ++i) + Sources[i]->ReadUnusedLocalTypedefNameCandidates(Decls); +} + void MultiplexExternalSemaSource::ReadLocallyScopedExternCDecls( SmallVectorImpl<NamedDecl*> &Decls) { for(size_t i = 0; i < Sources.size(); ++i) diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index c762d5fdacb..69a4356b450 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -597,6 +597,19 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD, return Complete; } +void Sema::emitAndClearUnusedLocalTypedefWarnings() { + if (ExternalSource) + ExternalSource->ReadUnusedLocalTypedefNameCandidates( + UnusedLocalTypedefNameCandidates); + for (const TypedefNameDecl *TD : UnusedLocalTypedefNameCandidates) { + if (TD->isReferenced()) + continue; + Diag(TD->getLocation(), diag::warn_unused_local_typedef) + << isa<TypeAliasDecl>(TD) << TD->getDeclName(); + } + UnusedLocalTypedefNameCandidates.clear(); +} + /// ActOnEndOfTranslationUnit - This is called at the very end of the /// translation unit when EOF is reached and all but the top-level scope is /// popped. @@ -719,6 +732,10 @@ void Sema::ActOnEndOfTranslationUnit() { } } + // Warnings emitted in ActOnEndOfTranslationUnit() should be emitted for + // modules when they are built, not every time they are used. + emitAndClearUnusedLocalTypedefWarnings(); + // Modules don't need any of the checking below. TUScope = nullptr; return; @@ -827,6 +844,8 @@ void Sema::ActOnEndOfTranslationUnit() { if (ExternalSource) ExternalSource->ReadUndefinedButUsed(UndefinedButUsed); checkUndefinedButUsed(*this); + + emitAndClearUnusedLocalTypedefWarnings(); } if (!Diags.isIgnored(diag::warn_unused_private_field, SourceLocation())) { diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index b1b8b5d1dc9..d3f0fad3151 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -612,6 +612,9 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, } } + if (auto *TD = dyn_cast_or_null<TypedefNameDecl>(SD)) + MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false); + // If we're just performing this lookup for error-recovery purposes, // don't extend the nested-name-specifier. Just return now. if (ErrorRecoveryLookup) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8834705e742..27f69adff56 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -380,6 +380,7 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, DiagnoseUseOfDecl(IIDecl, NameLoc); T = Context.getTypeDeclType(TD); + MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false); // NOTE: avoid constructing an ElaboratedType(Loc) if this is a // constructor or destructor name (in such a case, the scope specifier @@ -928,6 +929,7 @@ Corrected: NamedDecl *FirstDecl = (*Result.begin())->getUnderlyingDecl(); if (TypeDecl *Type = dyn_cast<TypeDecl>(FirstDecl)) { DiagnoseUseOfDecl(Type, NameLoc); + MarkAnyDeclReferenced(Type->getLocation(), Type, /*OdrUse=*/false); QualType T = Context.getTypeDeclType(Type); if (SS.isNotEmpty()) return buildNestedType(*this, SS, T, NameLoc); @@ -1395,10 +1397,22 @@ static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D) { if (isa<LabelDecl>(D)) return true; + + // Except for labels, we only care about unused decls that are local to + // functions. + bool WithinFunction = D->getDeclContext()->isFunctionOrMethod(); + if (const auto *R = dyn_cast<CXXRecordDecl>(D->getDeclContext())) + // For dependent types, the diagnostic is deferred. + WithinFunction = + WithinFunction || (R->isLocalClass() && !R->isDependentType()); + if (!WithinFunction) + return false; + + if (isa<TypedefNameDecl>(D)) + return true; // White-list anything that isn't a local variable. - if (!isa<VarDecl>(D) || isa<ParmVarDecl>(D) || isa<ImplicitParamDecl>(D) || - !D->getDeclContext()->isFunctionOrMethod()) + if (!isa<VarDecl>(D) || isa<ParmVarDecl>(D) || isa<ImplicitParamDecl>(D)) return false; // Types of valid local variables should be complete, so this should succeed. @@ -1461,11 +1475,30 @@ static void GenerateFixForUnusedDecl(const NamedDecl *D, ASTContext &Ctx, return; } +void Sema::DiagnoseUnusedNestedTypedefs(const RecordDecl *D) { + if (D->getTypeForDecl()->isDependentType()) + return; + + for (auto *TmpD : D->decls()) { + if (const auto *T = dyn_cast<TypedefNameDecl>(TmpD)) + DiagnoseUnusedDecl(T); + else if(const auto *R = dyn_cast<RecordDecl>(TmpD)) + DiagnoseUnusedNestedTypedefs(R); + } +} + /// DiagnoseUnusedDecl - Emit warnings about declarations that are not used /// unless they are marked attr(unused). void Sema::DiagnoseUnusedDecl(const NamedDecl *D) { if (!ShouldDiagnoseUnusedDecl(D)) return; + + if (auto *TD = dyn_cast<TypedefNameDecl>(D)) { + // typedefs can be referenced later on, so the diagnostics are emitted + // at end-of-translation-unit. + UnusedLocalTypedefNameCandidates.insert(TD); + return; + } FixItHint Hint; GenerateFixForUnusedDecl(D, Context, Hint); @@ -1505,8 +1538,11 @@ void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) { if (!D->getDeclName()) continue; // Diagnose unused variables in this scope. - if (!S->hasUnrecoverableErrorOccurred()) + if (!S->hasUnrecoverableErrorOccurred()) { DiagnoseUnusedDecl(D); + if (const auto *RD = dyn_cast<RecordDecl>(D)) + DiagnoseUnusedNestedTypedefs(RD); + } // If this was a forward reference to a label, verify it was defined. if (LabelDecl *LD = dyn_cast<LabelDecl>(D)) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index ab0bfcd9f90..8d2a3258b10 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -19,6 +19,7 @@ #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" #include "clang/AST/TypeLoc.h" @@ -2710,6 +2711,40 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { return Result; } +namespace { +/// \brief Marks all typedefs in all local classes in a type referenced. +/// +/// In a function like +/// auto f() { +/// struct S { typedef int a; }; +/// return S(); +/// } +/// +/// the local type escapes and could be referenced in some TUs but not in +/// others. Pretend that all local typedefs are always referenced, to not warn +/// on this. This isn't necessary if f has internal linkage, or the typedef +/// is private. +class LocalTypedefNameReferencer + : public RecursiveASTVisitor<LocalTypedefNameReferencer> { +public: + LocalTypedefNameReferencer(Sema &S) : S(S) {} + bool VisitRecordType(const RecordType *RT); +private: + Sema &S; +}; +bool LocalTypedefNameReferencer::VisitRecordType(const RecordType *RT) { + auto *R = dyn_cast<CXXRecordDecl>(RT->getDecl()); + if (!R || !R->isLocalClass() || !R->isLocalClass()->isExternallyVisible() || + R->isDependentType()) + return true; + for (auto *TmpD : R->decls()) + if (auto *T = dyn_cast<TypedefNameDecl>(TmpD)) + if (T->getAccess() != AS_private || R->hasFriends()) + S.MarkAnyDeclReferenced(T->getLocation(), T, /*OdrUse=*/false); + return true; +} +} + /// Deduce the return type for a function from a returned expression, per /// C++1y [dcl.spec.auto]p6. bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, @@ -2755,6 +2790,11 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, if (DAR != DAR_Succeeded) return true; + + // If a local type is part of the returned type, mark its fields as + // referenced. + LocalTypedefNameReferencer Referencer(*this); + Referencer.TraverseType(RetExpr->getType()); } else { // In the case of a return with no operand, the initializer is considered // to be void(). diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp index 989999f18d9..69cf6459d22 100644 --- a/clang/lib/Sema/SemaStmtAsm.cpp +++ b/clang/lib/Sema/SemaStmtAsm.cpp @@ -467,9 +467,10 @@ bool Sema::LookupInlineAsmField(StringRef Base, StringRef Member, NamedDecl *FoundDecl = BaseResult.getFoundDecl(); if (VarDecl *VD = dyn_cast<VarDecl>(FoundDecl)) RT = VD->getType()->getAs<RecordType>(); - else if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(FoundDecl)) + else if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(FoundDecl)) { + MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false); RT = TD->getUnderlyingType()->getAs<RecordType>(); - else if (TypeDecl *TD = dyn_cast<TypeDecl>(FoundDecl)) + } else if (TypeDecl *TD = dyn_cast<TypeDecl>(FoundDecl)) RT = TD->getTypeForDecl()->getAs<RecordType>(); if (!RT) return true; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index fb05718ff1b..3c37a3e2d06 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -7945,6 +7945,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, if (TypeDecl *Type = dyn_cast<TypeDecl>(Result.getFoundDecl())) { // We found a type. Build an ElaboratedType, since the // typename-specifier was just sugar. + MarkAnyDeclReferenced(Type->getLocation(), Type, /*OdrUse=*/false); return Context.getElaboratedType(ETK_Typename, QualifierLoc.getNestedNameSpecifier(), Context.getTypeDeclType(Type)); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index ce18f6e3698..62a3e11ac9a 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1200,6 +1200,9 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) { SemaRef.InstantiateClassMembers(D->getLocation(), Record, TemplateArgs, TSK_ImplicitInstantiation); } + + SemaRef.DiagnoseUnusedNestedTypedefs(Record); + return Record; } @@ -3653,7 +3656,7 @@ void Sema::BuildVariableInstantiation( // Diagnose unused local variables with dependent types, where the diagnostic // will have been deferred. if (!NewVar->isInvalidDecl() && - NewVar->getDeclContext()->isFunctionOrMethod() && !NewVar->isUsed() && + NewVar->getDeclContext()->isFunctionOrMethod() && OldVar->getType()->isDependentType()) DiagnoseUnusedDecl(NewVar); } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 5b0aa1484b9..8f1d88c6d58 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3285,6 +3285,12 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { } OptimizeOffPragmaLocation = ReadSourceLocation(F, Record[0]); break; + + case UNUSED_LOCAL_TYPEDEF_NAME_CANDIDATES: + for (unsigned I = 0, N = Record.size(); I != N; ++I) + UnusedLocalTypedefNameCandidates.push_back( + getGlobalDeclID(F, Record[I])); + break; } } } @@ -7196,6 +7202,18 @@ void ASTReader::ReadDynamicClasses(SmallVectorImpl<CXXRecordDecl *> &Decls) { DynamicClasses.clear(); } +void ASTReader::ReadUnusedLocalTypedefNameCandidates( + llvm::SmallSetVector<const TypedefNameDecl *, 4> &Decls) { + for (unsigned I = 0, N = UnusedLocalTypedefNameCandidates.size(); I != N; + ++I) { + TypedefNameDecl *D = dyn_cast_or_null<TypedefNameDecl>( + GetDecl(UnusedLocalTypedefNameCandidates[I])); + if (D) + Decls.insert(D); + } + UnusedLocalTypedefNameCandidates.clear(); +} + void ASTReader::ReadLocallyScopedExternCDecls(SmallVectorImpl<NamedDecl *> &Decls) { for (unsigned I = 0, N = LocallyScopedExternCDecls.size(); I != N; ++I) { diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 165119b7c6b..973d8a28e14 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -4284,6 +4284,11 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, } } + // Build a record containing all of the UnusedLocalTypedefNameCandidates. + RecordData UnusedLocalTypedefNameCandidates; + for (const TypedefNameDecl *TD : SemaRef.UnusedLocalTypedefNameCandidates) + AddDeclRef(TD, UnusedLocalTypedefNameCandidates); + // Build a record containing all of dynamic classes declarations. RecordData DynamicClasses; AddLazyVectorDecls(*this, SemaRef.DynamicClasses, DynamicClasses); @@ -4561,6 +4566,11 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, if (!DynamicClasses.empty()) Stream.EmitRecord(DYNAMIC_CLASSES, DynamicClasses); + // Write the record containing potentially unused local typedefs. + if (!UnusedLocalTypedefNameCandidates.empty()) + Stream.EmitRecord(UNUSED_LOCAL_TYPEDEF_NAME_CANDIDATES, + UnusedLocalTypedefNameCandidates); + // Write the record containing pending implicit instantiations. if (!PendingInstantiations.empty()) Stream.EmitRecord(PENDING_IMPLICIT_INSTANTIATIONS, PendingInstantiations); |

