/* -*- 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/.
*/
/** Look for fields that are only ever assigned a single constant value.
We dmp a list of values assigned to fields, and a list of field definitions. Then we will post-process the 2 lists and find the set of interesting fields.
Be warned that it produces around 5G of log file.
The process goes something like this: $ make check $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='singlevalfields' check $ ./compilerplugins/clang/singlevalfields.py
Note that the actual process may involve a fair amount of undoing, hand editing, and general messing around to get it to work :-)
@TODO we don't spot fields that have been zero-initialised via calloc or rtl_allocateZeroMemory or memset @TODO calls to lambdas where a reference to the field is taken
// try to limit the voluminous output a little static std::set<MyFieldAssignmentInfo> assignedSet; static std::set<MyFieldInfo> definitionSet;
/** escape the value string to make it easier to parse the output file in python */
std::string escape(std::string s)
{
std::string out; for (size_t i=0; i<s.length(); ++i) if (int(s[i]) >= 32)
out += s[i]; else
out += "\\" + std::to_string((int)s[i]); return out;
}
class SingleValFields: public RecursiveASTVisitor<SingleValFields>, public loplugin::Plugin
{ public: explicit SingleValFields(loplugin::InstantiationData const & data):
Plugin(data) {}
bool SingleValFields::VisitCXXConstructorDecl( const CXXConstructorDecl* decl )
{ // doesn't count as a write to fields because it's self->self if (decl->isCopyOrMoveConstructor()) returntrue;
for(auto it = decl->init_begin(); it != decl->init_end(); ++it)
{ const CXXCtorInitializer* init = *it; const FieldDecl* fieldDecl = init->getMember(); if( !fieldDecl ) continue;
MyFieldAssignmentInfo aInfo;
niceName(fieldDecl, aInfo); const Expr * expr = init->getInit(); // unwrap any single-arg constructors, this helps to find smart pointers // that are only assigned nullptr if (auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(expr)) if (cxxConstructExpr->getNumArgs() == 1)
expr = cxxConstructExpr->getArg(0);
aInfo.value = getExprValue(expr);
assignedSet.insert(aInfo);
} returntrue;
}
bool SingleValFields::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
{ const VarDecl* varDecl = dyn_cast_or_null<VarDecl>(declRefExpr->getDecl()); if (!varDecl) returntrue; if (isa<ParmVarDecl>(varDecl)) returntrue; if (varDecl->getType().isConstQualified()) returntrue; if (!(varDecl->isStaticLocal() || varDecl->isStaticDataMember() || varDecl->hasGlobalStorage())) returntrue;
walkPotentialAssign(varDecl, declRefExpr); returntrue;
}
void SingleValFields::walkPotentialAssign( const DeclaratorDecl* fieldOrVarDecl, const Stmt* memberExpr )
{ const FunctionDecl* parentFunction = getParentFunctionDecl(memberExpr); if (parentFunction)
{ auto methodDecl = dyn_cast<CXXMethodDecl>(parentFunction); if (methodDecl && (methodDecl->isCopyAssignmentOperator() || methodDecl->isMoveAssignmentOperator())) return; if (methodDecl && methodDecl->getIdentifier()
&& (compat::starts_with(methodDecl->getName(), "Clone") || compat::starts_with(methodDecl->getName(), "clone"))) return; auto cxxConstructorDecl = dyn_cast<CXXConstructorDecl>(parentFunction); if (cxxConstructorDecl && cxxConstructorDecl->isCopyOrMoveConstructor()) return;
}
// walk up the tree until we find something interesting const Stmt* child = memberExpr; const Stmt* parent = getParentStmt(memberExpr); bool bPotentiallyAssignedTo = false; bool bDump = false;
std::string assignValue = "?";
// check for field being returned by non-const ref eg. Foo& getFoo() { return f; } if (parentFunction && parent && isa<ReturnStmt>(parent)) { const Stmt* parent2 = getParentStmt(parent); if (parent2 && isa<CompoundStmt>(parent2)) {
QualType qt = parentFunction->getReturnType().getDesugaredType(compiler.getASTContext()); if (!qt.isConstQualified() && qt->isReferenceType()) {
bPotentiallyAssignedTo = true;
}
}
}
while (!bPotentiallyAssignedTo) { // check for field being accessed by a reference variable e.g. Foo& f = m.foo; auto parentsList = compiler.getASTContext().getParents(*child); auto it = parentsList.begin(); if (it != parentsList.end()) { const VarDecl *varDecl = it->get<VarDecl>(); if (varDecl) {
QualType qt = varDecl->getType().getDesugaredType(compiler.getASTContext()); if (!qt.isConstQualified() && qt->isReferenceType()) {
bPotentiallyAssignedTo = true; break;
}
}
}
if (!parent) { return;
} if (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent) || isa<ParenListExpr>(parent)
|| isa<ExprWithCleanups>(parent))
{
child = parent;
parent = getParentStmt(parent);
} elseif (isa<UnaryOperator>(parent))
{ const UnaryOperator* unaryOperator = dyn_cast<UnaryOperator>(parent); int x = unaryOperator->getOpcode(); if (x == UO_AddrOf || x == UO_PostInc || x == UO_PostDec || x == UO_PreInc || x == UO_PreDec) {
assignValue = "?";
bPotentiallyAssignedTo = true; break;
} // cannot be assigned to anymore break;
} elseif (auto callExpr = dyn_cast<CallExpr>(parent))
{
checkCallExpr(child, callExpr, assignValue, bPotentiallyAssignedTo); break;
} elseif (isa<CXXConstructExpr>(parent))
{ const CXXConstructExpr* consExpr = dyn_cast<CXXConstructExpr>(parent); const CXXConstructorDecl* consDecl = consExpr->getConstructor(); for (unsigned i = 0; i < consExpr->getNumArgs(); ++i) { if (i >= consDecl->getNumParams()) // can happen in template code break; if (consExpr->getArg(i) == child) { const ParmVarDecl* parmVarDecl = consDecl->getParamDecl(i);
QualType qt = parmVarDecl->getType().getDesugaredType(compiler.getASTContext()); if (!qt.isConstQualified() && qt->isReferenceType()) {
bPotentiallyAssignedTo = true;
} break;
}
} break;
} elseif (isa<BinaryOperator>(parent))
{ const BinaryOperator* binaryOp = dyn_cast<BinaryOperator>(parent); auto op = binaryOp->getOpcode(); if ( binaryOp->getLHS() != child ) { // if the expr is on the RHS, do nothing
} elseif ( op == BO_Assign ) {
assignValue = getExprValue(binaryOp->getRHS());
bPotentiallyAssignedTo = true;
} elseif ( 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 )
{
bPotentiallyAssignedTo = true;
} break;
} elseif ( isa<CompoundStmt>(parent)
|| isa<SwitchStmt>(parent) || isa<CaseStmt>(parent) || isa<DefaultStmt>(parent)
|| isa<DoStmt>(parent) || isa<WhileStmt>(parent)
|| isa<IfStmt>(parent)
|| isa<ForStmt>(parent)
|| isa<ReturnStmt>(parent)
|| isa<CXXNewExpr>(parent)
|| isa<CXXDeleteExpr>(parent)
|| isa<ConditionalOperator>(parent)
|| isa<CXXTypeidExpr>(parent)
|| isa<ArraySubscriptExpr>(parent)
|| isa<CXXDependentScopeMemberExpr>(parent)
|| isa<DeclStmt>(parent)
|| isa<UnaryExprOrTypeTraitExpr>(parent)
|| isa<UnresolvedMemberExpr>(parent)
|| isa<MaterializeTemporaryExpr>(parent) //???
|| isa<InitListExpr>(parent)
|| isa<DesignatedInitExpr>(parent)
|| isa<CXXUnresolvedConstructExpr>(parent)
|| isa<LambdaExpr>(parent)
|| isa<PackExpansionExpr>(parent)
|| isa<CXXPseudoDestructorExpr>(parent)
)
{ break;
} elseif ( isa<ArrayInitLoopExpr>(parent) || isa<AtomicExpr>(parent) || isa<GCCAsmStmt>(parent) || isa<VAArgExpr>(parent))
{
bPotentiallyAssignedTo = true; break;
} elseif (isa<DeclRefExpr>(parent)) // things like o3tl::convertNarrowing pass members as template params
{ break;
} else {
bPotentiallyAssignedTo = true;
bDump = true; break;
}
} if (bDump)
{
report(
DiagnosticsEngine::Warning, "oh dear, what can the matter be?",
memberExpr->getBeginLoc())
<< memberExpr->getSourceRange();
parent->dump();
} if (bPotentiallyAssignedTo)
{
MyFieldAssignmentInfo aInfo;
niceName(fieldOrVarDecl, aInfo);
aInfo.value = assignValue;
assignedSet.insert(aInfo);
}
}
void SingleValFields::checkCallExpr(const Stmt* child, const CallExpr* callExpr, std::string& assignValue, bool& bPotentiallyAssignedTo)
{ if (callExpr->getCallee() == child) { return;
} const FunctionDecl* functionDecl; if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(callExpr)) {
functionDecl = memberCallExpr->getMethodDecl();
} else {
functionDecl = callExpr->getDirectCallee();
} if (functionDecl) { if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(callExpr)) { if (operatorCallExpr->getArg(0) == child) { const CXXMethodDecl* calleeMethodDecl = dyn_cast_or_null<CXXMethodDecl>(operatorCallExpr->getDirectCallee()); if (calleeMethodDecl) { if (operatorCallExpr->getOperator() == OO_Equal) {
assignValue = getExprValue(operatorCallExpr->getArg(1));
bPotentiallyAssignedTo = true; return;
}
}
}
} for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) { if (i >= functionDecl->getNumParams()) // can happen in template code break; if (callExpr->getArg(i) == child) { const ParmVarDecl* parmVarDecl = functionDecl->getParamDecl(i);
QualType qt = parmVarDecl->getType().getDesugaredType(compiler.getASTContext()); if (!qt.isConstQualified() && qt->isReferenceType()) {
assignValue = "?";
bPotentiallyAssignedTo = true;
} break;
}
} return;
} // check for function pointers const FieldDecl* calleeFieldDecl = dyn_cast_or_null<FieldDecl>(callExpr->getCalleeDecl()); if (!calleeFieldDecl) { return;
}
QualType qt = calleeFieldDecl->getType().getDesugaredType(compiler.getASTContext()); if (!qt->isPointerType()) { return;
}
qt = qt->getPointeeType().getDesugaredType(compiler.getASTContext()); const FunctionProtoType* proto = qt->getAs<FunctionProtoType>(); if (!proto) { return;
} for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) { if (i >= proto->getNumParams()) // can happen in template code break; if (callExpr->getArg(i) == child) {
QualType qt = proto->getParamType(i).getDesugaredType(compiler.getASTContext()); if (!qt.isConstQualified() && qt->isReferenceType()) {
assignValue = "?";
bPotentiallyAssignedTo = true;
} break;
}
}
}
std::string SingleValFields::getExprValue(const Expr* arg)
{ if (!arg) return"?";
arg = arg->IgnoreParenCasts();
arg = arg->IgnoreImplicit(); // ignore this, it seems to trigger an infinite recursion if (isa<UnaryExprOrTypeTraitExpr>(arg)) return"?"; if (arg->isValueDependent()) return"?"; // for stuff like: OUString foo = "xxx"; if (auto stringLiteral = dyn_cast<clang::StringLiteral>(arg))
{ if (stringLiteral->getCharByteWidth() == 1) return stringLiteral->getString().str(); return"?";
} // ParenListExpr containing a CXXNullPtrLiteralExpr and has a NULL type pointer if (auto parenListExpr = dyn_cast<ParenListExpr>(arg))
{ if (parenListExpr->getNumExprs() == 1) return getExprValue(parenListExpr->getExpr(0)); return"?";
} if (auto constructExpr = dyn_cast<CXXConstructExpr>(arg))
{ if (constructExpr->getNumArgs() >= 1
&& isa<clang::StringLiteral>(constructExpr->getArg(0)))
{ auto stringLiteral = dyn_cast<clang::StringLiteral>(constructExpr->getArg(0)); if (stringLiteral->getCharByteWidth() == 1) return stringLiteral->getString().str(); return"?";
}
} if (arg->getType()->isFloatingType())
{
APFloat x1(0.0f); if (arg->EvaluateAsFloat(x1, compiler.getASTContext()))
{
std::string s;
llvm::raw_string_ostream os(s);
x1.print(os); return os.str();
}
}
APSInt x1; if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext())) return compat::toString(x1, 10); if (isa<CXXNullPtrLiteralExpr>(arg)) return"0"; return"?";
}
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.