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/.
13 #include <unordered_map>
14 #include <unordered_set>
18 #include "clang/AST/CXXInheritance.h"
19 #include "clang/AST/StmtVisitor.h"
22 TODO deal with C++ operator overload assign
27 //static bool startswith(const std::string& rStr, const char* pSubStr) {
28 // return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
30 class BuriedAssign
: public loplugin::FilteringPlugin
<BuriedAssign
>
33 explicit BuriedAssign(loplugin::InstantiationData
const& data
)
34 : FilteringPlugin(data
)
38 virtual void run() override
40 std::string
fn(handler
.getMainFileName());
41 loplugin::normalizeDotDotInFilePath(fn
);
42 // getParentStmt appears not to be working very well here
43 if (fn
== SRCDIR
"/stoc/source/inspect/introspection.cxx"
44 || fn
== SRCDIR
"/stoc/source/corereflection/criface.cxx")
46 // calling an acquire via function pointer
47 if (fn
== SRCDIR
"/cppu/source/uno/lbenv.cxx"
48 || fn
== SRCDIR
"cppu/source/typelib/static_types.cxx")
50 // false+, not sure why
51 if (fn
== SRCDIR
"/vcl/source/window/menu.cxx")
53 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
56 bool VisitBinaryOperator(BinaryOperator
const*);
57 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr
const*);
60 void checkExpr(Expr
const*);
63 static bool isAssignmentOp(clang::BinaryOperatorKind op
)
65 // We ignore BO_ShrAssign i.e. >>= because we use that everywhere for
66 // extracting data from css::uno::Any
67 return op
== BO_Assign
|| op
== BO_MulAssign
|| op
== BO_DivAssign
|| op
== BO_RemAssign
68 || op
== BO_AddAssign
|| op
== BO_SubAssign
|| op
== BO_ShlAssign
|| op
== BO_AndAssign
69 || op
== BO_XorAssign
|| op
== BO_OrAssign
;
72 static bool isAssignmentOp(clang::OverloadedOperatorKind Opc
)
74 // Same logic as CXXOperatorCallExpr::isAssignmentOp(), which our supported clang
76 // Except that we ignore OO_GreaterGreaterEqual i.e. >>= because we use that everywhere for
77 // extracting data from css::uno::Any
78 return Opc
== OO_Equal
|| Opc
== OO_StarEqual
|| Opc
== OO_SlashEqual
|| Opc
== OO_PercentEqual
79 || Opc
== OO_PlusEqual
|| Opc
== OO_MinusEqual
|| Opc
== OO_LessLessEqual
80 || Opc
== OO_AmpEqual
|| Opc
== OO_CaretEqual
|| Opc
== OO_PipeEqual
;
83 bool BuriedAssign::VisitBinaryOperator(BinaryOperator
const* binaryOp
)
85 if (ignoreLocation(binaryOp
))
88 if (!isAssignmentOp(binaryOp
->getOpcode()))
95 bool BuriedAssign::VisitCXXOperatorCallExpr(CXXOperatorCallExpr
const* cxxOper
)
97 if (ignoreLocation(cxxOper
))
99 if (!isAssignmentOp(cxxOper
->getOperator()))
105 void BuriedAssign::checkExpr(Expr
const* binaryOp
)
107 if (compiler
.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(binaryOp
)))
109 if (compiler
.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(binaryOp
)))
113 Note: I tried writing this plugin without getParentStmt, but in order to make that work, I had to
114 hack things like TraverseWhileStmt to call TraverseStmt on the child nodes myself, so I could know whether I was inside the body or the condition.
115 But I could not get that to work, so switched to this approach.
118 // look up past the temporary nodes
119 Stmt
const* child
= binaryOp
;
120 Stmt
const* parent
= getParentStmt(binaryOp
);
123 // This part is not ideal, but getParentStmt() appears to fail us in some cases, notably when the assignment
124 // is inside a decl like:
128 if (!(isa
<MaterializeTemporaryExpr
>(parent
) || isa
<CXXBindTemporaryExpr
>(parent
)
129 || isa
<ImplicitCastExpr
>(parent
) || isa
<CXXConstructExpr
>(parent
)
130 || isa
<ParenExpr
>(parent
) || isa
<ExprWithCleanups
>(parent
)))
133 parent
= getParentStmt(parent
);
136 if (isa
<CompoundStmt
>(parent
))
138 // ignore chained assignment like "a = b = 1;"
139 if (auto cxxOper
= dyn_cast
<CXXOperatorCallExpr
>(parent
))
141 if (cxxOper
->getOperator() == OO_Equal
)
144 // ignore chained assignment like "a = b = 1;"
145 if (auto parentBinOp
= dyn_cast
<BinaryOperator
>(parent
))
147 if (parentBinOp
->getOpcode() == BO_Assign
)
150 // ignore chained assignment like "int a = b = 1;"
151 if (isa
<DeclStmt
>(parent
))
154 if (isa
<CaseStmt
>(parent
) || isa
<DefaultStmt
>(parent
) || isa
<LabelStmt
>(parent
)
155 || isa
<ForStmt
>(parent
) || isa
<CXXForRangeStmt
>(parent
) || isa
<IfStmt
>(parent
)
156 || isa
<DoStmt
>(parent
) || isa
<WhileStmt
>(parent
) || isa
<ReturnStmt
>(parent
))
159 // now check for the statements where we don't care at all if we see a buried assignment
162 if (isa
<CompoundStmt
>(parent
))
164 if (isa
<CaseStmt
>(parent
) || isa
<DefaultStmt
>(parent
) || isa
<LabelStmt
>(parent
))
166 // Ignore assign in these statements, just seems to be part of the "natural" idiom of C/C++
167 // TODO: perhaps include ReturnStmt?
168 if (auto forStmt
= dyn_cast
<ForStmt
>(parent
))
170 if (child
== forStmt
->getBody())
174 if (auto forRangeStmt
= dyn_cast
<CXXForRangeStmt
>(parent
))
176 if (child
== forRangeStmt
->getBody())
180 if (auto ifStmt
= dyn_cast
<IfStmt
>(parent
))
182 if (child
== ifStmt
->getCond())
185 if (auto doStmt
= dyn_cast
<DoStmt
>(parent
))
187 if (child
== doStmt
->getCond())
190 if (auto whileStmt
= dyn_cast
<WhileStmt
>(parent
))
192 if (child
== whileStmt
->getBody())
196 if (isa
<ReturnStmt
>(parent
))
198 // This appears to be a valid way of making it obvious that we need to call acquire when assigning such ref-counted
200 // rtl_uString_acquire( a = b );
201 if (auto callExpr
= dyn_cast
<CallExpr
>(parent
))
203 if (callExpr
->getDirectCallee() && callExpr
->getDirectCallee()->getIdentifier())
205 auto name
= callExpr
->getDirectCallee()->getName();
206 if (name
== "rtl_uString_acquire" || name
== "_acquire"
207 || name
== "typelib_typedescriptionreference_acquire")
212 parent
= getParentStmt(parent
);
217 report(DiagnosticsEngine::Warning
, "buried assignment, very hard to read",
218 compat::getBeginLoc(binaryOp
))
219 << binaryOp
->getSourceRange();
222 // off by default because it uses getParentStmt so it's more expensive to run
223 loplugin::Plugin::Registration
<BuriedAssign
> X("buriedassign", false);
226 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */