/* -*- 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/.
*/
This analysis attempts to implement "logical const" as opposed to "technical const", which means we ignore always-const nature of std::unique_ptr::operator->
This is not a sophisticated analysis. It deliberately skips all of the hard cases for now. It is an exercise in getting the most benefit for the least effort.
*/ namespace
{
class ConstMethod: public loplugin::FunctionAddress<loplugin::FilteringPlugin<ConstMethod>>
{ public: explicit ConstMethod(loplugin::InstantiationData const & data): FunctionAddress(data) {}
bool ConstMethod::VisitCXXMethodDecl(const CXXMethodDecl * cxxMethodDecl)
{ if (ignoreLocation(cxxMethodDecl) || !cxxMethodDecl->isThisDeclarationADefinition()) { returntrue;
} if (cxxMethodDecl->isConst()) returntrue; // ignore stuff that forms part of the stable URE interface if (isInUnoIncludeFile(cxxMethodDecl)) { returntrue;
} // TODO ignore template stuff for now if (cxxMethodDecl->getTemplatedKind() != FunctionDecl::TK_NonTemplate) { returntrue;
} if (cxxMethodDecl->isDeleted()) returntrue; if (cxxMethodDecl->isStatic()) returntrue; if (cxxMethodDecl->isOverloadedOperator()) returntrue; if (isa<CXXConstructorDecl>(cxxMethodDecl)) returntrue; if (isa<CXXDestructorDecl>(cxxMethodDecl)) returntrue; if (cxxMethodDecl->getParent()->getDescribedClassTemplate() != nullptr ) { returntrue;
} // ignore virtual methods if (cxxMethodDecl->isVirtual() ) { returntrue;
} // ignore macro expansions so we can ignore the IMPL_LINK macros from include/tools/link.hxx // TODO make this more precise if (cxxMethodDecl->getLocation().isMacroID()) returntrue;
if (!cxxMethodDecl->getIdentifier()) returntrue; // if (cxxMethodDecl->getNumParams() > 0) // return true; // returning pointers or refs to non-const stuff, and then having the whole method // be const doesn't seem like a good idea auto tc = loplugin::TypeCheck(cxxMethodDecl->getReturnType()); if (tc.Pointer().NonConst()) returntrue; if (tc.LvalueReference().NonConst()) returntrue; // a Get method that returns void is probably doing something that has side-effects if (tc.Void()) returntrue;
// something lacking in my analysis here if (loplugin::DeclCheck(cxxMethodDecl).Function("GetDescr").Class("SwRangeRedline").GlobalNamespace()) returntrue;
interestingMethodSet.insert(cxxMethodDecl);
returntrue;
}
bool ConstMethod::VisitCXXThisExpr( const CXXThisExpr* cxxThisExpr )
{ if (!currCXXMethodDecl) returntrue; if (ignoreLocation(cxxThisExpr)) returntrue; // ignore stuff that forms part of the stable URE interface if (isInUnoIncludeFile(cxxThisExpr->getBeginLoc())) returntrue; if (interestingMethodSet.find(currCXXMethodDecl) == interestingMethodSet.end()) returntrue; // no need to check again if we have already eliminated this one if (methodCannotBeConstSet.find(currCXXMethodDecl) != methodCannotBeConstSet.end()) returntrue; if (!checkIfCanBeConst(cxxThisExpr, currCXXMethodDecl))
methodCannotBeConstSet.insert(currCXXMethodDecl);
returntrue;
}
// Walk up from a statement that contains a CXXThisExpr, checking if the usage means that the // related CXXMethodDecl can be const. bool ConstMethod::checkIfCanBeConst(const Stmt* stmt, const CXXMethodDecl* cxxMethodDecl)
{ const Stmt* parent = getParentStmt( stmt ); if (!parent) { auto parentsRange = compiler.getASTContext().getParents(*stmt); if ( parentsRange.begin() == parentsRange.end()) returntrue; auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>()); if (!varDecl)
{
report(
DiagnosticsEngine::Warning, "no parent?",
stmt->getBeginLoc())
<< stmt->getSourceRange(); returnfalse;
} return varDecl->getType()->isIntegralOrEnumerationType()
|| loplugin::TypeCheck(varDecl->getType()).Pointer().Const()
|| loplugin::TypeCheck(varDecl->getType()).LvalueReference().Const();
}
if (auto unaryOperator = dyn_cast<UnaryOperator>(parent)) {
UnaryOperator::Opcode op = unaryOperator->getOpcode(); if (op == UO_PreInc || op == UO_PostInc
|| op == UO_PreDec || op == UO_PostDec) { returnfalse;
} if (op == UO_Deref || op == UO_AddrOf) { return checkIfCanBeConst(parent, cxxMethodDecl);
} returntrue;
} elseif (auto binaryOp = dyn_cast<BinaryOperator>(parent)) {
BinaryOperator::Opcode op = binaryOp->getOpcode(); if (binaryOp->getRHS() == stmt) { returntrue;
} 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) { returnfalse;
} // // for pointer arithmetic need to check parent // if (binaryOp->getType()->isPointerType()) { // return checkIfCanBeConst(parent, cxxMethodDecl); // } returntrue;
} elseif (auto constructExpr = dyn_cast<CXXConstructExpr>(parent)) { const CXXConstructorDecl * constructorDecl = constructExpr->getConstructor(); for (unsigned i = 0; i < constructExpr->getNumArgs(); ++i) { if (constructExpr->getArg(i) == stmt) { return isPointerOrReferenceToConst(constructorDecl->getParamDecl(i)->getType());
}
} returnfalse; // TODO ??
} elseif (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent)) { const CXXMethodDecl* calleeMethodDecl = dyn_cast_or_null<CXXMethodDecl>(operatorCallExpr->getDirectCallee()); if (calleeMethodDecl) { // unary operator if (calleeMethodDecl->getNumParams() == 0) { // some classes like std::unique_ptr do not do a very good job with their operator-> which is always const if (operatorCallExpr->getOperator() == OO_Arrow || operatorCallExpr->getOperator() == OO_Star) { return checkIfCanBeConst(parent, cxxMethodDecl);
} return calleeMethodDecl->isConst();
} // some classes like std::unique_ptr do not do a very good job with their operator[] which is always const if (calleeMethodDecl->getNumParams() == 1 && operatorCallExpr->getArg(0) == stmt) { if (operatorCallExpr->getOperator() == OO_Subscript) { returnfalse;
}
} // binary operator if (operatorCallExpr->getArg(0) == stmt) { return calleeMethodDecl->isConst();
} unsignedconst n = std::min(
operatorCallExpr->getNumArgs(),
calleeMethodDecl->getNumParams()); for (unsigned i = 1; i < n; ++i) if (operatorCallExpr->getArg(i) == stmt) { return isPointerOrReferenceToConst(calleeMethodDecl->getParamDecl(i - 1)->getType());
}
} else { const Expr* callee = operatorCallExpr->getCallee()->IgnoreParenImpCasts(); const DeclRefExpr* dr = dyn_cast<DeclRefExpr>(callee); const FunctionDecl* calleeFunctionDecl = nullptr; if (dr) {
calleeFunctionDecl = dyn_cast<FunctionDecl>(dr->getDecl());
} if (calleeFunctionDecl) { for (unsigned i = 0; i < operatorCallExpr->getNumArgs(); ++i) { if (operatorCallExpr->getArg(i) == stmt) { return isPointerOrReferenceToConst(calleeFunctionDecl->getParamDecl(i)->getType());
}
}
}
} returnfalse; // TODO ???
} elseif (auto callExpr = dyn_cast<CallExpr>(parent)) {
QualType functionType = callExpr->getCallee()->getType(); if (functionType->isFunctionPointerType()) {
functionType = functionType->getPointeeType();
} if (const FunctionProtoType* prototype = functionType->getAs<FunctionProtoType>()) { // TODO could do better if (prototype->isVariadic()) { returnfalse;
} if (callExpr->getCallee() == stmt) { returntrue;
} for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) { if (callExpr->getArg(i) == stmt) { return isPointerOrReferenceToConst(prototype->getParamType(i));
}
}
} const FunctionDecl* calleeFunctionDecl = callExpr->getDirectCallee(); if (calleeFunctionDecl)
{ if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(parent)) { const MemberExpr* memberExpr = dyn_cast<MemberExpr>(stmt); if (memberExpr && memberCallExpr->getImplicitObjectArgument() == memberExpr->getBase())
{ const CXXMethodDecl* calleeMethodDecl = dyn_cast<CXXMethodDecl>(calleeFunctionDecl); // some classes like std::unique_ptr do not do a very good job with their get() which is always const if (calleeMethodDecl->getIdentifier() && calleeMethodDecl->getName() == "get") { return checkIfCanBeConst(parent, cxxMethodDecl);
} // VclPtr<T>'s implicit conversion to T* if (isa<CXXConversionDecl>(calleeMethodDecl)) { if (loplugin::DeclCheck(calleeMethodDecl->getParent()).Class("OWeakObject").Namespace("cppu").GlobalNamespace()) returnfalse; return checkIfCanBeConst(parent, cxxMethodDecl);
} return calleeMethodDecl->isConst();
}
} // TODO could do better if (calleeFunctionDecl->isVariadic()) { returnfalse;
} if (callExpr->getCallee() == stmt) { returntrue;
} for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) { if (i >= calleeFunctionDecl->getNumParams()) // can happen in template code returnfalse; if (callExpr->getArg(i) == stmt) { return isPointerOrReferenceToConst(calleeFunctionDecl->getParamDecl(i)->getType());
}
}
} returnfalse; // TODO ???? // } else if (auto callExpr = dyn_cast<ObjCMessageExpr>(parent)) { // if (callExpr->getInstanceReceiver() == stmt) { // return true; // } // if (auto const method = callExpr->getMethodDecl()) { // // TODO could do better // if (method->isVariadic()) { // return false; // } // assert(method->param_size() == callExpr->getNumArgs()); // for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) { // if (callExpr->getArg(i) == stmt) { // return isPointerOrReferenceToConst( // method->param_begin()[i]->getType()); // } // } // } // return false; // TODO ????
} elseif (isa<CXXReinterpretCastExpr>(parent)) { returnfalse;
} elseif (isa<ImplicitCastExpr>(parent)) { return checkIfCanBeConst(parent, cxxMethodDecl);
} elseif (isa<CXXStaticCastExpr>(parent)) { return checkIfCanBeConst(parent, cxxMethodDecl);
} elseif (isa<CXXDynamicCastExpr>(parent)) { return checkIfCanBeConst(parent, cxxMethodDecl);
} elseif (isa<CXXFunctionalCastExpr>(parent)) { return checkIfCanBeConst(parent, cxxMethodDecl);
} elseif (isa<CXXConstCastExpr>(parent)) { returnfalse;
} elseif (isa<CStyleCastExpr>(parent)) { return checkIfCanBeConst(parent, cxxMethodDecl); // } else if (isa<CastExpr>(parent)) { // all other cast expression subtypes // if (auto e = dyn_cast<ExplicitCastExpr>(parent)) { // if (loplugin::TypeCheck(e->getTypeAsWritten()).Void()) { // if (auto const sub = dyn_cast<DeclRefExpr>( // e->getSubExpr()->IgnoreParenImpCasts())) // { // if (sub->getDecl() == cxxMethodDecl) { // return false; // } // } // } // } // return checkIfCanBeConst(parent, cxxMethodDecl);
} elseif (isa<MemberExpr>(parent)) { return checkIfCanBeConst(parent, cxxMethodDecl);
} elseif (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent)) { if (arraySubscriptExpr->getIdx() == stmt) returntrue; return checkIfCanBeConst(parent, cxxMethodDecl);
} elseif (isa<ParenExpr>(parent)) { return checkIfCanBeConst(parent, cxxMethodDecl);
} elseif (auto declStmt = dyn_cast<DeclStmt>(parent)) { for (Decl const * decl : declStmt->decls()) if (auto varDecl = dyn_cast<VarDecl>(decl)) { if (varDecl->getInit() == stmt) { auto tc = loplugin::TypeCheck(varDecl->getType()); if (tc.LvalueReference() && !tc.LvalueReference().Const()) returnfalse; if (tc.Pointer() && !tc.Pointer().Const()) returnfalse; returntrue;
}
} // fall through
} elseif (isa<ReturnStmt>(parent)) { return !isPointerOrReferenceToNonConst(cxxMethodDecl->getReturnType());
} elseif (isa<InitListExpr>(parent)) { returnfalse; // TODO could be improved
} elseif (isa<IfStmt>(parent)) { returntrue;
} elseif (isa<WhileStmt>(parent)) { returntrue;
} elseif (isa<ForStmt>(parent)) { returntrue;
} elseif (isa<CompoundStmt>(parent)) { returntrue;
} elseif (isa<SwitchStmt>(parent)) { returntrue;
} elseif (isa<DoStmt>(parent)) { returntrue;
} elseif (isa<CXXDeleteExpr>(parent)) { returnfalse; // } else if (isa<VAArgExpr>(parent)) { // return false;
} elseif (isa<CXXDependentScopeMemberExpr>(parent)) { returnfalse;
} elseif (isa<MaterializeTemporaryExpr>(parent)) { return checkIfCanBeConst(parent, cxxMethodDecl);
} elseif (auto conditionalExpr = dyn_cast<ConditionalOperator>(parent)) { if (conditionalExpr->getCond() == stmt) returntrue; return checkIfCanBeConst(parent, cxxMethodDecl); // } else if (isa<UnaryExprOrTypeTraitExpr>(parent)) { // return false; // ???
} elseif (isa<CXXNewExpr>(parent)) { // for (auto pa : cxxNewExpr->placement_arguments()) // if (pa == stmt) // return false; returntrue; // because the Stmt must be a parameter to the expression, probably an array length // } else if (auto lambdaExpr = dyn_cast<LambdaExpr>(parent)) { //// for (auto it = lambdaExpr->capture_begin(); it != lambdaExpr->capture_end(); ++it) //// { //// if (it->capturesVariable() && it->getCapturedVar() == cxxMethodDecl) //// return it->getCaptureKind() != LCK_ByRef; //// } // return true; // } else if (isa<CXXTypeidExpr>(parent)) { // return true;
} elseif (isa<ParenListExpr>(parent)) { returntrue;
} elseif (isa<CXXUnresolvedConstructExpr>(parent)) { returnfalse; // } else if (isa<UnresolvedMemberExpr>(parent)) { // return false; // } else if (isa<PackExpansionExpr>(parent)) { // return false;
} elseif (isa<ExprWithCleanups>(parent)) { return checkIfCanBeConst(parent, cxxMethodDecl); // } else if (isa<CaseStmt>(parent)) { // return true; // } else if (isa<CXXPseudoDestructorExpr>(parent)) { // return false; // } else if (isa<CXXDependentScopeMemberExpr>(parent)) { // return false; // } else if (isa<ObjCIvarRefExpr>(parent)) { // return checkIfCanBeConst(parent, cxxMethodDecl);
} elseif (isa<CXXTemporaryObjectExpr>(parent)) { returntrue;
} elseif (isa<CXXBindTemporaryExpr>(parent)) { returntrue;
} if (parent)
parent->dump(); // if (cxxMethodDecl) // cxxMethodDecl->dump();
report(
DiagnosticsEngine::Warning, "oh dear, what can the matter be?",
parent->getBeginLoc())
<< parent->getSourceRange(); returnfalse;
}
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 ist noch experimentell.