// Original idea from tml. // Look for variables that are (a) initialised from zero or one constants. (b) only used in one spot. // In which case, we might as well inline it.
namespace
{
Expr const * lookThroughInitListExpr(Expr const * expr) { if (autoconst ile = dyn_cast<InitListExpr>(expr->IgnoreParenImpCasts())) { if (ile->getNumInits() == 1) { return ile->getInit(0);
}
} return expr;
}
class ConstantValueDependentExpressionVisitor: public ConstStmtVisitor<ConstantValueDependentExpressionVisitor, bool>
{
ASTContext const & context_;
for (autoconst & varDecl : maVarDeclSet)
{ if (maVarDeclToIgnoreSet.find(varDecl) != maVarDeclToIgnoreSet.end()) continue; int noUses = 0; auto it = maVarUsesMap.find(varDecl); if (it != maVarUsesMap.end())
noUses = it->second; if (noUses > 1) continue;
report(DiagnosticsEngine::Warning, "var used only once, should be inlined or declared const",
varDecl->getLocation())
<< varDecl->getSourceRange(); if (it != maVarUsesMap.end())
report(DiagnosticsEngine::Note, "used here",
maVarUseSourceRangeMap[varDecl].getBegin())
<< maVarUseSourceRangeMap[varDecl];
}
}
bool VisitMemberExpr(MemberExpr const * expr) { // ignore cases like: // const OUString("xxx") xxx; // rtl_something(xxx.pData); // where we cannot inline the declaration. if (isa<FieldDecl>(expr->getMemberDecl())) {
recordIgnore(expr);
} returntrue;
}
bool VisitUnaryOperator(UnaryOperator const * expr) { // if we take the address of it, or we modify it, ignore it
UnaryOperator::Opcode op = expr->getOpcode(); if (op == UO_AddrOf || op == UO_PreInc || op == UO_PostInc
|| op == UO_PreDec || op == UO_PostDec)
{
recordIgnore(expr->getSubExpr());
} returntrue;
}
bool VisitBinaryOperator(BinaryOperator const * expr) { // if we assign it another value, or modify it, ignore it
BinaryOperator::Opcode op = expr->getOpcode(); if (op == BO_Assign || op == BO_PtrMemD || op == BO_PtrMemI || op == BO_MulAssign
|| op == BO_DivAssign || op == BO_RemAssign || op == BO_AddAssign
|| op == BO_SubAssign || op == BO_ShlAssign || op == BO_ShrAssign
|| op == BO_AndAssign || op == BO_XorAssign || op == BO_OrAssign)
{
recordIgnore(expr->getLHS());
} returntrue;
}
bool VisitCallExpr(CallExpr const * expr) { unsigned firstArg = 0; if (autoconst cmce = dyn_cast<CXXMemberCallExpr>(expr)) { if (autoconst e1 = cmce->getMethodDecl()) { if (!(e1->isConst() || e1->isStatic())) {
recordIgnore(cmce->getImplicitObjectArgument());
}
} elseif (autoconst e2 = dyn_cast<BinaryOperator>(
cmce->getCallee()->IgnoreParenImpCasts()))
{ switch (e2->getOpcode()) { case BO_PtrMemD: case BO_PtrMemI: if (!e2->getRHS()->getType()->getAs<MemberPointerType>()
->getPointeeType()->getAs<FunctionProtoType>()
->isConst())
{
recordIgnore(e2->getLHS());
} break; default: break;
}
}
} elseif (autoconst coce = dyn_cast<CXXOperatorCallExpr>(expr)) { if (autoconst cmd = dyn_cast_or_null<CXXMethodDecl>(
coce->getDirectCallee()))
{ if (!cmd->isStatic()) {
assert(coce->getNumArgs() != 0); if (!cmd->isConst()) {
recordIgnore(coce->getArg(0));
}
firstArg = 1;
}
}
} // ignore those ones we are passing by reference const FunctionDecl* calleeFunctionDecl = expr->getDirectCallee(); if (calleeFunctionDecl) { for (unsigned i = firstArg; i < expr->getNumArgs(); ++i) { if (i < calleeFunctionDecl->getNumParams()) {
QualType qt { calleeFunctionDecl->getParamDecl(i)->getType() }; if (loplugin::TypeCheck(qt).LvalueReference().NonConst()) {
recordIgnore(expr->getArg(i));
} if (loplugin::TypeCheck(qt).Pointer().NonConst()) {
recordIgnore(expr->getArg(i));
}
}
}
} returntrue;
}
bool VisitCXXConstructExpr(CXXConstructExpr const * expr) { // ignore those ones we are passing by reference const CXXConstructorDecl* cxxConstructorDecl = expr->getConstructor(); for (unsigned i = 0; i < expr->getNumArgs(); ++i) { if (i < cxxConstructorDecl->getNumParams()) {
QualType qt { cxxConstructorDecl->getParamDecl(i)->getType() }; if (loplugin::TypeCheck(qt).LvalueReference().NonConst()) {
recordIgnore(expr->getArg(i));
} if (loplugin::TypeCheck(qt).Pointer().NonConst()) {
recordIgnore(expr->getArg(i));
}
}
} returntrue;
}
void recordIgnore(Expr const * expr) { for (;;) {
expr = expr->IgnoreParenImpCasts(); if (autoconst e = dyn_cast<MemberExpr>(expr)) { if (isa<FieldDecl>(e->getMemberDecl())) {
expr = e->getBase(); continue;
}
} if (autoconst e = dyn_cast<ArraySubscriptExpr>(expr)) {
expr = e->getBase(); continue;
} if (autoconst e = dyn_cast<BinaryOperator>(expr)) { if (e->getOpcode() == BO_PtrMemD) {
expr = e->getLHS(); continue;
}
} break;
} autoconst dre = dyn_cast<DeclRefExpr>(expr); if (dre == nullptr) { return;
} autoconst var = dyn_cast<VarDecl>(dre->getDecl()); if (var == nullptr) { return;
}
maVarDeclToIgnoreSet.insert(var);
}
};
bool OnceVar::TraverseFunctionDecl( FunctionDecl* functionDecl )
{ // Ignore functions that contains #ifdef-ery, can be quite tricky // to make useful changes when this plugin fires in such functions if (containsPreprocessingConditionalInclusion(
functionDecl->getSourceRange())) returntrue; return RecursiveASTVisitor::TraverseFunctionDecl(functionDecl);
}
bool OnceVar::VisitVarDecl( const VarDecl* varDecl )
{ if (ignoreLocation(varDecl)) { returntrue;
} if (autoconst init = varDecl->getInit()) {
recordIgnore(lookThroughInitListExpr(init));
} if (varDecl->isExceptionVariable() || isa<ParmVarDecl>(varDecl)) { returntrue;
} // ignore stuff in header files (which should really not be there, but anyhow) if (!compiler.getSourceManager().isInMainFile(varDecl->getLocation())) { returntrue;
} // Ignore macros like FD_ZERO if (compiler.getSourceManager().isMacroBodyExpansion(varDecl->getBeginLoc())) { returntrue;
} if (varDecl->hasGlobalStorage()) { returntrue;
} autoconst tc = loplugin::TypeCheck(varDecl->getType()); if (!varDecl->getType().isCXX11PODType(compiler.getASTContext())
&& !tc.Class("OString").Namespace("rtl").GlobalNamespace()
&& !tc.Class("OUString").Namespace("rtl").GlobalNamespace()
&& !tc.Class("OStringBuffer").Namespace("rtl").GlobalNamespace()
&& !tc.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
&& !tc.Class("Color").GlobalNamespace()
&& !tc.Class("Pair").GlobalNamespace()
&& !tc.Class("Point").GlobalNamespace()
&& !tc.Class("Size").GlobalNamespace()
&& !tc.Class("Range").GlobalNamespace()
&& !tc.Class("Selection").GlobalNamespace()
&& !tc.Class("Rectangle").Namespace("tools").GlobalNamespace())
{ returntrue;
} if (varDecl->getType()->isPointerType()) returntrue; // if it's declared const, ignore it, it's there to make the code easier to read if (tc.Const()) returntrue;
if (!varDecl->hasInit()) returntrue;
// check for string or scalar literals bool foundStringLiteral = false; const Expr * initExpr = varDecl->getInit(); if (auto e = dyn_cast<ExprWithCleanups>(initExpr)) {
initExpr = e->getSubExpr();
} if (isa<clang::StringLiteral>(initExpr)) {
foundStringLiteral = true;
} elseif (auto constructExpr = dyn_cast<CXXConstructExpr>(initExpr)) { if (constructExpr->getNumArgs() == 0) {
foundStringLiteral = true; // i.e., empty string
} else { auto stringLit2 = dyn_cast<clang::StringLiteral>(constructExpr->getArg(0));
foundStringLiteral = stringLit2 != nullptr;
}
} if (!foundStringLiteral) { autoconst init = varDecl->getInit(); if (!(init->isValueDependent()
? isConstantValueDependentExpression(init)
: init->isConstantInitializer(
compiler.getASTContext(), false/*ForRef*/)))
{ returntrue;
}
}
maVarDeclSet.insert(varDecl);
returntrue;
}
bool OnceVar::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
{ if (ignoreLocation(declRefExpr)) { returntrue;
} const Decl* decl = declRefExpr->getDecl(); if (!isa<VarDecl>(decl) || isa<ParmVarDecl>(decl)) { returntrue;
} const VarDecl * varDecl = dyn_cast<VarDecl>(decl)->getCanonicalDecl(); // ignore stuff in header files (which should really not be there, but anyhow) if (!compiler.getSourceManager().isInMainFile(varDecl->getLocation())) { 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.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.