update credits
[LibreOffice.git] / compilerplugins / clang / constmethod.cxx
blobbd9c4db18d177c28f02171875c6bfd7bf2dd9f54
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 "config_clang.h"
18 #include "plugin.hxx"
19 #include "check.hxx"
20 #include "functionaddress.hxx"
22 #include "clang/AST/ParentMapContext.h"
24 /**
25 Find methods that can be declared const.
27 This analysis attempts to implement "logical const" as opposed to "technical const", which means
28 we ignore always-const nature of std::unique_ptr::operator->
30 This is not a sophisticated analysis. It deliberately skips all of the hard cases for now.
31 It is an exercise in getting the most benefit for the least effort.
33 namespace
36 class ConstMethod:
37 public loplugin::FunctionAddress<loplugin::FilteringPlugin<ConstMethod>>
39 public:
40 explicit ConstMethod(loplugin::InstantiationData const & data): FunctionAddress(data) {}
42 virtual void run() override {
43 std::string fn(handler.getMainFileName());
44 loplugin::normalizeDotDotInFilePath(fn);
45 // things I'm not sure about
46 if (loplugin::hasPathnamePrefix(fn, SRCDIR "/svl/unx/source/svdde/ddedummy.cxx")
47 || loplugin::hasPathnamePrefix(fn, SRCDIR "/svl/source/numbers/zformat.cxx")
48 || loplugin::hasPathnamePrefix(fn, SRCDIR "/svl/source/numbers/zforscan.cxx")
49 || loplugin::hasPathnamePrefix(fn, SRCDIR "/svl/source/numbers/zforlist.cxx")
50 || loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/source/gdi/impgraph.cxx")
51 || loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/source/image/ImplImage.cxx")
52 || loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/source/filter/wmf/wmfwr.cxx")
53 || loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/unx/generic/app/i18n_im.cxx")
54 || loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/unx/generic/app/randrwrapper.cxx")
55 || loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/unx/gtk3/gtkinst.cxx")
56 || loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/unx/gtk3/gtkframe.cxx")
57 || loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/skia/gdiimpl.cxx")
58 || loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/qt5/")
59 || loplugin::hasPathnamePrefix(fn, SRCDIR "/package/source/xstor/owriteablestream.cxx")
60 || loplugin::hasPathnamePrefix(fn, SRCDIR "/package/source/zippackage/ZipPackage.cxx")
61 || loplugin::hasPathnamePrefix(fn, SRCDIR "/toolkit/")
62 || loplugin::hasPathnamePrefix(fn, SRCDIR "/canvas/")
63 || loplugin::hasPathnamePrefix(fn, SRCDIR "/accessibility/")
64 || loplugin::hasPathnamePrefix(fn, SRCDIR "/framework/")
65 || loplugin::hasPathnamePrefix(fn, SRCDIR "/basic/")
66 || loplugin::hasPathnamePrefix(fn, SRCDIR "/sfx2/")
67 || loplugin::hasPathnamePrefix(fn, SRCDIR "/xmloff/")
68 || loplugin::hasPathnamePrefix(fn, SRCDIR "/connectivity/")
69 || loplugin::hasPathnamePrefix(fn, SRCDIR "/editeng/")
70 || loplugin::hasPathnamePrefix(fn, SRCDIR "/scripting/")
71 || loplugin::hasPathnamePrefix(fn, SRCDIR "/ucb/")
72 || loplugin::hasPathnamePrefix(fn, SRCDIR "/svx/")
73 || loplugin::hasPathnamePrefix(fn, SRCDIR "/basctl/")
74 || loplugin::hasPathnamePrefix(fn, SRCDIR "/chart2/")
76 return;
78 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
80 for (const CXXMethodDecl *pMethodDecl : interestingMethodSet) {
81 if (methodCannotBeConstSet.find(pMethodDecl) != methodCannotBeConstSet.end())
82 continue;
83 auto canonicalDecl = pMethodDecl->getCanonicalDecl();
84 if (getFunctionsWithAddressTaken().find((FunctionDecl const *)canonicalDecl)
85 != getFunctionsWithAddressTaken().end())
86 continue;
87 // things that I don't think should be logically const
88 std::string fqn = pMethodDecl->getQualifiedNameAsString();
89 if (fqn == "comphelper::EmbeddedObjectContainer::CommitImageSubStorage"
90 || fqn == "SvtLinguConfig::SetProperty"
91 || fqn == "SvtLinguConfig::ReplaceSetProperties"
92 || fqn == "SystemWindow::UpdatePositionData"
93 || fqn == "OutputDevice::SelectClipRegion"
94 || fqn == "OutputDevice::BlendBitmap")
95 continue;
96 StringRef aFileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(canonicalDecl->getBeginLoc()));
97 // leave the kit API alone
98 if (loplugin::isSamePathname(aFileName, SRCDIR "/include/LibreOfficeKit/LibreOfficeKit.hxx"))
99 continue;
100 // don't feel like touching this right now
101 if (loplugin::isSamePathname(aFileName, SRCDIR "/include/vcl/weld.hxx"))
102 continue;
103 report(
104 DiagnosticsEngine::Warning,
105 "this method can be const",
106 pMethodDecl->getBeginLoc())
107 << pMethodDecl->getSourceRange();
108 if (canonicalDecl->getLocation() != pMethodDecl->getLocation()) {
109 report(
110 DiagnosticsEngine::Note,
111 "canonical method declaration here",
112 canonicalDecl->getBeginLoc())
113 << canonicalDecl->getSourceRange();
118 bool TraverseCXXMethodDecl(CXXMethodDecl *);
119 bool TraverseCXXConversionDecl(CXXConversionDecl *);
120 bool VisitCXXMethodDecl(const CXXMethodDecl *);
121 bool VisitCXXThisExpr(const CXXThisExpr *);
123 private:
124 bool isPointerOrReferenceToConst(const QualType& qt);
125 bool isPointerOrReferenceToNonConst(const QualType& qt);
126 bool checkIfCanBeConst(const Stmt*, const CXXMethodDecl*);
128 std::unordered_set<const CXXMethodDecl*> interestingMethodSet;
129 std::unordered_set<const CXXMethodDecl*> methodCannotBeConstSet;
130 CXXMethodDecl const * currCXXMethodDecl;
133 bool ConstMethod::TraverseCXXMethodDecl(CXXMethodDecl * cxxMethodDecl)
135 currCXXMethodDecl = cxxMethodDecl;
136 bool rv = RecursiveASTVisitor<ConstMethod>::TraverseCXXMethodDecl(cxxMethodDecl);
137 currCXXMethodDecl = nullptr;
138 return rv;
141 bool ConstMethod::TraverseCXXConversionDecl(CXXConversionDecl * cxxConversionDecl)
143 currCXXMethodDecl = cxxConversionDecl;
144 bool rv = RecursiveASTVisitor<ConstMethod>::TraverseCXXConversionDecl(cxxConversionDecl);
145 currCXXMethodDecl = nullptr;
146 return rv;
149 bool ConstMethod::VisitCXXMethodDecl(const CXXMethodDecl * cxxMethodDecl)
151 if (ignoreLocation(cxxMethodDecl) || !cxxMethodDecl->isThisDeclarationADefinition()) {
152 return true;
154 if (cxxMethodDecl->isConst())
155 return true;
156 // ignore stuff that forms part of the stable URE interface
157 if (isInUnoIncludeFile(cxxMethodDecl)) {
158 return true;
160 // TODO ignore template stuff for now
161 if (cxxMethodDecl->getTemplatedKind() != FunctionDecl::TK_NonTemplate) {
162 return true;
164 if (cxxMethodDecl->isDeleted())
165 return true;
166 if (cxxMethodDecl->isStatic())
167 return true;
168 if (cxxMethodDecl->isOverloadedOperator())
169 return true;
170 if (isa<CXXConstructorDecl>(cxxMethodDecl))
171 return true;
172 if (isa<CXXDestructorDecl>(cxxMethodDecl))
173 return true;
174 if (cxxMethodDecl->getParent()->getDescribedClassTemplate() != nullptr ) {
175 return true;
177 // ignore virtual methods
178 if (cxxMethodDecl->isVirtual() ) {
179 return true;
181 // ignore macro expansions so we can ignore the IMPL_LINK macros from include/tools/link.hxx
182 // TODO make this more precise
183 if (cxxMethodDecl->getLocation().isMacroID())
184 return true;
186 if (!cxxMethodDecl->getIdentifier())
187 return true;
188 // if (cxxMethodDecl->getNumParams() > 0)
189 // return true;
190 // returning pointers or refs to non-const stuff, and then having the whole method
191 // be const doesn't seem like a good idea
192 auto tc = loplugin::TypeCheck(cxxMethodDecl->getReturnType());
193 if (tc.Pointer().NonConst())
194 return true;
195 if (tc.LvalueReference().NonConst())
196 return true;
197 // a Get method that returns void is probably doing something that has side-effects
198 if (tc.Void())
199 return true;
201 // StringRef name = cxxMethodDecl->getName();
202 // if (!name.startswith("get") && !name.startswith("Get")
203 // && !name.startswith("is") && !name.startswith("Is")
204 // && !name.startswith("has") && !name.startswith("Has"))
205 // return true;
207 // something lacking in my analysis here
208 if (loplugin::DeclCheck(cxxMethodDecl).Function("GetDescr").Class("SwRangeRedline").GlobalNamespace())
209 return true;
211 interestingMethodSet.insert(cxxMethodDecl);
213 return true;
216 bool ConstMethod::VisitCXXThisExpr( const CXXThisExpr* cxxThisExpr )
218 if (!currCXXMethodDecl)
219 return true;
220 if (ignoreLocation(cxxThisExpr))
221 return true;
222 // ignore stuff that forms part of the stable URE interface
223 if (isInUnoIncludeFile(cxxThisExpr->getBeginLoc()))
224 return true;
225 if (interestingMethodSet.find(currCXXMethodDecl) == interestingMethodSet.end())
226 return true;
227 // no need to check again if we have already eliminated this one
228 if (methodCannotBeConstSet.find(currCXXMethodDecl) != methodCannotBeConstSet.end())
229 return true;
230 if (!checkIfCanBeConst(cxxThisExpr, currCXXMethodDecl))
231 methodCannotBeConstSet.insert(currCXXMethodDecl);
233 return true;
236 // Walk up from a statement that contains a CXXThisExpr, checking if the usage means that the
237 // related CXXMethodDecl can be const.
238 bool ConstMethod::checkIfCanBeConst(const Stmt* stmt, const CXXMethodDecl* cxxMethodDecl)
240 const Stmt* parent = getParentStmt( stmt );
241 if (!parent) {
242 auto parentsRange = compiler.getASTContext().getParents(*stmt);
243 if ( parentsRange.begin() == parentsRange.end())
244 return true;
245 auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>());
246 if (!varDecl)
248 report(
249 DiagnosticsEngine::Warning,
250 "no parent?",
251 stmt->getBeginLoc())
252 << stmt->getSourceRange();
253 return false;
255 return varDecl->getType()->isIntegralOrEnumerationType()
256 || loplugin::TypeCheck(varDecl->getType()).Pointer().Const()
257 || loplugin::TypeCheck(varDecl->getType()).LvalueReference().Const();
260 if (auto unaryOperator = dyn_cast<UnaryOperator>(parent)) {
261 UnaryOperator::Opcode op = unaryOperator->getOpcode();
262 if (op == UO_PreInc || op == UO_PostInc
263 || op == UO_PreDec || op == UO_PostDec) {
264 return false;
266 if (op == UO_Deref || op == UO_AddrOf) {
267 return checkIfCanBeConst(parent, cxxMethodDecl);
269 return true;
270 } else if (auto binaryOp = dyn_cast<BinaryOperator>(parent)) {
271 BinaryOperator::Opcode op = binaryOp->getOpcode();
272 if (binaryOp->getRHS() == stmt) {
273 return true;
275 if (op == BO_Assign || op == BO_PtrMemD || op == BO_PtrMemI || op == BO_MulAssign
276 || op == BO_DivAssign || op == BO_RemAssign || op == BO_AddAssign
277 || op == BO_SubAssign || op == BO_ShlAssign || op == BO_ShrAssign
278 || op == BO_AndAssign || op == BO_XorAssign || op == BO_OrAssign) {
279 return false;
281 // // for pointer arithmetic need to check parent
282 // if (binaryOp->getType()->isPointerType()) {
283 // return checkIfCanBeConst(parent, cxxMethodDecl);
284 // }
285 return true;
286 } else if (auto constructExpr = dyn_cast<CXXConstructExpr>(parent)) {
287 const CXXConstructorDecl * constructorDecl = constructExpr->getConstructor();
288 for (unsigned i = 0; i < constructExpr->getNumArgs(); ++i) {
289 if (constructExpr->getArg(i) == stmt) {
290 return isPointerOrReferenceToConst(constructorDecl->getParamDecl(i)->getType());
293 return false; // TODO ??
294 } else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent)) {
295 const CXXMethodDecl* calleeMethodDecl = dyn_cast_or_null<CXXMethodDecl>(operatorCallExpr->getDirectCallee());
296 if (calleeMethodDecl) {
297 // unary operator
298 if (calleeMethodDecl->getNumParams() == 0) {
299 // some classes like std::unique_ptr do not do a very good job with their operator-> which is always const
300 if (operatorCallExpr->getOperator() == OO_Arrow || operatorCallExpr->getOperator() == OO_Star) {
301 return checkIfCanBeConst(parent, cxxMethodDecl);
303 return calleeMethodDecl->isConst();
305 // some classes like std::unique_ptr do not do a very good job with their operator[] which is always const
306 if (calleeMethodDecl->getNumParams() == 1 && operatorCallExpr->getArg(0) == stmt) {
307 if (operatorCallExpr->getOperator() == OO_Subscript) {
308 return false;
311 // binary operator
312 if (operatorCallExpr->getArg(0) == stmt) {
313 return calleeMethodDecl->isConst();
315 unsigned const n = std::min(
316 operatorCallExpr->getNumArgs(),
317 calleeMethodDecl->getNumParams());
318 for (unsigned i = 1; i < n; ++i)
319 if (operatorCallExpr->getArg(i) == stmt) {
320 return isPointerOrReferenceToConst(calleeMethodDecl->getParamDecl(i - 1)->getType());
322 } else {
323 const Expr* callee = operatorCallExpr->getCallee()->IgnoreParenImpCasts();
324 const DeclRefExpr* dr = dyn_cast<DeclRefExpr>(callee);
325 const FunctionDecl* calleeFunctionDecl = nullptr;
326 if (dr) {
327 calleeFunctionDecl = dyn_cast<FunctionDecl>(dr->getDecl());
329 if (calleeFunctionDecl) {
330 for (unsigned i = 0; i < operatorCallExpr->getNumArgs(); ++i) {
331 if (operatorCallExpr->getArg(i) == stmt) {
332 return isPointerOrReferenceToConst(calleeFunctionDecl->getParamDecl(i)->getType());
337 return false; // TODO ???
338 } else if (auto callExpr = dyn_cast<CallExpr>(parent)) {
339 QualType functionType = callExpr->getCallee()->getType();
340 if (functionType->isFunctionPointerType()) {
341 functionType = functionType->getPointeeType();
343 if (const FunctionProtoType* prototype = functionType->getAs<FunctionProtoType>()) {
344 // TODO could do better
345 if (prototype->isVariadic()) {
346 return false;
348 if (callExpr->getCallee() == stmt) {
349 return true;
351 for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
352 if (callExpr->getArg(i) == stmt) {
353 return isPointerOrReferenceToConst(prototype->getParamType(i));
357 const FunctionDecl* calleeFunctionDecl = callExpr->getDirectCallee();
358 if (calleeFunctionDecl)
360 if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(parent)) {
361 const MemberExpr* memberExpr = dyn_cast<MemberExpr>(stmt);
362 if (memberExpr && memberCallExpr->getImplicitObjectArgument() == memberExpr->getBase())
364 const CXXMethodDecl* calleeMethodDecl = dyn_cast<CXXMethodDecl>(calleeFunctionDecl);
365 // some classes like std::unique_ptr do not do a very good job with their get() which is always const
366 if (calleeMethodDecl->getIdentifier() && calleeMethodDecl->getName() == "get") {
367 return checkIfCanBeConst(parent, cxxMethodDecl);
369 // VclPtr<T>'s implicit conversion to T*
370 if (isa<CXXConversionDecl>(calleeMethodDecl)) {
371 if (loplugin::DeclCheck(calleeMethodDecl->getParent()).Class("OWeakObject").Namespace("cppu").GlobalNamespace())
372 return false;
373 return checkIfCanBeConst(parent, cxxMethodDecl);
375 return calleeMethodDecl->isConst();
378 // TODO could do better
379 if (calleeFunctionDecl->isVariadic()) {
380 return false;
382 if (callExpr->getCallee() == stmt) {
383 return true;
385 for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
386 if (i >= calleeFunctionDecl->getNumParams()) // can happen in template code
387 return false;
388 if (callExpr->getArg(i) == stmt) {
389 return isPointerOrReferenceToConst(calleeFunctionDecl->getParamDecl(i)->getType());
393 return false; // TODO ????
394 // } else if (auto callExpr = dyn_cast<ObjCMessageExpr>(parent)) {
395 // if (callExpr->getInstanceReceiver() == stmt) {
396 // return true;
397 // }
398 // if (auto const method = callExpr->getMethodDecl()) {
399 // // TODO could do better
400 // if (method->isVariadic()) {
401 // return false;
402 // }
403 // assert(method->param_size() == callExpr->getNumArgs());
404 // for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
405 // if (callExpr->getArg(i) == stmt) {
406 // return isPointerOrReferenceToConst(
407 // method->param_begin()[i]->getType());
408 // }
409 // }
410 // }
411 // return false; // TODO ????
412 } else if (isa<CXXReinterpretCastExpr>(parent)) {
413 return false;
414 } else if (isa<ImplicitCastExpr>(parent)) {
415 return checkIfCanBeConst(parent, cxxMethodDecl);
416 } else if (isa<CXXStaticCastExpr>(parent)) {
417 return checkIfCanBeConst(parent, cxxMethodDecl);
418 } else if (isa<CXXDynamicCastExpr>(parent)) {
419 return checkIfCanBeConst(parent, cxxMethodDecl);
420 } else if (isa<CXXFunctionalCastExpr>(parent)) {
421 return checkIfCanBeConst(parent, cxxMethodDecl);
422 } else if (isa<CXXConstCastExpr>(parent)) {
423 return false;
424 } else if (isa<CStyleCastExpr>(parent)) {
425 return checkIfCanBeConst(parent, cxxMethodDecl);
426 // } else if (isa<CastExpr>(parent)) { // all other cast expression subtypes
427 // if (auto e = dyn_cast<ExplicitCastExpr>(parent)) {
428 // if (loplugin::TypeCheck(e->getTypeAsWritten()).Void()) {
429 // if (auto const sub = dyn_cast<DeclRefExpr>(
430 // e->getSubExpr()->IgnoreParenImpCasts()))
431 // {
432 // if (sub->getDecl() == cxxMethodDecl) {
433 // return false;
434 // }
435 // }
436 // }
437 // }
438 // return checkIfCanBeConst(parent, cxxMethodDecl);
439 } else if (isa<MemberExpr>(parent)) {
440 return checkIfCanBeConst(parent, cxxMethodDecl);
441 } else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent)) {
442 if (arraySubscriptExpr->getIdx() == stmt)
443 return true;
444 return checkIfCanBeConst(parent, cxxMethodDecl);
445 } else if (isa<ParenExpr>(parent)) {
446 return checkIfCanBeConst(parent, cxxMethodDecl);
447 } else if (auto declStmt = dyn_cast<DeclStmt>(parent)) {
448 for (Decl const * decl : declStmt->decls())
449 if (auto varDecl = dyn_cast<VarDecl>(decl)) {
450 if (varDecl->getInit() == stmt) {
451 auto tc = loplugin::TypeCheck(varDecl->getType());
452 if (tc.LvalueReference() && !tc.LvalueReference().Const())
453 return false;
454 if (tc.Pointer() && !tc.Pointer().Const())
455 return false;
456 return true;
459 // fall through
460 } else if (isa<ReturnStmt>(parent)) {
461 return !isPointerOrReferenceToNonConst(cxxMethodDecl->getReturnType());
462 } else if (isa<InitListExpr>(parent)) {
463 return false; // TODO could be improved
464 } else if (isa<IfStmt>(parent)) {
465 return true;
466 } else if (isa<WhileStmt>(parent)) {
467 return true;
468 } else if (isa<ForStmt>(parent)) {
469 return true;
470 } else if (isa<CompoundStmt>(parent)) {
471 return true;
472 } else if (isa<SwitchStmt>(parent)) {
473 return true;
474 } else if (isa<DoStmt>(parent)) {
475 return true;
476 } else if (isa<CXXDeleteExpr>(parent)) {
477 return false;
478 // } else if (isa<VAArgExpr>(parent)) {
479 // return false;
480 } else if (isa<CXXDependentScopeMemberExpr>(parent)) {
481 return false;
482 } else if (isa<MaterializeTemporaryExpr>(parent)) {
483 return checkIfCanBeConst(parent, cxxMethodDecl);
484 } else if (auto conditionalExpr = dyn_cast<ConditionalOperator>(parent)) {
485 if (conditionalExpr->getCond() == stmt)
486 return true;
487 return checkIfCanBeConst(parent, cxxMethodDecl);
488 // } else if (isa<UnaryExprOrTypeTraitExpr>(parent)) {
489 // return false; // ???
490 } else if (isa<CXXNewExpr>(parent)) {
491 // for (auto pa : cxxNewExpr->placement_arguments())
492 // if (pa == stmt)
493 // return false;
494 return true; // because the Stmt must be a parameter to the expression, probably an array length
495 // } else if (auto lambdaExpr = dyn_cast<LambdaExpr>(parent)) {
496 //// for (auto it = lambdaExpr->capture_begin(); it != lambdaExpr->capture_end(); ++it)
497 //// {
498 //// if (it->capturesVariable() && it->getCapturedVar() == cxxMethodDecl)
499 //// return it->getCaptureKind() != LCK_ByRef;
500 //// }
501 // return true;
502 // } else if (isa<CXXTypeidExpr>(parent)) {
503 // return true;
504 } else if (isa<ParenListExpr>(parent)) {
505 return true;
506 } else if (isa<CXXUnresolvedConstructExpr>(parent)) {
507 return false;
508 // } else if (isa<UnresolvedMemberExpr>(parent)) {
509 // return false;
510 // } else if (isa<PackExpansionExpr>(parent)) {
511 // return false;
512 } else if (isa<ExprWithCleanups>(parent)) {
513 return checkIfCanBeConst(parent, cxxMethodDecl);
514 // } else if (isa<CaseStmt>(parent)) {
515 // return true;
516 // } else if (isa<CXXPseudoDestructorExpr>(parent)) {
517 // return false;
518 // } else if (isa<CXXDependentScopeMemberExpr>(parent)) {
519 // return false;
520 // } else if (isa<ObjCIvarRefExpr>(parent)) {
521 // return checkIfCanBeConst(parent, cxxMethodDecl);
522 } else if (isa<CXXTemporaryObjectExpr>(parent)) {
523 return true;
524 } else if (isa<CXXBindTemporaryExpr>(parent)) {
525 return true;
527 if (parent)
528 parent->dump();
529 // if (cxxMethodDecl)
530 // cxxMethodDecl->dump();
531 report(
532 DiagnosticsEngine::Warning,
533 "oh dear, what can the matter be?",
534 parent->getBeginLoc())
535 << parent->getSourceRange();
536 return false;
539 bool ConstMethod::isPointerOrReferenceToConst(const QualType& qt) {
540 auto const type = loplugin::TypeCheck(qt);
541 if (type.Pointer()) {
542 return bool(type.Pointer().Const());
543 } else if (type.LvalueReference()) {
544 return bool(type.LvalueReference().Const());
546 return false;
549 bool ConstMethod::isPointerOrReferenceToNonConst(const QualType& qt) {
550 auto const type = loplugin::TypeCheck(qt);
551 if (type.Pointer()) {
552 return !bool(type.Pointer().Const());
553 } else if (type.LvalueReference()) {
554 return !bool(type.LvalueReference().Const());
556 return false;
559 loplugin::Plugin::Registration< ConstMethod > X("constmethod", false);
563 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */