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