summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/unusedmethods.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/unusedmethods.cxx')
-rw-r--r--compilerplugins/clang/unusedmethods.cxx145
1 files changed, 82 insertions, 63 deletions
diff --git a/compilerplugins/clang/unusedmethods.cxx b/compilerplugins/clang/unusedmethods.cxx
index 31ff68da6093..e5fab5b9c023 100644
--- a/compilerplugins/clang/unusedmethods.cxx
+++ b/compilerplugins/clang/unusedmethods.cxx
@@ -19,7 +19,7 @@
Dump a list of calls to methods, and a list of method definitions.
Then we will post-process the 2 lists and find the set of unused methods.
-Be warned that it produces around 2.4G of log file.
+Be warned that it produces around 4G of log file.
The process goes something like this:
$ make check
@@ -62,6 +62,11 @@ static std::set<MyFuncInfo> callSet;
static std::set<MyFuncInfo> definitionSet;
+static bool startswith(const std::string& s, const std::string& prefix)
+{
+ return s.rfind(prefix,0) == 0;
+}
+
class UnusedMethods:
public RecursiveASTVisitor<UnusedMethods>, public loplugin::Plugin
{
@@ -78,21 +83,29 @@ public:
for (const MyFuncInfo & s : callSet)
output += "call:\t" + s.returnType + "\t" + s.nameAndParams + "\n";
for (const MyFuncInfo & s : definitionSet)
- output += "definition:\t" + s.returnType + "\t" + s.nameAndParams + "\t" + s.sourceLocation + "\n";
+ {
+ //treat all UNO interfaces as having been called, since they are part of our external ABI
+ if (!startswith(s.nameAndParams, "com::sun::star::"))
+ output += "definition:\t" + s.returnType + "\t" + s.nameAndParams + "\t" + s.sourceLocation + "\n";
+ }
ofstream myfile;
myfile.open( SRCDIR "/unusedmethods.log", ios::app | ios::out);
myfile << output;
myfile.close();
}
+ bool shouldVisitTemplateInstantiations () const { return true; }
+
bool VisitCallExpr(CallExpr* );
bool VisitFunctionDecl( const FunctionDecl* decl );
bool VisitDeclRefExpr( const DeclRefExpr* );
bool VisitCXXConstructExpr( const CXXConstructExpr* );
bool VisitVarDecl( const VarDecl* );
+ bool VisitCXXRecordDecl( CXXRecordDecl* );
private:
void logCallToRootMethods(const FunctionDecl* functionDecl);
MyFuncInfo niceName(const FunctionDecl* functionDecl);
+ std::string fullyQualifiedName(const FunctionDecl* functionDecl);
};
MyFuncInfo UnusedMethods::niceName(const FunctionDecl* functionDecl)
@@ -136,10 +149,36 @@ MyFuncInfo UnusedMethods::niceName(const FunctionDecl* functionDecl)
return aInfo;
}
+std::string UnusedMethods::fullyQualifiedName(const FunctionDecl* functionDecl)
+{
+ std::string ret = compat::getReturnType(*functionDecl).getCanonicalType().getAsString();
+ ret += " ";
+ if (isa<CXXMethodDecl>(functionDecl)) {
+ const CXXRecordDecl* recordDecl = dyn_cast<CXXMethodDecl>(functionDecl)->getParent();
+ ret += recordDecl->getQualifiedNameAsString();
+ ret += "::";
+ }
+ ret += functionDecl->getNameAsString() + "(";
+ bool bFirst = true;
+ for (const ParmVarDecl *pParmVarDecl : functionDecl->params()) {
+ if (bFirst)
+ bFirst = false;
+ else
+ ret += ",";
+ ret += pParmVarDecl->getType().getCanonicalType().getAsString();
+ }
+ ret += ")";
+ if (isa<CXXMethodDecl>(functionDecl) && dyn_cast<CXXMethodDecl>(functionDecl)->isConst()) {
+ ret += " const";
+ }
+
+ return ret;
+}
+
void UnusedMethods::logCallToRootMethods(const FunctionDecl* functionDecl)
{
functionDecl = functionDecl->getCanonicalDecl();
- bool bPrinted = false;
+ bool bCalledSuperMethod = false;
if (isa<CXXMethodDecl>(functionDecl)) {
// For virtual/overriding methods, we need to pretend we called the root method(s),
// so that they get marked as used.
@@ -148,50 +187,24 @@ void UnusedMethods::logCallToRootMethods(const FunctionDecl* functionDecl)
it != methodDecl->end_overridden_methods(); ++it)
{
logCallToRootMethods(*it);
- bPrinted = true;
+ bCalledSuperMethod = true;
}
}
- if (!bPrinted)
+ if (!bCalledSuperMethod)
{
+ while (functionDecl->getTemplateInstantiationPattern())
+ functionDecl = functionDecl->getTemplateInstantiationPattern();
callSet.insert(niceName(functionDecl));
}
}
-static bool startsWith(const std::string& s, const char* other)
-{
- return s.compare(0, strlen(other), other) == 0;
-}
-
-static bool isStandardStuff(const std::string& input)
-{
- std::string s = input;
- if (startsWith(s,"class "))
- s = s.substr(6);
- else if (startsWith(s,"struct "))
- s = s.substr(7);
- // ignore UNO interface definitions, cannot change those
- return startsWith(s, "com::sun::star::")
- // ignore stuff in the C++ stdlib and boost
- || startsWith(s, "std::") || startsWith(s, "boost::") || startsWith(s, "class boost::") || startsWith(s, "__gnu_debug::")
- // external library
- || startsWith(s, "mdds::")
- // can't change our rtl layer
- || startsWith(s, "rtl::")
- // ignore anonymous namespace stuff, it is compilation-unit-local and the compiler will detect any
- // unused code there
- || startsWith(s, "(anonymous namespace)::");
-}
-
// prevent recursive templates from blowing up the stack
-static std::set<const FunctionDecl*> traversedFunctionSet;
+static std::set<std::string> traversedFunctionSet;
bool UnusedMethods::VisitCallExpr(CallExpr* expr)
{
- // I don't use the normal ignoreLocation() here, because I __want__ to include files that are
- // compiled in the $WORKDIR since they may refer to normal code
- SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc( expr->getLocStart() );
- if( compiler.getSourceManager().isInSystemHeader( expansionLoc ))
- return true;
+ // Note that I don't ignore ANYTHING here, because I want to get calls to my code that result
+ // from template instantiation deep inside the STL and other external code
FunctionDecl* calleeFunctionDecl = expr->getDirectCallee();
if (calleeFunctionDecl == nullptr) {
@@ -229,7 +242,7 @@ gotfunc:
// if the function is templated. However, if we are inside a template function,
// calling another function on the same template, the same problem occurs.
// Rather than tracking all of that, just traverse anything we have not already traversed.
- if (traversedFunctionSet.insert(calleeFunctionDecl).second)
+ if (traversedFunctionSet.insert(fullyQualifiedName(calleeFunctionDecl)).second)
TraverseFunctionDecl(calleeFunctionDecl);
logCallToRootMethods(calleeFunctionDecl);
@@ -238,12 +251,6 @@ gotfunc:
bool UnusedMethods::VisitCXXConstructExpr(const CXXConstructExpr* expr)
{
- // I don't use the normal ignoreLocation() here, because I __want__ to include files that are
- // compiled in the $WORKDIR since they may refer to normal code
- SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc( expr->getLocStart() );
- if( compiler.getSourceManager().isInSystemHeader( expansionLoc ))
- return true;
-
const CXXConstructorDecl *consDecl = expr->getConstructor();
consDecl = consDecl->getCanonicalDecl();
if (consDecl->getTemplatedKind() == FunctionDecl::TemplatedKind::TK_NonTemplate
@@ -252,7 +259,7 @@ bool UnusedMethods::VisitCXXConstructExpr(const CXXConstructExpr* expr)
}
// if we see a call to a constructor, it may effectively create a whole new class,
// if the constructor's class is templated.
- if (!traversedFunctionSet.insert(consDecl).second)
+ if (!traversedFunctionSet.insert(fullyQualifiedName(consDecl)).second)
return true;
const CXXRecordDecl* parent = consDecl->getParent();
@@ -266,10 +273,6 @@ bool UnusedMethods::VisitCXXConstructExpr(const CXXConstructExpr* expr)
bool UnusedMethods::VisitFunctionDecl( const FunctionDecl* functionDecl )
{
- if (ignoreLocation(functionDecl)) {
- return true;
- }
-
functionDecl = functionDecl->getCanonicalDecl();
const CXXMethodDecl* methodDecl = dyn_cast_or_null<CXXMethodDecl>(functionDecl);
@@ -282,9 +285,6 @@ bool UnusedMethods::VisitFunctionDecl( const FunctionDecl* functionDecl )
functionDecl->getCanonicalDecl()->getNameInfo().getLoc()))) {
return true;
}
- if (methodDecl && isStandardStuff(methodDecl->getParent()->getQualifiedNameAsString())) {
- return true;
- }
if (isa<CXXDestructorDecl>(functionDecl)) {
return true;
}
@@ -295,19 +295,14 @@ bool UnusedMethods::VisitFunctionDecl( const FunctionDecl* functionDecl )
return true;
}
- definitionSet.insert(niceName(functionDecl));
+ if( !ignoreLocation( functionDecl ))
+ definitionSet.insert(niceName(functionDecl));
return true;
}
// this catches places that take the address of a method
bool UnusedMethods::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
{
- // I don't use the normal ignoreLocation() here, because I __want__ to include files that are
- // compiled in the $WORKDIR since they may refer to normal code
- SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc( declRefExpr->getLocStart() );
- if( compiler.getSourceManager().isInSystemHeader( expansionLoc ))
- return true;
-
const Decl* functionDecl = declRefExpr->getDecl();
if (!isa<FunctionDecl>(functionDecl)) {
return true;
@@ -320,11 +315,6 @@ bool UnusedMethods::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
bool UnusedMethods::VisitVarDecl( const VarDecl* varDecl )
{
varDecl = varDecl->getCanonicalDecl();
- // I don't use the normal ignoreLocation() here, because I __want__ to include files that are
- // compiled in the $WORKDIR since they may refer to normal code
- SourceLocation expansionLoc = compiler.getSourceManager().getExpansionLoc( varDecl->getLocStart() );
- if( compiler.getSourceManager().isInSystemHeader( expansionLoc ))
- return true;
if (varDecl->getStorageClass() != SC_Static)
return true;
@@ -343,6 +333,35 @@ bool UnusedMethods::VisitVarDecl( const VarDecl* varDecl )
return true;
}
+// Sometimes a class will inherit from something, and in the process invoke a template,
+// which can create new methods.
+//
+bool UnusedMethods::VisitCXXRecordDecl( CXXRecordDecl* recordDecl )
+{
+ recordDecl = recordDecl->getCanonicalDecl();
+ if (!recordDecl->hasDefinition())
+ return true;
+// workaround clang-3.5 issue
+#if __clang_major__ > 3 || ( __clang_major__ == 3 && __clang_minor__ >= 6 )
+ for(CXXBaseSpecifier* baseSpecifier = recordDecl->bases_begin();
+ baseSpecifier != recordDecl->bases_end(); ++baseSpecifier)
+ {
+ const Type *baseType = baseSpecifier->getType().getTypePtr();
+ if (isa<TypedefType>(baseSpecifier->getType())) {
+ baseType = dyn_cast<TypedefType>(baseType)->desugar().getTypePtr();
+ }
+ if (isa<RecordType>(baseType)) {
+ const RecordType *baseRecord = dyn_cast<RecordType>(baseType);
+ CXXRecordDecl* baseRecordDecl = dyn_cast<CXXRecordDecl>(baseRecord->getDecl());
+ if (baseRecordDecl && baseRecordDecl->getTemplateInstantiationPattern()) {
+ TraverseCXXRecordDecl(baseRecordDecl);
+ }
+ }
+ }
+#endif
+ return true;
+}
+
loplugin::Plugin::Registration< UnusedMethods > X("unusedmethods", false);
}