diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 1656240bc4ee..0473d1ca1f75 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -227,6 +227,12 @@ private: /// required. unsigned Used : 1; + /// \brief Whether this declaration was "referenced". + /// The difference with 'Used' is whether the reference appears in a + /// evaluated context or not, e.g. functions used in uninstantiated templates + /// are regarded as "referenced" but not "used". + unsigned Referenced : 1; + protected: /// Access - Used by C++ decls for the access specifier. // NOTE: VC++ treats enums as signed, avoid using the AccessSpecifier enum @@ -261,7 +267,7 @@ protected: Decl(Kind DK, DeclContext *DC, SourceLocation L) : NextDeclInContext(0), DeclCtx(DC), Loc(L), DeclKind(DK), InvalidDecl(0), - HasAttrs(false), Implicit(false), Used(false), + HasAttrs(false), Implicit(false), Used(false), Referenced(false), Access(AS_none), PCHLevel(0), ChangedAfterLoad(false), IdentifierNamespace(getIdentifierNamespaceForKind(DK)), HasCachedLinkage(0) @@ -271,7 +277,7 @@ protected: Decl(Kind DK, EmptyShell Empty) : NextDeclInContext(0), DeclKind(DK), InvalidDecl(0), - HasAttrs(false), Implicit(false), Used(false), + HasAttrs(false), Implicit(false), Used(false), Referenced(false), Access(AS_none), PCHLevel(0), ChangedAfterLoad(false), IdentifierNamespace(getIdentifierNamespaceForKind(DK)), HasCachedLinkage(0) @@ -408,6 +414,11 @@ public: void setUsed(bool U = true) { Used = U; } + /// \brief Whether this declaration was referenced. + bool isReferenced() const; + + void setReferenced(bool R = true) { Referenced = R; } + /// \brief Determine the availability of the given declaration. /// /// This routine will determine the most restrictive availability of diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index c8465508c8b6..15f3dbd4640d 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -154,6 +154,8 @@ def UnusedLabel : DiagGroup<"unused-label">; def UnusedParameter : DiagGroup<"unused-parameter">; def UnusedValue : DiagGroup<"unused-value">; def UnusedVariable : DiagGroup<"unused-variable">; +def UnneededInternalDecl : DiagGroup<"unneeded-internal-declaration">; +def UnneededMemberFunction : DiagGroup<"unneeded-member-function">; def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">; def ReadOnlySetterAttrs : DiagGroup<"readonly-setter-attrs">; def Reorder : DiagGroup<"reorder">; @@ -198,11 +200,16 @@ def Conversion : DiagGroup<"conversion", BoolConversions]>, DiagCategory<"Value Conversion Issue">; +def Unneeded : DiagGroup<"unneeded", + [UnneededInternalDecl + //,UnneededMemberFunction (clean-up llvm before enabling) + ]>; + def Unused : DiagGroup<"unused", [UnusedArgument, UnusedFunction, UnusedLabel, // UnusedParameter, (matches GCC's behavior) // UnusedMemberFunction, (clean-up llvm before enabling) - UnusedValue, UnusedVariable]>, + UnusedValue, UnusedVariable, Unneeded]>, DiagCategory<"Unused Entity Issue">; // Format settings. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3ca45d4cf35f..ce592a3c953d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -116,6 +116,12 @@ def warn_unused_member_function : Warning<"unused member function %0">, InGroup, DefaultIgnore; def warn_used_but_marked_unused: Warning<"%0 was marked unused but was used">, InGroup, DefaultIgnore; +def warn_unneeded_internal_decl : Warning< + "%select{function|variable}0 %1 is not needed and will not be emitted">, + InGroup, DefaultIgnore; +def warn_unneeded_member_function : Warning< + "member function %0 is not needed and will not be emitted">, + InGroup, DefaultIgnore; def warn_parameter_size: Warning< "%0 is a large (%1 bytes) pass-by-value argument; " diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 673b718ba0ed..41a036142504 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -242,6 +242,18 @@ bool Decl::isUsed(bool CheckUsedAttr) const { return false; } +bool Decl::isReferenced() const { + if (Referenced) + return true; + + // Check redeclarations. + for (redecl_iterator I = redecls_begin(), E = redecls_end(); I != E; ++I) + if (I->Referenced) + return true; + + return false; +} + /// \brief Determine the availability of the given declaration based on /// the target platform. /// diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index a83722d82452..403cf6246cfe 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -473,16 +473,30 @@ void Sema::ActOnEndOfTranslationUnit() { DiagD = FD; if (DiagD->isDeleted()) continue; // Deleted functions are supposed to be unused. - Diag(DiagD->getLocation(), - isa(DiagD) ? diag::warn_unused_member_function - : diag::warn_unused_function) - << DiagD->getDeclName(); + if (DiagD->isReferenced()) { + if (isa(DiagD)) + Diag(DiagD->getLocation(), diag::warn_unneeded_member_function) + << DiagD->getDeclName(); + else + Diag(DiagD->getLocation(), diag::warn_unneeded_internal_decl) + << /*function*/0 << DiagD->getDeclName(); + } else { + Diag(DiagD->getLocation(), + isa(DiagD) ? diag::warn_unused_member_function + : diag::warn_unused_function) + << DiagD->getDeclName(); + } } else { const VarDecl *DiagD = cast(*I)->getDefinition(); if (!DiagD) DiagD = cast(*I); - Diag(DiagD->getLocation(), diag::warn_unused_variable) - << DiagD->getDeclName(); + if (DiagD->isReferenced()) { + Diag(DiagD->getLocation(), diag::warn_unneeded_internal_decl) + << /*variable*/1 << DiagD->getDeclName(); + } else { + Diag(DiagD->getLocation(), diag::warn_unused_variable) + << DiagD->getDeclName(); + } } } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 9415673a7747..8eb0177046da 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -9885,6 +9885,8 @@ Sema::PopExpressionEvaluationContext() { void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) { assert(D && "No declaration?"); + D->setReferenced(); + if (D->isUsed(false)) return; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 3fe5fd5c9528..0ff7ff4d4064 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -298,8 +298,10 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { Var->setAccess(D->getAccess()); - if (!D->isStaticDataMember()) + if (!D->isStaticDataMember()) { Var->setUsed(D->isUsed(false)); + Var->setReferenced(D->isReferenced()); + } // FIXME: In theory, we could have a previous declaration for variables that // are not static data members. diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 5b4a64eeb5c9..889ef7324c83 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -214,6 +214,7 @@ void ASTDeclReader::VisitDecl(Decl *D) { } D->setImplicit(Record[Idx++]); D->setUsed(Record[Idx++]); + D->setReferenced(Record[Idx++]); D->setAccess((AccessSpecifier)Record[Idx++]); D->setPCHLevel(Record[Idx++] + (F.Type <= ASTReader::PCH)); } diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 1306ffda6dd1..b3b55f584aec 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -141,6 +141,7 @@ void ASTDeclWriter::VisitDecl(Decl *D) { Writer.WriteAttributes(D->getAttrs(), Record); Record.push_back(D->isImplicit()); Record.push_back(D->isUsed(false)); + Record.push_back(D->isReferenced()); Record.push_back(D->getAccess()); Record.push_back(D->getPCHLevel()); } @@ -1133,6 +1134,7 @@ void ASTWriter::WriteDeclsBlockAbbrevs() { Abv->Add(BitCodeAbbrevOp(0)); // HasAttrs Abv->Add(BitCodeAbbrevOp(0)); // isImplicit Abv->Add(BitCodeAbbrevOp(0)); // isUsed + Abv->Add(BitCodeAbbrevOp(0)); // isReferenced Abv->Add(BitCodeAbbrevOp(AS_none)); // C++ AccessSpecifier Abv->Add(BitCodeAbbrevOp(0)); // PCH level diff --git a/clang/test/Sema/warn-unused-function.c b/clang/test/Sema/warn-unused-function.c index 5bbcf18a623d..8f4cdcba881e 100644 --- a/clang/test/Sema/warn-unused-function.c +++ b/clang/test/Sema/warn-unused-function.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -Wunused-function -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wunused-function -Wunneeded-internal-declaration -verify %s // RUN: %clang_cc1 -fsyntax-only -verify -Wunused %s // RUN: %clang_cc1 -fsyntax-only -verify -Wall %s @@ -6,7 +6,7 @@ void foo() {} static void f2() {} static void f1() {f2();} // expected-warning{{unused}} -static int f0() { return 17; } // expected-warning{{unused}} +static int f0() { return 17; } // expected-warning{{not needed and will not be emitted}} int x = sizeof(f0()); static void f3(); @@ -46,7 +46,7 @@ static void f12(void) { } // expected-warning{{unused}} static void f12(void); // PR7923 -static void unused(void) { unused(); } // expected-warning{{unused}} +static void unused(void) { unused(); } // expected-warning{{not needed and will not be emitted}} // rdar://8728293 static void cleanupMalloc(char * const * const allocation) { } diff --git a/clang/test/SemaCXX/warn-unused-filescoped.cpp b/clang/test/SemaCXX/warn-unused-filescoped.cpp index c32acb0b48be..dbff4b0e68c1 100644 --- a/clang/test/SemaCXX/warn-unused-filescoped.cpp +++ b/clang/test/SemaCXX/warn-unused-filescoped.cpp @@ -78,3 +78,12 @@ namespace test4 { void test(A a); // expected-warning {{unused function}} extern "C" void test4(A a); } + +namespace rdar8733476 { + static void foo() { } // expected-warning {{not needed and will not be emitted}} + + template + void bar() { + foo(); + } +}