forked from OSchip/llvm-project
We regard a function as 'unused' from the codegen perspective, so our warnings diverge from
gcc's unused warnings which don't get emitted if the function is referenced even in an unevaluated context (e.g. in templates, sizeof, etc.). Also, saying that a function is 'unused' because it won't get codegen'ed is somewhat misleading. - Don't emit 'unused' warnings for functions that are referenced in any part of the user's code. - A warning that an internal function/variable won't get emitted is useful though, so introduce -Wunneeded-internal-declaration which will warn if a function/variable with internal linkage is not "needed" ('used' from the codegen perspective), e.g: static void foo() { } template <int> void bar() { foo(); } test.cpp:1:13: warning: function 'foo' is not needed and will not be emitted static void foo() { } ^ Addresses rdar://8733476. llvm-svn: 129794
This commit is contained in:
parent
aedbe0f347
commit
1618023018
|
@ -227,6 +227,12 @@ private:
|
||||||
/// required.
|
/// required.
|
||||||
unsigned Used : 1;
|
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:
|
protected:
|
||||||
/// Access - Used by C++ decls for the access specifier.
|
/// Access - Used by C++ decls for the access specifier.
|
||||||
// NOTE: VC++ treats enums as signed, avoid using the AccessSpecifier enum
|
// NOTE: VC++ treats enums as signed, avoid using the AccessSpecifier enum
|
||||||
|
@ -261,7 +267,7 @@ protected:
|
||||||
Decl(Kind DK, DeclContext *DC, SourceLocation L)
|
Decl(Kind DK, DeclContext *DC, SourceLocation L)
|
||||||
: NextDeclInContext(0), DeclCtx(DC),
|
: NextDeclInContext(0), DeclCtx(DC),
|
||||||
Loc(L), DeclKind(DK), InvalidDecl(0),
|
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),
|
Access(AS_none), PCHLevel(0), ChangedAfterLoad(false),
|
||||||
IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
|
IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
|
||||||
HasCachedLinkage(0)
|
HasCachedLinkage(0)
|
||||||
|
@ -271,7 +277,7 @@ protected:
|
||||||
|
|
||||||
Decl(Kind DK, EmptyShell Empty)
|
Decl(Kind DK, EmptyShell Empty)
|
||||||
: NextDeclInContext(0), DeclKind(DK), InvalidDecl(0),
|
: 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),
|
Access(AS_none), PCHLevel(0), ChangedAfterLoad(false),
|
||||||
IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
|
IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
|
||||||
HasCachedLinkage(0)
|
HasCachedLinkage(0)
|
||||||
|
@ -408,6 +414,11 @@ public:
|
||||||
|
|
||||||
void setUsed(bool U = true) { Used = U; }
|
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.
|
/// \brief Determine the availability of the given declaration.
|
||||||
///
|
///
|
||||||
/// This routine will determine the most restrictive availability of
|
/// This routine will determine the most restrictive availability of
|
||||||
|
|
|
@ -154,6 +154,8 @@ def UnusedLabel : DiagGroup<"unused-label">;
|
||||||
def UnusedParameter : DiagGroup<"unused-parameter">;
|
def UnusedParameter : DiagGroup<"unused-parameter">;
|
||||||
def UnusedValue : DiagGroup<"unused-value">;
|
def UnusedValue : DiagGroup<"unused-value">;
|
||||||
def UnusedVariable : DiagGroup<"unused-variable">;
|
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 UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">;
|
||||||
def ReadOnlySetterAttrs : DiagGroup<"readonly-setter-attrs">;
|
def ReadOnlySetterAttrs : DiagGroup<"readonly-setter-attrs">;
|
||||||
def Reorder : DiagGroup<"reorder">;
|
def Reorder : DiagGroup<"reorder">;
|
||||||
|
@ -198,11 +200,16 @@ def Conversion : DiagGroup<"conversion",
|
||||||
BoolConversions]>,
|
BoolConversions]>,
|
||||||
DiagCategory<"Value Conversion Issue">;
|
DiagCategory<"Value Conversion Issue">;
|
||||||
|
|
||||||
|
def Unneeded : DiagGroup<"unneeded",
|
||||||
|
[UnneededInternalDecl
|
||||||
|
//,UnneededMemberFunction (clean-up llvm before enabling)
|
||||||
|
]>;
|
||||||
|
|
||||||
def Unused : DiagGroup<"unused",
|
def Unused : DiagGroup<"unused",
|
||||||
[UnusedArgument, UnusedFunction, UnusedLabel,
|
[UnusedArgument, UnusedFunction, UnusedLabel,
|
||||||
// UnusedParameter, (matches GCC's behavior)
|
// UnusedParameter, (matches GCC's behavior)
|
||||||
// UnusedMemberFunction, (clean-up llvm before enabling)
|
// UnusedMemberFunction, (clean-up llvm before enabling)
|
||||||
UnusedValue, UnusedVariable]>,
|
UnusedValue, UnusedVariable, Unneeded]>,
|
||||||
DiagCategory<"Unused Entity Issue">;
|
DiagCategory<"Unused Entity Issue">;
|
||||||
|
|
||||||
// Format settings.
|
// Format settings.
|
||||||
|
|
|
@ -116,6 +116,12 @@ def warn_unused_member_function : Warning<"unused member function %0">,
|
||||||
InGroup<UnusedMemberFunction>, DefaultIgnore;
|
InGroup<UnusedMemberFunction>, DefaultIgnore;
|
||||||
def warn_used_but_marked_unused: Warning<"%0 was marked unused but was used">,
|
def warn_used_but_marked_unused: Warning<"%0 was marked unused but was used">,
|
||||||
InGroup<UsedButMarkedUnused>, DefaultIgnore;
|
InGroup<UsedButMarkedUnused>, DefaultIgnore;
|
||||||
|
def warn_unneeded_internal_decl : Warning<
|
||||||
|
"%select{function|variable}0 %1 is not needed and will not be emitted">,
|
||||||
|
InGroup<UnneededInternalDecl>, DefaultIgnore;
|
||||||
|
def warn_unneeded_member_function : Warning<
|
||||||
|
"member function %0 is not needed and will not be emitted">,
|
||||||
|
InGroup<UnneededMemberFunction>, DefaultIgnore;
|
||||||
|
|
||||||
def warn_parameter_size: Warning<
|
def warn_parameter_size: Warning<
|
||||||
"%0 is a large (%1 bytes) pass-by-value argument; "
|
"%0 is a large (%1 bytes) pass-by-value argument; "
|
||||||
|
|
|
@ -242,6 +242,18 @@ bool Decl::isUsed(bool CheckUsedAttr) const {
|
||||||
return false;
|
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
|
/// \brief Determine the availability of the given declaration based on
|
||||||
/// the target platform.
|
/// the target platform.
|
||||||
///
|
///
|
||||||
|
|
|
@ -473,16 +473,30 @@ void Sema::ActOnEndOfTranslationUnit() {
|
||||||
DiagD = FD;
|
DiagD = FD;
|
||||||
if (DiagD->isDeleted())
|
if (DiagD->isDeleted())
|
||||||
continue; // Deleted functions are supposed to be unused.
|
continue; // Deleted functions are supposed to be unused.
|
||||||
Diag(DiagD->getLocation(),
|
if (DiagD->isReferenced()) {
|
||||||
isa<CXXMethodDecl>(DiagD) ? diag::warn_unused_member_function
|
if (isa<CXXMethodDecl>(DiagD))
|
||||||
: diag::warn_unused_function)
|
Diag(DiagD->getLocation(), diag::warn_unneeded_member_function)
|
||||||
<< DiagD->getDeclName();
|
<< DiagD->getDeclName();
|
||||||
|
else
|
||||||
|
Diag(DiagD->getLocation(), diag::warn_unneeded_internal_decl)
|
||||||
|
<< /*function*/0 << DiagD->getDeclName();
|
||||||
|
} else {
|
||||||
|
Diag(DiagD->getLocation(),
|
||||||
|
isa<CXXMethodDecl>(DiagD) ? diag::warn_unused_member_function
|
||||||
|
: diag::warn_unused_function)
|
||||||
|
<< DiagD->getDeclName();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const VarDecl *DiagD = cast<VarDecl>(*I)->getDefinition();
|
const VarDecl *DiagD = cast<VarDecl>(*I)->getDefinition();
|
||||||
if (!DiagD)
|
if (!DiagD)
|
||||||
DiagD = cast<VarDecl>(*I);
|
DiagD = cast<VarDecl>(*I);
|
||||||
Diag(DiagD->getLocation(), diag::warn_unused_variable)
|
if (DiagD->isReferenced()) {
|
||||||
<< DiagD->getDeclName();
|
Diag(DiagD->getLocation(), diag::warn_unneeded_internal_decl)
|
||||||
|
<< /*variable*/1 << DiagD->getDeclName();
|
||||||
|
} else {
|
||||||
|
Diag(DiagD->getLocation(), diag::warn_unused_variable)
|
||||||
|
<< DiagD->getDeclName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9885,6 +9885,8 @@ Sema::PopExpressionEvaluationContext() {
|
||||||
void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) {
|
void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) {
|
||||||
assert(D && "No declaration?");
|
assert(D && "No declaration?");
|
||||||
|
|
||||||
|
D->setReferenced();
|
||||||
|
|
||||||
if (D->isUsed(false))
|
if (D->isUsed(false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -298,8 +298,10 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
|
||||||
|
|
||||||
Var->setAccess(D->getAccess());
|
Var->setAccess(D->getAccess());
|
||||||
|
|
||||||
if (!D->isStaticDataMember())
|
if (!D->isStaticDataMember()) {
|
||||||
Var->setUsed(D->isUsed(false));
|
Var->setUsed(D->isUsed(false));
|
||||||
|
Var->setReferenced(D->isReferenced());
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: In theory, we could have a previous declaration for variables that
|
// FIXME: In theory, we could have a previous declaration for variables that
|
||||||
// are not static data members.
|
// are not static data members.
|
||||||
|
|
|
@ -214,6 +214,7 @@ void ASTDeclReader::VisitDecl(Decl *D) {
|
||||||
}
|
}
|
||||||
D->setImplicit(Record[Idx++]);
|
D->setImplicit(Record[Idx++]);
|
||||||
D->setUsed(Record[Idx++]);
|
D->setUsed(Record[Idx++]);
|
||||||
|
D->setReferenced(Record[Idx++]);
|
||||||
D->setAccess((AccessSpecifier)Record[Idx++]);
|
D->setAccess((AccessSpecifier)Record[Idx++]);
|
||||||
D->setPCHLevel(Record[Idx++] + (F.Type <= ASTReader::PCH));
|
D->setPCHLevel(Record[Idx++] + (F.Type <= ASTReader::PCH));
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,7 @@ void ASTDeclWriter::VisitDecl(Decl *D) {
|
||||||
Writer.WriteAttributes(D->getAttrs(), Record);
|
Writer.WriteAttributes(D->getAttrs(), Record);
|
||||||
Record.push_back(D->isImplicit());
|
Record.push_back(D->isImplicit());
|
||||||
Record.push_back(D->isUsed(false));
|
Record.push_back(D->isUsed(false));
|
||||||
|
Record.push_back(D->isReferenced());
|
||||||
Record.push_back(D->getAccess());
|
Record.push_back(D->getAccess());
|
||||||
Record.push_back(D->getPCHLevel());
|
Record.push_back(D->getPCHLevel());
|
||||||
}
|
}
|
||||||
|
@ -1133,6 +1134,7 @@ void ASTWriter::WriteDeclsBlockAbbrevs() {
|
||||||
Abv->Add(BitCodeAbbrevOp(0)); // HasAttrs
|
Abv->Add(BitCodeAbbrevOp(0)); // HasAttrs
|
||||||
Abv->Add(BitCodeAbbrevOp(0)); // isImplicit
|
Abv->Add(BitCodeAbbrevOp(0)); // isImplicit
|
||||||
Abv->Add(BitCodeAbbrevOp(0)); // isUsed
|
Abv->Add(BitCodeAbbrevOp(0)); // isUsed
|
||||||
|
Abv->Add(BitCodeAbbrevOp(0)); // isReferenced
|
||||||
Abv->Add(BitCodeAbbrevOp(AS_none)); // C++ AccessSpecifier
|
Abv->Add(BitCodeAbbrevOp(AS_none)); // C++ AccessSpecifier
|
||||||
Abv->Add(BitCodeAbbrevOp(0)); // PCH level
|
Abv->Add(BitCodeAbbrevOp(0)); // PCH level
|
||||||
|
|
||||||
|
|
|
@ -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 -Wunused %s
|
||||||
// RUN: %clang_cc1 -fsyntax-only -verify -Wall %s
|
// RUN: %clang_cc1 -fsyntax-only -verify -Wall %s
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ void foo() {}
|
||||||
static void f2() {}
|
static void f2() {}
|
||||||
static void f1() {f2();} // expected-warning{{unused}}
|
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());
|
int x = sizeof(f0());
|
||||||
|
|
||||||
static void f3();
|
static void f3();
|
||||||
|
@ -46,7 +46,7 @@ static void f12(void) { } // expected-warning{{unused}}
|
||||||
static void f12(void);
|
static void f12(void);
|
||||||
|
|
||||||
// PR7923
|
// 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
|
// rdar://8728293
|
||||||
static void cleanupMalloc(char * const * const allocation) { }
|
static void cleanupMalloc(char * const * const allocation) { }
|
||||||
|
|
|
@ -78,3 +78,12 @@ namespace test4 {
|
||||||
void test(A a); // expected-warning {{unused function}}
|
void test(A a); // expected-warning {{unused function}}
|
||||||
extern "C" void test4(A a);
|
extern "C" void test4(A a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace rdar8733476 {
|
||||||
|
static void foo() { } // expected-warning {{not needed and will not be emitted}}
|
||||||
|
|
||||||
|
template <int>
|
||||||
|
void bar() {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue