[patch] Implementing -Wunused-local-typedefs

Nico Weber thakis at chromium.org
Sun Jul 27 17:13:32 PDT 2014


Now gets this right:

template <class T>
void template_fun(T t) {
  typename T::Foo s3foo;
}
void template_fun_user() {
  struct Local {
    typedef int Foo; // no-diag
    typedef int Bar; // expected-warning {{unused typedef 'Bar'}}
  } p;
  template_fun(p);
}

It also no longer warns on S::Foo in:

template<class T>
void tf(T t) {
  struct S {
    typedef int Foo;
    typedef int Bar;
  };
  S::Foo f;
}

(It stopped warning on S::Bar too – there's a FIXME in the test that
explains why and how to fix that.)



On Sat, Jul 26, 2014 at 9:26 PM, Nico Weber <thakis at chromium.org> wrote:

> I'm delighted to announce that, after almost two months of hard work, a
> new patch set is available for review!
>
> With these tantalizing features and fixes:
> * TypedefNameDecls everywhere
> * MarkAnyDeclReferenced() used throughout
> * Instead of immediately emitting a diag, the TypedefNameDecls are stashed
> in a set and the diagnostics for things in the set are emitted at end-of-TU
> (except for TypedefNameDecls that have been referenced since then)
> * It's now called -Wunused-local-typedef in singular, with an alias for
> the gcc spelling
> * More tests
>
> On Tue, May 6, 2014 at 6:01 PM, Richard Smith <richard at metafoo.co.uk>
> wrote:
>
>>  def UnusedConstVariable : DiagGroup<"unused-const-variable">;
>>  def UnusedVariable : DiagGroup<"unused-variable",
>>                                 [UnusedConstVariable]>;
>> +def UnusedLocalTypedefs : DiagGroup<"unused-local-typedefs">;
>>  def UnusedPropertyIvar :  DiagGroup<"unused-property-ivar">;
>>
>> I'm a little uncomfortable about this flag being plural and the others
>> being singular. GCC compatible, you say? =(
>>
>>
>> +def warn_unused_local_typedefs : Warning<"unused typedef %0">,
>>
>> This one should definitely be singular.
>>
>> Reformatting of lib/Parse/ParseExprCXX.cpp looks unrelated. Feel free to
>> commit that separately.
>>
>> +    if (TypedefDecl* TD = dyn_cast_or_null<TypedefDecl>(SD))
>>
>> * should go on the right. Also, use 'auto' for a variable initialized
>> from cast/dyn_cast/etc.
>>
>> Rather than calling setReferenced directly, please call
>> Sema::MarkAnyDeclReferenced.
>>
>>  +static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D,
>> +                                     const DeclContext *DC) {
>> [...]
>>  +  if (!DC->isFunctionOrMethod())
>> +    return false;
>>
>> Please document what 'DC' is here; it's really not obvious. But it seems
>> to me that you can replace DC here and below with just a bool
>> WithinFunction.
>>
>
> Done.
>
>
>>  You can also remove it entirely and check whether D->getDeclContext() is
>> a function, method, or local class.
>>
>> +void Sema::DiagnoseUnusedDecl(const NamedDecl *D, const DeclContext *DC)
>> {
>> +  if (DC->isFunctionOrMethod())
>> +    if (const RecordDecl *RD = dyn_cast<RecordDecl>(D))
>> +      for (auto *TmpD : RD->decls())
>> +        if (isa<TypedefDecl>(TmpD) || isa<RecordDecl>(TmpD))
>> +          DiagnoseUnusedDecl(cast<NamedDecl>(TmpD), DC);
>>
>> This would make more sense as a layer between ActOnPopScope and
>> DiagnoseUnusedDecl.
>>
>
> Done.
>
>
>> A comment explaining why we only do this within a function or method
>> would help ("within a function or method, we defer these checks until the
>> end of the surrounding scope").
>>
>> Also, this isn't necessarily correct in C++14, since a local type's
>> members can be first used once we've already left the function:
>>
>>   auto f() {
>>     struct S { typedef int t; };
>>     return S();
>>   }
>>   auto x = f();
>>   decltype(x)::t y;
>>
>> What happens if the local type is used as an argument to a function
>> template, which in turn uses the typedef? Can we really diagnose this
>> before end-of-TU?
>>
>
> Done.
>
>
>>  +  else if (isa<TypedefDecl>(D))  // TODO: Warn on unused c++11 aliases
>> too?
>> +    DiagID = diag::warn_unused_local_typedefs;
>>
>> Yes, please change this to TypedefNameDecl (throughout the patch). It
>> doesn't make sense to handle these differently, since one can be a
>> redeclaration of the other.
>>
>
> Done.
>
>
>>
>> Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
>> ===================================================================
>>  --- lib/Sema/SemaTemplateInstantiateDecl.cpp (revision 207920)
>> +++ lib/Sema/SemaTemplateInstantiateDecl.cpp (working copy)
>> @@ -3651,7 +3651,7 @@
>>    if (!NewVar->isInvalidDecl() &&
>>        NewVar->getDeclContext()->isFunctionOrMethod() &&
>> !NewVar->isUsed() &&
>>        OldVar->getType()->isDependentType())
>> -    DiagnoseUnusedDecl(NewVar);
>> +    DiagnoseUnusedDecl(NewVar, NewVar->getDeclContext());
>>
>> Hmm, why do we diagnose unused decls from template instantiation at all?
>>
>
> r103362 added this, r133580 tweaked it some. Can you think of an example
> where a template-local typedef is unused only in some instantiations of
> that template?
>
>
>>
>> On Sat, May 3, 2014 at 10:37 PM, Nico Weber <thakis at chromium.org> wrote:
>>
>>> With one more setReferenced() call (in SemaCXXScopeSpec.cpp). This
>>> version has successfully compiled several thousand files of chromium
>>> (all that I've tried so far).
>>>
>>> On Sat, May 3, 2014 at 6:35 PM, Nico Weber <thakis at chromium.org> wrote:
>>> > About point 2: The patch currently incorrectly warns on the "v"
>>> typedef in:
>>> >
>>> >   template <class T> struct V { typedef int iterator; };
>>> >   void f() {
>>> >     typedef V<int> v;
>>> >     v::iterator it;
>>> >   }
>>> >
>>> > So there's at least one more place where I need to insert a
>>> > setReferenced() I'll send an updated version once I found where, but
>>> > I'm thankful for comments on the overall approach in the meantime too.
>>> >
>>> > On Sat, May 3, 2014 at 6:08 PM, Nico Weber <thakis at chromium.org>
>>> wrote:
>>> >> (Forgot to say: This is also PR18265.)
>>> >>
>>> >> On Sat, May 3, 2014 at 6:07 PM, Nico Weber <thakis at chromium.org>
>>> wrote:
>>> >>> Hi,
>>> >>>
>>> >>> gcc has a warning -Wunused-local-typedefs that warns on unused local
>>> >>> typedefs. Inspired by r207870, I thought it'd be nice if clang had
>>> >>> this warning too. This warning requires the following three things:
>>> >>>
>>> >>>
>>> >>> 1.) A bit to track if a typedef has been referenced.
>>> >>>
>>> >>> Decl already has isUsed and isReferenced, but they don't seem to be
>>> >>> used for TypedefDecls, so I'm just using isReferenced on TypedefDecls
>>> >>> for this.
>>> >>>
>>> >>>
>>> >>> 2.) Setting that bit on typedefs that are used.
>>> >>>
>>> >>> The three strategies I can think of:
>>> >>> a.) Do this in Sema::DiagnoseUseOfDecl(), this seems to be already
>>> >>> called for decls all over the place, and an "if isa<TypedefDecl>(D)
>>> >>> D->setReferenced()" seems to do the trick.
>>> >>> b.) Do this in LookupName
>>> >>> c.) Do this explicitly in the places where it's actually necessary.
>>> >>>
>>> >>> The attached patch goes with the last approach. The three places I
>>> >>> found where it's needed are Sema::getTypeName(),
>>> Sema::ClassifyName(),
>>> >>> and Sema::LookupInlineAsmField(). The first two are called by the
>>> >>> parser for almost everything, while the third is called for
>>> references
>>> >>> to structs from MS inline assembly. That last one is a bit scary as
>>> it
>>> >>> means it's possible that there are other places this is needed that I
>>> >>> missed.
>>> >>>
>>> >>>
>>> >>> 3.) A way to check all typedefs in a scope when that scope ends.
>>> >>>
>>> >>> I added this to Sema::ActOnPopScope() which is where the
>>> >>> unused-variable and unused-label warnings are created. This works
>>> >>> well, but to catch the unused local typedef in
>>> >>>
>>> >>>   {
>>> >>>     struct A {
>>> >>>       struct B { typedef int SoDeep; };
>>> >>>     };
>>> >>>   }
>>> >>>
>>> >>> it means that that code now needs to recurse into all RecordDecl in a
>>> >>> scope, which it didn't need to do previously.
>>> >>>
>>> >>>
>>> >>> Let me know how badly I've chosen in each instance :-)
>>> >>>
>>> >>> Thanks,
>>> >>> Nico
>>> >>>
>>> >>> ps: If this goes in, a follow-up question is if this should warn on
>>> >>> unused C++11 type aliases too – it'd just require
>>> >>> s/TypedefDecl/TypedefNameDecl/ in two places in the patch, so it
>>> >>> should be an easy follow-up.
>>>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20140727/da556776/attachment.html>
-------------- next part --------------
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td	(revision 214046)
+++ include/clang/Basic/DiagnosticGroups.td	(working copy)
@@ -401,6 +401,7 @@
 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">;
@@ -523,7 +524,7 @@
                        [UnusedArgument, UnusedFunction, UnusedLabel,
                         // UnusedParameter, (matches GCC's behavior)
                         // UnusedMemberFunction, (clean-up llvm before enabling)
-                        UnusedPrivateField,
+                        UnusedPrivateField, UnusedLocalTypedef,
                         UnusedValue, UnusedVariable, UnusedPropertyIvar]>,
                         DiagCategory<"Unused Entity Issue">;
 
@@ -617,6 +618,8 @@
                 [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: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td	(revision 214046)
+++ include/clang/Basic/DiagnosticSemaKinds.td	(working copy)
@@ -175,6 +175,9 @@
   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: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h	(revision 214046)
+++ include/clang/Sema/Sema.h	(working copy)
@@ -385,16 +385,18 @@
   /// FieldCollector - Collects CXXFieldDecls during parsing of C++ classes.
   std::unique_ptr<CXXFieldCollector> FieldCollector;
 
+  /// \brief Set containing all declared private fields that are not used.
   typedef llvm::SmallSetVector<const NamedDecl*, 16> NamedDeclSetType;
-
-  /// \brief Set containing all declared private fields that are not used.
   NamedDeclSetType UnusedPrivateFields;
 
-  typedef llvm::SmallPtrSet<const CXXRecordDecl*, 8> RecordDeclSetTy;
+  /// \brief Set containing all typedefs that are likely unused.
+  llvm::SmallSetVector<const TypedefNameDecl *, 4>
+      UnusedLocalTypedefNameCandidates;
 
   /// PureVirtualClassDiagSet - a set of class declarations which we have
   /// emitted a list of pure virtual functions. Used to prevent emitting the
   /// same list more than once.
+  typedef llvm::SmallPtrSet<const CXXRecordDecl*, 8> RecordDeclSetTy;
   std::unique_ptr<RecordDeclSetTy> PureVirtualClassDiagSet;
 
   /// ParsingInitForAutoVars - a set of declarations with auto types for which
@@ -1392,8 +1394,8 @@
 
   /// List of decls defined in a function prototype. This contains EnumConstants
   /// that incorrectly end up in translation unit scope because there is no
-  /// function to pin them on. ActOnFunctionDeclarator reads this list and patches
-  /// them into the FunctionDecl.
+  /// function to pin them on. ActOnFunctionDeclarator reads this list and
+  /// patches them into the FunctionDecl.
   std::vector<NamedDecl*> DeclsInPrototypeScope;
 
   DeclGroupPtrTy ConvertDeclToDeclGroup(Decl *Ptr, Decl *OwnedType = nullptr);
@@ -3187,7 +3189,7 @@
   /// DiagnoseUnusedExprResult - If the statement passed in is an expression
   /// whose result is unused, warn.
   void DiagnoseUnusedExprResult(const Stmt *S);
-  void DiagnoseUnusedDecl(const NamedDecl *ND);
+  void DiagnoseUnusedDecl(const NamedDecl *ND, bool WithinFunction);
 
   /// Emit \p DiagID if statement located on \p StmtLoc has a suspicious null
   /// statement as a \p Body, and it is located on the same line.
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp	(revision 214046)
+++ lib/Sema/Sema.cpp	(working copy)
@@ -823,6 +823,15 @@
     checkUndefinedButUsed(*this);
   }
 
+  if (!Diags.isIgnored(diag::warn_unused_local_typedef, SourceLocation())) {
+    for (const TypedefNameDecl *TD : UnusedLocalTypedefNameCandidates) {
+      if (TD->isReferenced())
+        continue;
+      Diag(TD->getLocation(), diag::warn_unused_local_typedef)
+          << isa<TypeAliasDecl>(TD) << TD->getDeclName();
+    }
+  }
+
   if (!Diags.isIgnored(diag::warn_unused_private_field, SourceLocation())) {
     RecordCompleteMap RecordsComplete;
     RecordCompleteMap MNCComplete;
Index: lib/Sema/SemaCXXScopeSpec.cpp
===================================================================
--- lib/Sema/SemaCXXScopeSpec.cpp	(revision 214046)
+++ lib/Sema/SemaCXXScopeSpec.cpp	(working copy)
@@ -612,6 +612,9 @@
        }
     }
 
+    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: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp	(revision 214046)
+++ lib/Sema/SemaDecl.cpp	(working copy)
@@ -377,6 +377,7 @@
     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
@@ -925,6 +926,7 @@
   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);
@@ -1382,7 +1384,7 @@
     UnusedFileScopedDecls.push_back(D);
 }
 
-static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D) {
+static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D, bool WithinFunction) {
   if (D->isInvalidDecl())
     return false;
 
@@ -1392,10 +1394,17 @@
 
   if (isa<LabelDecl>(D))
     return true;
+
+  // Except for labels, we only care about unused decls that are local to
+  // functions.
+  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.
@@ -1458,11 +1467,31 @@
   return;
 }
 
+static void DiagnoseUnusedNestedTypedefs(Sema &S, const RecordDecl *D,
+                                          bool WithinFunction) {
+  if (D->getTypeForDecl()->isDependentType())
+    return;
+
+  for (auto *TmpD : D->decls()) {
+    if (const auto* T = dyn_cast<TypedefNameDecl>(TmpD))
+      S.DiagnoseUnusedDecl(T, WithinFunction);
+    else if(const auto *R = dyn_cast<RecordDecl>(TmpD))
+        DiagnoseUnusedNestedTypedefs(S, R, WithinFunction);
+  }
+}
+
 /// DiagnoseUnusedDecl - Emit warnings about declarations that are not used
 /// unless they are marked attr(unused).
-void Sema::DiagnoseUnusedDecl(const NamedDecl *D) {
-  if (!ShouldDiagnoseUnusedDecl(D))
+void Sema::DiagnoseUnusedDecl(const NamedDecl *D, bool WithinFunction) {
+  if (!ShouldDiagnoseUnusedDecl(D, WithinFunction))
     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);
@@ -1502,8 +1531,12 @@
     if (!D->getDeclName()) continue;
 
     // Diagnose unused variables in this scope.
-    if (!S->hasUnrecoverableErrorOccurred())
-      DiagnoseUnusedDecl(D);
+    if (!S->hasUnrecoverableErrorOccurred()) {
+      bool WithinFunction = D->getDeclContext()->isFunctionOrMethod();
+      DiagnoseUnusedDecl(D, WithinFunction);
+      if (const auto *RD = dyn_cast<RecordDecl>(D))
+        DiagnoseUnusedNestedTypedefs(*this, RD, WithinFunction);
+    }
     
     // If this was a forward reference to a label, verify it was defined.
     if (LabelDecl *LD = dyn_cast<LabelDecl>(D))
Index: lib/Sema/SemaStmtAsm.cpp
===================================================================
--- lib/Sema/SemaStmtAsm.cpp	(revision 214046)
+++ lib/Sema/SemaStmtAsm.cpp	(working copy)
@@ -443,8 +443,10 @@
   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))
     RT = TD->getTypeForDecl()->getAs<RecordType>();
   if (!RT)
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp	(revision 214046)
+++ lib/Sema/SemaTemplate.cpp	(working copy)
@@ -7941,6 +7941,7 @@
     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: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp	(revision 214046)
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp	(working copy)
@@ -2389,12 +2389,10 @@
   // declaration (for instance, if there was a prior implicit instantiation).
   bool Ignored;
   if (PrevDecl &&
-      SemaRef.CheckSpecializationInstantiationRedecl(D->getLocation(),
-                                                     D->getSpecializationKind(),
-                                                     PrevDecl,
-                                                     PrevDecl->getSpecializationKind(),
-                                                     PrevDecl->getPointOfInstantiation(),
-                                                     Ignored))
+      SemaRef.CheckSpecializationInstantiationRedecl(
+          D->getLocation(), D->getSpecializationKind(), PrevDecl,
+          PrevDecl->getSpecializationKind(),
+          PrevDecl->getPointOfInstantiation(), Ignored))
     return nullptr;
 
   // If PrevDecl was a definition and D is also a definition, diagnose.
@@ -3653,7 +3651,7 @@
   if (!NewVar->isInvalidDecl() &&
       NewVar->getDeclContext()->isFunctionOrMethod() && !NewVar->isUsed() &&
       OldVar->getType()->isDependentType())
-    DiagnoseUnusedDecl(NewVar);
+    DiagnoseUnusedDecl(NewVar, NewVar->getDeclContext()->isFunctionOrMethod());
 }
 
 /// \brief Instantiate the initializer of a variable.
Index: test/Misc/ast-dump-color.cpp
===================================================================
--- test/Misc/ast-dump-color.cpp	(revision 214046)
+++ test/Misc/ast-dump-color.cpp	(working copy)
@@ -89,7 +89,7 @@
 //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: test/SemaCXX/implicit-exception-spec.cpp
===================================================================
--- test/SemaCXX/implicit-exception-spec.cpp	(revision 214046)
+++ test/SemaCXX/implicit-exception-spec.cpp	(working copy)
@@ -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: test/SemaCXX/warn-unused-filescoped.cpp
===================================================================
--- test/SemaCXX/warn-unused-filescoped.cpp	(revision 214046)
+++ test/SemaCXX/warn-unused-filescoped.cpp	(working copy)
@@ -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
 
Index: test/SemaCXX/warn-unused-local-typedefs.cpp
===================================================================
--- test/SemaCXX/warn-unused-local-typedefs.cpp	(revision 0)
+++ test/SemaCXX/warn-unused-local-typedefs.cpp	(working copy)
@@ -0,0 +1,133 @@
+// RUN: %clang_cc1 -fsyntax-only -Wunused-local-typedefs -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;
+
+  // FIXME: typedef that's only used in a constexpr
+}
+
+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));
+}
+
+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
+
+    // FIXME: It'd be nice if this got a diag. The problem is that S2 is a
+    // dependent type when the template is parsed, so it's not diagnosed at
+    // that time, and we don't go back and emit warnings at instantiation time.
+    typedef int Foo2;
+  };
+
+  typename S2::Foo s2foo;
+  typename T::Foo s3foo;
+}
+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
+}
+
+// Local typedefs can be used after the scope they were in has closed:
+auto sneaky() {
+  struct S { typedef int t; };
+  return S();
+}
+auto x = sneaky();
+decltype(x)::t y;


More information about the cfe-commits mailing list