[en_US] Added 13 autocorrect words
[LibreOffice.git] / compilerplugins / clang / buriedassign.cxx
blob8d0e6cc79e59132e90cf4690c730862ce0c0bd47
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 <cassert>
11 #include <string>
12 #include <iostream>
13 #include <unordered_map>
14 #include <unordered_set>
16 #include "plugin.hxx"
17 #include "check.hxx"
18 #include "clang/AST/CXXInheritance.h"
19 #include "clang/AST/StmtVisitor.h"
21 /**
22 TODO deal with C++ operator overload assign
25 namespace
27 //static bool startswith(const std::string& rStr, const char* pSubStr) {
28 // return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
29 //}
30 class BuriedAssign : public loplugin::FilteringPlugin<BuriedAssign>
32 public:
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")
45 return;
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")
49 return;
50 // false+, not sure why
51 if (fn == SRCDIR "/vcl/source/window/menu.cxx")
52 return;
53 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
56 bool VisitBinaryOperator(BinaryOperator const*);
57 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr const*);
59 private:
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
75 // doesn't have yet.
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))
86 return true;
88 if (!isAssignmentOp(binaryOp->getOpcode()))
89 return true;
91 checkExpr(binaryOp);
92 return true;
95 bool BuriedAssign::VisitCXXOperatorCallExpr(CXXOperatorCallExpr const* cxxOper)
97 if (ignoreLocation(cxxOper))
98 return true;
99 if (!isAssignmentOp(cxxOper->getOperator()))
100 return true;
101 checkExpr(cxxOper);
102 return true;
105 void BuriedAssign::checkExpr(Expr const* binaryOp)
107 if (compiler.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(binaryOp)))
108 return;
109 if (compiler.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(binaryOp)))
110 return;
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);
121 while (true)
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:
125 // int x = a = 1;
126 if (!parent)
127 return;
128 if (!(isa<MaterializeTemporaryExpr>(parent) || isa<CXXBindTemporaryExpr>(parent)
129 || isa<ImplicitCastExpr>(parent) || isa<CXXConstructExpr>(parent)
130 || isa<ParenExpr>(parent) || isa<ExprWithCleanups>(parent)))
131 break;
132 child = parent;
133 parent = getParentStmt(parent);
136 if (isa<CompoundStmt>(parent))
137 return;
138 // ignore chained assignment like "a = b = 1;"
139 if (auto cxxOper = dyn_cast<CXXOperatorCallExpr>(parent))
141 if (cxxOper->getOperator() == OO_Equal)
142 return;
144 // ignore chained assignment like "a = b = 1;"
145 if (auto parentBinOp = dyn_cast<BinaryOperator>(parent))
147 if (parentBinOp->getOpcode() == BO_Assign)
148 return;
150 // ignore chained assignment like "int a = b = 1;"
151 if (isa<DeclStmt>(parent))
152 return;
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))
157 return;
159 // now check for the statements where we don't care at all if we see a buried assignment
160 while (true)
162 if (isa<CompoundStmt>(parent))
163 break;
164 if (isa<CaseStmt>(parent) || isa<DefaultStmt>(parent) || isa<LabelStmt>(parent))
165 return;
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())
171 break;
172 return;
174 if (auto forRangeStmt = dyn_cast<CXXForRangeStmt>(parent))
176 if (child == forRangeStmt->getBody())
177 break;
178 return;
180 if (auto ifStmt = dyn_cast<IfStmt>(parent))
182 if (child == ifStmt->getCond())
183 return;
185 if (auto doStmt = dyn_cast<DoStmt>(parent))
187 if (child == doStmt->getCond())
188 return;
190 if (auto whileStmt = dyn_cast<WhileStmt>(parent))
192 if (child == whileStmt->getBody())
193 break;
194 return;
196 if (isa<ReturnStmt>(parent))
197 return;
198 // This appears to be a valid way of making it obvious that we need to call acquire when assigning such ref-counted
199 // stuff e.g.
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")
208 return;
211 child = parent;
212 parent = getParentStmt(parent);
213 if (!parent)
214 break;
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: */