bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / constparams.cxx
blob94c4f74bee61bfd001fdf36fa9abe1096e111da1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <algorithm>
11 #include <string>
12 #include <unordered_set>
13 #include <unordered_map>
14 #include <iostream>
16 #include "plugin.hxx"
17 #include "compat.hxx"
18 #include "check.hxx"
19 #include "functionaddress.hxx"
21 /**
22 Find pointer and reference params that can be declared const.
24 This is not a sophisticated analysis. It deliberately skips all of the hard cases for now.
25 It is an exercise in getting the most benefit for the least effort.
27 namespace
30 class ConstParams:
31 public loplugin::FunctionAddress<ConstParams>
33 public:
34 explicit ConstParams(loplugin::InstantiationData const & data): loplugin::FunctionAddress<ConstParams>(data) {}
36 virtual void run() override {
37 std::string fn(handler.getMainFileName());
38 loplugin::normalizeDotDotInFilePath(fn);
39 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/sal/")
40 || fn == SRCDIR "/jurt/source/pipe/staticsalhack.cxx"
41 || loplugin::hasPathnamePrefix(fn, SRCDIR "/bridges/")
42 || loplugin::hasPathnamePrefix(fn, SRCDIR "/binaryurp/")
43 || loplugin::hasPathnamePrefix(fn, SRCDIR "/stoc/")
44 || loplugin::hasPathnamePrefix(fn, WORKDIR "/YaccTarget/unoidl/source/sourceprovider-parser.cxx")
45 // some weird calling through a function pointer
46 || loplugin::hasPathnamePrefix(fn, SRCDIR "/svtools/source/table/defaultinputhandler.cxx")
47 || loplugin::hasPathnamePrefix(fn, SRCDIR "/sdext/source/pdfimport/test/pdfunzip.cxx")
48 // windows only
49 || loplugin::hasPathnamePrefix(fn, SRCDIR "/basic/source/sbx/sbxdec.cxx")
50 || loplugin::hasPathnamePrefix(fn, SRCDIR "/sfx2/source/doc/syspath.cxx")
51 // ignore this for now
52 || loplugin::hasPathnamePrefix(fn, SRCDIR "/libreofficekit")
53 // I end up with a
54 // CXXMemberCallExpr
55 // to a
56 // BuiltinType '<bound member function type>'
57 // and the AST gives me no further useful information.
58 || loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/core/doc/docfly.cxx")
59 || loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/core/doc/DocumentContentOperationsManager.cxx")
60 || loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/core/fields/cellfml.cxx")
61 || loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/filter/ww8/ww8par6.cxx")
63 return;
65 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
67 for (const ParmVarDecl *pParmVarDecl : interestingParamSet) {
68 auto functionDecl = parmToFunction[pParmVarDecl];
69 auto canonicalDecl = functionDecl->getCanonicalDecl();
70 if (getFunctionsWithAddressTaken().find(canonicalDecl)
71 != getFunctionsWithAddressTaken().end())
73 continue;
75 std::string fname = functionDecl->getQualifiedNameAsString();
76 report(
77 DiagnosticsEngine::Warning,
78 "this parameter can be const %0",
79 compat::getBeginLoc(pParmVarDecl))
80 << fname << pParmVarDecl->getSourceRange();
81 if (canonicalDecl->getLocation() != functionDecl->getLocation()) {
82 unsigned idx = pParmVarDecl->getFunctionScopeIndex();
83 const ParmVarDecl* pOther = canonicalDecl->getParamDecl(idx);
84 report(
85 DiagnosticsEngine::Note,
86 "canonical parameter declaration here",
87 compat::getBeginLoc(pOther))
88 << pOther->getSourceRange();
90 //functionDecl->dump();
94 bool TraverseFunctionDecl(FunctionDecl *);
95 bool TraverseCXXMethodDecl(CXXMethodDecl * f);
96 bool TraverseCXXConstructorDecl(CXXConstructorDecl * f);
97 bool VisitDeclRefExpr(const DeclRefExpr *);
99 private:
100 bool CheckTraverseFunctionDecl(FunctionDecl *);
101 bool checkIfCanBeConst(const Stmt*, const ParmVarDecl*);
102 // integral or enumeration or const * or const &
103 bool isOkForParameter(const QualType& qt);
104 bool isPointerOrReferenceToNonConst(const QualType& qt);
106 std::unordered_set<const ParmVarDecl*> interestingParamSet;
107 std::unordered_map<const ParmVarDecl*, const FunctionDecl*> parmToFunction;
108 FunctionDecl* currentFunctionDecl = nullptr;
111 bool ConstParams::TraverseFunctionDecl(FunctionDecl * functionDecl)
113 // We cannot short-circuit the traverse here entirely without breaking the
114 // loplugin::FunctionAddress stuff.
115 auto prev = currentFunctionDecl;
116 if (CheckTraverseFunctionDecl(functionDecl))
117 currentFunctionDecl = functionDecl;
118 auto rv = loplugin::FunctionAddress<ConstParams>::TraverseFunctionDecl(functionDecl);
119 currentFunctionDecl = prev;
120 return rv;
122 bool ConstParams::TraverseCXXMethodDecl(CXXMethodDecl * f)
124 auto prev = currentFunctionDecl;
125 if (CheckTraverseFunctionDecl(f))
126 currentFunctionDecl = f;
127 auto rv = loplugin::FunctionAddress<ConstParams>::TraverseCXXMethodDecl(f);
128 currentFunctionDecl = prev;
129 return rv;
131 bool ConstParams::TraverseCXXConstructorDecl(CXXConstructorDecl * f)
133 auto prev = currentFunctionDecl;
134 if (CheckTraverseFunctionDecl(f))
135 currentFunctionDecl = f;
136 auto rv = loplugin::FunctionAddress<ConstParams>::TraverseCXXConstructorDecl(f);
137 currentFunctionDecl = prev;
138 return rv;
141 bool ConstParams::CheckTraverseFunctionDecl(FunctionDecl * functionDecl)
143 if (ignoreLocation(functionDecl) || !functionDecl->isThisDeclarationADefinition()) {
144 return false;
146 // ignore stuff that forms part of the stable URE interface
147 if (isInUnoIncludeFile(functionDecl)) {
148 return false;
150 if (functionDecl->isDeleted())
151 return false;
152 // ignore virtual methods
153 if (isa<CXXMethodDecl>(functionDecl)
154 && dyn_cast<CXXMethodDecl>(functionDecl)->isVirtual() ) {
155 return false;
157 // ignore C main
158 if (functionDecl->isMain()) {
159 return false;
162 // ignore the macros from include/tools/link.hxx
163 auto canonicalDecl = functionDecl->getCanonicalDecl();
164 if (compiler.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(canonicalDecl))
165 || compiler.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(canonicalDecl))) {
166 StringRef name { Lexer::getImmediateMacroName(
167 compat::getBeginLoc(canonicalDecl), compiler.getSourceManager(), compiler.getLangOpts()) };
168 if (name.startswith("DECL_LINK") || name.startswith("DECL_STATIC_LINK"))
169 return false;
170 auto loc2 = compat::getImmediateExpansionRange(compiler.getSourceManager(), compat::getBeginLoc(canonicalDecl)).first;
171 if (compiler.getSourceManager().isMacroBodyExpansion(loc2))
173 StringRef name2 { Lexer::getImmediateMacroName(
174 loc2, compiler.getSourceManager(), compiler.getLangOpts()) };
175 if (name2.startswith("DECL_DLLPRIVATE_LINK"))
176 return false;
180 if (functionDecl->getIdentifier())
182 StringRef name = functionDecl->getName();
183 if ( name == "file_write"
184 || name == "SalMainPipeExchangeSignal_impl"
185 || name.startswith("SbRtl_")
186 || name == "GoNext"
187 || name == "GoPrevious"
188 || name.startswith("Read_F_")
189 // UNO component entry points
190 || name.endswith("component_getFactory")
191 || name == "egiGraphicExport"
192 || name == "etiGraphicExport"
193 || name == "epsGraphicExport"
194 // callback for some external code?
195 || name == "ScAddInAsyncCallBack"
196 // used as function pointers
197 || name == "Read_Footnote"
198 || name == "Read_Field"
199 || name == "Read_And"
200 // passed as a LINK<> to another method
201 || name == "GlobalBasicErrorHdl_Impl"
202 // template
203 || name == "extract_throw" || name == "readProp"
205 return false;
209 std::string fqn = functionDecl->getQualifiedNameAsString();
210 if ( fqn == "connectivity::jdbc::GlobalRef::set"
211 || fqn == "(anonymous namespace)::ReorderNotifier::operator()"
212 || fqn == "static_txtattr_cast")
213 return false;
215 // calculate the ones we want to check
216 bool foundInterestingParam = false;
217 for (const ParmVarDecl *pParmVarDecl : functionDecl->parameters()) {
218 // ignore unused params
219 if (pParmVarDecl->getName().empty()
220 || pParmVarDecl->hasAttr<UnusedAttr>())
221 continue;
222 auto const type = loplugin::TypeCheck(pParmVarDecl->getType());
223 if (!( type.Pointer().NonConst()
224 || type.LvalueReference().NonConst()))
225 continue;
226 // since we normally can't change typedefs, just ignore them
227 if (isa<TypedefType>(pParmVarDecl->getType()))
228 continue;
229 // some typedefs turn into these
230 if (isa<DecayedType>(pParmVarDecl->getType()))
231 continue;
232 // TODO ignore these for now, has some effects I don't understand
233 if (type.Pointer().Pointer())
234 continue;
235 // const is meaningless when applied to function pointer types
236 if (pParmVarDecl->getType()->isFunctionPointerType())
237 continue;
238 interestingParamSet.insert(pParmVarDecl);
239 parmToFunction[pParmVarDecl] = functionDecl;
240 foundInterestingParam = true;
242 return foundInterestingParam;
245 bool ConstParams::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
247 if (!currentFunctionDecl)
248 return true;
249 const ParmVarDecl* parmVarDecl = dyn_cast_or_null<ParmVarDecl>(declRefExpr->getDecl());
250 if (!parmVarDecl)
251 return true;
252 if (interestingParamSet.find(parmVarDecl) == interestingParamSet.end())
253 return true;
254 if (!checkIfCanBeConst(declRefExpr, parmVarDecl))
255 interestingParamSet.erase(parmVarDecl);
256 return true;
259 // Walk up from a statement that contains a DeclRefExpr, checking if the usage means that the
260 // related ParamVarDecl can be const.
261 bool ConstParams::checkIfCanBeConst(const Stmt* stmt, const ParmVarDecl* parmVarDecl)
263 const Stmt* parent = getParentStmt( stmt );
264 if (!parent)
266 // check if we're inside a CXXCtorInitializer
267 auto parentsRange = compiler.getASTContext().getParents(*stmt);
268 if ( parentsRange.begin() != parentsRange.end())
270 if (auto cxxConstructorDecl = dyn_cast_or_null<CXXConstructorDecl>(parentsRange.begin()->get<Decl>()))
272 for ( auto cxxCtorInitializer : cxxConstructorDecl->inits())
274 if ( cxxCtorInitializer->getInit() == stmt)
276 if (cxxCtorInitializer->isAnyMemberInitializer())
278 // if the member is not pointer-to-const or ref-to-const or value, we cannot make the param const
279 auto fieldDecl = cxxCtorInitializer->getAnyMember();
280 auto tc = loplugin::TypeCheck(fieldDecl->getType());
281 if (tc.Pointer() || tc.LvalueReference())
282 return tc.Pointer().Const() || tc.LvalueReference().Const();
283 else
284 return true;
286 else
288 // probably base initialiser, but no simple way to look up the relevant constructor decl
289 return false;
294 if (auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>()))
296 return isOkForParameter(varDecl->getType());
299 parmVarDecl->dump();
300 stmt->dump();
301 report(
302 DiagnosticsEngine::Warning,
303 "no parent?",
304 compat::getBeginLoc(stmt))
305 << stmt->getSourceRange();
306 return false;
309 if (auto unaryOperator = dyn_cast<UnaryOperator>(parent)) {
310 UnaryOperator::Opcode op = unaryOperator->getOpcode();
311 if (op == UO_AddrOf || op == UO_PreInc || op == UO_PostInc
312 || op == UO_PreDec || op == UO_PostDec) {
313 return false;
315 if (op == UO_Deref) {
316 return checkIfCanBeConst(parent, parmVarDecl);
318 return true;
319 } else if (auto binaryOp = dyn_cast<BinaryOperator>(parent)) {
320 BinaryOperator::Opcode op = binaryOp->getOpcode();
321 if (binaryOp->getRHS() == stmt && op == BO_Assign) {
322 return isOkForParameter(binaryOp->getLHS()->getType());
324 if (binaryOp->getRHS() == stmt) {
325 return true;
327 if (op == BO_Assign || op == BO_PtrMemD || op == BO_PtrMemI || op == BO_MulAssign
328 || op == BO_DivAssign || op == BO_RemAssign || op == BO_AddAssign
329 || op == BO_SubAssign || op == BO_ShlAssign || op == BO_ShrAssign
330 || op == BO_AndAssign || op == BO_XorAssign || op == BO_OrAssign) {
331 return false;
333 // for pointer arithmetic need to check parent
334 if (binaryOp->getType()->isPointerType()) {
335 return checkIfCanBeConst(parent, parmVarDecl);
337 return true;
338 } else if (auto constructExpr = dyn_cast<CXXConstructExpr>(parent)) {
339 const CXXConstructorDecl * constructorDecl = constructExpr->getConstructor();
340 for (unsigned i = 0; i < constructExpr->getNumArgs(); ++i) {
341 if (constructExpr->getArg(i) == stmt) {
342 return isOkForParameter(constructorDecl->getParamDecl(i)->getType());
345 } else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent)) {
346 const CXXMethodDecl* calleeMethodDecl = dyn_cast_or_null<CXXMethodDecl>(operatorCallExpr->getDirectCallee());
347 if (calleeMethodDecl) {
348 // unary operator
349 if (calleeMethodDecl->getNumParams() == 0)
350 return calleeMethodDecl->isConst();
351 // Same logic as CXXOperatorCallExpr::isAssignmentOp(), which our supported clang
352 // doesn't have yet.
353 auto Opc = operatorCallExpr->getOperator();
354 if (Opc == OO_Equal || Opc == OO_StarEqual ||
355 Opc == OO_SlashEqual || Opc == OO_PercentEqual ||
356 Opc == OO_PlusEqual || Opc == OO_MinusEqual ||
357 Opc == OO_LessLessEqual || Opc == OO_GreaterGreaterEqual ||
358 Opc == OO_AmpEqual || Opc == OO_CaretEqual ||
359 Opc == OO_PipeEqual)
361 if (operatorCallExpr->getArg(0) == stmt) // assigning to the param
362 return false;
363 // not all operator= take a const&
364 return isOkForParameter(calleeMethodDecl->getParamDecl(0)->getType());
366 if (operatorCallExpr->getOperator() == OO_Subscript && operatorCallExpr->getArg(1) == stmt)
367 return true;
368 if (operatorCallExpr->getOperator() == OO_EqualEqual || operatorCallExpr->getOperator() == OO_ExclaimEqual)
369 return true;
370 // binary operator
371 if (operatorCallExpr->getArg(0) == stmt)
372 return calleeMethodDecl->isConst();
373 unsigned const n = std::min(
374 operatorCallExpr->getNumArgs(),
375 calleeMethodDecl->getNumParams() + 1);
376 for (unsigned i = 1; i < n; ++i)
377 if (operatorCallExpr->getArg(i) == stmt) {
378 auto qt = calleeMethodDecl->getParamDecl(i - 1)->getType();
379 return isOkForParameter(qt);
381 } else {
382 const Expr* callee = operatorCallExpr->getCallee()->IgnoreParenImpCasts();
383 const DeclRefExpr* dr = dyn_cast<DeclRefExpr>(callee);
384 const FunctionDecl* calleeFunctionDecl = nullptr;
385 if (dr) {
386 calleeFunctionDecl = dyn_cast<FunctionDecl>(dr->getDecl());
388 if (calleeFunctionDecl) {
389 for (unsigned i = 0; i < operatorCallExpr->getNumArgs(); ++i) {
390 if (operatorCallExpr->getArg(i) == stmt) {
391 return isOkForParameter(calleeFunctionDecl->getParamDecl(i)->getType());
396 return false;
397 } else if (auto callExpr = dyn_cast<CallExpr>(parent)) {
398 QualType functionType = callExpr->getCallee()->getType();
399 if (functionType->isFunctionPointerType()) {
400 functionType = functionType->getPointeeType();
402 if (const FunctionProtoType* prototype = functionType->getAs<FunctionProtoType>()) {
403 // TODO could do better
404 if (prototype->isVariadic()) {
405 return false;
407 if (callExpr->getCallee() == stmt) {
408 return true;
410 for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
411 if (callExpr->getArg(i) == stmt) {
412 return isOkForParameter(prototype->getParamType(i));
416 const FunctionDecl* calleeFunctionDecl = callExpr->getDirectCallee();
417 if (calleeFunctionDecl)
419 if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(parent)) {
420 const MemberExpr* memberExpr = dyn_cast<MemberExpr>(stmt);
421 if (memberExpr && memberCallExpr->getImplicitObjectArgument() == memberExpr->getBase())
423 const CXXMethodDecl* calleeMethodDecl = dyn_cast<CXXMethodDecl>(calleeFunctionDecl);
424 return calleeMethodDecl->isConst();
427 // TODO could do better
428 if (calleeFunctionDecl->isVariadic()) {
429 return false;
431 if (callExpr->getCallee() == stmt) {
432 return true;
434 for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
435 if (i >= calleeFunctionDecl->getNumParams()) // can happen in template code
436 return false;
437 if (callExpr->getArg(i) == stmt) {
438 return isOkForParameter(calleeFunctionDecl->getParamDecl(i)->getType());
442 return false;
443 } else if (auto callExpr = dyn_cast<ObjCMessageExpr>(parent)) {
444 if (callExpr->getInstanceReceiver() == stmt) {
445 return true;
447 if (auto const method = callExpr->getMethodDecl()) {
448 // TODO could do better
449 if (method->isVariadic()) {
450 return false;
452 assert(method->param_size() == callExpr->getNumArgs());
453 for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
454 if (callExpr->getArg(i) == stmt) {
455 return isOkForParameter(
456 method->param_begin()[i]->getType());
460 } else if (isa<CXXReinterpretCastExpr>(parent)) {
461 return false;
462 } else if (isa<CXXConstCastExpr>(parent)) {
463 return false;
464 } else if (isa<CastExpr>(parent)) { // all other cast expression subtypes
465 if (auto e = dyn_cast<ExplicitCastExpr>(parent)) {
466 if (loplugin::TypeCheck(e->getTypeAsWritten()).Void()) {
467 if (auto const sub = dyn_cast<DeclRefExpr>(
468 e->getSubExpr()->IgnoreParenImpCasts()))
470 if (sub->getDecl() == parmVarDecl)
471 return false;
475 return checkIfCanBeConst(parent, parmVarDecl);
476 } else if (isa<MemberExpr>(parent)) {
477 return checkIfCanBeConst(parent, parmVarDecl);
478 } else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent)) {
479 if (arraySubscriptExpr->getIdx() == stmt)
480 return true;
481 return checkIfCanBeConst(parent, parmVarDecl);
482 } else if (isa<ParenExpr>(parent)) {
483 return checkIfCanBeConst(parent, parmVarDecl);
484 } else if (isa<DeclStmt>(parent)) {
485 // TODO could do better here, but would require tracking the target(s)
486 //return false;
487 } else if (isa<ReturnStmt>(parent)) {
488 return !isPointerOrReferenceToNonConst(currentFunctionDecl->getReturnType());
489 } else if (isa<InitListExpr>(parent)) {
490 return false;
491 } else if (isa<IfStmt>(parent)) {
492 return true;
493 } else if (isa<WhileStmt>(parent)) {
494 return true;
495 } else if (isa<ForStmt>(parent)) {
496 return true;
497 } else if (isa<CompoundStmt>(parent)) {
498 return true;
499 } else if (isa<SwitchStmt>(parent)) {
500 return true;
501 } else if (isa<DoStmt>(parent)) {
502 return true;
503 } else if (isa<CXXDeleteExpr>(parent)) {
504 return false;
505 } else if (isa<VAArgExpr>(parent)) {
506 return false;
507 } else if (isa<CXXDependentScopeMemberExpr>(parent)) {
508 return false;
509 } else if (isa<MaterializeTemporaryExpr>(parent)) {
510 return checkIfCanBeConst(parent, parmVarDecl);
511 } else if (auto conditionalExpr = dyn_cast<ConditionalOperator>(parent)) {
512 if (conditionalExpr->getCond() == stmt)
513 return true;
514 return checkIfCanBeConst(parent, parmVarDecl);
515 } else if (isa<UnaryExprOrTypeTraitExpr>(parent)) {
516 return false; // ???
517 } else if (auto cxxNewExpr = dyn_cast<CXXNewExpr>(parent)) {
518 for (unsigned i = 0; i < cxxNewExpr->getNumPlacementArgs(); ++i)
519 if (cxxNewExpr->getPlacementArg(i) == stmt)
520 return false;
521 return true; // ???
522 } else if (auto lambdaExpr = dyn_cast<LambdaExpr>(parent)) {
523 for (auto it = lambdaExpr->capture_begin(); it != lambdaExpr->capture_end(); ++it)
525 if (it->capturesVariable() && it->getCapturedVar() == parmVarDecl)
526 return it->getCaptureKind() != LCK_ByRef;
528 return false;
529 } else if (isa<CXXTypeidExpr>(parent)) {
530 return true;
531 } else if (isa<ParenListExpr>(parent)) {
532 return false; // could be improved, seen in constructors when calling base class constructor
533 } else if (isa<CXXUnresolvedConstructExpr>(parent)) {
534 return false;
535 } else if (isa<UnresolvedMemberExpr>(parent)) {
536 return false;
537 } else if (isa<PackExpansionExpr>(parent)) {
538 return false;
539 } else if (isa<ExprWithCleanups>(parent)) {
540 return checkIfCanBeConst(parent, parmVarDecl);
541 } else if (isa<CaseStmt>(parent)) {
542 return true;
543 } else if (isa<CXXPseudoDestructorExpr>(parent)) {
544 return false;
545 } else if (isa<CXXDependentScopeMemberExpr>(parent)) {
546 return false;
547 } else if (isa<ObjCIvarRefExpr>(parent)) {
548 return checkIfCanBeConst(parent, parmVarDecl);
550 parent->dump();
551 parmVarDecl->dump();
552 report(
553 DiagnosticsEngine::Warning,
554 "oh dear, what can the matter be?",
555 compat::getBeginLoc(parent))
556 << parent->getSourceRange();
557 return true;
560 bool ConstParams::isOkForParameter(const QualType& qt) {
561 if (qt->isIntegralOrEnumerationType())
562 return true;
563 auto const type = loplugin::TypeCheck(qt);
564 if (type.Pointer()) {
565 return bool(type.Pointer().Const());
566 } else if (type.LvalueReference().Const().Pointer()) {
567 // If we have a method that takes (T* t) and it calls std::vector<T*>::push_back
568 // then the type of push_back is T * const &
569 // There is probably a more elegant way to check this, but it will probably require
570 // recalculating types while walking up the AST.
571 return false;
572 } else if (type.LvalueReference()) {
573 return bool(type.LvalueReference().Const());
575 return false;
578 bool ConstParams::isPointerOrReferenceToNonConst(const QualType& qt) {
579 auto const type = loplugin::TypeCheck(qt);
580 if (type.Pointer()) {
581 return !bool(type.Pointer().Const());
582 } else if (type.LvalueReference()) {
583 return !bool(type.LvalueReference().Const());
585 return false;
588 loplugin::Plugin::Registration< ConstParams > X("constparams", false);
592 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */