// try to limit the voluminous output a little // if the value is nullptr, that indicates that we touched that field from more than one function static std::unordered_map<const FieldDecl*, const FunctionDecl*> touchedMap;
class FieldCanBeLocal : public loplugin::FilteringPlugin<FieldCanBeLocal>
{ public: explicit FieldCanBeLocal(loplugin::InstantiationData const& data)
: FilteringPlugin(data)
{
}
RecordDecl* insideMoveOrCopyOrCloneDeclParent = nullptr;
RecordDecl* insideStreamOutputOperator = nullptr; // For reasons I do not understand, parentFunctionDecl() is not reliable, so // we store the parent function on the way down the AST.
FunctionDecl* insideFunctionDecl = nullptr;
};
aInfo.fieldName = fieldDecl->getNameAsString(); // sometimes the name (if it's an anonymous thing) contains the full path of the build folder, which we don't need
size_t idx = aInfo.fieldName.find(SRCDIR); if (idx != std::string::npos)
{
aInfo.fieldName = aInfo.fieldName.replace(idx, strlen(SRCDIR), "");
}
aInfo.fieldType = fieldDecl->getType().getAsString();
bool FieldCanBeLocal::TraverseCXXConstructorDecl(CXXConstructorDecl* cxxConstructorDecl)
{ auto copy = insideMoveOrCopyOrCloneDeclParent; if (!ignoreLocation(cxxConstructorDecl->getBeginLoc())
&& cxxConstructorDecl->isThisDeclarationADefinition())
{ if (cxxConstructorDecl->isCopyOrMoveConstructor())
insideMoveOrCopyOrCloneDeclParent = cxxConstructorDecl->getParent();
} bool ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(cxxConstructorDecl);
insideMoveOrCopyOrCloneDeclParent = copy; return ret;
}
bool FieldCanBeLocal::TraverseCXXMethodDecl(CXXMethodDecl* cxxMethodDecl)
{ auto copy1 = insideMoveOrCopyOrCloneDeclParent; auto copy2 = insideFunctionDecl; if (!ignoreLocation(cxxMethodDecl->getBeginLoc())
&& cxxMethodDecl->isThisDeclarationADefinition())
{ if (cxxMethodDecl->isCopyAssignmentOperator() || cxxMethodDecl->isMoveAssignmentOperator()
|| (cxxMethodDecl->getIdentifier()
&& (compat::starts_with(cxxMethodDecl->getName(), "Clone")
|| compat::starts_with(cxxMethodDecl->getName(), "clone")
|| compat::starts_with(cxxMethodDecl->getName(), "createClone"))))
insideMoveOrCopyOrCloneDeclParent = cxxMethodDecl->getParent(); // these are similar in that they tend to simply enumerate all the fields of an object without putting // them to some useful purpose auto op = cxxMethodDecl->getOverloadedOperator(); if (op == OO_EqualEqual || op == OO_ExclaimEqual)
insideMoveOrCopyOrCloneDeclParent = cxxMethodDecl->getParent();
}
insideFunctionDecl = cxxMethodDecl; bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(cxxMethodDecl);
insideMoveOrCopyOrCloneDeclParent = copy1;
insideFunctionDecl = copy2; return ret;
}
bool FieldCanBeLocal::TraverseFunctionDecl(FunctionDecl* functionDecl)
{ auto copy1 = insideStreamOutputOperator; auto copy2 = insideFunctionDecl; auto copy3 = insideMoveOrCopyOrCloneDeclParent; if (functionDecl->getLocation().isValid() && !ignoreLocation(functionDecl->getBeginLoc())
&& functionDecl->isThisDeclarationADefinition())
{ auto op = functionDecl->getOverloadedOperator(); if (op == OO_LessLess && functionDecl->getNumParams() == 2)
{
QualType qt = functionDecl->getParamDecl(1)->getType();
insideStreamOutputOperator
= qt.getNonReferenceType().getUnqualifiedType()->getAsCXXRecordDecl();
} // these are similar in that they tend to simply enumerate all the fields of an object without putting // them to some useful purpose if (op == OO_EqualEqual || op == OO_ExclaimEqual)
{
QualType qt = functionDecl->getParamDecl(1)->getType();
insideMoveOrCopyOrCloneDeclParent
= qt.getNonReferenceType().getUnqualifiedType()->getAsCXXRecordDecl();
}
}
insideFunctionDecl = functionDecl; bool ret = RecursiveASTVisitor::TraverseFunctionDecl(functionDecl);
insideStreamOutputOperator = copy1;
insideFunctionDecl = copy2;
insideMoveOrCopyOrCloneDeclParent = copy3; return ret;
}
bool FieldCanBeLocal::VisitMemberExpr(const MemberExpr* memberExpr)
{ const ValueDecl* decl = memberExpr->getMemberDecl(); const FieldDecl* fieldDecl = dyn_cast<FieldDecl>(decl); if (!fieldDecl)
{ returntrue;
}
fieldDecl = fieldDecl->getCanonicalDecl(); if (ignoreLocation(fieldDecl->getBeginLoc()))
{ returntrue;
} // ignore stuff that forms part of the stable URE interface if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(fieldDecl->getLocation())))
{ returntrue;
}
if (insideMoveOrCopyOrCloneDeclParent || insideStreamOutputOperator)
{
RecordDecl const* cxxRecordDecl1 = fieldDecl->getParent(); // we don't care about reads from a field when inside the copy/move constructor/operator= for that field if (cxxRecordDecl1 && (cxxRecordDecl1 == insideMoveOrCopyOrCloneDeclParent)) returntrue; // we don't care about reads when the field is being used in an output operator, this is normally // debug stuff if (cxxRecordDecl1 && (cxxRecordDecl1 == insideStreamOutputOperator)) returntrue;
}
checkTouched(fieldDecl, insideFunctionDecl);
returntrue;
}
bool FieldCanBeLocal::VisitDeclRefExpr(const DeclRefExpr* declRefExpr)
{ const Decl* decl = declRefExpr->getDecl(); const FieldDecl* fieldDecl = dyn_cast<FieldDecl>(decl); if (!fieldDecl)
{ returntrue;
}
fieldDecl = fieldDecl->getCanonicalDecl(); if (ignoreLocation(fieldDecl->getBeginLoc()))
{ returntrue;
} // ignore stuff that forms part of the stable URE interface if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(fieldDecl->getLocation())))
{ returntrue;
}
checkTouched(fieldDecl, insideFunctionDecl);
returntrue;
}
// fields that are assigned via member initialisers do not get visited in VisitDeclRef, so // have to do it here bool FieldCanBeLocal::VisitCXXConstructorDecl(const CXXConstructorDecl* cxxConstructorDecl)
{ if (ignoreLocation(cxxConstructorDecl->getBeginLoc()))
{ returntrue;
} // ignore stuff that forms part of the stable URE interface if (isInUnoIncludeFile(
compiler.getSourceManager().getSpellingLoc(cxxConstructorDecl->getLocation())))
{ returntrue;
}
// templates make EvaluateAsInt crash inside clang if (cxxConstructorDecl->isDependentContext()) returntrue;
// we don't care about writes to a field when inside the copy/move constructor/operator= for that field if (insideMoveOrCopyOrCloneDeclParent
&& cxxConstructorDecl->getParent() == insideMoveOrCopyOrCloneDeclParent) returntrue;
for (auto it = cxxConstructorDecl->init_begin(); it != cxxConstructorDecl->init_end(); ++it)
{ const CXXCtorInitializer* init = *it; const FieldDecl* fieldDecl = init->getMember(); if (!fieldDecl) continue; if (init->getInit() && isSomeKindOfConstant(init->getInit()))
checkTouched(fieldDecl, cxxConstructorDecl); else
touchedMap[fieldDecl] = nullptr;
} returntrue;
}
// Fields that are assigned via init-list-expr do not get visited in VisitDeclRef, so // have to do it here. bool FieldCanBeLocal::VisitInitListExpr(const InitListExpr* initListExpr)
{ if (ignoreLocation(initListExpr->getBeginLoc())) returntrue;
QualType varType = initListExpr->getType().getDesugaredType(compiler.getASTContext()); auto recordType = varType->getAs<RecordType>(); if (!recordType) returntrue;
auto recordDecl = recordType->getDecl(); for (auto it = recordDecl->field_begin(); it != recordDecl->field_end(); ++it)
{
checkTouched(*it, insideFunctionDecl);
}
returntrue;
}
void FieldCanBeLocal::checkTouched(const FieldDecl* fieldDecl, const FunctionDecl* functionDecl)
{ auto methodDecl = dyn_cast_or_null<CXXMethodDecl>(functionDecl); if (!methodDecl)
{
touchedMap[fieldDecl] = nullptr; return;
} if (methodDecl->getParent() != fieldDecl->getParent())
{
touchedMap[fieldDecl] = nullptr; return;
} auto it = touchedMap.find(fieldDecl); if (it == touchedMap.end())
touchedMap.emplace(fieldDecl, functionDecl); elseif (it->second != functionDecl)
it->second = nullptr;
}
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.