1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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  */
9 
10 #include <algorithm>
11 #include <string>
12 #include <unordered_set>
13 #include <unordered_map>
14 #include <iostream>
15 
16 #include "plugin.hxx"
17 #include "compat.hxx"
18 #include "check.hxx"
19 #include "functionaddress.hxx"
20 
21 /**
22    Find pointer and reference params that can be declared const.
23 
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.
26 */
27 namespace
28 {
29 
30 class ConstParams:
31     public loplugin::FunctionAddress<loplugin::FilteringPlugin<ConstParams>>
32 {
33 public:
ConstParams(loplugin::InstantiationData const & data)34     explicit ConstParams(loplugin::InstantiationData const & data): FunctionAddress(data) {}
35 
run()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             // FunctionAddress not working well enough here
54             || loplugin::hasPathnamePrefix(fn, SRCDIR "/pyuno/source/module/pyuno_struct.cxx")
55             || loplugin::hasPathnamePrefix(fn, SRCDIR "/pyuno/source/module/pyuno.cxx")
56             || loplugin::hasPathnamePrefix(fn, SRCDIR "/sw/source/filter/ascii/ascatr.cxx")
57             )
58             return;
59 
60         TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
61 
62         for (const ParmVarDecl *pParmVarDecl : interestingParamSet) {
63             auto functionDecl = parmToFunction[pParmVarDecl];
64             auto canonicalDecl = functionDecl->getCanonicalDecl();
65             if (getFunctionsWithAddressTaken().find(canonicalDecl)
66                 != getFunctionsWithAddressTaken().end())
67             {
68                 continue;
69             }
70             std::string fname = functionDecl->getQualifiedNameAsString();
71             report(
72                 DiagnosticsEngine::Warning,
73                 "this parameter can be const %0",
74                 compat::getBeginLoc(pParmVarDecl))
75                 << fname << pParmVarDecl->getSourceRange();
76             if (canonicalDecl->getLocation() != functionDecl->getLocation()) {
77                 unsigned idx = pParmVarDecl->getFunctionScopeIndex();
78                 const ParmVarDecl* pOther = canonicalDecl->getParamDecl(idx);
79                 report(
80                     DiagnosticsEngine::Note,
81                     "canonical parameter declaration here",
82                     compat::getBeginLoc(pOther))
83                     << pOther->getSourceRange();
84             }
85             //functionDecl->dump();
86         }
87     }
88 
89     bool TraverseFunctionDecl(FunctionDecl *);
90     bool TraverseCXXMethodDecl(CXXMethodDecl * f);
91     bool TraverseCXXConstructorDecl(CXXConstructorDecl * f);
92     bool VisitDeclRefExpr(const DeclRefExpr *);
93 
94 private:
95     bool CheckTraverseFunctionDecl(FunctionDecl *);
96     bool checkIfCanBeConst(const Stmt*, const ParmVarDecl*);
97     // integral or enumeration or const * or const &
98     bool isOkForParameter(const QualType& qt);
99     bool isPointerOrReferenceToNonConst(const QualType& qt);
100 
101     std::unordered_set<const ParmVarDecl*> interestingParamSet;
102     std::unordered_map<const ParmVarDecl*, const FunctionDecl*> parmToFunction;
103     FunctionDecl* currentFunctionDecl = nullptr;
104 };
105 
TraverseFunctionDecl(FunctionDecl * functionDecl)106 bool ConstParams::TraverseFunctionDecl(FunctionDecl * functionDecl)
107 {
108     // We cannot short-circuit the traverse here entirely without breaking the
109     // loplugin::FunctionAddress stuff.
110     auto prev = currentFunctionDecl;
111     if (CheckTraverseFunctionDecl(functionDecl))
112         currentFunctionDecl = functionDecl;
113     auto rv = FunctionAddress::TraverseFunctionDecl(functionDecl);
114     currentFunctionDecl = prev;
115     return rv;
116 }
TraverseCXXMethodDecl(CXXMethodDecl * f)117 bool ConstParams::TraverseCXXMethodDecl(CXXMethodDecl * f)
118 {
119     auto prev = currentFunctionDecl;
120     if (CheckTraverseFunctionDecl(f))
121         currentFunctionDecl = f;
122     auto rv = FunctionAddress::TraverseCXXMethodDecl(f);
123     currentFunctionDecl = prev;
124     return rv;
125 }
TraverseCXXConstructorDecl(CXXConstructorDecl * f)126 bool ConstParams::TraverseCXXConstructorDecl(CXXConstructorDecl * f)
127 {
128     auto prev = currentFunctionDecl;
129     if (CheckTraverseFunctionDecl(f))
130         currentFunctionDecl = f;
131     auto rv = FunctionAddress::TraverseCXXConstructorDecl(f);
132     currentFunctionDecl = prev;
133     return rv;
134 }
135 
CheckTraverseFunctionDecl(FunctionDecl * functionDecl)136 bool ConstParams::CheckTraverseFunctionDecl(FunctionDecl * functionDecl)
137 {
138     if (ignoreLocation(functionDecl) || !functionDecl->isThisDeclarationADefinition()) {
139         return false;
140     }
141     // ignore stuff that forms part of the stable URE interface
142     if (isInUnoIncludeFile(functionDecl)) {
143         return false;
144     }
145     if (functionDecl->isDeleted())
146         return false;
147     // ignore virtual methods
148     if (isa<CXXMethodDecl>(functionDecl)
149         && dyn_cast<CXXMethodDecl>(functionDecl)->isVirtual() ) {
150         return false;
151     }
152     // ignore C main
153     if (functionDecl->isMain()) {
154         return false;
155     }
156 
157     // ignore the macros from include/tools/link.hxx
158     auto canonicalDecl = functionDecl->getCanonicalDecl();
159     if (compiler.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(canonicalDecl))
160         || compiler.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(canonicalDecl))) {
161         StringRef name { Lexer::getImmediateMacroName(
162                 compat::getBeginLoc(canonicalDecl), compiler.getSourceManager(), compiler.getLangOpts()) };
163         if (name.startswith("DECL_LINK") || name.startswith("DECL_STATIC_LINK"))
164             return false;
165         auto loc2 = compat::getImmediateExpansionRange(compiler.getSourceManager(), compat::getBeginLoc(canonicalDecl)).first;
166         if (compiler.getSourceManager().isMacroBodyExpansion(loc2))
167         {
168             StringRef name2 { Lexer::getImmediateMacroName(
169                     loc2, compiler.getSourceManager(), compiler.getLangOpts()) };
170             if (name2.startswith("DECL_DLLPRIVATE_LINK"))
171                 return false;
172         }
173     }
174 
175     if (functionDecl->getIdentifier())
176     {
177         StringRef name = functionDecl->getName();
178         if (   name == "file_write"
179             || name == "SalMainPipeExchangeSignal_impl"
180             || name.startswith("SbRtl_")
181             || name == "GoNext"
182             || name == "GoPrevious"
183             || name.startswith("Read_F_")
184                 // UNO component entry points
185             || name.endswith("component_getFactory")
186             || name == "egiGraphicExport"
187             || name == "etiGraphicExport"
188             || name == "epsGraphicExport"
189             // callback for some external code?
190             || name == "ScAddInAsyncCallBack"
191             // used as function pointers
192             || name == "Read_Footnote"
193             || name == "Read_Field"
194             || name == "Read_And"
195             // passed as a LINK<> to another method
196             || name == "GlobalBasicErrorHdl_Impl"
197             // template
198             || name == "extract_throw" || name == "readProp"
199             // callbacks
200             || name == "signalDragDropReceived" || name == "signal_column_clicked" || name == "signal_key_press"
201             )
202             return false;
203 
204     }
205 
206     std::string fqn = functionDecl->getQualifiedNameAsString();
207     if ( fqn == "connectivity::jdbc::GlobalRef::set"
208       || fqn == "(anonymous namespace)::ReorderNotifier::operator()"
209       || fqn == "static_txtattr_cast")
210         return false;
211 
212     // calculate the ones we want to check
213     bool foundInterestingParam = false;
214     for (const ParmVarDecl *pParmVarDecl : functionDecl->parameters()) {
215         // ignore unused params
216         if (pParmVarDecl->getName().empty()
217             || pParmVarDecl->hasAttr<UnusedAttr>())
218             continue;
219         auto const type = loplugin::TypeCheck(pParmVarDecl->getType());
220         if (!( type.Pointer().NonConst()
221              || type.LvalueReference().NonConst()))
222             continue;
223         // since we normally can't change typedefs, just ignore them
224         if (isa<TypedefType>(pParmVarDecl->getType()))
225             continue;
226         // some typedefs turn into these
227         if (isa<DecayedType>(pParmVarDecl->getType()))
228             continue;
229         // TODO ignore these for now, has some effects I don't understand
230         if (type.Pointer().Pointer())
231             continue;
232         // const is meaningless when applied to function pointer types
233         if (pParmVarDecl->getType()->isFunctionPointerType())
234             continue;
235         interestingParamSet.insert(pParmVarDecl);
236         parmToFunction[pParmVarDecl] = functionDecl;
237         foundInterestingParam = true;
238     }
239     return foundInterestingParam;
240 }
241 
VisitDeclRefExpr(const DeclRefExpr * declRefExpr)242 bool ConstParams::VisitDeclRefExpr( const DeclRefExpr* declRefExpr )
243 {
244     if (!currentFunctionDecl)
245         return true;
246     const ParmVarDecl* parmVarDecl = dyn_cast_or_null<ParmVarDecl>(declRefExpr->getDecl());
247     if (!parmVarDecl)
248         return true;
249     if (interestingParamSet.find(parmVarDecl) == interestingParamSet.end())
250         return true;
251     if (!checkIfCanBeConst(declRefExpr, parmVarDecl))
252         interestingParamSet.erase(parmVarDecl);
253     return true;
254 }
255 
256 // Walk up from a statement that contains a DeclRefExpr, checking if the usage means that the
257 // related ParamVarDecl can be const.
checkIfCanBeConst(const Stmt * stmt,const ParmVarDecl * parmVarDecl)258 bool ConstParams::checkIfCanBeConst(const Stmt* stmt, const ParmVarDecl* parmVarDecl)
259 {
260     const Stmt* parent = getParentStmt( stmt );
261     if (!parent)
262     {
263         // check if we're inside a CXXCtorInitializer
264         auto parentsRange = compiler.getASTContext().getParents(*stmt);
265         if ( parentsRange.begin() != parentsRange.end())
266         {
267             if (auto cxxConstructorDecl = dyn_cast_or_null<CXXConstructorDecl>(parentsRange.begin()->get<Decl>()))
268             {
269                 for ( auto cxxCtorInitializer : cxxConstructorDecl->inits())
270                 {
271                     if ( cxxCtorInitializer->getInit() == stmt)
272                     {
273                         if (cxxCtorInitializer->isAnyMemberInitializer())
274                         {
275                             // if the member is not pointer-to-const or ref-to-const or value, we cannot make the param const
276                             auto fieldDecl = cxxCtorInitializer->getAnyMember();
277                             auto tc = loplugin::TypeCheck(fieldDecl->getType());
278                             if (tc.Pointer() || tc.LvalueReference())
279                                 return tc.Pointer().Const() || tc.LvalueReference().Const();
280                             else
281                                 return true;
282                         }
283                         else
284                         {
285                             // probably base initialiser, but no simple way to look up the relevant constructor decl
286                             return false;
287                         }
288                     }
289                 }
290             }
291             if (auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>()))
292             {
293                 return isOkForParameter(varDecl->getType());
294             }
295         }
296         parmVarDecl->dump();
297         stmt->dump();
298         report(
299              DiagnosticsEngine::Warning,
300              "no parent?",
301               compat::getBeginLoc(stmt))
302               << stmt->getSourceRange();
303         return false;
304     }
305 
306     if (auto unaryOperator = dyn_cast<UnaryOperator>(parent)) {
307         UnaryOperator::Opcode op = unaryOperator->getOpcode();
308         if (op == UO_AddrOf || op == UO_PreInc || op == UO_PostInc
309             || op == UO_PreDec || op == UO_PostDec) {
310             return false;
311         }
312         if (op == UO_Deref) {
313             return checkIfCanBeConst(parent, parmVarDecl);
314         }
315         return true;
316     } else if (auto binaryOp = dyn_cast<BinaryOperator>(parent)) {
317         BinaryOperator::Opcode op = binaryOp->getOpcode();
318         if (binaryOp->getRHS() == stmt && op == BO_Assign) {
319             return isOkForParameter(binaryOp->getLHS()->getType());
320         }
321         if (binaryOp->getRHS() == stmt) {
322             return true;
323         }
324         if (op == BO_Assign || op == BO_PtrMemD || op == BO_PtrMemI || op == BO_MulAssign
325             || op == BO_DivAssign || op == BO_RemAssign || op == BO_AddAssign
326             || op == BO_SubAssign || op == BO_ShlAssign || op == BO_ShrAssign
327             || op == BO_AndAssign || op == BO_XorAssign || op == BO_OrAssign) {
328             return false;
329         }
330         // for pointer arithmetic need to check parent
331         if (binaryOp->getType()->isPointerType()) {
332             return checkIfCanBeConst(parent, parmVarDecl);
333         }
334         return true;
335     } else if (auto constructExpr = dyn_cast<CXXConstructExpr>(parent)) {
336         const CXXConstructorDecl * constructorDecl = constructExpr->getConstructor();
337         for (unsigned i = 0; i < constructExpr->getNumArgs(); ++i) {
338             if (constructExpr->getArg(i) == stmt) {
339                 return isOkForParameter(constructorDecl->getParamDecl(i)->getType());
340             }
341         }
342     } else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent)) {
343         const CXXMethodDecl* calleeMethodDecl = dyn_cast_or_null<CXXMethodDecl>(operatorCallExpr->getDirectCallee());
344         if (calleeMethodDecl) {
345             // unary operator
346             if (calleeMethodDecl->getNumParams() == 0)
347                 return calleeMethodDecl->isConst();
348             // Same logic as CXXOperatorCallExpr::isAssignmentOp(), which our supported clang
349             // doesn't have yet.
350             auto Opc = operatorCallExpr->getOperator();
351             if (Opc == OO_Equal || Opc == OO_StarEqual ||
352                 Opc == OO_SlashEqual || Opc == OO_PercentEqual ||
353                 Opc == OO_PlusEqual || Opc == OO_MinusEqual ||
354                 Opc == OO_LessLessEqual || Opc == OO_GreaterGreaterEqual ||
355                 Opc == OO_AmpEqual || Opc == OO_CaretEqual ||
356                 Opc == OO_PipeEqual)
357             {
358                 if (operatorCallExpr->getArg(0) == stmt) // assigning to the param
359                     return false;
360                 // not all operator= take a const&
361                 return isOkForParameter(calleeMethodDecl->getParamDecl(0)->getType());
362             }
363             if (operatorCallExpr->getOperator() == OO_Subscript && operatorCallExpr->getArg(1) == stmt)
364                 return true;
365             if (operatorCallExpr->getOperator() == OO_EqualEqual || operatorCallExpr->getOperator() == OO_ExclaimEqual)
366                 return true;
367             // binary operator
368             if (operatorCallExpr->getArg(0) == stmt)
369                 return calleeMethodDecl->isConst();
370             unsigned const n = std::min(
371                 operatorCallExpr->getNumArgs(),
372                 calleeMethodDecl->getNumParams() + 1);
373             for (unsigned i = 1; i < n; ++i)
374                 if (operatorCallExpr->getArg(i) == stmt) {
375                     auto qt = calleeMethodDecl->getParamDecl(i - 1)->getType();
376                     return isOkForParameter(qt);
377                 }
378         } else {
379             const Expr* callee = operatorCallExpr->getCallee()->IgnoreParenImpCasts();
380             const DeclRefExpr* dr = dyn_cast<DeclRefExpr>(callee);
381             const FunctionDecl* calleeFunctionDecl = nullptr;
382             if (dr) {
383                 calleeFunctionDecl = dyn_cast<FunctionDecl>(dr->getDecl());
384             }
385             if (calleeFunctionDecl) {
386                 for (unsigned i = 0; i < operatorCallExpr->getNumArgs(); ++i) {
387                     if (operatorCallExpr->getArg(i) == stmt) {
388                         return isOkForParameter(calleeFunctionDecl->getParamDecl(i)->getType());
389                     }
390                 }
391             }
392         }
393         return false;
394     } else if (auto callExpr = dyn_cast<CallExpr>(parent)) {
395         QualType functionType = callExpr->getCallee()->getType();
396         if (functionType->isFunctionPointerType()) {
397             functionType = functionType->getPointeeType();
398         }
399         if (const FunctionProtoType* prototype = functionType->getAs<FunctionProtoType>()) {
400             // TODO could do better
401             if (prototype->isVariadic()) {
402                 return false;
403             }
404             if (callExpr->getCallee() == stmt) {
405                 return true;
406             }
407             for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
408                 if (callExpr->getArg(i) == stmt) {
409                     return isOkForParameter(prototype->getParamType(i));
410                 }
411             }
412         }
413         const FunctionDecl* calleeFunctionDecl = callExpr->getDirectCallee();
414         if (calleeFunctionDecl)
415         {
416             if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(parent)) {
417                 const MemberExpr* memberExpr = dyn_cast<MemberExpr>(stmt);
418                 if (memberExpr && memberCallExpr->getImplicitObjectArgument() == memberExpr->getBase())
419                 {
420                     const CXXMethodDecl* calleeMethodDecl = dyn_cast<CXXMethodDecl>(calleeFunctionDecl);
421                     return calleeMethodDecl->isConst();
422                 }
423             }
424             // TODO could do better
425             if (calleeFunctionDecl->isVariadic()) {
426                 return false;
427             }
428             if (callExpr->getCallee() == stmt) {
429                 return true;
430             }
431             for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
432                 if (i >= calleeFunctionDecl->getNumParams()) // can happen in template code
433                     return false;
434                 if (callExpr->getArg(i) == stmt) {
435                     return isOkForParameter(calleeFunctionDecl->getParamDecl(i)->getType());
436                 }
437             }
438         }
439         return false;
440     } else if (auto callExpr = dyn_cast<ObjCMessageExpr>(parent)) {
441         if (callExpr->getInstanceReceiver() == stmt) {
442             return true;
443         }
444         if (auto const method = callExpr->getMethodDecl()) {
445             // TODO could do better
446             if (method->isVariadic()) {
447                 return false;
448             }
449             assert(method->param_size() == callExpr->getNumArgs());
450             for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
451                 if (callExpr->getArg(i) == stmt) {
452                     return isOkForParameter(
453                         method->param_begin()[i]->getType());
454                 }
455             }
456         }
457     } else if (isa<CXXReinterpretCastExpr>(parent)) {
458         return false;
459     } else if (isa<CXXConstCastExpr>(parent)) {
460         return false;
461     } else if (isa<CastExpr>(parent)) { // all other cast expression subtypes
462         if (auto e = dyn_cast<ExplicitCastExpr>(parent)) {
463             if (loplugin::TypeCheck(e->getTypeAsWritten()).Void()) {
464                 if (auto const sub = dyn_cast<DeclRefExpr>(
465                         e->getSubExpr()->IgnoreParenImpCasts()))
466                 {
467                     if (sub->getDecl() == parmVarDecl)
468                         return false;
469                 }
470             }
471         }
472         return checkIfCanBeConst(parent, parmVarDecl);
473     } else if (isa<MemberExpr>(parent)) {
474         return checkIfCanBeConst(parent, parmVarDecl);
475     } else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent)) {
476         if (arraySubscriptExpr->getIdx() == stmt)
477             return true;
478         return checkIfCanBeConst(parent, parmVarDecl);
479     } else if (isa<ParenExpr>(parent)) {
480         return checkIfCanBeConst(parent, parmVarDecl);
481     } else if (isa<DeclStmt>(parent)) {
482         // TODO could do better here, but would require tracking the target(s)
483         //return false;
484     } else if (isa<ReturnStmt>(parent)) {
485         return !isPointerOrReferenceToNonConst(currentFunctionDecl->getReturnType());
486     } else if (isa<InitListExpr>(parent)) {
487         return false;
488     } else if (isa<IfStmt>(parent)) {
489         return true;
490     } else if (isa<WhileStmt>(parent)) {
491         return true;
492     } else if (isa<ForStmt>(parent)) {
493         return true;
494     } else if (isa<CompoundStmt>(parent)) {
495         return true;
496     } else if (isa<SwitchStmt>(parent)) {
497         return true;
498     } else if (isa<DoStmt>(parent)) {
499         return true;
500     } else if (isa<CXXDeleteExpr>(parent)) {
501         return false;
502     } else if (isa<VAArgExpr>(parent)) {
503         return false;
504     } else if (isa<CXXDependentScopeMemberExpr>(parent)) {
505         return false;
506     } else if (isa<MaterializeTemporaryExpr>(parent)) {
507         return checkIfCanBeConst(parent, parmVarDecl);
508     } else if (auto conditionalExpr = dyn_cast<ConditionalOperator>(parent)) {
509         if (conditionalExpr->getCond() == stmt)
510             return true;
511         return checkIfCanBeConst(parent, parmVarDecl);
512     } else if (isa<UnaryExprOrTypeTraitExpr>(parent)) {
513         return false; // ???
514     } else if (auto cxxNewExpr = dyn_cast<CXXNewExpr>(parent)) {
515         for (unsigned i = 0; i < cxxNewExpr->getNumPlacementArgs(); ++i)
516             if (cxxNewExpr->getPlacementArg(i) == stmt)
517                 return false;
518         return true; // ???
519     } else if (auto lambdaExpr = dyn_cast<LambdaExpr>(parent)) {
520         for (auto it = lambdaExpr->capture_begin(); it != lambdaExpr->capture_end(); ++it)
521         {
522             if (it->capturesVariable() && it->getCapturedVar() == parmVarDecl)
523                 return it->getCaptureKind() != LCK_ByRef;
524         }
525         return false;
526     } else if (isa<CXXTypeidExpr>(parent)) {
527         return true;
528     } else if (isa<ParenListExpr>(parent)) {
529         return false; // could be improved, seen in constructors when calling base class constructor
530     } else if (isa<CXXUnresolvedConstructExpr>(parent)) {
531         return false;
532     } else if (isa<UnresolvedMemberExpr>(parent)) {
533         return false;
534     } else if (isa<PackExpansionExpr>(parent)) {
535         return false;
536     } else if (isa<ExprWithCleanups>(parent)) {
537         return checkIfCanBeConst(parent, parmVarDecl);
538     } else if (isa<CaseStmt>(parent)) {
539         return true;
540     } else if (isa<CXXPseudoDestructorExpr>(parent)) {
541         return false;
542     } else if (isa<CXXDependentScopeMemberExpr>(parent)) {
543         return false;
544     } else if (isa<ObjCIvarRefExpr>(parent)) {
545         return checkIfCanBeConst(parent, parmVarDecl);
546     }
547     parent->dump();
548     parmVarDecl->dump();
549     report(
550          DiagnosticsEngine::Warning,
551          "oh dear, what can the matter be?",
552           compat::getBeginLoc(parent))
553           << parent->getSourceRange();
554     return true;
555 }
556 
isOkForParameter(const QualType & qt)557 bool ConstParams::isOkForParameter(const QualType& qt) {
558     if (qt->isIntegralOrEnumerationType())
559         return true;
560     auto const type = loplugin::TypeCheck(qt);
561     if (type.Pointer()) {
562         return bool(type.Pointer().Const());
563     } else if (type.LvalueReference().Const().Pointer()) {
564         // If we have a method that takes (T* t) and it calls std::vector<T*>::push_back
565         // then the type of push_back is T * const &
566         // There is probably a more elegant way to check this, but it will probably require
567         // recalculating types while walking up the AST.
568         return false;
569     } else if (type.LvalueReference()) {
570         return bool(type.LvalueReference().Const());
571     }
572     return false;
573 }
574 
isPointerOrReferenceToNonConst(const QualType & qt)575 bool ConstParams::isPointerOrReferenceToNonConst(const QualType& qt) {
576     auto const type = loplugin::TypeCheck(qt);
577     if (type.Pointer()) {
578         return !bool(type.Pointer().Const());
579     } else if (type.LvalueReference()) {
580         return !bool(type.LvalueReference().Const());
581     }
582     return false;
583 }
584 
585 loplugin::Plugin::Registration< ConstParams > X("constparams", false);
586 
587 }
588 
589 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
590