1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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  */
9 
10 #ifndef LO_CLANG_SHARED_PLUGINS
11 
12 #include <cassert>
13 #include <string>
14 #include <iostream>
15 #include <fstream>
16 #include <set>
17 #include <unordered_set>
18 
19 #include <clang/AST/CXXInheritance.h>
20 
21 #include "config_clang.h"
22 
23 #include "compat.hxx"
24 #include "plugin.hxx"
25 
26 /**
27 look for unnecessary parentheses
28 */
29 
30 namespace {
31 
32 // Like clang::Stmt::IgnoreImplicit (lib/AST/Stmt.cpp), but also ignoring CXXConstructExpr and
33 // looking through implicit UserDefinedConversion's member function call:
ignoreAllImplicit(Expr const * expr)34 Expr const * ignoreAllImplicit(Expr const * expr) {
35     while (true)
36     {
37         auto oldExpr = expr;
38         if (auto const e = dyn_cast<ExprWithCleanups>(expr)) {
39             expr = e->getSubExpr();
40         }
41         else if (auto const e = dyn_cast<CXXConstructExpr>(expr)) {
42             if (e->getNumArgs() == 1) {
43                 expr = e->getArg(0);
44             }
45         }
46         else if (auto const e = dyn_cast<MaterializeTemporaryExpr>(expr)) {
47             expr = compat::getSubExpr(e);
48         }
49         else if (auto const e = dyn_cast<CXXBindTemporaryExpr>(expr)) {
50             expr = e->getSubExpr();
51         }
52         else if (auto const e = dyn_cast<ImplicitCastExpr>(expr)) {
53             expr = e->getSubExpr();
54             if (e->getCastKind() == CK_UserDefinedConversion) {
55                 auto const ce = cast<CXXMemberCallExpr>(expr);
56                 assert(ce->getNumArgs() == 0);
57                 expr = ce->getImplicitObjectArgument();
58             }
59         }
60 #if CLANG_VERSION >= 80000
61         else if (auto const e = dyn_cast<ConstantExpr>(expr)) {
62             expr = e->getSubExpr();
63         }
64 #endif
65         if (expr == oldExpr)
66             return expr;
67     }
68     return expr;
69 }
70 
isParenWorthyOpcode(BinaryOperatorKind op)71 bool isParenWorthyOpcode(BinaryOperatorKind op) {
72     return !(BinaryOperator::isMultiplicativeOp(op) || BinaryOperator::isAdditiveOp(op)
73              || compat::isPtrMemOp(op));
74 }
75 
76 class UnnecessaryParen:
77     public loplugin::FilteringRewritePlugin<UnnecessaryParen>
78 {
79 public:
UnnecessaryParen(loplugin::InstantiationData const & data)80     explicit UnnecessaryParen(loplugin::InstantiationData const & data):
81         FilteringRewritePlugin(data) {}
82 
preRun()83     virtual bool preRun() override
84     {
85         StringRef fn(handler.getMainFileName());
86         // fixing this, makes the source in the .y files look horrible
87         if (loplugin::isSamePathname(fn, WORKDIR "/YaccTarget/unoidl/source/sourceprovider-parser.cxx"))
88             return false;
89         if (loplugin::isSamePathname(fn, WORKDIR "/YaccTarget/idlc/source/parser.cxx"))
90             return false;
91         return true;
92     }
run()93     virtual void run() override
94     {
95         if( preRun())
96             TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
97     }
98 
99     bool VisitParenExpr(const ParenExpr *);
100     bool VisitIfStmt(const IfStmt *);
101     bool VisitDoStmt(const DoStmt *);
102     bool VisitWhileStmt(const WhileStmt *);
103     bool VisitForStmt(ForStmt const * stmt);
104     bool VisitSwitchStmt(const SwitchStmt *);
105     bool VisitCaseStmt(const CaseStmt *);
106     bool VisitReturnStmt(const ReturnStmt* );
107     bool VisitCallExpr(const CallExpr *);
108     bool VisitVarDecl(const VarDecl *);
109     bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *);
110     bool VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr const *);
111     bool VisitConditionalOperator(ConditionalOperator const * expr);
112     bool VisitBinaryConditionalOperator(BinaryConditionalOperator const * expr);
113     bool VisitMemberExpr(const MemberExpr *f);
114     bool VisitCXXDeleteExpr(const CXXDeleteExpr *);
115 
VisitImplicitCastExpr(ImplicitCastExpr const * expr)116     bool VisitImplicitCastExpr(ImplicitCastExpr const * expr) {
117         if (ignoreLocation(expr)) {
118             return true;
119         }
120         if (expr->getCastKind() != CK_UserDefinedConversion) {
121             return true;
122         }
123         // Filter out a MemberExpr (resp. a ParenExpr sub-expr, if any, as would be found by
124         // VisitMemberExpr) that is part of a CXXMemberCallExpr which in turn is part of an
125         // ImplicitCastExpr, so that VisitMemberExpr doesn't erroneously pick it up (and note that
126         // CXXMemberCallExpr's getImplicitObjectArgument() skips past the underlying MemberExpr):
127         if (auto const e1 = dyn_cast<CXXMemberCallExpr>(expr->getSubExpr())) {
128             if (auto const e2 = dyn_cast<ParenExpr>(
129                     e1->getImplicitObjectArgument()->IgnoreImpCasts()))
130             {
131                 handled_.insert(e2);
132             }
133         }
134         return true;
135     }
136 
137 private:
138     void VisitSomeStmt(Stmt const * stmt, const Expr* cond, StringRef stmtName);
139 
140     void handleUnreachableCodeConditionParens(Expr const * expr);
141 
142     // Hack for libxml2's BAD_CAST object-like macro (expanding to "(xmlChar *)"), which is
143     // typically used as if it were a function-like macro, e.g., as "BAD_CAST(pName)" in
144     // SwNode::dumpAsXml (sw/source/core/docnode/node.cxx):
145     bool isPrecededBy_BAD_CAST(Expr const * expr);
146 
147     bool badCombination(SourceLocation loc, int prevOffset, int nextOffset);
148 
149     bool removeParens(ParenExpr const * expr);
150 
151     std::unordered_set<ParenExpr const *> handled_;
152 };
153 
VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr const * expr)154 bool UnnecessaryParen::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr const * expr)
155 {
156     if (expr->getKind() == UETT_SizeOf && !expr->isArgumentType()) {
157         if (auto const e = dyn_cast<ParenExpr>(ignoreAllImplicit(expr->getArgumentExpr()))) {
158             handled_.insert(e);
159         }
160     }
161     return true;
162 }
163 
VisitConditionalOperator(ConditionalOperator const * expr)164 bool UnnecessaryParen::VisitConditionalOperator(ConditionalOperator const * expr) {
165     handleUnreachableCodeConditionParens(expr->getCond());
166     return true;
167 }
168 
VisitBinaryConditionalOperator(BinaryConditionalOperator const * expr)169 bool UnnecessaryParen::VisitBinaryConditionalOperator(BinaryConditionalOperator const * expr) {
170     handleUnreachableCodeConditionParens(expr->getCond());
171     return true;
172 }
173 
VisitParenExpr(const ParenExpr * parenExpr)174 bool UnnecessaryParen::VisitParenExpr(const ParenExpr* parenExpr)
175 {
176     if (ignoreLocation(parenExpr))
177         return true;
178     if (compat::getBeginLoc(parenExpr).isMacroID())
179         return true;
180     if (handled_.find(parenExpr) != handled_.end())
181         return true;
182 
183     auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
184 
185     if (auto subParenExpr = dyn_cast<ParenExpr>(subExpr))
186     {
187         if (compat::getBeginLoc(subParenExpr).isMacroID())
188             return true;
189         report(
190             DiagnosticsEngine::Warning, "parentheses around parentheses",
191             compat::getBeginLoc(parenExpr))
192             << parenExpr->getSourceRange();
193         handled_.insert(subParenExpr);
194     }
195 
196     // Somewhat redundantly add parenExpr to handled_, so that issues within InitListExpr don't get
197     // reported twice (without having to change TraverseInitListExpr to only either traverse the
198     // syntactic or semantic form, as other plugins do):
199 
200     if (isa<DeclRefExpr>(subExpr)) {
201         if (!isPrecededBy_BAD_CAST(parenExpr)) {
202             report(
203                 DiagnosticsEngine::Warning, "unnecessary parentheses around identifier",
204                 compat::getBeginLoc(parenExpr))
205                 << parenExpr->getSourceRange();
206             handled_.insert(parenExpr);
207         }
208     } else if (isa<IntegerLiteral>(subExpr) || isa<CharacterLiteral>(subExpr)
209                || isa<FloatingLiteral>(subExpr) || isa<ImaginaryLiteral>(subExpr)
210                || isa<CXXBoolLiteralExpr>(subExpr) || isa<CXXNullPtrLiteralExpr>(subExpr)
211                || isa<ObjCBoolLiteralExpr>(subExpr))
212     {
213         auto const loc = compat::getBeginLoc(subExpr);
214         if (loc.isMacroID() && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(loc))
215         {
216             // just in case the macro could also expand to something that /would/ require
217             // parentheses here
218             return true;
219         }
220         report(
221             DiagnosticsEngine::Warning, "unnecessary parentheses around literal",
222             compat::getBeginLoc(parenExpr))
223             << parenExpr->getSourceRange();
224         handled_.insert(parenExpr);
225     } else if (auto const e = dyn_cast<clang::StringLiteral>(subExpr)) {
226         if (e->getNumConcatenated() == 1 && !isPrecededBy_BAD_CAST(parenExpr)) {
227             report(
228                 DiagnosticsEngine::Warning,
229                 "unnecessary parentheses around single-token string literal",
230                 compat::getBeginLoc(parenExpr))
231                 << parenExpr->getSourceRange();
232             handled_.insert(parenExpr);
233         }
234     } else if (auto const e = dyn_cast<UnaryOperator>(subExpr)) {
235         auto const op = e->getOpcode();
236         if (op == UO_Plus || op == UO_Minus) {
237             auto const e2 = e->getSubExpr();
238             if (isa<IntegerLiteral>(e2) || isa<FloatingLiteral>(e2) || isa<ImaginaryLiteral>(e2)) {
239                 report(
240                     DiagnosticsEngine::Warning,
241                     "unnecessary parentheses around signed numeric literal",
242                     compat::getBeginLoc(parenExpr))
243                     << parenExpr->getSourceRange();
244                 handled_.insert(parenExpr);
245             }
246         }
247     } else if (isa<CXXNamedCastExpr>(subExpr)) {
248         if (!removeParens(parenExpr)) {
249             report(
250                 DiagnosticsEngine::Warning, "unnecessary parentheses around cast",
251                 compat::getBeginLoc(parenExpr))
252                 << parenExpr->getSourceRange();
253         }
254         handled_.insert(parenExpr);
255     } else if (auto memberExpr = dyn_cast<MemberExpr>(subExpr)) {
256         if (isa<CXXThisExpr>(ignoreAllImplicit(memberExpr->getBase()))) {
257             report(
258                 DiagnosticsEngine::Warning, "unnecessary parentheses around member expr",
259                 compat::getBeginLoc(parenExpr))
260                 << parenExpr->getSourceRange();
261             handled_.insert(parenExpr);
262         }
263     }
264 
265     return true;
266 }
267 
VisitIfStmt(const IfStmt * ifStmt)268 bool UnnecessaryParen::VisitIfStmt(const IfStmt* ifStmt)
269 {
270     handleUnreachableCodeConditionParens(ifStmt->getCond());
271     VisitSomeStmt(ifStmt, ifStmt->getCond(), "if");
272     return true;
273 }
274 
VisitDoStmt(const DoStmt * doStmt)275 bool UnnecessaryParen::VisitDoStmt(const DoStmt* doStmt)
276 {
277     VisitSomeStmt(doStmt, doStmt->getCond(), "do");
278     return true;
279 }
280 
VisitWhileStmt(const WhileStmt * whileStmt)281 bool UnnecessaryParen::VisitWhileStmt(const WhileStmt* whileStmt)
282 {
283     handleUnreachableCodeConditionParens(whileStmt->getCond());
284     VisitSomeStmt(whileStmt, whileStmt->getCond(), "while");
285     return true;
286 }
287 
VisitForStmt(ForStmt const * stmt)288 bool UnnecessaryParen::VisitForStmt(ForStmt const * stmt) {
289     if (auto const cond = stmt->getCond()) {
290         handleUnreachableCodeConditionParens(cond);
291     }
292     return true;
293 }
294 
VisitSwitchStmt(const SwitchStmt * switchStmt)295 bool UnnecessaryParen::VisitSwitchStmt(const SwitchStmt* switchStmt)
296 {
297     VisitSomeStmt(switchStmt, switchStmt->getCond(), "switch");
298     return true;
299 }
300 
VisitCaseStmt(const CaseStmt * caseStmt)301 bool UnnecessaryParen::VisitCaseStmt(const CaseStmt* caseStmt)
302 {
303     VisitSomeStmt(caseStmt, caseStmt->getLHS(), "case");
304     return true;
305 }
306 
VisitReturnStmt(const ReturnStmt * returnStmt)307 bool UnnecessaryParen::VisitReturnStmt(const ReturnStmt* returnStmt)
308 {
309     if (ignoreLocation(returnStmt))
310         return true;
311 
312     if (!returnStmt->getRetValue())
313         return true;
314     auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(returnStmt->getRetValue()));
315     if (!parenExpr)
316         return true;
317     if (compat::getBeginLoc(parenExpr).isMacroID())
318         return true;
319     // assignments need extra parentheses or they generate a compiler warning
320     auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
321     if (binaryOp && binaryOp->getOpcode() == BO_Assign)
322         return true;
323 
324     // only non-operator-calls for now
325     auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
326     if (isa<CallExpr>(subExpr) && !isa<CXXOperatorCallExpr>(subExpr))
327     {
328         report(
329             DiagnosticsEngine::Warning, "parentheses immediately inside return statement",
330             compat::getBeginLoc(parenExpr))
331             << parenExpr->getSourceRange();
332         handled_.insert(parenExpr);
333     }
334     return true;
335 }
336 
VisitSomeStmt(const Stmt * stmt,const Expr * cond,StringRef stmtName)337 void UnnecessaryParen::VisitSomeStmt(const Stmt * stmt, const Expr* cond, StringRef stmtName)
338 {
339     if (ignoreLocation(stmt))
340         return;
341 
342     auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(cond));
343     if (parenExpr) {
344         if (handled_.find(parenExpr) != handled_.end()) {
345             return;
346         }
347         if (compat::getBeginLoc(parenExpr).isMacroID())
348             return;
349         // assignments need extra parentheses or they generate a compiler warning
350         auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
351         if (binaryOp && binaryOp->getOpcode() == BO_Assign)
352             return;
353         if (auto const opCall = dyn_cast<CXXOperatorCallExpr>(parenExpr->getSubExpr())) {
354             if (opCall->getOperator() == OO_Equal) {
355                 return;
356             }
357         }
358         report(
359             DiagnosticsEngine::Warning, "parentheses immediately inside %0 statement",
360             compat::getBeginLoc(parenExpr))
361             << stmtName
362             << parenExpr->getSourceRange();
363         handled_.insert(parenExpr);
364     }
365 }
366 
VisitCallExpr(const CallExpr * callExpr)367 bool UnnecessaryParen::VisitCallExpr(const CallExpr* callExpr)
368 {
369     if (ignoreLocation(callExpr))
370         return true;
371     if (callExpr->getNumArgs() == 0 || isa<CXXOperatorCallExpr>(callExpr))
372         return true;
373 
374     // if we are calling a >1 arg method, are we using the defaults?
375     if (callExpr->getNumArgs() > 1)
376     {
377         if (!isa<CXXDefaultArgExpr>(callExpr->getArg(1)))
378             return true;
379     }
380 
381     auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(callExpr->getArg(0)));
382     if (!parenExpr)
383         return true;
384     if (compat::getBeginLoc(parenExpr).isMacroID())
385         return true;
386     // assignments need extra parentheses or they generate a compiler warning
387     auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
388     if (binaryOp && binaryOp->getOpcode() == BO_Assign)
389         return true;
390     report(
391         DiagnosticsEngine::Warning, "parentheses immediately inside single-arg call",
392         compat::getBeginLoc(parenExpr))
393         << parenExpr->getSourceRange();
394     handled_.insert(parenExpr);
395     return true;
396 }
397 
VisitCXXDeleteExpr(const CXXDeleteExpr * deleteExpr)398 bool UnnecessaryParen::VisitCXXDeleteExpr(const CXXDeleteExpr* deleteExpr)
399 {
400     if (ignoreLocation(deleteExpr))
401         return true;
402 
403     auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(deleteExpr->getArgument()));
404     if (!parenExpr)
405         return true;
406     if (compat::getBeginLoc(parenExpr).isMacroID())
407         return true;
408     // assignments need extra parentheses or they generate a compiler warning
409     auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
410     if (binaryOp && binaryOp->getOpcode() == BO_Assign)
411         return true;
412     report(
413         DiagnosticsEngine::Warning, "parentheses immediately inside delete expr",
414         compat::getBeginLoc(parenExpr))
415         << parenExpr->getSourceRange();
416     handled_.insert(parenExpr);
417     return true;
418 }
419 
VisitCXXOperatorCallExpr(const CXXOperatorCallExpr * callExpr)420 bool UnnecessaryParen::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr* callExpr)
421 {
422     if (ignoreLocation(callExpr))
423         return true;
424     if (callExpr->getNumArgs() != 2)
425         return true;
426 
427     // Same logic as CXXOperatorCallExpr::isAssignmentOp(), which our supported clang
428     // doesn't have yet.
429     auto Opc = callExpr->getOperator();
430     if (Opc != OO_Equal && Opc != OO_StarEqual &&
431         Opc != OO_SlashEqual && Opc != OO_PercentEqual &&
432         Opc != OO_PlusEqual && Opc != OO_MinusEqual &&
433         Opc != OO_LessLessEqual && Opc != OO_GreaterGreaterEqual &&
434         Opc != OO_AmpEqual && Opc != OO_CaretEqual &&
435         Opc != OO_PipeEqual)
436         return true;
437     auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(callExpr->getArg(1)));
438     if (!parenExpr)
439         return true;
440     if (compat::getBeginLoc(parenExpr).isMacroID())
441         return true;
442     // Sometimes parentheses make the RHS of an assignment easier to read by
443     // visually disambiguating the = from a call to ==
444     auto sub = parenExpr->getSubExpr();
445 #if CLANG_VERSION >= 100000
446     if (auto const e = dyn_cast<CXXRewrittenBinaryOperator>(sub)) {
447         if (isParenWorthyOpcode(e->getDecomposedForm().Opcode)) {
448             return true;
449         }
450     }
451 #endif
452     if (auto subBinOp = dyn_cast<BinaryOperator>(sub))
453     {
454         if (isParenWorthyOpcode(subBinOp->getOpcode()))
455             return true;
456     }
457     if (auto subOperatorCall = dyn_cast<CXXOperatorCallExpr>(sub))
458     {
459         auto op = subOperatorCall->getOperator();
460         if (!((op >= OO_Plus && op <= OO_Exclaim) || (op >= OO_ArrowStar && op <= OO_Subscript)))
461             return true;
462     }
463     if (isa<ConditionalOperator>(sub))
464         return true;
465 
466     report(
467         DiagnosticsEngine::Warning, "parentheses immediately inside assignment",
468         compat::getBeginLoc(parenExpr))
469         << parenExpr->getSourceRange();
470     handled_.insert(parenExpr);
471     return true;
472 }
473 
VisitVarDecl(const VarDecl * varDecl)474 bool UnnecessaryParen::VisitVarDecl(const VarDecl* varDecl)
475 {
476     if (ignoreLocation(varDecl))
477         return true;
478     if (!varDecl->getInit())
479         return true;
480 
481     auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(varDecl->getInit()));
482     if (!parenExpr)
483         return true;
484     if (compat::getBeginLoc(parenExpr).isMacroID())
485         return true;
486 
487     // Sometimes parentheses make the RHS of an assignment easier to read by
488     // visually disambiguating the = from a call to ==
489     auto sub = parenExpr->getSubExpr();
490 #if CLANG_VERSION >= 100000
491     if (auto const e = dyn_cast<CXXRewrittenBinaryOperator>(sub)) {
492         sub = e->getDecomposedForm().InnerBinOp;
493     }
494 #endif
495     if (auto subBinOp = dyn_cast<BinaryOperator>(sub))
496     {
497         if (!(subBinOp->isMultiplicativeOp() || subBinOp->isAdditiveOp() || subBinOp->isPtrMemOp()))
498             return true;
499     }
500     if (auto subOperatorCall = dyn_cast<CXXOperatorCallExpr>(sub))
501     {
502         auto op = subOperatorCall->getOperator();
503         if (!((op >= OO_Plus && op <= OO_Exclaim) || (op >= OO_ArrowStar && op <= OO_Subscript)))
504             return true;
505     }
506     if (isa<ConditionalOperator>(sub))
507         return true;
508 
509     // these two are for "parentheses were disambiguated as a function declaration [-Werror,-Wvexing-parse]"
510     auto const sub2 = sub->IgnoreImplicit();
511     if (isa<CXXTemporaryObjectExpr>(sub2)
512         || isa<CXXFunctionalCastExpr>(sub2))
513         return true;
514 
515     report(
516         DiagnosticsEngine::Warning, "parentheses immediately inside vardecl statement",
517         compat::getBeginLoc(parenExpr))
518         << parenExpr->getSourceRange();
519     handled_.insert(parenExpr);
520     return true;
521 }
522 
VisitMemberExpr(const MemberExpr * memberExpr)523 bool UnnecessaryParen::VisitMemberExpr(const MemberExpr* memberExpr)
524 {
525     if (ignoreLocation(memberExpr))
526         return true;
527 
528     auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(memberExpr->getBase()));
529     if (!parenExpr)
530         return true;
531     if (handled_.find(parenExpr) != handled_.end())
532         return true;
533     if (compat::getBeginLoc(parenExpr).isMacroID())
534         return true;
535 
536     auto sub = parenExpr->getSubExpr();
537     if (isa<CallExpr>(sub)) {
538         if (isa<CXXOperatorCallExpr>(sub))
539            return true;
540     } else if (isa<CXXConstructExpr>(sub)) {
541         // warn
542     } else if (isa<MemberExpr>(sub)) {
543         // warn
544     } else if (isa<DeclRefExpr>(sub)) {
545         // warn
546     } else
547         return true;
548 
549     report(
550         DiagnosticsEngine::Warning, "unnecessary parentheses around member expr",
551         compat::getBeginLoc(parenExpr))
552         << parenExpr->getSourceRange();
553     handled_.insert(parenExpr);
554     return true;
555 }
556 
557 // Conservatively assume any parenthesised integer or Boolean (incl. Objective-C ones) literal in
558 // certain condition expressions (i.e., those for which handleUnreachableCodeConditionParens is
559 // called) to be parenthesised to silence Clang -Wunreachable-code, if that is either the whole
560 // condition expression or appears as a certain sub-expression (looking at what isConfigurationValue
561 // in Clang's lib/Analysis/ReachableCode.cpp looks for, descending into certain unary and binary
562 // operators):
handleUnreachableCodeConditionParens(Expr const * expr)563 void UnnecessaryParen::handleUnreachableCodeConditionParens(Expr const * expr) {
564     // Cf. :
565     auto const e = ignoreAllImplicit(expr);
566     if (auto const e1 = dyn_cast<ParenExpr>(e)) {
567         auto const sub = e1->getSubExpr();
568         if (isa<IntegerLiteral>(sub) || isa<CXXBoolLiteralExpr>(sub)
569             || isa<ObjCBoolLiteralExpr>(sub))
570         {
571             handled_.insert(e1);
572         }
573     } else if (auto const e1 = dyn_cast<UnaryOperator>(e)) {
574         if (e1->getOpcode() == UO_LNot) {
575             handleUnreachableCodeConditionParens(e1->getSubExpr());
576         }
577     } else if (auto const e1 = dyn_cast<BinaryOperator>(e)) {
578         if (e1->isLogicalOp() || e1->isComparisonOp()) {
579             handleUnreachableCodeConditionParens(e1->getLHS());
580             handleUnreachableCodeConditionParens(e1->getRHS());
581         }
582     }
583 }
584 
isPrecededBy_BAD_CAST(Expr const * expr)585 bool UnnecessaryParen::isPrecededBy_BAD_CAST(Expr const * expr) {
586     if (compat::getBeginLoc(expr).isMacroID()) {
587         return false;
588     }
589     SourceManager& SM = compiler.getSourceManager();
590     const char *p1 = SM.getCharacterData( compat::getBeginLoc(expr).getLocWithOffset(-10) );
591     const char *p2 = SM.getCharacterData( compat::getBeginLoc(expr) );
592     return std::string(p1, p2 - p1).find("BAD_CAST") != std::string::npos;
593 }
594 
595 namespace {
596 
badCombinationChar(char c)597 bool badCombinationChar(char c) {
598     return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'
599         || c == '+' || c == '-' || c == '\'' || c == '"';
600 }
601 
602 }
603 
badCombination(SourceLocation loc,int prevOffset,int nextOffset)604 bool UnnecessaryParen::badCombination(SourceLocation loc, int prevOffset, int nextOffset) {
605     //TODO: check for start/end of file; take backslash-newline line concatenation into account
606     auto const c1
607         = compiler.getSourceManager().getCharacterData(loc.getLocWithOffset(prevOffset))[0];
608     auto const c2
609         = compiler.getSourceManager().getCharacterData(loc.getLocWithOffset(nextOffset))[0];
610     // An approximation of avoiding whatever combinations that would cause two adjacent tokens to be
611     // lexed differently, using, for now, letters (TODO: non-ASCII ones) and digits and '_'; '+' and
612     // '-' (to avoid ++, etc.); '\'' and '"' (to avoid u'x' or "foo"bar, etc.):
613     return badCombinationChar(c1) && badCombinationChar(c2);
614 }
615 
removeParens(ParenExpr const * expr)616 bool UnnecessaryParen::removeParens(ParenExpr const * expr) {
617     if (rewriter == nullptr) {
618         return false;
619     }
620     auto const firstBegin = compat::getBeginLoc(expr);
621     auto secondBegin = compat::getEndLoc(expr);
622     if (firstBegin.isMacroID() || secondBegin.isMacroID()) {
623         return false;
624     }
625     unsigned firstLen = Lexer::MeasureTokenLength(
626         firstBegin, compiler.getSourceManager(), compiler.getLangOpts());
627     for (auto l = firstBegin.getLocWithOffset(std::max<unsigned>(firstLen, 1));;
628          l = l.getLocWithOffset(1))
629     {
630         unsigned n = Lexer::MeasureTokenLength(
631             l, compiler.getSourceManager(), compiler.getLangOpts());
632         if (n != 0) {
633             break;
634         }
635         ++firstLen;
636     }
637     unsigned secondLen = Lexer::MeasureTokenLength(
638         secondBegin, compiler.getSourceManager(), compiler.getLangOpts());
639     for (;;) {
640         auto l = secondBegin.getLocWithOffset(-1);
641         auto const c = compiler.getSourceManager().getCharacterData(l)[0];
642         if (c == '\n') {
643             if (compiler.getSourceManager().getCharacterData(l.getLocWithOffset(-1))[0] == '\\') {
644                 break;
645             }
646         } else if (!(c == ' ' || c == '\t' || c == '\v' || c == '\f')) {
647             break;
648         }
649         secondBegin = l;
650         ++secondLen;
651     }
652     if (!replaceText(firstBegin, firstLen, badCombination(firstBegin, -1, firstLen) ? " " : "")) {
653         if (isDebugMode()) {
654             report(
655                 DiagnosticsEngine::Fatal,
656                 "TODO: cannot rewrite opening parenthesis, needs investigation",
657                 firstBegin);
658             report(
659                 DiagnosticsEngine::Note, "when removing these parentheses", expr->getExprLoc())
660                 << expr->getSourceRange();
661         }
662         return false;
663     }
664     if (!replaceText(secondBegin, secondLen, badCombination(secondBegin, -1, secondLen) ? " " : ""))
665     {
666         //TODO: roll back first change
667         if (isDebugMode()) {
668             report(
669                 DiagnosticsEngine::Fatal,
670                 "TODO: cannot rewrite closing parenthesis, needs investigation",
671                 secondBegin);
672             report(
673                 DiagnosticsEngine::Note, "when removing these parentheses", expr->getExprLoc())
674                 << expr->getSourceRange();
675         }
676         return false;
677     }
678     return true;
679 }
680 
681 loplugin::Plugin::Registration< UnnecessaryParen > unnecessaryparen("unnecessaryparen", true);
682 
683 }
684 
685 #endif // LO_CLANG_SHARED_PLUGINS
686 
687 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
688