[patch] Implementing -Wunused-local-typedefs
Nico Weber
thakis at chromium.org
Sat May 3 18:07:03 PDT 2014
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 --------------
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td (revision 207920)
+++ include/clang/Basic/DiagnosticGroups.td (working copy)
@@ -382,6 +382,7 @@
def UnusedConstVariable : DiagGroup<"unused-const-variable">;
def UnusedVariable : DiagGroup<"unused-variable",
[UnusedConstVariable]>;
+def UnusedLocalTypedefs : DiagGroup<"unused-local-typedefs">;
def UnusedPropertyIvar : DiagGroup<"unused-property-ivar">;
def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">;
def UserDefinedLiterals : DiagGroup<"user-defined-literals">;
@@ -502,7 +503,7 @@
[UnusedArgument, UnusedFunction, UnusedLabel,
// UnusedParameter, (matches GCC's behavior)
// UnusedMemberFunction, (clean-up llvm before enabling)
- UnusedPrivateField,
+ UnusedPrivateField, UnusedLocalTypedefs,
UnusedValue, UnusedVariable, UnusedPropertyIvar]>,
DiagCategory<"Unused Entity Issue">;
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td (revision 207920)
+++ include/clang/Basic/DiagnosticSemaKinds.td (working copy)
@@ -167,6 +167,8 @@
InGroup<UnusedParameter>, DefaultIgnore;
def warn_unused_variable : Warning<"unused variable %0">,
InGroup<UnusedVariable>, DefaultIgnore;
+def warn_unused_local_typedefs : Warning<"unused typedef %0">,
+ InGroup<UnusedLocalTypedefs>, 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 207920)
+++ include/clang/Sema/Sema.h (working copy)
@@ -3158,7 +3158,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, const DeclContext *DC);
/// 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/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp (revision 207922)
+++ lib/Sema/SemaDecl.cpp (working copy)
@@ -310,6 +310,7 @@
DiagnoseUseOfDecl(IIDecl, NameLoc);
T = Context.getTypeDeclType(TD);
+ TD->setReferenced();
// NOTE: avoid constructing an ElaboratedType(Loc) if this is a
// constructor or destructor name (in such a case, the scope specifier
@@ -813,6 +814,7 @@
NamedDecl *FirstDecl = (*Result.begin())->getUnderlyingDecl();
if (TypeDecl *Type = dyn_cast<TypeDecl>(FirstDecl)) {
DiagnoseUseOfDecl(Type, NameLoc);
+ Type->setReferenced();
QualType T = Context.getTypeDeclType(Type);
if (SS.isNotEmpty())
return buildNestedType(*this, SS, T, NameLoc);
@@ -1268,7 +1270,8 @@
UnusedFileScopedDecls.push_back(D);
}
-static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D) {
+static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D,
+ const DeclContext *DC) {
if (D->isInvalidDecl())
return false;
@@ -1278,10 +1281,15 @@
if (isa<LabelDecl>(D))
return true;
+
+ if (!DC->isFunctionOrMethod())
+ return false;
+
+ if (isa<TypedefDecl>(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.
@@ -1346,8 +1354,14 @@
/// 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, 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);
+
+ if (!ShouldDiagnoseUnusedDecl(D, DC))
return;
FixItHint Hint;
@@ -1358,6 +1372,8 @@
DiagID = diag::warn_unused_exception_param;
else if (isa<LabelDecl>(D))
DiagID = diag::warn_unused_label;
+ else if (isa<TypedefDecl>(D)) // TODO: Warn on unused c++11 aliases too?
+ DiagID = diag::warn_unused_local_typedefs;
else
DiagID = diag::warn_unused_variable;
@@ -1389,7 +1405,7 @@
// Diagnose unused variables in this scope.
if (!S->hasUnrecoverableErrorOccurred())
- DiagnoseUnusedDecl(D);
+ DiagnoseUnusedDecl(D, D->getDeclContext());
// 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 207920)
+++ lib/Sema/SemaStmtAsm.cpp (working copy)
@@ -441,8 +441,10 @@
NamedDecl *FoundDecl = BaseResult.getFoundDecl();
if (VarDecl *VD = dyn_cast<VarDecl>(FoundDecl))
RT = VD->getType()->getAs<RecordType>();
- else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(FoundDecl))
+ else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(FoundDecl)) {
+ TD->setReferenced();
RT = TD->getUnderlyingType()->getAs<RecordType>();
+ }
if (!RT)
return true;
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());
}
/// \brief Instantiate the initializer of a variable.
Index: test/SemaCXX/implicit-exception-spec.cpp
===================================================================
--- test/SemaCXX/implicit-exception-spec.cpp (revision 207920)
+++ 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 207920)
+++ 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,93 @@
+// RUN: %clang_cc1 -Wunused-local-typedefs -fasm-blocks -verify -std=c++11 %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'}}
+
+ 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, 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 cint; // no diag
+ constexpr int kFoo = (int)(cint)5;
+
+ typedef struct {
+ int a;
+ int b;
+ } A; // no diag
+ __asm mov eax, [eax].A.b
+}
+
+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<typename T>
+void g() {
+ typedef T foo; // expected-warning{{unused typedef 'foo'}}
+}
+
+void t5() {
+ class A {
+ };
+ typedef A tA;
+ typedef A tA2; // expected-warning{{unused typedef 'tA2'}}
+ class B {
+ friend tA;
+ };
+}
More information about the cfe-commits
mailing list