for (auto it = stmt->child_begin(); it != stmt->child_end(); ++it) if (*it)
findBufferAssignOrAdd(stmt, *it);
returntrue;
}
bool BufferAdd::VisitCallExpr(CallExpr const* callExpr)
{ if (ignoreLocation(callExpr)) returntrue;
// calls to "buffer = foo" are OK // calls to "xxx = foo1 + buffer" are OK if (auto operatorCall = dyn_cast<CXXOperatorCallExpr>(callExpr))
{ auto op = operatorCall->getOperator(); if (op == OO_PlusEqual || op == OO_Equal || op == OO_Plus) returntrue;
}
// exclude buffer vars where the var is passed as a parameter to another function for (unsigned i = 0; i != callExpr->getNumArgs(); ++i)
{ auto a = ignore(callExpr->getArg(i)); if (auto declRefExpr = dyn_cast<DeclRefExpr>(a)) if (auto varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()))
{
badMap.insert(varDecl); if (varDecl->getName() == "noelf7")
callExpr->dump();
}
} returntrue;
}
bool BufferAdd::VisitCXXConstructExpr(CXXConstructExpr const* callExpr)
{ if (ignoreLocation(callExpr)) returntrue;
for (unsigned i = 0; i != callExpr->getNumArgs(); ++i)
{ auto a = ignore(callExpr->getArg(i)); if (auto declRefExpr = dyn_cast<DeclRefExpr>(a)) if (auto varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()))
badMap.insert(varDecl);
} returntrue;
}
bool BufferAdd::VisitUnaryOperator(const UnaryOperator* unaryOp)
{ if (ignoreLocation(unaryOp)) returntrue; if (unaryOp->getOpcode() != UO_AddrOf) returntrue; auto a = ignore(unaryOp->getSubExpr()); if (auto declRefExpr = dyn_cast<DeclRefExpr>(a)) if (auto varDecl = dyn_cast<VarDecl>(declRefExpr->getDecl()))
badMap.insert(varDecl); returntrue;
}
void BufferAdd::findBufferAssignOrAdd(const Stmt* parentStmt, Stmt const* stmt)
{ if (auto exprCleanup = dyn_cast<ExprWithCleanups>(stmt))
stmt = exprCleanup->getSubExpr(); if (auto switchCase = dyn_cast<SwitchCase>(stmt))
stmt = switchCase->getSubStmt(); if (auto declStmt = dyn_cast<DeclStmt>(stmt))
{ if (declStmt->isSingleDecl()) if (auto varDeclLHS = dyn_cast_or_null<VarDecl>(declStmt->getSingleDecl()))
{ auto tc = loplugin::TypeCheck(varDeclLHS->getType()); if (!tc.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
&& !tc.Class("OStringBuffer").Namespace("rtl").GlobalNamespace()) return; if (varDeclLHS->getStorageDuration() == SD_Static) return; if (!varDeclLHS->hasInit()) return; auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(ignore(varDeclLHS->getInit())); if (cxxConstructExpr)
{
addToGoodMap(varDeclLHS, parentStmt); return;
} if (!isSideEffectFree(varDeclLHS->getInit()))
badMap.insert(varDeclLHS); else
addToGoodMap(varDeclLHS, parentStmt);
} return;
}
// check for single calls to buffer method
if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(stmt))
{ if (auto declRefExprLHS
= dyn_cast<DeclRefExpr>(ignore(memberCallExpr->getImplicitObjectArgument())))
{ auto methodDecl = memberCallExpr->getMethodDecl(); if (methodDecl && methodDecl->getIdentifier()) if (auto varDeclLHS = dyn_cast<VarDecl>(declRefExprLHS->getDecl()))
{ auto tc = loplugin::TypeCheck(varDeclLHS->getType()); if (tc.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
|| tc.Class("OStringBuffer").Namespace("rtl").GlobalNamespace())
{ if (isMethodOkToMerge(memberCallExpr))
addToGoodMap(varDeclLHS, parentStmt); else
badMap.insert(varDeclLHS);
}
} return;
}
}
// check for assignment to string buffer if (auto operatorCall = dyn_cast<CXXOperatorCallExpr>(stmt))
{ auto op = operatorCall->getOperator(); if (op == OO_PlusEqual || op == OO_Equal)
{ if (auto declRefExprLHS = dyn_cast<DeclRefExpr>(ignore(operatorCall->getArg(0))))
{ if (auto varDeclLHS = dyn_cast<VarDecl>(declRefExprLHS->getDecl()))
{ auto tc = loplugin::TypeCheck(varDeclLHS->getType()); if (tc.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
|| tc.Class("OStringBuffer").Namespace("rtl").GlobalNamespace())
{
addToGoodMap(varDeclLHS, parentStmt); return;
}
}
}
}
}
// now check for chained append calls
auto expr = dyn_cast<Expr>(stmt); if (!expr) return; auto tc = loplugin::TypeCheck(expr->getType()); if (!tc.Class("OUStringBuffer").Namespace("rtl").GlobalNamespace()
&& !tc.Class("OStringBuffer").Namespace("rtl").GlobalNamespace()) return;
// unwrap the chain (which runs from right to left) const VarDecl* varDeclLHS = nullptr; bool good = true; while (true)
{ auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(expr); if (!memberCallExpr) break;
good &= isMethodOkToMerge(memberCallExpr);
if (varDeclLHS)
{ if (good)
addToGoodMap(varDeclLHS, parentStmt); else
badMap.insert(varDeclLHS);
}
}
void BufferAdd::addToGoodMap(const VarDecl* varDecl, const Stmt* parentStmt)
{ // check that vars are all inside the same compoundstmt, if they are not, we cannot combine them auto it = goodMap.find(varDecl); if (it != goodMap.end())
{ if (it->second == parentStmt) return; // don't treat these as parents, otherwise we eliminate .append.append sequences if (isa<MemberExpr>(parentStmt)) return; if (isa<CXXMemberCallExpr>(parentStmt)) return;
badMap.insert(varDecl);
} else
goodMap.emplace(varDecl, parentStmt);
}
bool BufferAdd::isMethodOkToMerge(CXXMemberCallExpr const* memberCall)
{ auto methodDecl = memberCall->getMethodDecl(); if (methodDecl->getNumParams() == 0) returntrue;
if (autoconst id = methodDecl->getIdentifier())
{ auto name = id->getName(); if (name == "appendUninitialized" || name == "setLength" || name == "remove"
|| name == "insert" || name == "appendAscii" || name == "appendUtf32") returnfalse;
}
auto rhs = memberCall->getArg(0); if (!isSideEffectFree(rhs)) returnfalse; returntrue;
}
bool BufferAdd::isSideEffectFree(Expr const* expr)
{
expr = ignore(expr); // I don't think the OUStringAppend functionality can handle this efficiently if (isa<ConditionalOperator>(expr)) returnfalse; // Multiple statements have a well defined evaluation order (sequence points between them) // but a single expression may be evaluated in arbitrary order; // if there are side effects in one of the sub-expressions that have an effect on another subexpression, // the result may be incorrect, and you don't necessarily notice in tests because the order is compiler-dependent. // for example see commit afd743141f7a7dd05914d0872c9afe079f16fe0c where such a refactoring introduced such a bug. // So only consider simple RHS expressions. if (!expr->HasSideEffects(compiler.getASTContext())) returntrue;
// check for chained adds which are side-effect free if (auto operatorCall = dyn_cast<CXXOperatorCallExpr>(expr))
{ auto op = operatorCall->getOperator(); if (op == OO_PlusEqual || op == OO_Plus || op == OO_Equal) if (isSideEffectFree(operatorCall->getArg(0))
&& isSideEffectFree(operatorCall->getArg(1))) returntrue;
}
if (auto callExpr = dyn_cast<CallExpr>(expr))
{ // check for calls through OUString::number/OUString::unacquired if (auto calleeMethodDecl = dyn_cast_or_null<CXXMethodDecl>(callExpr->getCalleeDecl())) if (calleeMethodDecl && calleeMethodDecl->getIdentifier())
{ if (callExpr->getNumArgs() > 0)
{ auto tc = loplugin::TypeCheck(calleeMethodDecl->getParent()); if (tc.Class("OUString") || tc.Class("OString"))
{ if (isSideEffectFree(callExpr->getArg(0))) returntrue;
}
}
} if (auto calleeFunctionDecl = dyn_cast_or_null<FunctionDecl>(callExpr->getCalleeDecl())) if (calleeFunctionDecl && calleeFunctionDecl->getIdentifier())
{ auto name = calleeFunctionDecl->getName(); // check for calls through OUStringToOString if (name == "OUStringToOString" || name == "OStringToOUString") if (isSideEffectFree(callExpr->getArg(0))) returntrue; // allowlist some known-safe methods if (compat::ends_with(name, "ResId") || name == "GetXMLToken") if (isSideEffectFree(callExpr->getArg(0))) returntrue;
} // O[U]String::operator std::[u16]string_view: if (autoconst d = dyn_cast_or_null<CXXConversionDecl>(callExpr->getCalleeDecl()))
{ auto tc = loplugin::TypeCheck(d->getParent()); if (tc.Class("OString") || tc.Class("OUString"))
{ returntrue;
}
}
}
// sometimes we have a constructor call on the RHS if (auto constructExpr = dyn_cast<CXXConstructExpr>(expr))
{ auto dc = loplugin::DeclCheck(constructExpr->getConstructor()); if (dc.MemberFunction().Class("OUString") || dc.MemberFunction().Class("OString")
|| dc.MemberFunction().Class("OUStringBuffer")
|| dc.MemberFunction().Class("OStringBuffer")) if (constructExpr->getNumArgs() == 0 || isSideEffectFree(constructExpr->getArg(0))) returntrue; // Expr::HasSideEffects does not like stuff that passes through OUStringLiteral auto dc2 = loplugin::DeclCheck(constructExpr->getConstructor()->getParent()); if (dc2.Class("OUStringLiteral").Namespace("rtl").GlobalNamespace()) returntrue;
}
// when adding literals, we sometimes get this if (auto functionalCastExpr = dyn_cast<CXXFunctionalCastExpr>(expr))
{ auto tc = loplugin::TypeCheck(functionalCastExpr->getType()); if (tc.Class("OUStringLiteral").Namespace("rtl").GlobalNamespace()) return isSideEffectFree(functionalCastExpr->getSubExpr());
}
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.