diff options
Diffstat (limited to 'compilerplugins/clang/constparams.cxx')
-rw-r--r-- | compilerplugins/clang/constparams.cxx | 595 |
1 files changed, 0 insertions, 595 deletions
diff --git a/compilerplugins/clang/constparams.cxx b/compilerplugins/clang/constparams.cxx deleted file mode 100644 index 95c8184009d7..000000000000 --- a/compilerplugins/clang/constparams.cxx +++ /dev/null @@ -1,595 +0,0 @@ -/* -*- 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/. - */ - -#include <algorithm> -#include <string> -#include <unordered_set> -#include <unordered_map> -#include <iostream> - -#include "config_clang.h" - -#include "plugin.hxx" -#include "compat.hxx" -#include "check.hxx" -#include "functionaddress.hxx" - -#if CLANG_VERSION >= 110000 -#include "clang/AST/ParentMapContext.h" -#endif - -/** - Find pointer and reference params that can be declared const. - - 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 ConstParams: - public loplugin::FunctionAddress<loplugin::FilteringPlugin<ConstParams>> -{ -public: - explicit ConstParams(loplugin::InstantiationData const & data): FunctionAddress(data) {} - - virtual void run() override { - std::string fn(handler.getMainFileName()); - loplugin::normalizeDotDotInFilePath(fn); - if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/") - || fn == SRCDIR "/jurt/source/pipe/staticsalhack.cxx" - || loplugin::hasPathnamePrefix(fn, SRCDIR "/bridges/") - || loplugin::hasPathnamePrefix(fn, SRCDIR "/binaryurp/") - || loplugin::hasPathnamePrefix(fn, SRCDIR "/stoc/") - || loplugin::hasPathnamePrefix(fn, WORKDIR "/YaccTarget/unoidl/source/sourceprovider-parser.cxx") - // some weird calling through a function pointer - || loplugin::hasPathnamePrefix(fn, SRCDIR "/svtools/source/table/defaultinputhandler.cxx") - || loplugin::hasPathnamePrefix(fn, SRCDIR "/sdext/source/pdfimport/test/pdfunzip.cxx") - // windows only - || loplugin::hasPathnamePrefix(fn, SRCDIR "/basic/source/sbx/sbxdec.cxx") - || loplugin::hasPathnamePrefix(fn, SRCDIR "/sfx2/source/doc/syspath.cxx") - // ignore this for now - || loplugin::hasPathnamePrefix(fn, SRCDIR "/libreofficekit") - // FunctionAddress not working well enough here - || loplugin::hasPathnamePrefix(fn, SRCDIR "/pyuno/source/module/pyuno_struct.cxx") - || loplugin::hasPathnamePrefix(fn, SRCDIR "/pyuno/source/module/pyuno.cxx") - || loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/filter/ascii/ascatr.cxx") - ) - return; - - TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); - - for (const ParmVarDecl *pParmVarDecl : interestingParamSet) { - auto functionDecl = parmToFunction[pParmVarDecl]; - auto canonicalDecl = functionDecl->getCanonicalDecl(); - if (getFunctionsWithAddressTaken().find(canonicalDecl) - != getFunctionsWithAddressTaken().end()) - { - continue; - } - std::string fname = functionDecl->getQualifiedNameAsString(); - report( - DiagnosticsEngine::Warning, - "this parameter can be const %0", - compat::getBeginLoc(pParmVarDecl)) - << fname << pParmVarDecl->getSourceRange(); - if (canonicalDecl->getLocation() != functionDecl->getLocation()) { - unsigned idx = pParmVarDecl->getFunctionScopeIndex(); - const ParmVarDecl* pOther = canonicalDecl->getParamDecl(idx); - report( - DiagnosticsEngine::Note, - "canonical parameter declaration here", - compat::getBeginLoc(pOther)) - << pOther->getSourceRange(); - } - //functionDecl->dump(); - } - } - - bool TraverseFunctionDecl(FunctionDecl *); - bool TraverseCXXMethodDecl(CXXMethodDecl * f); - bool TraverseCXXConstructorDecl(CXXConstructorDecl * f); - bool VisitDeclRefExpr(const DeclRefExpr *); - -private: - bool CheckTraverseFunctionDecl(FunctionDecl *); - bool checkIfCanBeConst(const Stmt*, const ParmVarDecl*); - // integral or enumeration or const * or const & - bool isOkForParameter(const QualType& qt); - bool isPointerOrReferenceToNonConst(const QualType& qt); - - std::unordered_set<const ParmVarDecl*> interestingParamSet; - std::unordered_map<const ParmVarDecl*, const FunctionDecl*> parmToFunction; - FunctionDecl* currentFunctionDecl = nullptr; -}; - -bool ConstParams::TraverseFunctionDecl(FunctionDecl * functionDecl) -{ - // We cannot short-circuit the traverse here entirely without breaking the - // loplugin::FunctionAddress stuff. - auto prev = currentFunctionDecl; - if (CheckTraverseFunctionDecl(functionDecl)) - currentFunctionDecl = functionDecl; - auto rv = FunctionAddress::TraverseFunctionDecl(functionDecl); - currentFunctionDecl = prev; - return rv; -} -bool ConstParams::TraverseCXXMethodDecl(CXXMethodDecl * f) -{ - auto prev = currentFunctionDecl; - if (CheckTraverseFunctionDecl(f)) - currentFunctionDecl = f; - auto rv = FunctionAddress::TraverseCXXMethodDecl(f); - currentFunctionDecl = prev; - return rv; -} -bool ConstParams::TraverseCXXConstructorDecl(CXXConstructorDecl * f) -{ - auto prev = currentFunctionDecl; - if (CheckTraverseFunctionDecl(f)) - currentFunctionDecl = f; - auto rv = FunctionAddress::TraverseCXXConstructorDecl(f); - currentFunctionDecl = prev; - return rv; -} - -bool ConstParams::CheckTraverseFunctionDecl(FunctionDecl * functionDecl) -{ - if (ignoreLocation(functionDecl) || !functionDecl->isThisDeclarationADefinition()) { - return false; - } - // ignore stuff that forms part of the stable URE interface - if (isInUnoIncludeFile(functionDecl)) { - return false; - } - if (functionDecl->isDeleted()) - return false; - // ignore virtual methods - if (isa<CXXMethodDecl>(functionDecl) - && dyn_cast<CXXMethodDecl>(functionDecl)->isVirtual() ) { - return false; - } - // ignore C main - if (functionDecl->isMain()) { - return false; - } - - // ignore the macros from include/tools/link.hxx - auto canonicalDecl = functionDecl->getCanonicalDecl(); - if (compiler.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(canonicalDecl)) - || compiler.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(canonicalDecl))) { - StringRef name { Lexer::getImmediateMacroName( - compat::getBeginLoc(canonicalDecl), compiler.getSourceManager(), compiler.getLangOpts()) }; - if (name.startswith("DECL_LINK") || name.startswith("DECL_STATIC_LINK")) - return false; - auto loc2 = compat::getImmediateExpansionRange(compiler.getSourceManager(), compat::getBeginLoc(canonicalDecl)).first; - if (compiler.getSourceManager().isMacroBodyExpansion(loc2)) - { - StringRef name2 { Lexer::getImmediateMacroName( - loc2, compiler.getSourceManager(), compiler.getLangOpts()) }; - if (name2.startswith("DECL_DLLPRIVATE_LINK")) - return false; - } - } - - if (functionDecl->getIdentifier()) - { - StringRef name = functionDecl->getName(); - if ( name == "file_write" - || name == "SalMainPipeExchangeSignal_impl" - || name.startswith("SbRtl_") - || name == "GoNext" - || name == "GoPrevious" - || name.startswith("Read_F_") - // UNO component entry points - || name.endswith("component_getFactory") - || name == "egiGraphicExport" - || name == "etiGraphicExport" - || name == "epsGraphicExport" - // callback for some external code? - || name == "ScAddInAsyncCallBack" - // used as function pointers - || name == "Read_Footnote" - || name == "Read_Field" - || name == "Read_And" - // passed as a LINK<> to another method - || name == "GlobalBasicErrorHdl_Impl" - // template - || name == "extract_throw" || name == "readProp" - // callbacks - || name == "signalDragDropReceived" || name == "signal_column_clicked" || name == "signal_key_press" - ) - return false; - - } - - std::string fqn = functionDecl->getQualifiedNameAsString(); - if ( fqn == "connectivity::jdbc::GlobalRef::set" - || fqn == "(anonymous namespace)::ReorderNotifier::operator()" - || fqn == "static_txtattr_cast") - return false; - - // calculate the ones we want to check - bool foundInterestingParam = false; - for (const ParmVarDecl *pParmVarDecl : functionDecl->parameters()) { - // ignore unused params - if (pParmVarDecl->getName().empty() - || pParmVarDecl->hasAttr<UnusedAttr>()) - continue; - auto const type = loplugin::TypeCheck(pParmVarDecl->getType()); - if (!( type.Pointer().NonConst() - || type.LvalueReference().NonConst())) - continue; - // since we normally can't change typedefs, just ignore them - if (isa<TypedefType>(pParmVarDecl->getType())) - continue; - // some typedefs turn into these - if (isa<DecayedType>(pParmVarDecl->getType())) - continue; - // TODO ignore these for now, has some effects I don't understand - if (type.Pointer().Pointer()) - continue; - // const is meaningless when applied to function pointer types - if (pParmVarDecl->getType()->isFunctionPointerType()) - continue; - interestingParamSet.insert(pParmVarDecl); - parmToFunction[pParmVarDecl] = functionDecl; - foundInterestingParam = true; - } - return foundInterestingParam; -} - -bool ConstParams::VisitDeclRefExpr( const DeclRefExpr* declRefExpr ) -{ - if (!currentFunctionDecl) - return true; - const ParmVarDecl* parmVarDecl = dyn_cast_or_null<ParmVarDecl>(declRefExpr->getDecl()); - if (!parmVarDecl) - return true; - if (interestingParamSet.find(parmVarDecl) == interestingParamSet.end()) - return true; - if (!checkIfCanBeConst(declRefExpr, parmVarDecl)) - interestingParamSet.erase(parmVarDecl); - return true; -} - -// Walk up from a statement that contains a DeclRefExpr, checking if the usage means that the -// related ParamVarDecl can be const. -bool ConstParams::checkIfCanBeConst(const Stmt* stmt, const ParmVarDecl* parmVarDecl) -{ - const Stmt* parent = getParentStmt( stmt ); - if (!parent) - { - // check if we're inside a CXXCtorInitializer - auto parentsRange = compiler.getASTContext().getParents(*stmt); - if ( parentsRange.begin() != parentsRange.end()) - { - if (auto cxxConstructorDecl = dyn_cast_or_null<CXXConstructorDecl>(parentsRange.begin()->get<Decl>())) - { - for ( auto cxxCtorInitializer : cxxConstructorDecl->inits()) - { - if ( cxxCtorInitializer->getInit() == stmt) - { - if (cxxCtorInitializer->isAnyMemberInitializer()) - { - // if the member is not pointer-to-const or ref-to-const or value, we cannot make the param const - auto fieldDecl = cxxCtorInitializer->getAnyMember(); - auto tc = loplugin::TypeCheck(fieldDecl->getType()); - if (tc.Pointer() || tc.LvalueReference()) - return tc.Pointer().Const() || tc.LvalueReference().Const(); - else - return true; - } - else - { - // probably base initialiser, but no simple way to look up the relevant constructor decl - return false; - } - } - } - } - if (auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>())) - { - return isOkForParameter(varDecl->getType()); - } - } - parmVarDecl->dump(); - stmt->dump(); - report( - DiagnosticsEngine::Warning, - "no parent?", - compat::getBeginLoc(stmt)) - << stmt->getSourceRange(); - return false; - } - - if (auto unaryOperator = dyn_cast<UnaryOperator>(parent)) { - UnaryOperator::Opcode op = unaryOperator->getOpcode(); - if (op == UO_AddrOf || op == UO_PreInc || op == UO_PostInc - || op == UO_PreDec || op == UO_PostDec) { - return false; - } - if (op == UO_Deref) { - return checkIfCanBeConst(parent, parmVarDecl); - } - return true; - } else if (auto binaryOp = dyn_cast<BinaryOperator>(parent)) { - BinaryOperator::Opcode op = binaryOp->getOpcode(); - if (binaryOp->getRHS() == stmt && op == BO_Assign) { - return isOkForParameter(binaryOp->getLHS()->getType()); - } - if (binaryOp->getRHS() == stmt) { - return true; - } - 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) { - return false; - } - // for pointer arithmetic need to check parent - if (binaryOp->getType()->isPointerType()) { - return checkIfCanBeConst(parent, parmVarDecl); - } - return true; - } else if (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 isOkForParameter(constructorDecl->getParamDecl(i)->getType()); - } - } - } else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent)) { - const CXXMethodDecl* calleeMethodDecl = dyn_cast_or_null<CXXMethodDecl>(operatorCallExpr->getDirectCallee()); - if (calleeMethodDecl) { - // unary operator - if (calleeMethodDecl->getNumParams() == 0) - return calleeMethodDecl->isConst(); - // Same logic as CXXOperatorCallExpr::isAssignmentOp(), which our supported clang - // doesn't have yet. - auto Opc = operatorCallExpr->getOperator(); - if (Opc == OO_Equal || Opc == OO_StarEqual || - Opc == OO_SlashEqual || Opc == OO_PercentEqual || - Opc == OO_PlusEqual || Opc == OO_MinusEqual || - Opc == OO_LessLessEqual || Opc == OO_GreaterGreaterEqual || - Opc == OO_AmpEqual || Opc == OO_CaretEqual || - Opc == OO_PipeEqual) - { - if (operatorCallExpr->getArg(0) == stmt) // assigning to the param - return false; - // not all operator= take a const& - return isOkForParameter(calleeMethodDecl->getParamDecl(0)->getType()); - } - if (operatorCallExpr->getOperator() == OO_Subscript && operatorCallExpr->getArg(1) == stmt) - return true; - if (operatorCallExpr->getOperator() == OO_EqualEqual || operatorCallExpr->getOperator() == OO_ExclaimEqual) - return true; - // binary operator - if (operatorCallExpr->getArg(0) == stmt) - return calleeMethodDecl->isConst(); - unsigned const n = std::min( - operatorCallExpr->getNumArgs(), - calleeMethodDecl->getNumParams() + 1); - for (unsigned i = 1; i < n; ++i) - if (operatorCallExpr->getArg(i) == stmt) { - auto qt = calleeMethodDecl->getParamDecl(i - 1)->getType(); - return isOkForParameter(qt); - } - } 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 isOkForParameter(calleeFunctionDecl->getParamDecl(i)->getType()); - } - } - } - } - return false; - } else if (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()) { - return false; - } - if (callExpr->getCallee() == stmt) { - return true; - } - for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) { - if (callExpr->getArg(i) == stmt) { - return isOkForParameter(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); - return calleeMethodDecl->isConst(); - } - } - // TODO could do better - if (calleeFunctionDecl->isVariadic()) { - return false; - } - if (callExpr->getCallee() == stmt) { - return true; - } - for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) { - if (i >= calleeFunctionDecl->getNumParams()) // can happen in template code - return false; - if (callExpr->getArg(i) == stmt) { - return isOkForParameter(calleeFunctionDecl->getParamDecl(i)->getType()); - } - } - } - return false; - } 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 isOkForParameter( - method->param_begin()[i]->getType()); - } - } - } - } else if (isa<CXXReinterpretCastExpr>(parent)) { - return false; - } else if (isa<CXXConstCastExpr>(parent)) { - return false; - } 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() == parmVarDecl) - return false; - } - } - } - return checkIfCanBeConst(parent, parmVarDecl); - } else if (isa<MemberExpr>(parent)) { - return checkIfCanBeConst(parent, parmVarDecl); - } else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent)) { - if (arraySubscriptExpr->getIdx() == stmt) - return true; - return checkIfCanBeConst(parent, parmVarDecl); - } else if (isa<ParenExpr>(parent)) { - return checkIfCanBeConst(parent, parmVarDecl); - } else if (isa<DeclStmt>(parent)) { - // TODO could do better here, but would require tracking the target(s) - //return false; - } else if (isa<ReturnStmt>(parent)) { - return !isPointerOrReferenceToNonConst(currentFunctionDecl->getReturnType()); - } else if (isa<InitListExpr>(parent)) { - return false; - } else if (isa<IfStmt>(parent)) { - return true; - } else if (isa<WhileStmt>(parent)) { - return true; - } else if (isa<ForStmt>(parent)) { - return true; - } else if (isa<CompoundStmt>(parent)) { - return true; - } else if (isa<SwitchStmt>(parent)) { - return true; - } else if (isa<DoStmt>(parent)) { - return true; - } else if (isa<CXXDeleteExpr>(parent)) { - return false; - } else if (isa<VAArgExpr>(parent)) { - return false; - } else if (isa<CXXDependentScopeMemberExpr>(parent)) { - return false; - } else if (isa<MaterializeTemporaryExpr>(parent)) { - return checkIfCanBeConst(parent, parmVarDecl); - } else if (auto conditionalExpr = dyn_cast<ConditionalOperator>(parent)) { - if (conditionalExpr->getCond() == stmt) - return true; - return checkIfCanBeConst(parent, parmVarDecl); - } else if (isa<UnaryExprOrTypeTraitExpr>(parent)) { - return false; // ??? - } else if (auto cxxNewExpr = dyn_cast<CXXNewExpr>(parent)) { - for (unsigned i = 0; i < cxxNewExpr->getNumPlacementArgs(); ++i) - if (cxxNewExpr->getPlacementArg(i) == stmt) - return false; - return true; // ??? - } else if (auto lambdaExpr = dyn_cast<LambdaExpr>(parent)) { - for (auto it = lambdaExpr->capture_begin(); it != lambdaExpr->capture_end(); ++it) - { - if (it->capturesVariable() && it->getCapturedVar() == parmVarDecl) - return it->getCaptureKind() != LCK_ByRef; - } - return false; - } else if (isa<CXXTypeidExpr>(parent)) { - return true; - } else if (isa<ParenListExpr>(parent)) { - return false; // could be improved, seen in constructors when calling base class constructor - } else if (isa<CXXUnresolvedConstructExpr>(parent)) { - return false; - } else if (isa<UnresolvedMemberExpr>(parent)) { - return false; - } else if (isa<PackExpansionExpr>(parent)) { - return false; - } else if (isa<ExprWithCleanups>(parent)) { - return checkIfCanBeConst(parent, parmVarDecl); - } 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, parmVarDecl); - } - parent->dump(); - parmVarDecl->dump(); - report( - DiagnosticsEngine::Warning, - "oh dear, what can the matter be?", - compat::getBeginLoc(parent)) - << parent->getSourceRange(); - return true; -} - -bool ConstParams::isOkForParameter(const QualType& qt) { - if (qt->isIntegralOrEnumerationType()) - return true; - auto const type = loplugin::TypeCheck(qt); - if (type.Pointer()) { - return bool(type.Pointer().Const()); - } else if (type.LvalueReference().Const().Pointer()) { - // If we have a method that takes (T* t) and it calls std::vector<T*>::push_back - // then the type of push_back is T * const & - // There is probably a more elegant way to check this, but it will probably require - // recalculating types while walking up the AST. - return false; - } else if (type.LvalueReference()) { - return bool(type.LvalueReference().Const()); - } - return false; -} - -bool ConstParams::isPointerOrReferenceToNonConst(const QualType& qt) { - auto const type = loplugin::TypeCheck(qt); - if (type.Pointer()) { - return !bool(type.Pointer().Const()); - } else if (type.LvalueReference()) { - return !bool(type.LvalueReference().Const()); - } - return false; -} - -loplugin::Plugin::Registration< ConstParams > X("constparams", false); - -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |