1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
19 This looks for unused enum constants
21 We search for 3 things
22 (a) constants that are declared but never used
23 (b) constants only used in a "read" fashion i.e. we compare stuff against them, but we never store a value anywhere
24 (c) constants only used in a "write" fashion i.e. we store a value, but never check for that value
26 (a) is fairly reliable but (b) and (c) will need some checking before acting on.
28 Be warned that it produces around 5G of log file.
30 The process goes something like this:
32 $ make FORCE_COMPILE_ALL=1 COMPILER_PLUGIN_TOOL='unusedenumconstants' check
33 $ ./compilerplugins/clang/unusedenumconstants.py
35 Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around
44 std::string parentClass
;
45 std::string fieldName
;
46 std::string sourceLocation
;
48 bool operator < (const MyFieldInfo
&lhs
, const MyFieldInfo
&rhs
)
50 return std::tie(lhs
.parentClass
, lhs
.fieldName
)
51 < std::tie(rhs
.parentClass
, rhs
.fieldName
);
55 // try to limit the voluminous output a little
56 static std::set
<MyFieldInfo
> definitionSet
;
57 static std::set
<MyFieldInfo
> writeSet
;
58 static std::set
<MyFieldInfo
> readSet
;
61 class UnusedEnumConstants
:
62 public RecursiveASTVisitor
<UnusedEnumConstants
>, public loplugin::Plugin
65 explicit UnusedEnumConstants(loplugin::InstantiationData
const & data
): Plugin(data
) {}
67 virtual void run() override
69 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
71 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
72 // writing to the same logfile
74 for (const MyFieldInfo
& s
: definitionSet
)
75 output
+= "definition:\t" + s
.parentClass
+ "\t" + s
.fieldName
+ "\t" + s
.sourceLocation
+ "\n";
76 for (const MyFieldInfo
& s
: writeSet
)
77 output
+= "write:\t" + s
.parentClass
+ "\t" + s
.fieldName
+ "\n";
78 for (const MyFieldInfo
& s
: readSet
)
79 output
+= "read:\t" + s
.parentClass
+ "\t" + s
.fieldName
+ "\n";
81 myfile
.open( WORKDIR
"/loplugin.unusedenumconstants.log", std::ios::app
| std::ios::out
);
86 bool shouldVisitTemplateInstantiations () const { return true; }
87 bool shouldVisitImplicitCode() const { return true; }
89 bool VisitEnumConstantDecl( const EnumConstantDecl
* );
90 bool VisitDeclRefExpr( const DeclRefExpr
* );
92 MyFieldInfo
niceName(const EnumConstantDecl
*);
95 MyFieldInfo
UnusedEnumConstants::niceName(const EnumConstantDecl
* enumConstantDecl
)
99 aInfo
.parentClass
= enumConstantDecl
->getType().getAsString();
100 aInfo
.fieldName
= enumConstantDecl
->getNameAsString();
101 // sometimes the name (if it's anonymous thing) contains the full path of the build folder, which we don't need
102 size_t idx
= aInfo
.fieldName
.find(SRCDIR
);
103 if (idx
!= std::string::npos
) {
104 aInfo
.fieldName
= aInfo
.fieldName
.replace(idx
, strlen(SRCDIR
), "");
107 SourceLocation expansionLoc
= compiler
.getSourceManager().getExpansionLoc( enumConstantDecl
->getLocation() );
108 StringRef name
= compiler
.getSourceManager().getFilename(expansionLoc
);
109 aInfo
.sourceLocation
= std::string(name
.substr(strlen(SRCDIR
)+1)) + ":" + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
110 loplugin::normalizeDotDotInFilePath(aInfo
.sourceLocation
);
115 bool UnusedEnumConstants::VisitEnumConstantDecl( const EnumConstantDecl
* enumConstantDecl
)
117 enumConstantDecl
= enumConstantDecl
->getCanonicalDecl();
118 if (ignoreLocation( enumConstantDecl
)) {
121 // ignore stuff that forms part of the stable URE interface
122 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(enumConstantDecl
->getLocation()))) {
126 definitionSet
.insert(niceName(enumConstantDecl
));
130 bool UnusedEnumConstants::VisitDeclRefExpr( const DeclRefExpr
* declRefExpr
)
132 auto enumConstantDecl
= dyn_cast
<EnumConstantDecl
>(declRefExpr
->getDecl());
133 if (!enumConstantDecl
) {
136 enumConstantDecl
= enumConstantDecl
->getCanonicalDecl();
137 if (ignoreLocation(enumConstantDecl
)) {
140 // ignore stuff that forms part of the stable URE interface
141 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(enumConstantDecl
->getLocation()))) {
145 const Stmt
* parent
= declRefExpr
;
147 parent
= getParentStmt(parent
);
153 // Could probably do better here.
154 // Sometimes this is a constructor-initialiser-expression, so just make a pessimistic assumption.
156 } else if (isa
<CallExpr
>(parent
) || isa
<InitListExpr
>(parent
) || isa
<ArraySubscriptExpr
>(parent
)
157 || isa
<ReturnStmt
>(parent
) || isa
<DeclStmt
>(parent
)
158 || isa
<CXXConstructExpr
>(parent
)
159 || isa
<CXXThrowExpr
>(parent
))
162 } else if (isa
<CaseStmt
>(parent
) || isa
<SwitchStmt
>(parent
))
165 } else if (const BinaryOperator
* binaryOp
= dyn_cast
<BinaryOperator
>(parent
))
167 if (BinaryOperator::isAssignmentOp(binaryOp
->getOpcode())) {
172 } else if (const CXXOperatorCallExpr
* operatorCall
= dyn_cast
<CXXOperatorCallExpr
>(parent
))
174 auto oo
= operatorCall
->getOperator();
176 || (oo
>= OO_PlusEqual
&& oo
<= OO_GreaterGreaterEqual
)) {
181 } else if (isa
<CastExpr
>(parent
) || isa
<UnaryOperator
>(parent
)
182 || isa
<ConditionalOperator
>(parent
) || isa
<ParenExpr
>(parent
)
183 || isa
<MaterializeTemporaryExpr
>(parent
)
184 || isa
<ExprWithCleanups
>(parent
))
187 } else if (isa
<CXXDefaultArgExpr
>(parent
))
189 // TODO this could be improved
191 } else if (isa
<DeclRefExpr
>(parent
))
193 // slightly weird case I saw in basegfx where the enum is being used as a template param
195 } else if (isa
<MemberExpr
>(parent
))
197 // slightly weird case I saw in sc where the enum is being used as a template param
199 } else if (isa
<UnresolvedLookupExpr
>(parent
))
207 // to let me know if I missed something
211 report( DiagnosticsEngine::Warning
,
212 "unhandled clang AST node type",
213 parent
->getLocStart());
217 writeSet
.insert(niceName(enumConstantDecl
));
220 readSet
.insert(niceName(enumConstantDecl
));
225 loplugin::Plugin::Registration
< UnusedEnumConstants
> X("unusedenumconstants", false);
229 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */