Skip to content

Commit 7288943

Browse files
committedSep 6, 2014
Add -Wunused-local-typedef, a warning that finds unused local typedefs.
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.) llvm-svn: 217298
1 parent aaa0b81 commit 7288943

25 files changed

+451
-12
lines changed
 

‎clang/include/clang/Basic/DiagnosticGroups.td

+4-1
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ def UnusedValue : DiagGroup<"unused-value", [UnusedComparison, UnusedResult]>;
402402
def UnusedConstVariable : DiagGroup<"unused-const-variable">;
403403
def UnusedVariable : DiagGroup<"unused-variable",
404404
[UnusedConstVariable]>;
405+
def UnusedLocalTypedef : DiagGroup<"unused-local-typedef">;
405406
def UnusedPropertyIvar : DiagGroup<"unused-property-ivar">;
406407
def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">;
407408
def UserDefinedLiterals : DiagGroup<"user-defined-literals">;
@@ -526,7 +527,7 @@ def Unused : DiagGroup<"unused",
526527
[UnusedArgument, UnusedFunction, UnusedLabel,
527528
// UnusedParameter, (matches GCC's behavior)
528529
// UnusedMemberFunction, (clean-up llvm before enabling)
529-
UnusedPrivateField,
530+
UnusedPrivateField, UnusedLocalTypedef,
530531
UnusedValue, UnusedVariable, UnusedPropertyIvar]>,
531532
DiagCategory<"Unused Entity Issue">;
532533

@@ -622,6 +623,8 @@ def : DiagGroup<"int-conversions",
622623
[IntConversion]>; // -Wint-conversions = -Wint-conversion
623624
def : DiagGroup<"vector-conversions",
624625
[VectorConversion]>; // -Wvector-conversions = -Wvector-conversion
626+
def : DiagGroup<"unused-local-typedefs", [UnusedLocalTypedef]>;
627+
// -Wunused-local-typedefs = -Wunused-local-typedef
625628

626629
// A warning group for warnings that we want to have on by default in clang,
627630
// but which aren't on by default in GCC.

‎clang/include/clang/Basic/DiagnosticSemaKinds.td

+3
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ def warn_unused_parameter : Warning<"unused parameter %0">,
175175
InGroup<UnusedParameter>, DefaultIgnore;
176176
def warn_unused_variable : Warning<"unused variable %0">,
177177
InGroup<UnusedVariable>, DefaultIgnore;
178+
def warn_unused_local_typedef : Warning<
179+
"unused %select{typedef|type alias}0 %1">,
180+
InGroup<UnusedLocalTypedef>, DefaultIgnore;
178181
def warn_unused_property_backing_ivar :
179182
Warning<"ivar %0 which backs the property is not "
180183
"referenced in this property's accessor">,

‎clang/include/clang/Sema/ExternalSemaSource.h

+13
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
#include "llvm/ADT/MapVector.h"
2121
#include <utility>
2222

23+
namespace llvm {
24+
template <class T, unsigned n> class SmallSetVector;
25+
}
26+
2327
namespace clang {
2428

2529
class CXXConstructorDecl;
@@ -132,6 +136,15 @@ class ExternalSemaSource : public ExternalASTSource {
132136
/// introduce the same declarations repeatedly.
133137
virtual void ReadDynamicClasses(SmallVectorImpl<CXXRecordDecl *> &Decls) {}
134138

139+
/// \brief Read the set of potentially unused typedefs known to the source.
140+
///
141+
/// The external source should append its own potentially unused local
142+
/// typedefs to the given vector of declarations. Note that this routine may
143+
/// be invoked multiple times; the external source should take care not to
144+
/// introduce the same declarations repeatedly.
145+
virtual void ReadUnusedLocalTypedefNameCandidates(
146+
llvm::SmallSetVector<const TypedefNameDecl *, 4> &Decls) {};
147+
135148
/// \brief Read the set of locally-scoped external declarations known to the
136149
/// external Sema source.
137150
///

‎clang/include/clang/Sema/MultiplexExternalSemaSource.h

+9
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,15 @@ class MultiplexExternalSemaSource : public ExternalSemaSource {
282282
/// introduce the same declarations repeatedly.
283283
void ReadDynamicClasses(SmallVectorImpl<CXXRecordDecl*> &Decls) override;
284284

285+
/// \brief Read the set of potentially unused typedefs known to the source.
286+
///
287+
/// The external source should append its own potentially unused local
288+
/// typedefs to the given vector of declarations. Note that this routine may
289+
/// be invoked multiple times; the external source should take care not to
290+
/// introduce the same declarations repeatedly.
291+
void ReadUnusedLocalTypedefNameCandidates(
292+
llvm::SmallSetVector<const TypedefNameDecl *, 4> &Decls) override;
293+
285294
/// \brief Read the set of locally-scoped extern "C" declarations known to the
286295
/// external Sema source.
287296
///

‎clang/include/clang/Sema/Sema.h

+7
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,10 @@ class Sema {
389389
/// \brief Set containing all declared private fields that are not used.
390390
NamedDeclSetType UnusedPrivateFields;
391391

392+
/// \brief Set containing all typedefs that are likely unused.
393+
llvm::SmallSetVector<const TypedefNameDecl *, 4>
394+
UnusedLocalTypedefNameCandidates;
395+
392396
typedef llvm::SmallPtrSet<const CXXRecordDecl*, 8> RecordDeclSetTy;
393397

394398
/// PureVirtualClassDiagSet - a set of class declarations which we have
@@ -1048,6 +1052,8 @@ class Sema {
10481052
/// \brief Retrieve the module loader associated with the preprocessor.
10491053
ModuleLoader &getModuleLoader() const;
10501054

1055+
void emitAndClearUnusedLocalTypedefWarnings();
1056+
10511057
void ActOnEndOfTranslationUnit();
10521058

10531059
void CheckDelegatingCtorCycles();
@@ -3209,6 +3215,7 @@ class Sema {
32093215
/// DiagnoseUnusedExprResult - If the statement passed in is an expression
32103216
/// whose result is unused, warn.
32113217
void DiagnoseUnusedExprResult(const Stmt *S);
3218+
void DiagnoseUnusedNestedTypedefs(const RecordDecl *D);
32123219
void DiagnoseUnusedDecl(const NamedDecl *ND);
32133220

32143221
/// Emit \p DiagID if statement located on \p StmtLoc has a suspicious null

‎clang/include/clang/Serialization/ASTBitCodes.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,10 @@ namespace clang {
545545
LATE_PARSED_TEMPLATE = 50,
546546

547547
/// \brief Record code for \#pragma optimize options.
548-
OPTIMIZE_PRAGMA_OPTIONS = 51
548+
OPTIMIZE_PRAGMA_OPTIONS = 51,
549+
550+
/// \brief Record code for potentially unused local typedef names.
551+
UNUSED_LOCAL_TYPEDEF_NAME_CANDIDATES = 52,
549552
};
550553

551554
/// \brief Record types used within a source manager block.

‎clang/include/clang/Serialization/ASTReader.h

+8
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,11 @@ class ASTReader
759759
/// at the end of the TU, in which case it directs CodeGen to emit the VTable.
760760
SmallVector<uint64_t, 16> DynamicClasses;
761761

762+
/// \brief The IDs of all potentially unused typedef names in the chain.
763+
///
764+
/// Sema tracks these to emit warnings.
765+
SmallVector<uint64_t, 16> UnusedLocalTypedefNameCandidates;
766+
762767
/// \brief The IDs of the declarations Sema stores directly.
763768
///
764769
/// Sema tracks a few important decls, such as namespace std, directly.
@@ -1789,6 +1794,9 @@ class ASTReader
17891794

17901795
void ReadDynamicClasses(SmallVectorImpl<CXXRecordDecl *> &Decls) override;
17911796

1797+
void ReadUnusedLocalTypedefNameCandidates(
1798+
llvm::SmallSetVector<const TypedefNameDecl *, 4> &Decls) override;
1799+
17921800
void ReadLocallyScopedExternCDecls(
17931801
SmallVectorImpl<NamedDecl *> &Decls) override;
17941802

‎clang/lib/Sema/MultiplexExternalSemaSource.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,12 @@ void MultiplexExternalSemaSource::ReadDynamicClasses(
242242
Sources[i]->ReadDynamicClasses(Decls);
243243
}
244244

245+
void MultiplexExternalSemaSource::ReadUnusedLocalTypedefNameCandidates(
246+
llvm::SmallSetVector<const TypedefNameDecl *, 4> &Decls) {
247+
for(size_t i = 0; i < Sources.size(); ++i)
248+
Sources[i]->ReadUnusedLocalTypedefNameCandidates(Decls);
249+
}
250+
245251
void MultiplexExternalSemaSource::ReadLocallyScopedExternCDecls(
246252
SmallVectorImpl<NamedDecl*> &Decls) {
247253
for(size_t i = 0; i < Sources.size(); ++i)

‎clang/lib/Sema/Sema.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,19 @@ static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
597597
return Complete;
598598
}
599599

600+
void Sema::emitAndClearUnusedLocalTypedefWarnings() {
601+
if (ExternalSource)
602+
ExternalSource->ReadUnusedLocalTypedefNameCandidates(
603+
UnusedLocalTypedefNameCandidates);
604+
for (const TypedefNameDecl *TD : UnusedLocalTypedefNameCandidates) {
605+
if (TD->isReferenced())
606+
continue;
607+
Diag(TD->getLocation(), diag::warn_unused_local_typedef)
608+
<< isa<TypeAliasDecl>(TD) << TD->getDeclName();
609+
}
610+
UnusedLocalTypedefNameCandidates.clear();
611+
}
612+
600613
/// ActOnEndOfTranslationUnit - This is called at the very end of the
601614
/// translation unit when EOF is reached and all but the top-level scope is
602615
/// popped.
@@ -719,6 +732,10 @@ void Sema::ActOnEndOfTranslationUnit() {
719732
}
720733
}
721734

735+
// Warnings emitted in ActOnEndOfTranslationUnit() should be emitted for
736+
// modules when they are built, not every time they are used.
737+
emitAndClearUnusedLocalTypedefWarnings();
738+
722739
// Modules don't need any of the checking below.
723740
TUScope = nullptr;
724741
return;
@@ -827,6 +844,8 @@ void Sema::ActOnEndOfTranslationUnit() {
827844
if (ExternalSource)
828845
ExternalSource->ReadUndefinedButUsed(UndefinedButUsed);
829846
checkUndefinedButUsed(*this);
847+
848+
emitAndClearUnusedLocalTypedefWarnings();
830849
}
831850

832851
if (!Diags.isIgnored(diag::warn_unused_private_field, SourceLocation())) {

‎clang/lib/Sema/SemaCXXScopeSpec.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,9 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S,
612612
}
613613
}
614614

615+
if (auto *TD = dyn_cast_or_null<TypedefNameDecl>(SD))
616+
MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false);
617+
615618
// If we're just performing this lookup for error-recovery purposes,
616619
// don't extend the nested-name-specifier. Just return now.
617620
if (ErrorRecoveryLookup)

‎clang/lib/Sema/SemaDecl.cpp

+39-3
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
380380
DiagnoseUseOfDecl(IIDecl, NameLoc);
381381

382382
T = Context.getTypeDeclType(TD);
383+
MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false);
383384

384385
// NOTE: avoid constructing an ElaboratedType(Loc) if this is a
385386
// constructor or destructor name (in such a case, the scope specifier
@@ -928,6 +929,7 @@ Sema::NameClassification Sema::ClassifyName(Scope *S,
928929
NamedDecl *FirstDecl = (*Result.begin())->getUnderlyingDecl();
929930
if (TypeDecl *Type = dyn_cast<TypeDecl>(FirstDecl)) {
930931
DiagnoseUseOfDecl(Type, NameLoc);
932+
MarkAnyDeclReferenced(Type->getLocation(), Type, /*OdrUse=*/false);
931933
QualType T = Context.getTypeDeclType(Type);
932934
if (SS.isNotEmpty())
933935
return buildNestedType(*this, SS, T, NameLoc);
@@ -1395,10 +1397,22 @@ static bool ShouldDiagnoseUnusedDecl(const NamedDecl *D) {
13951397

13961398
if (isa<LabelDecl>(D))
13971399
return true;
1400+
1401+
// Except for labels, we only care about unused decls that are local to
1402+
// functions.
1403+
bool WithinFunction = D->getDeclContext()->isFunctionOrMethod();
1404+
if (const auto *R = dyn_cast<CXXRecordDecl>(D->getDeclContext()))
1405+
// For dependent types, the diagnostic is deferred.
1406+
WithinFunction =
1407+
WithinFunction || (R->isLocalClass() && !R->isDependentType());
1408+
if (!WithinFunction)
1409+
return false;
1410+
1411+
if (isa<TypedefNameDecl>(D))
1412+
return true;
13981413

13991414
// White-list anything that isn't a local variable.
1400-
if (!isa<VarDecl>(D) || isa<ParmVarDecl>(D) || isa<ImplicitParamDecl>(D) ||
1401-
!D->getDeclContext()->isFunctionOrMethod())
1415+
if (!isa<VarDecl>(D) || isa<ParmVarDecl>(D) || isa<ImplicitParamDecl>(D))
14021416
return false;
14031417

14041418
// Types of valid local variables should be complete, so this should succeed.
@@ -1461,11 +1475,30 @@ static void GenerateFixForUnusedDecl(const NamedDecl *D, ASTContext &Ctx,
14611475
return;
14621476
}
14631477

1478+
void Sema::DiagnoseUnusedNestedTypedefs(const RecordDecl *D) {
1479+
if (D->getTypeForDecl()->isDependentType())
1480+
return;
1481+
1482+
for (auto *TmpD : D->decls()) {
1483+
if (const auto *T = dyn_cast<TypedefNameDecl>(TmpD))
1484+
DiagnoseUnusedDecl(T);
1485+
else if(const auto *R = dyn_cast<RecordDecl>(TmpD))
1486+
DiagnoseUnusedNestedTypedefs(R);
1487+
}
1488+
}
1489+
14641490
/// DiagnoseUnusedDecl - Emit warnings about declarations that are not used
14651491
/// unless they are marked attr(unused).
14661492
void Sema::DiagnoseUnusedDecl(const NamedDecl *D) {
14671493
if (!ShouldDiagnoseUnusedDecl(D))
14681494
return;
1495+
1496+
if (auto *TD = dyn_cast<TypedefNameDecl>(D)) {
1497+
// typedefs can be referenced later on, so the diagnostics are emitted
1498+
// at end-of-translation-unit.
1499+
UnusedLocalTypedefNameCandidates.insert(TD);
1500+
return;
1501+
}
14691502

14701503
FixItHint Hint;
14711504
GenerateFixForUnusedDecl(D, Context, Hint);
@@ -1505,8 +1538,11 @@ void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) {
15051538
if (!D->getDeclName()) continue;
15061539

15071540
// Diagnose unused variables in this scope.
1508-
if (!S->hasUnrecoverableErrorOccurred())
1541+
if (!S->hasUnrecoverableErrorOccurred()) {
15091542
DiagnoseUnusedDecl(D);
1543+
if (const auto *RD = dyn_cast<RecordDecl>(D))
1544+
DiagnoseUnusedNestedTypedefs(RD);
1545+
}
15101546

15111547
// If this was a forward reference to a label, verify it was defined.
15121548
if (LabelDecl *LD = dyn_cast<LabelDecl>(D))

‎clang/lib/Sema/SemaStmt.cpp

+40
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "clang/AST/EvaluatedExprVisitor.h"
2020
#include "clang/AST/ExprCXX.h"
2121
#include "clang/AST/ExprObjC.h"
22+
#include "clang/AST/RecursiveASTVisitor.h"
2223
#include "clang/AST/StmtCXX.h"
2324
#include "clang/AST/StmtObjC.h"
2425
#include "clang/AST/TypeLoc.h"
@@ -2710,6 +2711,40 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
27102711
return Result;
27112712
}
27122713

2714+
namespace {
2715+
/// \brief Marks all typedefs in all local classes in a type referenced.
2716+
///
2717+
/// In a function like
2718+
/// auto f() {
2719+
/// struct S { typedef int a; };
2720+
/// return S();
2721+
/// }
2722+
///
2723+
/// the local type escapes and could be referenced in some TUs but not in
2724+
/// others. Pretend that all local typedefs are always referenced, to not warn
2725+
/// on this. This isn't necessary if f has internal linkage, or the typedef
2726+
/// is private.
2727+
class LocalTypedefNameReferencer
2728+
: public RecursiveASTVisitor<LocalTypedefNameReferencer> {
2729+
public:
2730+
LocalTypedefNameReferencer(Sema &S) : S(S) {}
2731+
bool VisitRecordType(const RecordType *RT);
2732+
private:
2733+
Sema &S;
2734+
};
2735+
bool LocalTypedefNameReferencer::VisitRecordType(const RecordType *RT) {
2736+
auto *R = dyn_cast<CXXRecordDecl>(RT->getDecl());
2737+
if (!R || !R->isLocalClass() || !R->isLocalClass()->isExternallyVisible() ||
2738+
R->isDependentType())
2739+
return true;
2740+
for (auto *TmpD : R->decls())
2741+
if (auto *T = dyn_cast<TypedefNameDecl>(TmpD))
2742+
if (T->getAccess() != AS_private || R->hasFriends())
2743+
S.MarkAnyDeclReferenced(T->getLocation(), T, /*OdrUse=*/false);
2744+
return true;
2745+
}
2746+
}
2747+
27132748
/// Deduce the return type for a function from a returned expression, per
27142749
/// C++1y [dcl.spec.auto]p6.
27152750
bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD,
@@ -2755,6 +2790,11 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD,
27552790

27562791
if (DAR != DAR_Succeeded)
27572792
return true;
2793+
2794+
// If a local type is part of the returned type, mark its fields as
2795+
// referenced.
2796+
LocalTypedefNameReferencer Referencer(*this);
2797+
Referencer.TraverseType(RetExpr->getType());
27582798
} else {
27592799
// In the case of a return with no operand, the initializer is considered
27602800
// to be void().

‎clang/lib/Sema/SemaStmtAsm.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -467,9 +467,10 @@ bool Sema::LookupInlineAsmField(StringRef Base, StringRef Member,
467467
NamedDecl *FoundDecl = BaseResult.getFoundDecl();
468468
if (VarDecl *VD = dyn_cast<VarDecl>(FoundDecl))
469469
RT = VD->getType()->getAs<RecordType>();
470-
else if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(FoundDecl))
470+
else if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(FoundDecl)) {
471+
MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false);
471472
RT = TD->getUnderlyingType()->getAs<RecordType>();
472-
else if (TypeDecl *TD = dyn_cast<TypeDecl>(FoundDecl))
473+
} else if (TypeDecl *TD = dyn_cast<TypeDecl>(FoundDecl))
473474
RT = TD->getTypeForDecl()->getAs<RecordType>();
474475
if (!RT)
475476
return true;

‎clang/lib/Sema/SemaTemplate.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -7945,6 +7945,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
79457945
if (TypeDecl *Type = dyn_cast<TypeDecl>(Result.getFoundDecl())) {
79467946
// We found a type. Build an ElaboratedType, since the
79477947
// typename-specifier was just sugar.
7948+
MarkAnyDeclReferenced(Type->getLocation(), Type, /*OdrUse=*/false);
79487949
return Context.getElaboratedType(ETK_Typename,
79497950
QualifierLoc.getNestedNameSpecifier(),
79507951
Context.getTypeDeclType(Type));

‎clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,9 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) {
12001200
SemaRef.InstantiateClassMembers(D->getLocation(), Record, TemplateArgs,
12011201
TSK_ImplicitInstantiation);
12021202
}
1203+
1204+
SemaRef.DiagnoseUnusedNestedTypedefs(Record);
1205+
12031206
return Record;
12041207
}
12051208

@@ -3653,7 +3656,7 @@ void Sema::BuildVariableInstantiation(
36533656
// Diagnose unused local variables with dependent types, where the diagnostic
36543657
// will have been deferred.
36553658
if (!NewVar->isInvalidDecl() &&
3656-
NewVar->getDeclContext()->isFunctionOrMethod() && !NewVar->isUsed() &&
3659+
NewVar->getDeclContext()->isFunctionOrMethod() &&
36573660
OldVar->getType()->isDependentType())
36583661
DiagnoseUnusedDecl(NewVar);
36593662
}

0 commit comments

Comments
 (0)
Please sign in to comment.