/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* Find params on methods where the param is only ever passed as a single constant value.
The process goes something like this: $ make check $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='constantparam' check $ ./compilerplugins/clang/constantparam.py
TODO look for OUString and OString params and check for call-params that are always either "" or default constructed
FIXME this plugin manages to trigger crashes inside clang, when calling EvaluateAsInt, so I end up disabling it for a handful of files here and there.
*/
namespace {
struct MyCallSiteInfo
{
std::string returnType;
std::string nameAndParams;
std::string paramName;
std::string paramType; int paramIndex; // because in some declarations the names are empty
std::string callValue;
std::string sourceLocation;
}; booloperator < (const MyCallSiteInfo &lhs, const MyCallSiteInfo &rhs)
{ return std::tie(lhs.sourceLocation, lhs.paramIndex, lhs.callValue)
< std::tie(rhs.sourceLocation, rhs.paramIndex, rhs.callValue);
}
// try to limit the voluminous output a little static std::set<MyCallSiteInfo> callSet;
class ConstantParam: public loplugin::FunctionAddress<loplugin::FilteringPlugin<ConstantParam>>
{ public: explicit ConstantParam(loplugin::InstantiationData const & data): FunctionAddress(data) {}
// this catches places that take the address of a method for (auto functionDecl : getFunctionsWithAddressTaken())
{ for (unsigned i = 0; i < functionDecl->getNumParams(); ++i)
addToCallSet(functionDecl, i, functionDecl->getParamDecl(i)->getName(), "unknown3");
}
// dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes // writing to the same logfile
if (!functionDecl->getNameInfo().getLoc().isValid()) return; if (functionDecl->isVariadic()) return; // ignore stuff that forms part of the stable URE interface if (isInUnoIncludeFile(functionDecl)) return;
std::string ConstantParam::getCallValue(const Expr* arg)
{
arg = arg->IgnoreParenCasts(); if (isa<CXXDefaultArgExpr>(arg)) {
arg = dyn_cast<CXXDefaultArgExpr>(arg)->getExpr();
}
arg = arg->IgnoreParenCasts(); // ignore this, it seems to trigger an infinite recursion if (isa<UnaryExprOrTypeTraitExpr>(arg)) return"unknown1"; if (arg->isValueDependent()) return"unknown2";
APSInt x1; if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext()))
{ return compat::toString(x1, 10);
} if (isa<CXXNullPtrLiteralExpr>(arg)) { return"0";
} if (isa<MaterializeTemporaryExpr>(arg))
{ const CXXBindTemporaryExpr* strippedArg = dyn_cast_or_null<CXXBindTemporaryExpr>(arg->IgnoreParenCasts()); if (strippedArg)
{ auto temp = dyn_cast<CXXTemporaryObjectExpr>(strippedArg->getSubExpr()); if (temp->getNumArgs() == 0)
{ if (loplugin::TypeCheck(temp->getType()).Class("OUString").Namespace("rtl").GlobalNamespace()) { return"\"\"";
} if (loplugin::TypeCheck(temp->getType()).Class("OString").Namespace("rtl").GlobalNamespace()) { return"\"\"";
} return"defaultConstruct";
}
}
}
// Get the expression contents. // This helps us find params which are always initialised with something like "OUString()".
SourceManager& SM = compiler.getSourceManager();
SourceLocation startLoc = arg->getBeginLoc();
SourceLocation endLoc = arg->getEndLoc(); constchar *p1 = SM.getCharacterData( startLoc ); constchar *p2 = SM.getCharacterData( endLoc ); if (!p1 || !p2 || (p2 - p1) < 0 || (p2 - p1) > 40) { return"unknown";
} unsigned n = Lexer::MeasureTokenLength( endLoc, SM, compiler.getLangOpts());
std::string s( p1, p2 - p1 + n); // sanitize call value, makes using command line tools (and python) much less error prone for (autoconst & ch : s) if (ch < 32) return"sanitised";
// now normalize the value. For some params, like OUString, we can pass it as OUString() or "" and they are the same thing if (s == "OUString()")
s = "\"\""; elseif (s == "OString()")
s = "\"\""; return s;
}
bool ConstantParam::VisitCallExpr(const CallExpr * callExpr) { if (ignoreLocation(callExpr)) { returntrue;
} const FunctionDecl* functionDecl; if (isa<CXXMemberCallExpr>(callExpr)) {
functionDecl = dyn_cast<CXXMemberCallExpr>(callExpr)->getMethodDecl();
} else {
functionDecl = callExpr->getDirectCallee();
} if (!functionDecl) returntrue;
functionDecl = functionDecl->getCanonicalDecl(); // method overrides don't always specify the same default params (although they probably should) // so we need to work our way up to the root method while (isa<CXXMethodDecl>(functionDecl)) { const CXXMethodDecl* methodDecl = dyn_cast<CXXMethodDecl>(functionDecl); if (methodDecl->size_overridden_methods()==0) break;
functionDecl = *methodDecl->begin_overridden_methods();
} // work our way back to the root definition for template methods if (functionDecl->getInstantiatedFromMemberFunction())
functionDecl = functionDecl->getInstantiatedFromMemberFunction(); elseif (functionDecl->getTemplateInstantiationPattern())
functionDecl = functionDecl->getTemplateInstantiationPattern();
unsigned len = std::max(callExpr->getNumArgs(), functionDecl->getNumParams()); for (unsigned i = 0; i < len; ++i) { const Expr* valExpr; if (i < callExpr->getNumArgs())
valExpr = callExpr->getArg(i); elseif (i < functionDecl->getNumParams() && functionDecl->getParamDecl(i)->hasDefaultArg())
valExpr = functionDecl->getParamDecl(i)->getDefaultArg(); else // can happen in template code continue;
std::string callValue = getCallValue(valExpr);
std::string paramName = i < functionDecl->getNumParams()
? functionDecl->getParamDecl(i)->getName().str()
: "###" + std::to_string(i);
addToCallSet(functionDecl, i, paramName, callValue);
} returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.