bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / unnecessaryparen.cxx
blob9fb8fb85aa82bb5a5e3a384ea31040fe82f2c500
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 #ifndef LO_CLANG_SHARED_PLUGINS
12 #include <cassert>
13 #include <string>
14 #include <iostream>
15 #include <fstream>
16 #include <set>
17 #include <unordered_set>
19 #include <clang/AST/CXXInheritance.h>
21 #include "config_clang.h"
23 #include "compat.hxx"
24 #include "plugin.hxx"
26 /**
27 look for unnecessary parentheses
30 namespace {
32 // Like clang::Stmt::IgnoreImplicit (lib/AST/Stmt.cpp), but also ignoring CXXConstructExpr and
33 // looking through implicit UserDefinedConversion's member function call:
34 Expr const * ignoreAllImplicit(Expr const * expr) {
35 while (true)
37 auto oldExpr = expr;
38 if (auto const e = dyn_cast<ExprWithCleanups>(expr)) {
39 expr = e->getSubExpr();
41 else if (auto const e = dyn_cast<CXXConstructExpr>(expr)) {
42 if (e->getNumArgs() == 1) {
43 expr = e->getArg(0);
46 else if (auto const e = dyn_cast<MaterializeTemporaryExpr>(expr)) {
47 expr = e->GetTemporaryExpr();
49 else if (auto const e = dyn_cast<CXXBindTemporaryExpr>(expr)) {
50 expr = e->getSubExpr();
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();
60 #if CLANG_VERSION >= 80000
61 else if (auto const e = dyn_cast<ConstantExpr>(expr)) {
62 expr = e->getSubExpr();
64 #endif
65 if (expr == oldExpr)
66 return expr;
68 return expr;
71 class UnnecessaryParen:
72 public loplugin::FilteringRewritePlugin<UnnecessaryParen>
74 public:
75 explicit UnnecessaryParen(loplugin::InstantiationData const & data):
76 FilteringRewritePlugin(data) {}
78 virtual bool preRun() override
80 StringRef fn(handler.getMainFileName());
81 // fixing this, makes the source in the .y files look horrible
82 if (loplugin::isSamePathname(fn, WORKDIR "/YaccTarget/unoidl/source/sourceprovider-parser.cxx"))
83 return false;
84 if (loplugin::isSamePathname(fn, WORKDIR "/YaccTarget/idlc/source/parser.cxx"))
85 return false;
86 return true;
88 virtual void run() override
90 if( preRun())
91 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
94 bool VisitParenExpr(const ParenExpr *);
95 bool VisitIfStmt(const IfStmt *);
96 bool VisitDoStmt(const DoStmt *);
97 bool VisitWhileStmt(const WhileStmt *);
98 bool VisitForStmt(ForStmt const * stmt);
99 bool VisitSwitchStmt(const SwitchStmt *);
100 bool VisitCaseStmt(const CaseStmt *);
101 bool VisitReturnStmt(const ReturnStmt* );
102 bool VisitCallExpr(const CallExpr *);
103 bool VisitVarDecl(const VarDecl *);
104 bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *);
105 bool VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr const *);
106 bool VisitConditionalOperator(ConditionalOperator const * expr);
107 bool VisitBinaryConditionalOperator(BinaryConditionalOperator const * expr);
108 bool VisitMemberExpr(const MemberExpr *f);
109 bool VisitCXXDeleteExpr(const CXXDeleteExpr *);
110 private:
111 void VisitSomeStmt(Stmt const * stmt, const Expr* cond, StringRef stmtName);
113 void handleUnreachableCodeConditionParens(Expr const * expr);
115 // Hack for libxml2's BAD_CAST object-like macro (expanding to "(xmlChar *)"), which is
116 // typically used as if it were a function-like macro, e.g., as "BAD_CAST(pName)" in
117 // SwNode::dumpAsXml (sw/source/core/docnode/node.cxx):
118 bool isPrecededBy_BAD_CAST(Expr const * expr);
120 bool badCombination(SourceLocation loc, int prevOffset, int nextOffset);
122 bool removeParens(ParenExpr const * expr);
124 std::unordered_set<ParenExpr const *> handled_;
127 bool UnnecessaryParen::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr const * expr)
129 if (expr->getKind() == UETT_SizeOf && !expr->isArgumentType()) {
130 if (auto const e = dyn_cast<ParenExpr>(ignoreAllImplicit(expr->getArgumentExpr()))) {
131 handled_.insert(e);
134 return true;
137 bool UnnecessaryParen::VisitConditionalOperator(ConditionalOperator const * expr) {
138 handleUnreachableCodeConditionParens(expr->getCond());
139 return true;
142 bool UnnecessaryParen::VisitBinaryConditionalOperator(BinaryConditionalOperator const * expr) {
143 handleUnreachableCodeConditionParens(expr->getCond());
144 return true;
147 bool UnnecessaryParen::VisitParenExpr(const ParenExpr* parenExpr)
149 if (ignoreLocation(parenExpr))
150 return true;
151 if (compat::getBeginLoc(parenExpr).isMacroID())
152 return true;
153 if (handled_.find(parenExpr) != handled_.end())
154 return true;
156 auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
158 if (auto subParenExpr = dyn_cast<ParenExpr>(subExpr))
160 if (compat::getBeginLoc(subParenExpr).isMacroID())
161 return true;
162 report(
163 DiagnosticsEngine::Warning, "parentheses around parentheses",
164 compat::getBeginLoc(parenExpr))
165 << parenExpr->getSourceRange();
166 handled_.insert(subParenExpr);
169 // Somewhat redundantly add parenExpr to handled_, so that issues within InitListExpr don't get
170 // reported twice (without having to change TraverseInitListExpr to only either traverse the
171 // syntactic or semantic form, as other plugins do):
173 if (isa<DeclRefExpr>(subExpr)) {
174 if (!isPrecededBy_BAD_CAST(parenExpr)) {
175 report(
176 DiagnosticsEngine::Warning, "unnecessary parentheses around identifier",
177 compat::getBeginLoc(parenExpr))
178 << parenExpr->getSourceRange();
179 handled_.insert(parenExpr);
181 } else if (isa<IntegerLiteral>(subExpr) || isa<CharacterLiteral>(subExpr)
182 || isa<FloatingLiteral>(subExpr) || isa<ImaginaryLiteral>(subExpr)
183 || isa<CXXBoolLiteralExpr>(subExpr) || isa<CXXNullPtrLiteralExpr>(subExpr)
184 || isa<ObjCBoolLiteralExpr>(subExpr))
186 auto const loc = compat::getBeginLoc(subExpr);
187 if (loc.isMacroID() && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(loc))
189 // just in case the macro could also expand to something that /would/ require
190 // parentheses here
191 return true;
193 report(
194 DiagnosticsEngine::Warning, "unnecessary parentheses around literal",
195 compat::getBeginLoc(parenExpr))
196 << parenExpr->getSourceRange();
197 handled_.insert(parenExpr);
198 } else if (auto const e = dyn_cast<clang::StringLiteral>(subExpr)) {
199 if (e->getNumConcatenated() == 1 && !isPrecededBy_BAD_CAST(parenExpr)) {
200 report(
201 DiagnosticsEngine::Warning,
202 "unnecessary parentheses around single-token string literal",
203 compat::getBeginLoc(parenExpr))
204 << parenExpr->getSourceRange();
205 handled_.insert(parenExpr);
207 } else if (auto const e = dyn_cast<UnaryOperator>(subExpr)) {
208 auto const op = e->getOpcode();
209 if (op == UO_Plus || op == UO_Minus) {
210 auto const e2 = e->getSubExpr();
211 if (isa<IntegerLiteral>(e2) || isa<FloatingLiteral>(e2) || isa<ImaginaryLiteral>(e2)) {
212 report(
213 DiagnosticsEngine::Warning,
214 "unnecessary parentheses around signed numeric literal",
215 compat::getBeginLoc(parenExpr))
216 << parenExpr->getSourceRange();
217 handled_.insert(parenExpr);
220 } else if (isa<CXXNamedCastExpr>(subExpr)) {
221 if (!removeParens(parenExpr)) {
222 report(
223 DiagnosticsEngine::Warning, "unnecessary parentheses around cast",
224 compat::getBeginLoc(parenExpr))
225 << parenExpr->getSourceRange();
227 handled_.insert(parenExpr);
228 } else if (auto memberExpr = dyn_cast<MemberExpr>(subExpr)) {
229 if (isa<CXXThisExpr>(ignoreAllImplicit(memberExpr->getBase()))) {
230 report(
231 DiagnosticsEngine::Warning, "unnecessary parentheses around member expr",
232 compat::getBeginLoc(parenExpr))
233 << parenExpr->getSourceRange();
234 handled_.insert(parenExpr);
238 return true;
241 bool UnnecessaryParen::VisitIfStmt(const IfStmt* ifStmt)
243 handleUnreachableCodeConditionParens(ifStmt->getCond());
244 VisitSomeStmt(ifStmt, ifStmt->getCond(), "if");
245 return true;
248 bool UnnecessaryParen::VisitDoStmt(const DoStmt* doStmt)
250 VisitSomeStmt(doStmt, doStmt->getCond(), "do");
251 return true;
254 bool UnnecessaryParen::VisitWhileStmt(const WhileStmt* whileStmt)
256 handleUnreachableCodeConditionParens(whileStmt->getCond());
257 VisitSomeStmt(whileStmt, whileStmt->getCond(), "while");
258 return true;
261 bool UnnecessaryParen::VisitForStmt(ForStmt const * stmt) {
262 if (auto const cond = stmt->getCond()) {
263 handleUnreachableCodeConditionParens(cond);
265 return true;
268 bool UnnecessaryParen::VisitSwitchStmt(const SwitchStmt* switchStmt)
270 VisitSomeStmt(switchStmt, switchStmt->getCond(), "switch");
271 return true;
274 bool UnnecessaryParen::VisitCaseStmt(const CaseStmt* caseStmt)
276 VisitSomeStmt(caseStmt, caseStmt->getLHS(), "case");
277 return true;
280 bool UnnecessaryParen::VisitReturnStmt(const ReturnStmt* returnStmt)
282 if (ignoreLocation(returnStmt))
283 return true;
285 if (!returnStmt->getRetValue())
286 return true;
287 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(returnStmt->getRetValue()));
288 if (!parenExpr)
289 return true;
290 if (compat::getBeginLoc(parenExpr).isMacroID())
291 return true;
292 // assignments need extra parentheses or they generate a compiler warning
293 auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
294 if (binaryOp && binaryOp->getOpcode() == BO_Assign)
295 return true;
297 // only non-operator-calls for now
298 auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
299 if (isa<CallExpr>(subExpr) && !isa<CXXOperatorCallExpr>(subExpr))
301 report(
302 DiagnosticsEngine::Warning, "parentheses immediately inside return statement",
303 compat::getBeginLoc(parenExpr))
304 << parenExpr->getSourceRange();
305 handled_.insert(parenExpr);
307 return true;
310 void UnnecessaryParen::VisitSomeStmt(const Stmt * stmt, const Expr* cond, StringRef stmtName)
312 if (ignoreLocation(stmt))
313 return;
315 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(cond));
316 if (parenExpr) {
317 if (handled_.find(parenExpr) != handled_.end()) {
318 return;
320 if (compat::getBeginLoc(parenExpr).isMacroID())
321 return;
322 // assignments need extra parentheses or they generate a compiler warning
323 auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
324 if (binaryOp && binaryOp->getOpcode() == BO_Assign)
325 return;
326 if (auto const opCall = dyn_cast<CXXOperatorCallExpr>(parenExpr->getSubExpr())) {
327 if (opCall->getOperator() == OO_Equal) {
328 return;
331 report(
332 DiagnosticsEngine::Warning, "parentheses immediately inside %0 statement",
333 compat::getBeginLoc(parenExpr))
334 << stmtName
335 << parenExpr->getSourceRange();
336 handled_.insert(parenExpr);
340 bool UnnecessaryParen::VisitCallExpr(const CallExpr* callExpr)
342 if (ignoreLocation(callExpr))
343 return true;
344 if (callExpr->getNumArgs() != 1 || isa<CXXOperatorCallExpr>(callExpr))
345 return true;
347 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(callExpr->getArg(0)));
348 if (!parenExpr)
349 return true;
350 if (compat::getBeginLoc(parenExpr).isMacroID())
351 return true;
352 // assignments need extra parentheses or they generate a compiler warning
353 auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
354 if (binaryOp && binaryOp->getOpcode() == BO_Assign)
355 return true;
356 report(
357 DiagnosticsEngine::Warning, "parentheses immediately inside single-arg call",
358 compat::getBeginLoc(parenExpr))
359 << parenExpr->getSourceRange();
360 handled_.insert(parenExpr);
361 return true;
364 bool UnnecessaryParen::VisitCXXDeleteExpr(const CXXDeleteExpr* deleteExpr)
366 if (ignoreLocation(deleteExpr))
367 return true;
369 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(deleteExpr->getArgument()));
370 if (!parenExpr)
371 return true;
372 if (compat::getBeginLoc(parenExpr).isMacroID())
373 return true;
374 // assignments need extra parentheses or they generate a compiler warning
375 auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
376 if (binaryOp && binaryOp->getOpcode() == BO_Assign)
377 return true;
378 report(
379 DiagnosticsEngine::Warning, "parentheses immediately inside delete expr",
380 compat::getBeginLoc(parenExpr))
381 << parenExpr->getSourceRange();
382 handled_.insert(parenExpr);
383 return true;
386 bool UnnecessaryParen::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr* callExpr)
388 if (ignoreLocation(callExpr))
389 return true;
390 if (callExpr->getNumArgs() != 2)
391 return true;
393 // Same logic as CXXOperatorCallExpr::isAssignmentOp(), which our supported clang
394 // doesn't have yet.
395 auto Opc = callExpr->getOperator();
396 if (Opc != OO_Equal && Opc != OO_StarEqual &&
397 Opc != OO_SlashEqual && Opc != OO_PercentEqual &&
398 Opc != OO_PlusEqual && Opc != OO_MinusEqual &&
399 Opc != OO_LessLessEqual && Opc != OO_GreaterGreaterEqual &&
400 Opc != OO_AmpEqual && Opc != OO_CaretEqual &&
401 Opc != OO_PipeEqual)
402 return true;
403 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(callExpr->getArg(1)));
404 if (!parenExpr)
405 return true;
406 if (compat::getBeginLoc(parenExpr).isMacroID())
407 return true;
408 // Sometimes parentheses make the RHS of an assignment easier to read by
409 // visually disambiguating the = from a call to ==
410 auto sub = parenExpr->getSubExpr();
411 if (auto subBinOp = dyn_cast<BinaryOperator>(sub))
413 if (!(subBinOp->isMultiplicativeOp() || subBinOp->isAdditiveOp() || subBinOp->isPtrMemOp()))
414 return true;
416 if (auto subOperatorCall = dyn_cast<CXXOperatorCallExpr>(sub))
418 auto op = subOperatorCall->getOperator();
419 if (!((op >= OO_Plus && op <= OO_Exclaim) || (op >= OO_ArrowStar && op <= OO_Subscript)))
420 return true;
422 if (isa<ConditionalOperator>(sub))
423 return true;
425 report(
426 DiagnosticsEngine::Warning, "parentheses immediately inside assignment",
427 compat::getBeginLoc(parenExpr))
428 << parenExpr->getSourceRange();
429 handled_.insert(parenExpr);
430 return true;
433 bool UnnecessaryParen::VisitVarDecl(const VarDecl* varDecl)
435 if (ignoreLocation(varDecl))
436 return true;
437 if (!varDecl->getInit())
438 return true;
440 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(varDecl->getInit()));
441 if (!parenExpr)
442 return true;
443 if (compat::getBeginLoc(parenExpr).isMacroID())
444 return true;
446 // Sometimes parentheses make the RHS of an assignment easier to read by
447 // visually disambiguating the = from a call to ==
448 auto sub = parenExpr->getSubExpr();
449 if (auto subBinOp = dyn_cast<BinaryOperator>(sub))
451 if (!(subBinOp->isMultiplicativeOp() || subBinOp->isAdditiveOp() || subBinOp->isPtrMemOp()))
452 return true;
454 if (auto subOperatorCall = dyn_cast<CXXOperatorCallExpr>(sub))
456 auto op = subOperatorCall->getOperator();
457 if (!((op >= OO_Plus && op <= OO_Exclaim) || (op >= OO_ArrowStar && op <= OO_Subscript)))
458 return true;
460 if (isa<ConditionalOperator>(sub))
461 return true;
463 // these two are for "parentheses were disambiguated as a function declaration [-Werror,-Wvexing-parse]"
464 auto const sub2 = sub->IgnoreImplicit();
465 if (isa<CXXTemporaryObjectExpr>(sub2)
466 || isa<CXXFunctionalCastExpr>(sub2))
467 return true;
469 report(
470 DiagnosticsEngine::Warning, "parentheses immediately inside vardecl statement",
471 compat::getBeginLoc(parenExpr))
472 << parenExpr->getSourceRange();
473 handled_.insert(parenExpr);
474 return true;
477 bool UnnecessaryParen::VisitMemberExpr(const MemberExpr* memberExpr)
479 if (ignoreLocation(memberExpr))
480 return true;
482 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(memberExpr->getBase()));
483 if (!parenExpr)
484 return true;
485 if (handled_.find(parenExpr) != handled_.end())
486 return true;
487 if (compat::getBeginLoc(parenExpr).isMacroID())
488 return true;
490 auto sub = parenExpr->getSubExpr();
491 if (isa<CallExpr>(sub)) {
492 if (isa<CXXOperatorCallExpr>(sub))
493 return true;
494 } else if (isa<CXXConstructExpr>(sub)) {
495 // warn
496 } else if (isa<MemberExpr>(sub)) {
497 // warn
498 } else if (isa<DeclRefExpr>(sub)) {
499 // warn
500 } else
501 return true;
503 report(
504 DiagnosticsEngine::Warning, "unnecessary parentheses around member expr",
505 compat::getBeginLoc(parenExpr))
506 << parenExpr->getSourceRange();
507 handled_.insert(parenExpr);
508 return true;
511 // Conservatively assume any parenthesised integer or Boolean (incl. Objective-C ones) literal in
512 // certain condition expressions (i.e., those for which handleUnreachableCodeConditionParens is
513 // called) to be parenthesised to silence Clang -Wunreachable-code, if that is either the whole
514 // condition expression or appears as a certain sub-expression (looking at what isConfigurationValue
515 // in Clang's lib/Analysis/ReachableCode.cpp looks for, descending into certain unary and binary
516 // operators):
517 void UnnecessaryParen::handleUnreachableCodeConditionParens(Expr const * expr) {
518 // Cf. :
519 auto const e = ignoreAllImplicit(expr);
520 if (auto const e1 = dyn_cast<ParenExpr>(e)) {
521 auto const sub = e1->getSubExpr();
522 if (isa<IntegerLiteral>(sub) || isa<CXXBoolLiteralExpr>(sub)
523 || isa<ObjCBoolLiteralExpr>(sub))
525 handled_.insert(e1);
527 } else if (auto const e1 = dyn_cast<UnaryOperator>(e)) {
528 if (e1->getOpcode() == UO_LNot) {
529 handleUnreachableCodeConditionParens(e1->getSubExpr());
531 } else if (auto const e1 = dyn_cast<BinaryOperator>(e)) {
532 if (e1->isLogicalOp() || e1->isComparisonOp()) {
533 handleUnreachableCodeConditionParens(e1->getLHS());
534 handleUnreachableCodeConditionParens(e1->getRHS());
539 bool UnnecessaryParen::isPrecededBy_BAD_CAST(Expr const * expr) {
540 if (compat::getBeginLoc(expr).isMacroID()) {
541 return false;
543 SourceManager& SM = compiler.getSourceManager();
544 const char *p1 = SM.getCharacterData( compat::getBeginLoc(expr).getLocWithOffset(-10) );
545 const char *p2 = SM.getCharacterData( compat::getBeginLoc(expr) );
546 return std::string(p1, p2 - p1).find("BAD_CAST") != std::string::npos;
549 namespace {
551 bool badCombinationChar(char c) {
552 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'
553 || c == '+' || c == '-' || c == '\'' || c == '"';
558 bool UnnecessaryParen::badCombination(SourceLocation loc, int prevOffset, int nextOffset) {
559 //TODO: check for start/end of file; take backslash-newline line concatenation into account
560 auto const c1
561 = compiler.getSourceManager().getCharacterData(loc.getLocWithOffset(prevOffset))[0];
562 auto const c2
563 = compiler.getSourceManager().getCharacterData(loc.getLocWithOffset(nextOffset))[0];
564 // An approximation of avoiding whatever combinations that would cause two adjacent tokens to be
565 // lexed differently, using, for now, letters (TODO: non-ASCII ones) and digits and '_'; '+' and
566 // '-' (to avoid ++, etc.); '\'' and '"' (to avoid u'x' or "foo"bar, etc.):
567 return badCombinationChar(c1) && badCombinationChar(c2);
570 bool UnnecessaryParen::removeParens(ParenExpr const * expr) {
571 if (rewriter == nullptr) {
572 return false;
574 auto const firstBegin = compat::getBeginLoc(expr);
575 auto secondBegin = compat::getEndLoc(expr);
576 if (firstBegin.isMacroID() || secondBegin.isMacroID()) {
577 return false;
579 unsigned firstLen = Lexer::MeasureTokenLength(
580 firstBegin, compiler.getSourceManager(), compiler.getLangOpts());
581 for (auto l = firstBegin.getLocWithOffset(std::max<unsigned>(firstLen, 1));;
582 l = l.getLocWithOffset(1))
584 unsigned n = Lexer::MeasureTokenLength(
585 l, compiler.getSourceManager(), compiler.getLangOpts());
586 if (n != 0) {
587 break;
589 ++firstLen;
591 unsigned secondLen = Lexer::MeasureTokenLength(
592 secondBegin, compiler.getSourceManager(), compiler.getLangOpts());
593 for (;;) {
594 auto l = secondBegin.getLocWithOffset(-1);
595 auto const c = compiler.getSourceManager().getCharacterData(l)[0];
596 if (c == '\n') {
597 if (compiler.getSourceManager().getCharacterData(l.getLocWithOffset(-1))[0] == '\\') {
598 break;
600 } else if (!(c == ' ' || c == '\t' || c == '\v' || c == '\f')) {
601 break;
603 secondBegin = l;
604 ++secondLen;
606 if (!replaceText(firstBegin, firstLen, badCombination(firstBegin, -1, firstLen) ? " " : "")) {
607 if (isDebugMode()) {
608 report(
609 DiagnosticsEngine::Fatal,
610 "TODO: cannot rewrite opening parenthesis, needs investigation",
611 firstBegin);
612 report(
613 DiagnosticsEngine::Note, "when removing these parentheses", expr->getExprLoc())
614 << expr->getSourceRange();
616 return false;
618 if (!replaceText(secondBegin, secondLen, badCombination(secondBegin, -1, secondLen) ? " " : ""))
620 //TODO: roll back first change
621 if (isDebugMode()) {
622 report(
623 DiagnosticsEngine::Fatal,
624 "TODO: cannot rewrite closing parenthesis, needs investigation",
625 secondBegin);
626 report(
627 DiagnosticsEngine::Note, "when removing these parentheses", expr->getExprLoc())
628 << expr->getSourceRange();
630 return false;
632 return true;
635 loplugin::Plugin::Registration< UnnecessaryParen > unnecessaryparen("unnecessaryparen", true);
639 #endif // LO_CLANG_SHARED_PLUGINS
641 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */