summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/constparams.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /compilerplugins/clang/constparams.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compilerplugins/clang/constparams.cxx')
-rw-r--r--compilerplugins/clang/constparams.cxx595
1 files changed, 595 insertions, 0 deletions
diff --git a/compilerplugins/clang/constparams.cxx b/compilerplugins/clang/constparams.cxx
new file mode 100644
index 000000000..805949518
--- /dev/null
+++ b/compilerplugins/clang/constparams.cxx
@@ -0,0 +1,595 @@
+/* -*- 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 = 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: */