tdf#161878 Ignore nested SYMBOL field inside IF field
[LibreOffice.git] / compilerplugins / clang / locking2.cxx
blobd31b29b2023e48fc3917ca2ec99ac63f4b1d9b5b
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 #if !defined _WIN32 //TODO, #include <sys/file.h>
12 #include <cassert>
13 #include <string>
14 #include <iostream>
15 #include <fstream>
16 #include <unordered_set>
17 #include <vector>
18 #include <algorithm>
19 #include <optional>
20 #include <sys/file.h>
21 #include <unistd.h>
23 #include "config_clang.h"
25 #include "plugin.hxx"
26 #include "check.hxx"
27 #include "compat.hxx"
29 #include "clang/AST/ParentMapContext.h"
31 /**
32 Look for fields that are
33 (a) only assigned to in the constructor using field-init, and can therefore be const.
34 (b) protected via a mutex guard when accessed
36 Which normally means we can remove the mutex guard.
38 The process goes something like this:
39 $ make check
40 $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='locking2' check
41 $ ./compilerplugins/clang/locking2.py
44 namespace
46 struct MyFieldInfo
48 std::string parentClass;
49 std::string fieldName;
50 std::string fieldType;
51 std::string sourceLocation;
53 bool operator<(const MyFieldInfo& lhs, const MyFieldInfo& rhs)
55 return std::tie(lhs.parentClass, lhs.fieldName) < std::tie(rhs.parentClass, rhs.fieldName);
57 struct MyLockedInfo
59 const MemberExpr* memberExpr;
60 std::string parentClass;
61 std::string fieldName;
62 std::string sourceLocation;
64 bool operator<(const MyLockedInfo& lhs, const MyLockedInfo& rhs)
66 return std::tie(lhs.parentClass, lhs.fieldName, lhs.sourceLocation)
67 < std::tie(rhs.parentClass, rhs.fieldName, rhs.sourceLocation);
70 // try to limit the voluminous output a little
71 static std::set<MyFieldInfo> cannotBeConstSet;
72 static std::set<MyFieldInfo> definitionSet;
73 static std::set<MyLockedInfo> lockedAtSet;
75 /**
76 * Wrap the different kinds of callable and callee objects in the clang AST so I can define methods that handle everything.
78 class CallerWrapper
80 const CallExpr* m_callExpr;
81 const CXXConstructExpr* m_cxxConstructExpr;
83 public:
84 CallerWrapper(const CallExpr* callExpr)
85 : m_callExpr(callExpr)
86 , m_cxxConstructExpr(nullptr)
89 CallerWrapper(const CXXConstructExpr* cxxConstructExpr)
90 : m_callExpr(nullptr)
91 , m_cxxConstructExpr(cxxConstructExpr)
94 unsigned getNumArgs() const
96 return m_callExpr ? m_callExpr->getNumArgs() : m_cxxConstructExpr->getNumArgs();
98 const Expr* getArg(unsigned i) const
100 return m_callExpr ? m_callExpr->getArg(i) : m_cxxConstructExpr->getArg(i);
103 class CalleeWrapper
105 const FunctionDecl* m_calleeFunctionDecl = nullptr;
106 const CXXConstructorDecl* m_cxxConstructorDecl = nullptr;
107 const FunctionProtoType* m_functionPrototype = nullptr;
109 public:
110 explicit CalleeWrapper(const FunctionDecl* calleeFunctionDecl)
111 : m_calleeFunctionDecl(calleeFunctionDecl)
114 explicit CalleeWrapper(const CXXConstructExpr* cxxConstructExpr)
115 : m_cxxConstructorDecl(cxxConstructExpr->getConstructor())
118 explicit CalleeWrapper(const FunctionProtoType* functionPrototype)
119 : m_functionPrototype(functionPrototype)
122 unsigned getNumParams() const
124 if (m_calleeFunctionDecl)
125 return m_calleeFunctionDecl->getNumParams();
126 else if (m_cxxConstructorDecl)
127 return m_cxxConstructorDecl->getNumParams();
128 else if (m_functionPrototype->param_type_begin() == m_functionPrototype->param_type_end())
129 // FunctionProtoType will assert if we call getParamTypes() and it has no params
130 return 0;
131 else
132 return m_functionPrototype->getParamTypes().size();
134 const QualType getParamType(unsigned i) const
136 if (m_calleeFunctionDecl)
137 return m_calleeFunctionDecl->getParamDecl(i)->getType();
138 else if (m_cxxConstructorDecl)
139 return m_cxxConstructorDecl->getParamDecl(i)->getType();
140 else
141 return m_functionPrototype->getParamTypes()[i];
143 std::string getNameAsString() const
145 if (m_calleeFunctionDecl)
146 return m_calleeFunctionDecl->getNameAsString();
147 else if (m_cxxConstructorDecl)
148 return m_cxxConstructorDecl->getNameAsString();
149 else
150 return "";
152 CXXMethodDecl const* getAsCXXMethodDecl() const
154 if (m_calleeFunctionDecl)
155 return dyn_cast<CXXMethodDecl>(m_calleeFunctionDecl);
156 return nullptr;
160 class Locking2 : public RecursiveASTVisitor<Locking2>, public loplugin::Plugin
162 public:
163 explicit Locking2(loplugin::InstantiationData const& data)
164 : Plugin(data)
168 virtual void run() override;
170 bool shouldVisitTemplateInstantiations() const { return true; }
171 bool shouldVisitImplicitCode() const { return true; }
173 bool VisitFieldDecl(const FieldDecl*);
174 bool VisitMemberExpr(const MemberExpr*);
175 bool TraverseCXXConstructorDecl(CXXConstructorDecl*);
176 bool TraverseCXXMethodDecl(CXXMethodDecl*);
177 bool TraverseFunctionDecl(FunctionDecl*);
178 bool TraverseIfStmt(IfStmt*);
179 bool VisitCompoundStmt(const CompoundStmt*);
181 private:
182 MyFieldInfo niceName(const FieldDecl*);
183 MyLockedInfo niceNameLocked(const MemberExpr*);
184 void check(const FieldDecl* fieldDecl, const Expr* memberExpr);
185 bool isSomeKindOfZero(const Expr* arg);
186 bool IsPassedByNonConst(const FieldDecl* fieldDecl, const Stmt* child, CallerWrapper callExpr,
187 CalleeWrapper calleeFunctionDecl);
188 compat::optional<CalleeWrapper> getCallee(CallExpr const*);
190 RecordDecl* insideMoveOrCopyDeclParent = nullptr;
191 // For reasons I do not understand, parentFunctionDecl() is not reliable, so
192 // we store the parent function on the way down the AST.
193 FunctionDecl* insideFunctionDecl = nullptr;
194 std::vector<FieldDecl const*> insideConditionalCheckOfMemberSet;
196 bool isSolarMutexLockGuardStmt(const Stmt*);
197 const CXXThisExpr* isOtherMutexLockGuardStmt(const Stmt*);
200 void Locking2::run()
202 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
204 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
205 // writing to the same logfile
206 if (!isUnitTestMode())
208 std::string output;
209 for (const MyFieldInfo& s : cannotBeConstSet)
210 output += "write-outside-constructor:\t" + s.parentClass + "\t" + s.fieldName + "\n";
211 for (const MyFieldInfo& s : definitionSet)
212 output += "definition:\t" + s.parentClass + "\t" + s.fieldName + "\t" + s.fieldType
213 + "\t" + s.sourceLocation + "\n";
214 for (const MyLockedInfo& s : lockedAtSet)
215 output += "locked:\t" + s.parentClass + "\t" + s.fieldName + "\t" + s.sourceLocation
216 + "\n";
217 std::ofstream myfile;
218 myfile.open(WORKDIR "/loplugin.locking2.log", std::ios::app | std::ios::out);
219 myfile << output;
220 myfile.close();
222 else
224 for (const MyLockedInfo& s : lockedAtSet)
225 report(DiagnosticsEngine::Warning, "locked %0", s.memberExpr->getBeginLoc())
226 << s.fieldName;
230 MyFieldInfo Locking2::niceName(const FieldDecl* fieldDecl)
232 MyFieldInfo aInfo;
234 const RecordDecl* recordDecl = fieldDecl->getParent();
236 if (const CXXRecordDecl* cxxRecordDecl = dyn_cast<CXXRecordDecl>(recordDecl))
238 if (cxxRecordDecl->getTemplateInstantiationPattern())
239 cxxRecordDecl = cxxRecordDecl->getTemplateInstantiationPattern();
240 aInfo.parentClass = cxxRecordDecl->getQualifiedNameAsString();
242 else
244 aInfo.parentClass = recordDecl->getQualifiedNameAsString();
247 aInfo.fieldName = fieldDecl->getNameAsString();
248 // sometimes the name (if it's an anonymous thing) contains the full path of the build folder, which we don't need
249 size_t idx = aInfo.fieldName.find(SRCDIR);
250 if (idx != std::string::npos)
252 aInfo.fieldName = aInfo.fieldName.replace(idx, strlen(SRCDIR), "");
254 aInfo.fieldType = fieldDecl->getType().getAsString();
256 SourceLocation expansionLoc
257 = compiler.getSourceManager().getExpansionLoc(fieldDecl->getLocation());
258 StringRef name = getFilenameOfLocation(expansionLoc);
259 aInfo.sourceLocation
260 = std::string(name.substr(strlen(SRCDIR) + 1)) + ":"
261 + std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
262 loplugin::normalizeDotDotInFilePath(aInfo.sourceLocation);
264 return aInfo;
267 MyLockedInfo Locking2::niceNameLocked(const MemberExpr* memberExpr)
269 MyLockedInfo aInfo;
271 const FieldDecl* fieldDecl = dyn_cast<FieldDecl>(memberExpr->getMemberDecl());
272 const RecordDecl* recordDecl = fieldDecl->getParent();
274 if (const CXXRecordDecl* cxxRecordDecl = dyn_cast<CXXRecordDecl>(recordDecl))
276 if (cxxRecordDecl->getTemplateInstantiationPattern())
277 cxxRecordDecl = cxxRecordDecl->getTemplateInstantiationPattern();
278 aInfo.parentClass = cxxRecordDecl->getQualifiedNameAsString();
280 else
282 aInfo.parentClass = recordDecl->getQualifiedNameAsString();
284 aInfo.memberExpr = memberExpr;
286 aInfo.fieldName = fieldDecl->getNameAsString();
287 // sometimes the name (if it's an anonymous thing) contains the full path of the build folder, which we don't need
288 size_t idx = aInfo.fieldName.find(SRCDIR);
289 if (idx != std::string::npos)
291 aInfo.fieldName = aInfo.fieldName.replace(idx, strlen(SRCDIR), "");
294 SourceLocation expansionLoc
295 = compiler.getSourceManager().getExpansionLoc(memberExpr->getBeginLoc());
296 StringRef name = getFilenameOfLocation(expansionLoc);
297 aInfo.sourceLocation
298 = std::string(name.substr(strlen(SRCDIR) + 1)) + ":"
299 + std::to_string(compiler.getSourceManager().getSpellingLineNumber(expansionLoc));
300 loplugin::normalizeDotDotInFilePath(aInfo.sourceLocation);
302 return aInfo;
305 bool Locking2::VisitFieldDecl(const FieldDecl* fieldDecl)
307 fieldDecl = fieldDecl->getCanonicalDecl();
308 if (ignoreLocation(fieldDecl))
310 return true;
312 // ignore stuff that forms part of the stable URE interface
313 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(fieldDecl->getLocation())))
315 return true;
317 definitionSet.insert(niceName(fieldDecl));
318 return true;
321 bool Locking2::TraverseCXXConstructorDecl(CXXConstructorDecl* cxxConstructorDecl)
323 auto copy = insideMoveOrCopyDeclParent;
324 if (!ignoreLocation(cxxConstructorDecl) && cxxConstructorDecl->isThisDeclarationADefinition())
326 if (cxxConstructorDecl->isCopyOrMoveConstructor())
327 insideMoveOrCopyDeclParent = cxxConstructorDecl->getParent();
329 bool ret = RecursiveASTVisitor::TraverseCXXConstructorDecl(cxxConstructorDecl);
330 insideMoveOrCopyDeclParent = copy;
331 return ret;
334 bool Locking2::TraverseCXXMethodDecl(CXXMethodDecl* cxxMethodDecl)
336 auto copy1 = insideMoveOrCopyDeclParent;
337 auto copy2 = insideFunctionDecl;
338 if (!ignoreLocation(cxxMethodDecl) && cxxMethodDecl->isThisDeclarationADefinition())
340 if (cxxMethodDecl->isCopyAssignmentOperator() || cxxMethodDecl->isMoveAssignmentOperator())
341 insideMoveOrCopyDeclParent = cxxMethodDecl->getParent();
343 insideFunctionDecl = cxxMethodDecl;
344 bool ret = RecursiveASTVisitor::TraverseCXXMethodDecl(cxxMethodDecl);
345 insideMoveOrCopyDeclParent = copy1;
346 insideFunctionDecl = copy2;
347 return ret;
350 bool Locking2::TraverseFunctionDecl(FunctionDecl* functionDecl)
352 auto copy2 = insideFunctionDecl;
353 insideFunctionDecl = functionDecl;
354 bool ret = RecursiveASTVisitor::TraverseFunctionDecl(functionDecl);
355 insideFunctionDecl = copy2;
356 return ret;
359 bool Locking2::TraverseIfStmt(IfStmt* ifStmt)
361 FieldDecl const* memberFieldDecl = nullptr;
362 if (Expr const* cond = ifStmt->getCond())
364 if (auto memberExpr = dyn_cast<MemberExpr>(cond->IgnoreParenImpCasts()))
366 if ((memberFieldDecl = dyn_cast<FieldDecl>(memberExpr->getMemberDecl())))
367 insideConditionalCheckOfMemberSet.push_back(memberFieldDecl);
370 bool ret = RecursiveASTVisitor::TraverseIfStmt(ifStmt);
371 if (memberFieldDecl)
372 insideConditionalCheckOfMemberSet.pop_back();
373 return ret;
376 bool Locking2::VisitMemberExpr(const MemberExpr* memberExpr)
378 const ValueDecl* decl = memberExpr->getMemberDecl();
379 const FieldDecl* fieldDecl = dyn_cast<FieldDecl>(decl);
380 if (!fieldDecl)
382 return true;
384 fieldDecl = fieldDecl->getCanonicalDecl();
385 if (ignoreLocation(fieldDecl))
387 return true;
389 // ignore stuff that forms part of the stable URE interface
390 if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(fieldDecl->getLocation())))
392 return true;
395 check(fieldDecl, memberExpr);
397 return true;
400 void Locking2::check(const FieldDecl* fieldDecl, const Expr* memberExpr)
402 auto parentsRange = compiler.getASTContext().getParents(*memberExpr);
403 const Stmt* child = memberExpr;
404 const Stmt* parent
405 = parentsRange.begin() == parentsRange.end() ? nullptr : parentsRange.begin()->get<Stmt>();
406 // walk up the tree until we find something interesting
407 bool bCannotBeConst = false;
408 bool bDump = false;
409 auto walkUp = [&]() {
410 child = parent;
411 auto parentsRange = compiler.getASTContext().getParents(*parent);
412 parent = parentsRange.begin() == parentsRange.end() ? nullptr
413 : parentsRange.begin()->get<Stmt>();
417 if (!parent)
419 // check if we have an expression like
420 // int& r = m_field;
421 auto parentsRange = compiler.getASTContext().getParents(*child);
422 if (parentsRange.begin() != parentsRange.end())
424 auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>());
425 // The isImplicit() call is to avoid triggering when we see the vardecl which is part of a for-range statement,
426 // which is of type 'T&&' and also an l-value-ref ?
427 if (varDecl && !varDecl->isImplicit()
428 && loplugin::TypeCheck(varDecl->getType()).LvalueReference().NonConst())
430 bCannotBeConst = true;
433 break;
435 if (isa<CXXReinterpretCastExpr>(parent))
437 // once we see one of these, there is not much useful we can know
438 bCannotBeConst = true;
439 break;
441 else if (isa<CastExpr>(parent) || isa<MemberExpr>(parent) || isa<ParenExpr>(parent)
442 || isa<ParenListExpr>(parent) || isa<ArrayInitLoopExpr>(parent)
443 || isa<ExprWithCleanups>(parent))
445 walkUp();
447 else if (auto unaryOperator = dyn_cast<UnaryOperator>(parent))
449 UnaryOperator::Opcode op = unaryOperator->getOpcode();
450 if (op == UO_AddrOf || op == UO_PostInc || op == UO_PostDec || op == UO_PreInc
451 || op == UO_PreDec)
453 bCannotBeConst = true;
455 walkUp();
457 else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent))
459 auto callee = getCallee(operatorCallExpr);
460 if (callee)
462 // if calling a non-const operator on the field
463 auto calleeMethodDecl = callee->getAsCXXMethodDecl();
464 if (calleeMethodDecl && operatorCallExpr->getArg(0) == child
465 && !calleeMethodDecl->isConst())
467 bCannotBeConst = true;
469 else if (IsPassedByNonConst(fieldDecl, child, operatorCallExpr, *callee))
471 bCannotBeConst = true;
474 else
475 bCannotBeConst = true; // conservative, could improve
476 walkUp();
478 else if (auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(parent))
480 const CXXMethodDecl* calleeMethodDecl = cxxMemberCallExpr->getMethodDecl();
481 if (calleeMethodDecl)
483 // if calling a non-const method on the field
484 const Expr* tmp = dyn_cast<Expr>(child);
485 if (tmp->isBoundMemberFunction(compiler.getASTContext()))
487 tmp = dyn_cast<MemberExpr>(tmp)->getBase();
489 if (cxxMemberCallExpr->getImplicitObjectArgument() == tmp
490 && !calleeMethodDecl->isConst())
492 bCannotBeConst = true;
493 break;
495 if (IsPassedByNonConst(fieldDecl, child, cxxMemberCallExpr,
496 CalleeWrapper(calleeMethodDecl)))
497 bCannotBeConst = true;
499 else
500 bCannotBeConst = true; // can happen in templates
501 walkUp();
503 else if (auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(parent))
505 if (IsPassedByNonConst(fieldDecl, child, cxxConstructExpr,
506 CalleeWrapper(cxxConstructExpr)))
507 bCannotBeConst = true;
508 walkUp();
510 else if (auto callExpr = dyn_cast<CallExpr>(parent))
512 auto callee = getCallee(callExpr);
513 if (callee)
515 if (IsPassedByNonConst(fieldDecl, child, callExpr, *callee))
516 bCannotBeConst = true;
518 else
519 bCannotBeConst = true; // conservative, could improve
520 walkUp();
522 else if (auto binaryOp = dyn_cast<BinaryOperator>(parent))
524 BinaryOperator::Opcode op = binaryOp->getOpcode();
525 const bool assignmentOp = op == BO_Assign || op == BO_MulAssign || op == BO_DivAssign
526 || op == BO_RemAssign || op == BO_AddAssign
527 || op == BO_SubAssign || op == BO_ShlAssign
528 || op == BO_ShrAssign || op == BO_AndAssign
529 || op == BO_XorAssign || op == BO_OrAssign;
530 if (assignmentOp)
532 if (binaryOp->getLHS() == child)
533 bCannotBeConst = true;
534 else if (loplugin::TypeCheck(binaryOp->getLHS()->getType())
535 .LvalueReference()
536 .NonConst())
537 // if the LHS is a non-const reference, we could write to the field later on
538 bCannotBeConst = true;
540 walkUp();
542 else if (isa<ReturnStmt>(parent))
544 if (insideFunctionDecl)
546 auto tc = loplugin::TypeCheck(insideFunctionDecl->getReturnType());
547 if (tc.LvalueReference().NonConst())
548 bCannotBeConst = true;
550 break;
552 else if (isa<SwitchStmt>(parent) || isa<WhileStmt>(parent) || isa<ForStmt>(parent)
553 || isa<IfStmt>(parent) || isa<DoStmt>(parent) || isa<CXXForRangeStmt>(parent)
554 || isa<DefaultStmt>(parent))
556 break;
558 else
560 walkUp();
562 } while (true);
564 if (bDump)
566 report(DiagnosticsEngine::Warning, "oh dear, what can the matter be? writtenTo=%0",
567 memberExpr->getBeginLoc())
568 << bCannotBeConst << memberExpr->getSourceRange();
569 if (parent)
571 report(DiagnosticsEngine::Note, "parent over here", parent->getBeginLoc())
572 << parent->getSourceRange();
573 parent->dump();
575 memberExpr->dump();
576 fieldDecl->getType()->dump();
579 if (bCannotBeConst)
581 cannotBeConstSet.insert(niceName(fieldDecl));
585 bool Locking2::IsPassedByNonConst(const FieldDecl* fieldDecl, const Stmt* child,
586 CallerWrapper callExpr, CalleeWrapper calleeFunctionDecl)
588 unsigned len = std::min(callExpr.getNumArgs(), calleeFunctionDecl.getNumParams());
589 // if it's an array, passing it by value to a method typically means the
590 // callee takes a pointer and can modify the array
591 if (fieldDecl->getType()->isConstantArrayType())
593 for (unsigned i = 0; i < len; ++i)
594 if (callExpr.getArg(i) == child)
595 if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i)).Pointer().NonConst())
596 return true;
598 else
600 for (unsigned i = 0; i < len; ++i)
601 if (callExpr.getArg(i) == child)
602 if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i))
603 .LvalueReference()
604 .NonConst())
605 return true;
607 return false;
610 compat::optional<CalleeWrapper> Locking2::getCallee(CallExpr const* callExpr)
612 FunctionDecl const* functionDecl = callExpr->getDirectCallee();
613 if (functionDecl)
614 return CalleeWrapper(functionDecl);
616 // Extract the functionprototype from a type
617 clang::Type const* calleeType = callExpr->getCallee()->getType().getTypePtr();
618 if (auto pointerType = calleeType->getUnqualifiedDesugaredType()->getAs<clang::PointerType>())
620 if (auto prototype = pointerType->getPointeeType()
621 ->getUnqualifiedDesugaredType()
622 ->getAs<FunctionProtoType>())
624 return CalleeWrapper(prototype);
628 return compat::optional<CalleeWrapper>();
631 bool Locking2::VisitCompoundStmt(const CompoundStmt* compoundStmt)
633 if (ignoreLocation(compoundStmt))
634 return true;
635 if (compoundStmt->size() < 2)
636 return true;
638 const Stmt* firstStmt = *compoundStmt->body_begin();
639 bool solarMutex = isSolarMutexLockGuardStmt(firstStmt);
640 const CXXThisExpr* ignoreThisStmt = nullptr;
641 if (!solarMutex)
642 ignoreThisStmt = isOtherMutexLockGuardStmt(firstStmt);
643 if (!solarMutex && ignoreThisStmt == nullptr)
644 return true;
645 const ReturnStmt* returnStmt = dyn_cast<ReturnStmt>(*(compoundStmt->body_begin() + 1));
646 if (!returnStmt || !returnStmt->getRetValue())
647 return true;
648 const Expr* retValue = returnStmt->getRetValue()->IgnoreImplicit();
649 if (auto constructExpr = dyn_cast<CXXConstructExpr>(retValue))
650 retValue = constructExpr->getArg(0)->IgnoreImplicit();
651 const MemberExpr* memberExpr = dyn_cast<MemberExpr>(retValue);
652 if (!memberExpr)
653 return true;
655 lockedAtSet.insert(niceNameLocked(memberExpr));
657 return true;
660 bool Locking2::isSolarMutexLockGuardStmt(const Stmt* stmt)
662 auto declStmt = dyn_cast<DeclStmt>(stmt);
663 if (!declStmt)
664 return false;
665 if (!declStmt->isSingleDecl())
666 return false;
667 auto varDecl = dyn_cast<VarDecl>(declStmt->getSingleDecl());
668 if (!varDecl)
669 return false;
670 auto tc = loplugin::TypeCheck(varDecl->getType());
671 if (!tc.Class("SolarMutexGuard").GlobalNamespace()
672 && !tc.Class("SolarMutexClearableGuard").GlobalNamespace()
673 && !tc.Class("SolarMutexResettableGuard").GlobalNamespace()
674 && !tc.Class("SolarMutexTryAndBuyGuard").GlobalNamespace())
675 return false;
676 return true;
679 const CXXThisExpr* Locking2::isOtherMutexLockGuardStmt(const Stmt* stmt)
681 auto declStmt = dyn_cast<DeclStmt>(stmt);
682 if (!declStmt)
683 return nullptr;
684 if (!declStmt->isSingleDecl())
685 return nullptr;
686 auto varDecl = dyn_cast<VarDecl>(declStmt->getSingleDecl());
687 if (!varDecl)
688 return nullptr;
689 auto tc = loplugin::TypeCheck(varDecl->getType());
690 if (!tc.Class("unique_lock").StdNamespace() && !tc.Class("scoped_lock").StdNamespace()
691 && !tc.Class("Guard") && !tc.Class("ClearableGuard") && !tc.Class("ResettableGuard"))
692 return nullptr;
693 auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(varDecl->getInit());
694 if (!cxxConstructExpr || cxxConstructExpr->getNumArgs() < 1)
695 return nullptr;
696 auto arg0 = cxxConstructExpr->getArg(0);
697 if (auto memberExpr = dyn_cast<MemberExpr>(arg0))
699 const CXXThisExpr* thisStmt
700 = dyn_cast<CXXThisExpr>(memberExpr->getBase()->IgnoreImplicit());
701 return thisStmt;
703 else if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(arg0))
705 return dyn_cast_or_null<CXXThisExpr>(
706 memberCallExpr->getImplicitObjectArgument()->IgnoreImplicit());
708 return nullptr;
711 loplugin::Plugin::Registration<Locking2> X("locking2", false);
714 #endif
716 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */