1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #if !defined _WIN32 //TODO, #include <sys/file.h>
16 #include <unordered_set>
23 #include "config_clang.h"
29 #include "clang/AST/ParentMapContext.h"
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:
40 $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='locking2' check
41 $ ./compilerplugins/clang/locking2.py
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
);
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
;
76 * Wrap the different kinds of callable and callee objects in the clang AST so I can define methods that handle everything.
80 const CallExpr
* m_callExpr
;
81 const CXXConstructExpr
* m_cxxConstructExpr
;
84 CallerWrapper(const CallExpr
* callExpr
)
85 : m_callExpr(callExpr
)
86 , m_cxxConstructExpr(nullptr)
89 CallerWrapper(const CXXConstructExpr
* cxxConstructExpr
)
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
);
105 const FunctionDecl
* m_calleeFunctionDecl
= nullptr;
106 const CXXConstructorDecl
* m_cxxConstructorDecl
= nullptr;
107 const FunctionProtoType
* m_functionPrototype
= nullptr;
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
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();
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();
152 CXXMethodDecl
const* getAsCXXMethodDecl() const
154 if (m_calleeFunctionDecl
)
155 return dyn_cast
<CXXMethodDecl
>(m_calleeFunctionDecl
);
160 class Locking2
: public RecursiveASTVisitor
<Locking2
>, public loplugin::Plugin
163 explicit Locking2(loplugin::InstantiationData
const& 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
*);
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
*);
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())
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
217 std::ofstream myfile
;
218 myfile
.open(WORKDIR
"/loplugin.locking2.log", std::ios::app
| std::ios::out
);
224 for (const MyLockedInfo
& s
: lockedAtSet
)
225 report(DiagnosticsEngine::Warning
, "locked %0", s
.memberExpr
->getBeginLoc())
230 MyFieldInfo
Locking2::niceName(const FieldDecl
* fieldDecl
)
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();
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
);
260 = std::string(name
.substr(strlen(SRCDIR
) + 1)) + ":"
261 + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
262 loplugin::normalizeDotDotInFilePath(aInfo
.sourceLocation
);
267 MyLockedInfo
Locking2::niceNameLocked(const MemberExpr
* memberExpr
)
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();
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
);
298 = std::string(name
.substr(strlen(SRCDIR
) + 1)) + ":"
299 + std::to_string(compiler
.getSourceManager().getSpellingLineNumber(expansionLoc
));
300 loplugin::normalizeDotDotInFilePath(aInfo
.sourceLocation
);
305 bool Locking2::VisitFieldDecl(const FieldDecl
* fieldDecl
)
307 fieldDecl
= fieldDecl
->getCanonicalDecl();
308 if (ignoreLocation(fieldDecl
))
312 // ignore stuff that forms part of the stable URE interface
313 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(fieldDecl
->getLocation())))
317 definitionSet
.insert(niceName(fieldDecl
));
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
;
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
;
350 bool Locking2::TraverseFunctionDecl(FunctionDecl
* functionDecl
)
352 auto copy2
= insideFunctionDecl
;
353 insideFunctionDecl
= functionDecl
;
354 bool ret
= RecursiveASTVisitor::TraverseFunctionDecl(functionDecl
);
355 insideFunctionDecl
= copy2
;
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
);
372 insideConditionalCheckOfMemberSet
.pop_back();
376 bool Locking2::VisitMemberExpr(const MemberExpr
* memberExpr
)
378 const ValueDecl
* decl
= memberExpr
->getMemberDecl();
379 const FieldDecl
* fieldDecl
= dyn_cast
<FieldDecl
>(decl
);
384 fieldDecl
= fieldDecl
->getCanonicalDecl();
385 if (ignoreLocation(fieldDecl
))
389 // ignore stuff that forms part of the stable URE interface
390 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(fieldDecl
->getLocation())))
395 check(fieldDecl
, memberExpr
);
400 void Locking2::check(const FieldDecl
* fieldDecl
, const Expr
* memberExpr
)
402 auto parentsRange
= compiler
.getASTContext().getParents(*memberExpr
);
403 const Stmt
* child
= memberExpr
;
405 = parentsRange
.begin() == parentsRange
.end() ? nullptr : parentsRange
.begin()->get
<Stmt
>();
406 // walk up the tree until we find something interesting
407 bool bCannotBeConst
= false;
409 auto walkUp
= [&]() {
411 auto parentsRange
= compiler
.getASTContext().getParents(*parent
);
412 parent
= parentsRange
.begin() == parentsRange
.end() ? nullptr
413 : parentsRange
.begin()->get
<Stmt
>();
419 // check if we have an expression like
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;
435 if (isa
<CXXReinterpretCastExpr
>(parent
))
437 // once we see one of these, there is not much useful we can know
438 bCannotBeConst
= true;
441 else if (isa
<CastExpr
>(parent
) || isa
<MemberExpr
>(parent
) || isa
<ParenExpr
>(parent
)
442 || isa
<ParenListExpr
>(parent
) || isa
<ArrayInitLoopExpr
>(parent
)
443 || isa
<ExprWithCleanups
>(parent
))
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
453 bCannotBeConst
= true;
457 else if (auto operatorCallExpr
= dyn_cast
<CXXOperatorCallExpr
>(parent
))
459 auto callee
= getCallee(operatorCallExpr
);
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;
475 bCannotBeConst
= true; // conservative, could improve
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;
495 if (IsPassedByNonConst(fieldDecl
, child
, cxxMemberCallExpr
,
496 CalleeWrapper(calleeMethodDecl
)))
497 bCannotBeConst
= true;
500 bCannotBeConst
= true; // can happen in templates
503 else if (auto cxxConstructExpr
= dyn_cast
<CXXConstructExpr
>(parent
))
505 if (IsPassedByNonConst(fieldDecl
, child
, cxxConstructExpr
,
506 CalleeWrapper(cxxConstructExpr
)))
507 bCannotBeConst
= true;
510 else if (auto callExpr
= dyn_cast
<CallExpr
>(parent
))
512 auto callee
= getCallee(callExpr
);
515 if (IsPassedByNonConst(fieldDecl
, child
, callExpr
, *callee
))
516 bCannotBeConst
= true;
519 bCannotBeConst
= true; // conservative, could improve
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
;
532 if (binaryOp
->getLHS() == child
)
533 bCannotBeConst
= true;
534 else if (loplugin::TypeCheck(binaryOp
->getLHS()->getType())
537 // if the LHS is a non-const reference, we could write to the field later on
538 bCannotBeConst
= true;
542 else if (isa
<ReturnStmt
>(parent
))
544 if (insideFunctionDecl
)
546 auto tc
= loplugin::TypeCheck(insideFunctionDecl
->getReturnType());
547 if (tc
.LvalueReference().NonConst())
548 bCannotBeConst
= true;
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
))
566 report(DiagnosticsEngine::Warning
, "oh dear, what can the matter be? writtenTo=%0",
567 memberExpr
->getBeginLoc())
568 << bCannotBeConst
<< memberExpr
->getSourceRange();
571 report(DiagnosticsEngine::Note
, "parent over here", parent
->getBeginLoc())
572 << parent
->getSourceRange();
576 fieldDecl
->getType()->dump();
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())
600 for (unsigned i
= 0; i
< len
; ++i
)
601 if (callExpr
.getArg(i
) == child
)
602 if (loplugin::TypeCheck(calleeFunctionDecl
.getParamType(i
))
610 compat::optional
<CalleeWrapper
> Locking2::getCallee(CallExpr
const* callExpr
)
612 FunctionDecl
const* functionDecl
= callExpr
->getDirectCallee();
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
))
635 if (compoundStmt
->size() < 2)
638 const Stmt
* firstStmt
= *compoundStmt
->body_begin();
639 bool solarMutex
= isSolarMutexLockGuardStmt(firstStmt
);
640 const CXXThisExpr
* ignoreThisStmt
= nullptr;
642 ignoreThisStmt
= isOtherMutexLockGuardStmt(firstStmt
);
643 if (!solarMutex
&& ignoreThisStmt
== nullptr)
645 const ReturnStmt
* returnStmt
= dyn_cast
<ReturnStmt
>(*(compoundStmt
->body_begin() + 1));
646 if (!returnStmt
|| !returnStmt
->getRetValue())
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
);
655 lockedAtSet
.insert(niceNameLocked(memberExpr
));
660 bool Locking2::isSolarMutexLockGuardStmt(const Stmt
* stmt
)
662 auto declStmt
= dyn_cast
<DeclStmt
>(stmt
);
665 if (!declStmt
->isSingleDecl())
667 auto varDecl
= dyn_cast
<VarDecl
>(declStmt
->getSingleDecl());
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())
679 const CXXThisExpr
* Locking2::isOtherMutexLockGuardStmt(const Stmt
* stmt
)
681 auto declStmt
= dyn_cast
<DeclStmt
>(stmt
);
684 if (!declStmt
->isSingleDecl())
686 auto varDecl
= dyn_cast
<VarDecl
>(declStmt
->getSingleDecl());
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"))
693 auto cxxConstructExpr
= dyn_cast
<CXXConstructExpr
>(varDecl
->getInit());
694 if (!cxxConstructExpr
|| cxxConstructExpr
->getNumArgs() < 1)
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());
703 else if (auto memberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(arg0
))
705 return dyn_cast_or_null
<CXXThisExpr
>(
706 memberCallExpr
->getImplicitObjectArgument()->IgnoreImplicit());
711 loplugin::Plugin::Registration
<Locking2
> X("locking2", false);
716 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */