]> granicus.if.org Git - clang/commitdiff
Add -Wunused-local-typedef, a warning that finds unused local typedefs.
authorNico Weber <nicolasweber@gmx.de>
Sat, 6 Sep 2014 01:25:55 +0000 (01:25 +0000)
committerNico Weber <nicolasweber@gmx.de>
Sat, 6 Sep 2014 01:25:55 +0000 (01:25 +0000)
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.)

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@217298 91177308-0d34-0410-b5e6-96231b3b80d8

25 files changed:
include/clang/Basic/DiagnosticGroups.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/ExternalSemaSource.h
include/clang/Sema/MultiplexExternalSemaSource.h
include/clang/Sema/Sema.h
include/clang/Serialization/ASTBitCodes.h
include/clang/Serialization/ASTReader.h
lib/Sema/MultiplexExternalSemaSource.cpp
lib/Sema/Sema.cpp
lib/Sema/SemaCXXScopeSpec.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaStmt.cpp
lib/Sema/SemaStmtAsm.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
lib/Serialization/ASTReader.cpp
lib/Serialization/ASTWriter.cpp
test/Misc/ast-dump-color.cpp
test/Modules/Inputs/module.map
test/Modules/Inputs/warn-unused-local-typedef.h [new file with mode: 0644]
test/Modules/warn-unused-local-typedef.cpp [new file with mode: 0644]
test/SemaCXX/implicit-exception-spec.cpp
test/SemaCXX/warn-unused-filescoped.cpp
test/SemaCXX/warn-unused-local-typedef-serialize.cpp [new file with mode: 0644]
test/SemaCXX/warn-unused-local-typedef.cpp [new file with mode: 0644]

index 93328ca3ebdc0bd60652ef80785a6358a8591093..c9872d8b60936c7f5436267b3c7e574b91fa77ef 100644 (file)
@@ -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.
index 7098cc8127236d1d93894af4d1697af7deb7653a..995e7a5189eff6429bc7a6e130bfd1dbd6b7a507 100644 (file)
@@ -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">,
index dd5a2ba7dddccdc2448d387f6230a002b1605659..c0ef7121a6eea7e14ce84feed6b52d55f354bfbd 100644 (file)
 #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.
   ///
index bfb80f032e5c98e0e3b5430c772aa9e913b4b1db..f06d19629a3083f3e766f639b71fb8b1ee7e81e0 100644 (file)
@@ -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.
   ///
index cada46c48b53ea05642235efe1039d65644ac99b..a877855aa14442bd4e8381c16db9026aef447ff0 100644 (file)
@@ -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
index 26b7e3e18418149779440b235d6d08a7582ac1bf..4abe3c98742a44fdf243edef89c29049ad3b8641 100644 (file)
@@ -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.
index 69650f65827672ea2aa8f9944ee5cf1a7ad44276..12d27f105f274304403496e0d2bfaada82becdeb 100644 (file)
@@ -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;
 
index 97237dbf097d16dd89e804eb3bbee98771f981a7..449ddf43114de46228361beca49a31a3556ab89e 100644 (file)
@@ -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)
index c762d5fdacb96cc52dbaa2b11770f959a616d0e2..69a4356b450e20e79ecd3ca0c905a2c96944e923 100644 (file)
@@ -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())) {
index b1b8b5d1dc99ada9299aed91c5ad38cef5e6b3fa..d3f0fad3151079c8d452e6441b9824173443727e 100644 (file)
@@ -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)
index 8834705e74294f4eb4c00984e62449c5c20543ff..27f69adff56b6a3fd8e82b84fd9c5a2bb7a7db72 100644 (file)
@@ -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))
index ab0bfcd9f904457b0db6e5af8c68304491bf4a20..8d2a3258b10b41a1a8f8d0e720d112300b1b676e 100644 (file)
@@ -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().
index 989999f18d9d44b2cf1ea745b568c507fc411374..69cf6459d22fc31524466dba6b701a2788ef5342 100644 (file)
@@ -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;
index fb05718ff1b5571b26cf714d4ed8c9220b43e1ec..3c37a3e2d060a5d8d69bdc7241bd5f727769f999 100644 (file)
@@ -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));
index ce18f6e36985162205dfa49b30e7ee9d36403e76..62a3e11ac9a1ce3c53b69befe1a7752c10267ebc 100644 (file)
@@ -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);
 }
index 5b0aa1484b97f0a22e5d78c960513a331a13ae8e..8f1d88c6d58e5da6a25f5a418e553de7dcdecd2e 100644 (file)
@@ -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) {
index 165119b7c6b641ca090aadc52fd540fdbbf607ab..973d8a28e1412ce7e4f3419fcc02560c1575f611 100644 (file)
@@ -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);
index b4660b4a724e31bb87f8c3709180103ad9a69438..6e680a09a0fca51e3e608512ffb7ef602c8f0ed9 100644 (file)
@@ -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]]>
index e8011c70555f08ee1728f838d0ea9a82d5941485..7040ee7e5ab6194d2fa7dd302d537a9eda0eef47 100644 (file)
@@ -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 (file)
index 0000000..6006de0
--- /dev/null
@@ -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 (file)
index 0000000..a463621
--- /dev/null
@@ -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;
index e26f985f0d0ae0ea170f38ecfc6c04260e7d58e7..6864f29dae306b0dd9198f99f562ce86c93c9547 100644 (file)
@@ -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; };
index df4c47e71787d9d03f8a56fa8525bc1f25566344..18defee7d04a6fdb34d6ec964bbf808102defb7c 100644 (file)
@@ -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 (file)
index 0000000..62630fc
--- /dev/null
@@ -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 (file)
index 0000000..0ff44b0
--- /dev/null
@@ -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"