diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 93328ca3ebdc0bd60652ef80785a6358a8591093..c9872d8b60936c7f5436267b3c7e574b91fa77ef 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -402,6 +402,7 @@ def UnusedValue : DiagGroup<"unused-value", [UnusedComparison, UnusedResult]>; def UnusedConstVariable : DiagGroup<"unused-const-variable">; def UnusedVariable : DiagGroup<"unused-variable", [UnusedConstVariable]>; +def UnusedLocalTypedef : DiagGroup<"unused-local-typedef">; def UnusedPropertyIvar : DiagGroup<"unused-property-ivar">; def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">; def UserDefinedLiterals : DiagGroup<"user-defined-literals">; @@ -526,7 +527,7 @@ def Unused : DiagGroup<"unused", [UnusedArgument, UnusedFunction, UnusedLabel, // UnusedParameter, (matches GCC's behavior) // UnusedMemberFunction, (clean-up llvm before enabling) - UnusedPrivateField, + UnusedPrivateField, UnusedLocalTypedef, UnusedValue, UnusedVariable, UnusedPropertyIvar]>, DiagCategory<"Unused Entity Issue">; @@ -622,6 +623,8 @@ def : DiagGroup<"int-conversions", [IntConversion]>; // -Wint-conversions = -Wint-conversion def : DiagGroup<"vector-conversions", [VectorConversion]>; // -Wvector-conversions = -Wvector-conversion +def : DiagGroup<"unused-local-typedefs", [UnusedLocalTypedef]>; + // -Wunused-local-typedefs = -Wunused-local-typedef // A warning group for warnings that we want to have on by default in clang, // but which aren't on by default in GCC. diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 7098cc8127236d1d93894af4d1697af7deb7653a..995e7a5189eff6429bc7a6e130bfd1dbd6b7a507 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -175,6 +175,9 @@ def warn_unused_parameter : Warning<"unused parameter %0">, InGroup<UnusedParameter>, DefaultIgnore; def warn_unused_variable : Warning<"unused variable %0">, InGroup<UnusedVariable>, DefaultIgnore; +def warn_unused_local_typedef : Warning< + "unused %select{typedef|type alias}0 %1">, + InGroup<UnusedLocalTypedef>, DefaultIgnore; def warn_unused_property_backing_ivar : Warning<"ivar %0 which backs the property is not " "referenced in this property's accessor">, diff --git a/include/clang/Sema/ExternalSemaSource.h b/include/clang/Sema/ExternalSemaSource.h index dd5a2ba7dddccdc2448d387f6230a002b1605659..c0ef7121a6eea7e14ce84feed6b52d55f354bfbd 100644 --- a/include/clang/Sema/ExternalSemaSource.h +++ b/include/clang/Sema/ExternalSemaSource.h @@ -20,6 +20,10 @@ #include "llvm/ADT/MapVector.h" #include <utility> +namespace llvm { +template <class T, unsigned n> class SmallSetVector; +} + namespace clang { class CXXConstructorDecl; @@ -132,6 +136,15 @@ public: /// introduce the same declarations repeatedly. virtual void ReadDynamicClasses(SmallVectorImpl<CXXRecordDecl *> &Decls) {} + /// \brief Read the set of potentially unused typedefs known to the source. + /// + /// The external source should append its own potentially unused local + /// typedefs to the given vector of declarations. Note that this routine may + /// be invoked multiple times; the external source should take care not to + /// introduce the same declarations repeatedly. + virtual void ReadUnusedLocalTypedefNameCandidates( + llvm::SmallSetVector<const TypedefNameDecl *, 4> &Decls) {}; + /// \brief Read the set of locally-scoped external declarations known to the /// external Sema source. /// diff --git a/include/clang/Sema/MultiplexExternalSemaSource.h b/include/clang/Sema/MultiplexExternalSemaSource.h index bfb80f032e5c98e0e3b5430c772aa9e913b4b1db..f06d19629a3083f3e766f639b71fb8b1ee7e81e0 100644 --- a/include/clang/Sema/MultiplexExternalSemaSource.h +++ b/include/clang/Sema/MultiplexExternalSemaSource.h @@ -282,6 +282,15 @@ public: /// introduce the same declarations repeatedly. void ReadDynamicClasses(SmallVectorImpl<CXXRecordDecl*> &Decls) override; + /// \brief Read the set of potentially unused typedefs known to the source. + /// + /// The external source should append its own potentially unused local + /// typedefs to the given vector of declarations. Note that this routine may + /// be invoked multiple times; the external source should take care not to + /// introduce the same declarations repeatedly. + void ReadUnusedLocalTypedefNameCandidates( + llvm::SmallSetVector<const TypedefNameDecl *, 4> &Decls) override; + /// \brief Read the set of locally-scoped extern "C" declarations known to the /// external Sema source. /// diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index cada46c48b53ea05642235efe1039d65644ac99b..a877855aa14442bd4e8381c16db9026aef447ff0 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -389,6 +389,10 @@ public: /// \brief Set containing all declared private fields that are not used. NamedDeclSetType UnusedPrivateFields; + /// \brief Set containing all typedefs that are likely unused. + llvm::SmallSetVector<const TypedefNameDecl *, 4> + UnusedLocalTypedefNameCandidates; + typedef llvm::SmallPtrSet<const CXXRecordDecl*, 8> RecordDeclSetTy; /// PureVirtualClassDiagSet - a set of class declarations which we have @@ -1048,6 +1052,8 @@ public: /// \brief Retrieve the module loader associated with the preprocessor. ModuleLoader &getModuleLoader() const; + void emitAndClearUnusedLocalTypedefWarnings(); + void ActOnEndOfTranslationUnit(); void CheckDelegatingCtorCycles(); @@ -3209,6 +3215,7 @@ public: /// DiagnoseUnusedExprResult - If the statement passed in is an expression /// whose result is unused, warn. void DiagnoseUnusedExprResult(const Stmt *S); + void DiagnoseUnusedNestedTypedefs(const RecordDecl *D); void DiagnoseUnusedDecl(const NamedDecl *ND); /// Emit \p DiagID if statement located on \p StmtLoc has a suspicious null diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 26b7e3e18418149779440b235d6d08a7582ac1bf..4abe3c98742a44fdf243edef89c29049ad3b8641 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -545,7 +545,10 @@ namespace clang { LATE_PARSED_TEMPLATE = 50, /// \brief Record code for \#pragma optimize options. - OPTIMIZE_PRAGMA_OPTIONS = 51 + OPTIMIZE_PRAGMA_OPTIONS = 51, + + /// \brief Record code for potentially unused local typedef names. + UNUSED_LOCAL_TYPEDEF_NAME_CANDIDATES = 52, }; /// \brief Record types used within a source manager block. diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 69650f65827672ea2aa8f9944ee5cf1a7ad44276..12d27f105f274304403496e0d2bfaada82becdeb 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -759,6 +759,11 @@ private: /// at the end of the TU, in which case it directs CodeGen to emit the VTable. SmallVector<uint64_t, 16> DynamicClasses; + /// \brief The IDs of all potentially unused typedef names in the chain. + /// + /// Sema tracks these to emit warnings. + SmallVector<uint64_t, 16> UnusedLocalTypedefNameCandidates; + /// \brief The IDs of the declarations Sema stores directly. /// /// Sema tracks a few important decls, such as namespace std, directly. @@ -1789,6 +1794,9 @@ public: void ReadDynamicClasses(SmallVectorImpl<CXXRecordDecl *> &Decls) override; + void ReadUnusedLocalTypedefNameCandidates( + llvm::SmallSetVector<const TypedefNameDecl *, 4> &Decls) override; + void ReadLocallyScopedExternCDecls( SmallVectorImpl<NamedDecl *> &Decls) override; diff --git a/lib/Sema/MultiplexExternalSemaSource.cpp b/lib/Sema/MultiplexExternalSemaSource.cpp index 97237dbf097d16dd89e804eb3bbee98771f981a7..449ddf43114de46228361beca49a31a3556ab89e 100644 --- a/lib/Sema/MultiplexExternalSemaSource.cpp +++ b/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/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index c762d5fdacb96cc52dbaa2b11770f959a616d0e2..69a4356b450e20e79ecd3ca0c905a2c96944e923 100644 --- a/lib/Sema/Sema.cpp +++ b/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/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index b1b8b5d1dc99ada9299aed91c5ad38cef5e6b3fa..d3f0fad3151079c8d452e6441b9824173443727e 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/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/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 8834705e74294f4eb4c00984e62449c5c20543ff..27f69adff56b6a3fd8e82b84fd9c5a2bb7a7db72 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/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/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index ab0bfcd9f904457b0db6e5af8c68304491bf4a20..8d2a3258b10b41a1a8f8d0e720d112300b1b676e 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/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/lib/Sema/SemaStmtAsm.cpp b/lib/Sema/SemaStmtAsm.cpp index 989999f18d9d44b2cf1ea745b568c507fc411374..69cf6459d22fc31524466dba6b701a2788ef5342 100644 --- a/lib/Sema/SemaStmtAsm.cpp +++ b/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/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index fb05718ff1b5571b26cf714d4ed8c9220b43e1ec..3c37a3e2d060a5d8d69bdc7241bd5f727769f999 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/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/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index ce18f6e36985162205dfa49b30e7ee9d36403e76..62a3e11ac9a1ce3c53b69befe1a7752c10267ebc 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/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/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 5b0aa1484b97f0a22e5d78c960513a331a13ae8e..8f1d88c6d58e5da6a25f5a418e553de7dcdecd2e 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/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/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 165119b7c6b641ca090aadc52fd540fdbbf607ab..973d8a28e1412ce7e4f3419fcc02560c1575f611 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/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); diff --git a/test/Misc/ast-dump-color.cpp b/test/Misc/ast-dump-color.cpp index b4660b4a724e31bb87f8c3709180103ad9a69438..6e680a09a0fca51e3e608512ffb7ef602c8f0ed9 100644 --- a/test/Misc/ast-dump-color.cpp +++ b/test/Misc/ast-dump-color.cpp @@ -89,7 +89,7 @@ struct Invalid { //CHECK: {{^}}[[Blue]]| `-[[RESET]][[BLUE]]GuardedByAttr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:29[[RESET]], [[Yellow]]col:43[[RESET]]>{{$}} //CHECK: {{^}}[[Blue]]| `-[[RESET]][[MAGENTA]]DeclRefExpr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:40[[RESET]]> [[Green]]'class Mutex':'class Mutex'[[RESET]][[Cyan]] lvalue[[RESET]][[Cyan]][[RESET]] [[GREEN]]Var[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]][[CYAN]] 'mu1'[[RESET]] [[Green]]'class Mutex':'class Mutex'[[RESET]]{{$}} //CHECK: {{^}}[[Blue]]|-[[RESET]][[GREEN]]CXXRecordDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:28:1[[RESET]], [[Yellow]]line:30:1[[RESET]]> [[Yellow]]line:28:8[[RESET]] struct[[CYAN]] Invalid[[RESET]] definition -//CHECK: {{^}}[[Blue]]| |-[[RESET]][[GREEN]]CXXRecordDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:1[[RESET]], [[Yellow]]col:8[[RESET]]> [[Yellow]]col:8[[RESET]] implicit struct[[CYAN]] Invalid[[RESET]] +//CHECK: {{^}}[[Blue]]| |-[[RESET]][[GREEN]]CXXRecordDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:1[[RESET]], [[Yellow]]col:8[[RESET]]> [[Yellow]]col:8[[RESET]] implicit referenced struct[[CYAN]] Invalid[[RESET]] //CHECK: {{^}}[[Blue]]| |-[[RESET]][[GREEN]]CXXConstructorDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]line:29:3[[RESET]], [[Yellow]]col:42[[RESET]]> [[Yellow]]col:29[[RESET]] invalid[[CYAN]] Invalid[[RESET]] [[Green]]'void (int)'[[RESET]] //CHECK: {{^}}[[Blue]]| | |-[[RESET]][[GREEN]]ParmVarDecl[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:37[[RESET]], [[Yellow]]<invalid sloc>[[RESET]]> [[Yellow]]col:42[[RESET]] invalid [[Green]]'int'[[RESET]] //CHECK: {{^}}[[Blue]]| | `-[[RESET]][[BLUE]]NoInlineAttr[[RESET]][[Yellow]] 0x{{[0-9a-fA-F]*}}[[RESET]] <[[Yellow]]col:18[[RESET]]> diff --git a/test/Modules/Inputs/module.map b/test/Modules/Inputs/module.map index e8011c70555f08ee1728f838d0ea9a82d5941485..7040ee7e5ab6194d2fa7dd302d537a9eda0eef47 100644 --- a/test/Modules/Inputs/module.map +++ b/test/Modules/Inputs/module.map @@ -292,6 +292,10 @@ module warning { header "warning.h" } +module warn_unused_local_typedef { + header "warn-unused-local-typedef.h" +} + module initializer_list { header "initializer_list" } diff --git a/test/Modules/Inputs/warn-unused-local-typedef.h b/test/Modules/Inputs/warn-unused-local-typedef.h new file mode 100644 index 0000000000000000000000000000000000000000..6006de0cc8cc305a70159eae0c2e69fc7e47c81b --- /dev/null +++ b/test/Modules/Inputs/warn-unused-local-typedef.h @@ -0,0 +1 @@ +inline void myfun() { typedef int a; } diff --git a/test/Modules/warn-unused-local-typedef.cpp b/test/Modules/warn-unused-local-typedef.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a463621d4ef582a750ab64706bf36e94a0cd1743 --- /dev/null +++ b/test/Modules/warn-unused-local-typedef.cpp @@ -0,0 +1,9 @@ +// RUN: rm -rf %t +// RUN: %clang -Wunused-local-typedef -c -x objective-c++ -fcxx-modules -fmodules -fmodules-cache-path=%t -I %S/Inputs %s 2>&1 | FileCheck %s -check-prefix=CHECK_1 +// RUN: %clang -Wunused-local-typedef -c -x objective-c++ -fcxx-modules -fmodules -fmodules-cache-path=%t -I %S/Inputs %s 2>&1 | FileCheck %s -check-prefix=CHECK_2 -allow-empty + +// For modules, the warning should only fire the first time, when the module is +// built. +// CHECK_1: warning: unused typedef +// CHECK_2-NOT: warning: unused typedef +@import warn_unused_local_typedef; diff --git a/test/SemaCXX/implicit-exception-spec.cpp b/test/SemaCXX/implicit-exception-spec.cpp index e26f985f0d0ae0ea170f38ecfc6c04260e7d58e7..6864f29dae306b0dd9198f99f562ce86c93c9547 100644 --- a/test/SemaCXX/implicit-exception-spec.cpp +++ b/test/SemaCXX/implicit-exception-spec.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -std=c++11 -Wall %s +// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -std=c++11 -Wall -Wno-unused-local-typedefs %s template<bool b> struct ExceptionIf { static int f(); }; template<> struct ExceptionIf<false> { typedef int f; }; diff --git a/test/SemaCXX/warn-unused-filescoped.cpp b/test/SemaCXX/warn-unused-filescoped.cpp index df4c47e71787d9d03f8a56fa8525bc1f25566344..18defee7d04a6fdb34d6ec964bbf808102defb7c 100644 --- a/test/SemaCXX/warn-unused-filescoped.cpp +++ b/test/SemaCXX/warn-unused-filescoped.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -Wunused -Wunused-member-function -Wno-c++11-extensions -std=c++98 %s -// RUN: %clang_cc1 -fsyntax-only -verify -Wunused -Wunused-member-function -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wunused -Wunused-member-function -Wno-unused-local-typedefs -Wno-c++11-extensions -std=c++98 %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wunused -Wunused-member-function -Wno-unused-local-typedefs -std=c++11 %s #ifdef HEADER diff --git a/test/SemaCXX/warn-unused-local-typedef-serialize.cpp b/test/SemaCXX/warn-unused-local-typedef-serialize.cpp new file mode 100644 index 0000000000000000000000000000000000000000..62630fc4ff7a03a66e609e864c8c366c22e51062 --- /dev/null +++ b/test/SemaCXX/warn-unused-local-typedef-serialize.cpp @@ -0,0 +1,11 @@ +// RUN: %clang -x c++-header -c -Wunused-local-typedef %s -o %t.gch -Werror +// RUN: %clang -DBE_THE_SOURCE -c -Wunused-local-typedef -include %t %s 2>&1 | FileCheck %s +// RUN: %clang -DBE_THE_SOURCE -c -Wunused-local-typedef -include %t %s 2>&1 | FileCheck %s + +#ifndef BE_THE_SOURCE +inline void myfun() { +// The warning should fire every time the pch file is used, not when it's built. +// CHECK: warning: unused typedef + typedef int a; +} +#endif diff --git a/test/SemaCXX/warn-unused-local-typedef.cpp b/test/SemaCXX/warn-unused-local-typedef.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ff44b0082f8652d80c1b2482ed36fea8d5f7e4b --- /dev/null +++ b/test/SemaCXX/warn-unused-local-typedef.cpp @@ -0,0 +1,231 @@ +// RUN: %clang_cc1 -fsyntax-only -Wunused-local-typedef -verify -std=c++1y -fasm-blocks %s + +struct S { + typedef int Foo; // no diag +}; + +namespace N { + typedef int Foo; // no diag + typedef int Foo2; // no diag +} + +template <class T> class Vec {}; + +typedef int global_foo; // no diag + +void f() { + typedef int foo0; // expected-warning {{unused typedef 'foo0'}} + using foo0alias = int ; // expected-warning {{unused type alias 'foo0alias'}} + + typedef int foo1 __attribute__((unused)); // no diag + + typedef int foo2; + { + typedef int foo2; // expected-warning {{unused typedef 'foo2'}} + } + typedef foo2 foo3; // expected-warning {{unused typedef 'foo3'}} + + typedef int foo2_2; // expected-warning {{unused typedef 'foo2_2'}} + { + typedef int foo2_2; + typedef foo2_2 foo3_2; // expected-warning {{unused typedef 'foo3_2'}} + } + + typedef int foo4; + foo4 the_thing; + + typedef int* foo5; + typedef foo5* foo6; // no diag + foo6 *myptr; + + struct S2 { + typedef int Foo; // no diag + typedef int Foo2; // expected-warning {{unused typedef 'Foo2'}} + + struct Deeper { + typedef int DeepFoo; // expected-warning {{unused typedef 'DeepFoo'}} + }; + }; + + S2::Foo s2foo; + + typedef struct {} foostruct; // expected-warning {{unused typedef 'foostruct'}} + + typedef struct {} foostruct2; // no diag + foostruct2 fs2; + + typedef int vecint; // no diag + Vec<vecint> v; + + N::Foo nfoo; + + typedef int ConstExprInt; + static constexpr int a = (ConstExprInt)4; +} + +int printf(char const *, ...); + +void test() { + typedef signed long int superint; // no diag + printf("%f", (superint) 42); + + typedef signed long int superint2; // no diag + printf("%f", static_cast<superint2>(42)); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-local-typedef" + typedef int trungl_bot_was_here; // no diag +#pragma clang diagnostic pop + + typedef int foo; // expected-warning {{unused typedef 'foo'}} +} + +template <class T> +void template_fun(T t) { + typedef int foo; // expected-warning {{unused typedef 'foo'}} + typedef int bar; // no-diag + bar asdf; + + struct S2 { + typedef int Foo; // no diag + + typedef int Foo2; // expected-warning {{unused typedef 'Foo2'}} + + typedef int Foo3; // no diag + }; + + typename S2::Foo s2foo; + typename T::Foo s3foo; + + typedef typename S2::Foo3 TTSF; // expected-warning {{unused typedef 'TTSF'}} +} +void template_fun_user() { + struct Local { + typedef int Foo; // no-diag + typedef int Bar; // expected-warning {{unused typedef 'Bar'}} + } p; + template_fun(p); +} + +void typedef_in_nested_name() { + typedef struct { + typedef int Foo; + } A; + A::Foo adsf; + + using A2 = struct { + typedef int Foo; + }; + A2::Foo adsf2; +} + +void use_in_asm() { + typedef struct { + int a; + int b; + } A; + __asm mov eax, [eax].A.b + + using Alias = struct { + int a; + int b; + }; + __asm mov eax, [eax].Alias.b +} + +auto sneaky() { + struct S { + // Local typedefs can be used after the scope they were in has closed: + typedef int t; + + // Even if they aren't, this could be an inline function that could be used + // in another TU, so this shouldn't warn either: + typedef int s; + + private: + typedef int p; // expected-warning{{unused typedef 'p'}} + }; + return S(); +} +auto x = sneaky(); +decltype(x)::t y; + +static auto static_sneaky() { + struct S { + typedef int t; + // This function has internal linkage, so we can warn: + typedef int s; // expected-warning {{unused typedef 's'}} + }; + return S(); +} +auto sx = static_sneaky(); +decltype(sx)::t sy; + +auto sneaky_with_friends() { + struct S { + private: + friend class G; + // Can't warn if we have friends: + typedef int p; + }; + return S(); +} + +namespace { +auto nstatic_sneaky() { + struct S { + typedef int t; + // This function has internal linkage, so we can warn: + typedef int s; // expected-warning {{unused typedef 's'}} + }; + return S(); +} +auto nsx = nstatic_sneaky(); +decltype(nsx)::t nsy; +} + +// Like sneaky(), but returning pointer to local type +template<typename T> +struct remove_reference { typedef T type; }; +template<typename T> struct remove_reference<T&> { typedef T type; }; +auto pointer_sneaky() { + struct S { + typedef int t; + typedef int s; + }; + return (S*)nullptr; +} +remove_reference<decltype(*pointer_sneaky())>::type::t py; + +// Like sneaky(), but returning templated struct referencing local type. +template <class T> struct container { int a; T t; }; +auto template_sneaky() { + struct S { + typedef int t; + typedef int s; + }; + return container<S>(); +} +auto tx = template_sneaky(); +decltype(tx.t)::t ty; + +// Like sneaky(), but doing its sneakiness by returning a member function +// pointer. +auto sneaky_memfun() { + struct S { + typedef int type; + int n; + }; + return &S::n; +} + +template <class T> void sneaky_memfun_g(int T::*p) { + typename T::type X; +} + +void sneaky_memfun_h() { + sneaky_memfun_g(sneaky_memfun()); +} + +// This should not disable any warnings: +#pragma clang diagnostic ignored "-Wunused-local-typedef"