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 #ifndef LO_CLANG_SHARED_PLUGINS
17 #include <unordered_set>
19 #include <clang/AST/CXXInheritance.h>
21 #include "config_clang.h"
27 look for unnecessary parentheses
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
) {
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) {
46 else if (auto const e
= dyn_cast
<MaterializeTemporaryExpr
>(expr
)) {
47 expr
= compat::getSubExpr(e
);
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();
71 class UnnecessaryParen
:
72 public loplugin::FilteringRewritePlugin
<UnnecessaryParen
>
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"))
84 if (loplugin::isSamePathname(fn
, WORKDIR
"/YaccTarget/idlc/source/parser.cxx"))
88 virtual void run() override
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
*);
111 bool VisitImplicitCastExpr(ImplicitCastExpr
const * expr
) {
112 if (ignoreLocation(expr
)) {
115 if (expr
->getCastKind() != CK_UserDefinedConversion
) {
118 // Filter out a MemberExpr (resp. a ParenExpr sub-expr, if any, as would be found by
119 // VisitMemberExpr) that is part of a CXXMemberCallExpr which in turn is part of an
120 // ImplicitCastExpr, so that VisitMemberExpr doesn't erroneously pick it up (and note that
121 // CXXMemberCallExpr's getImplicitObjectArgument() skips past the underlying MemberExpr):
122 if (auto const e1
= dyn_cast
<CXXMemberCallExpr
>(expr
->getSubExpr())) {
123 if (auto const e2
= dyn_cast
<ParenExpr
>(
124 e1
->getImplicitObjectArgument()->IgnoreImpCasts()))
133 void VisitSomeStmt(Stmt
const * stmt
, const Expr
* cond
, StringRef stmtName
);
135 void handleUnreachableCodeConditionParens(Expr
const * expr
);
137 // Hack for libxml2's BAD_CAST object-like macro (expanding to "(xmlChar *)"), which is
138 // typically used as if it were a function-like macro, e.g., as "BAD_CAST(pName)" in
139 // SwNode::dumpAsXml (sw/source/core/docnode/node.cxx):
140 bool isPrecededBy_BAD_CAST(Expr
const * expr
);
142 bool badCombination(SourceLocation loc
, int prevOffset
, int nextOffset
);
144 bool removeParens(ParenExpr
const * expr
);
146 std::unordered_set
<ParenExpr
const *> handled_
;
149 bool UnnecessaryParen::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr
const * expr
)
151 if (expr
->getKind() == UETT_SizeOf
&& !expr
->isArgumentType()) {
152 if (auto const e
= dyn_cast
<ParenExpr
>(ignoreAllImplicit(expr
->getArgumentExpr()))) {
159 bool UnnecessaryParen::VisitConditionalOperator(ConditionalOperator
const * expr
) {
160 handleUnreachableCodeConditionParens(expr
->getCond());
164 bool UnnecessaryParen::VisitBinaryConditionalOperator(BinaryConditionalOperator
const * expr
) {
165 handleUnreachableCodeConditionParens(expr
->getCond());
169 bool UnnecessaryParen::VisitParenExpr(const ParenExpr
* parenExpr
)
171 if (ignoreLocation(parenExpr
))
173 if (compat::getBeginLoc(parenExpr
).isMacroID())
175 if (handled_
.find(parenExpr
) != handled_
.end())
178 auto subExpr
= ignoreAllImplicit(parenExpr
->getSubExpr());
180 if (auto subParenExpr
= dyn_cast
<ParenExpr
>(subExpr
))
182 if (compat::getBeginLoc(subParenExpr
).isMacroID())
185 DiagnosticsEngine::Warning
, "parentheses around parentheses",
186 compat::getBeginLoc(parenExpr
))
187 << parenExpr
->getSourceRange();
188 handled_
.insert(subParenExpr
);
191 // Somewhat redundantly add parenExpr to handled_, so that issues within InitListExpr don't get
192 // reported twice (without having to change TraverseInitListExpr to only either traverse the
193 // syntactic or semantic form, as other plugins do):
195 if (isa
<DeclRefExpr
>(subExpr
)) {
196 if (!isPrecededBy_BAD_CAST(parenExpr
)) {
198 DiagnosticsEngine::Warning
, "unnecessary parentheses around identifier",
199 compat::getBeginLoc(parenExpr
))
200 << parenExpr
->getSourceRange();
201 handled_
.insert(parenExpr
);
203 } else if (isa
<IntegerLiteral
>(subExpr
) || isa
<CharacterLiteral
>(subExpr
)
204 || isa
<FloatingLiteral
>(subExpr
) || isa
<ImaginaryLiteral
>(subExpr
)
205 || isa
<CXXBoolLiteralExpr
>(subExpr
) || isa
<CXXNullPtrLiteralExpr
>(subExpr
)
206 || isa
<ObjCBoolLiteralExpr
>(subExpr
))
208 auto const loc
= compat::getBeginLoc(subExpr
);
209 if (loc
.isMacroID() && compiler
.getSourceManager().isAtStartOfImmediateMacroExpansion(loc
))
211 // just in case the macro could also expand to something that /would/ require
216 DiagnosticsEngine::Warning
, "unnecessary parentheses around literal",
217 compat::getBeginLoc(parenExpr
))
218 << parenExpr
->getSourceRange();
219 handled_
.insert(parenExpr
);
220 } else if (auto const e
= dyn_cast
<clang::StringLiteral
>(subExpr
)) {
221 if (e
->getNumConcatenated() == 1 && !isPrecededBy_BAD_CAST(parenExpr
)) {
223 DiagnosticsEngine::Warning
,
224 "unnecessary parentheses around single-token string literal",
225 compat::getBeginLoc(parenExpr
))
226 << parenExpr
->getSourceRange();
227 handled_
.insert(parenExpr
);
229 } else if (auto const e
= dyn_cast
<UnaryOperator
>(subExpr
)) {
230 auto const op
= e
->getOpcode();
231 if (op
== UO_Plus
|| op
== UO_Minus
) {
232 auto const e2
= e
->getSubExpr();
233 if (isa
<IntegerLiteral
>(e2
) || isa
<FloatingLiteral
>(e2
) || isa
<ImaginaryLiteral
>(e2
)) {
235 DiagnosticsEngine::Warning
,
236 "unnecessary parentheses around signed numeric literal",
237 compat::getBeginLoc(parenExpr
))
238 << parenExpr
->getSourceRange();
239 handled_
.insert(parenExpr
);
242 } else if (isa
<CXXNamedCastExpr
>(subExpr
)) {
243 if (!removeParens(parenExpr
)) {
245 DiagnosticsEngine::Warning
, "unnecessary parentheses around cast",
246 compat::getBeginLoc(parenExpr
))
247 << parenExpr
->getSourceRange();
249 handled_
.insert(parenExpr
);
250 } else if (auto memberExpr
= dyn_cast
<MemberExpr
>(subExpr
)) {
251 if (isa
<CXXThisExpr
>(ignoreAllImplicit(memberExpr
->getBase()))) {
253 DiagnosticsEngine::Warning
, "unnecessary parentheses around member expr",
254 compat::getBeginLoc(parenExpr
))
255 << parenExpr
->getSourceRange();
256 handled_
.insert(parenExpr
);
263 bool UnnecessaryParen::VisitIfStmt(const IfStmt
* ifStmt
)
265 handleUnreachableCodeConditionParens(ifStmt
->getCond());
266 VisitSomeStmt(ifStmt
, ifStmt
->getCond(), "if");
270 bool UnnecessaryParen::VisitDoStmt(const DoStmt
* doStmt
)
272 VisitSomeStmt(doStmt
, doStmt
->getCond(), "do");
276 bool UnnecessaryParen::VisitWhileStmt(const WhileStmt
* whileStmt
)
278 handleUnreachableCodeConditionParens(whileStmt
->getCond());
279 VisitSomeStmt(whileStmt
, whileStmt
->getCond(), "while");
283 bool UnnecessaryParen::VisitForStmt(ForStmt
const * stmt
) {
284 if (auto const cond
= stmt
->getCond()) {
285 handleUnreachableCodeConditionParens(cond
);
290 bool UnnecessaryParen::VisitSwitchStmt(const SwitchStmt
* switchStmt
)
292 VisitSomeStmt(switchStmt
, switchStmt
->getCond(), "switch");
296 bool UnnecessaryParen::VisitCaseStmt(const CaseStmt
* caseStmt
)
298 VisitSomeStmt(caseStmt
, caseStmt
->getLHS(), "case");
302 bool UnnecessaryParen::VisitReturnStmt(const ReturnStmt
* returnStmt
)
304 if (ignoreLocation(returnStmt
))
307 if (!returnStmt
->getRetValue())
309 auto parenExpr
= dyn_cast
<ParenExpr
>(ignoreAllImplicit(returnStmt
->getRetValue()));
312 if (compat::getBeginLoc(parenExpr
).isMacroID())
314 // assignments need extra parentheses or they generate a compiler warning
315 auto binaryOp
= dyn_cast
<BinaryOperator
>(parenExpr
->getSubExpr());
316 if (binaryOp
&& binaryOp
->getOpcode() == BO_Assign
)
319 // only non-operator-calls for now
320 auto subExpr
= ignoreAllImplicit(parenExpr
->getSubExpr());
321 if (isa
<CallExpr
>(subExpr
) && !isa
<CXXOperatorCallExpr
>(subExpr
))
324 DiagnosticsEngine::Warning
, "parentheses immediately inside return statement",
325 compat::getBeginLoc(parenExpr
))
326 << parenExpr
->getSourceRange();
327 handled_
.insert(parenExpr
);
332 void UnnecessaryParen::VisitSomeStmt(const Stmt
* stmt
, const Expr
* cond
, StringRef stmtName
)
334 if (ignoreLocation(stmt
))
337 auto parenExpr
= dyn_cast
<ParenExpr
>(ignoreAllImplicit(cond
));
339 if (handled_
.find(parenExpr
) != handled_
.end()) {
342 if (compat::getBeginLoc(parenExpr
).isMacroID())
344 // assignments need extra parentheses or they generate a compiler warning
345 auto binaryOp
= dyn_cast
<BinaryOperator
>(parenExpr
->getSubExpr());
346 if (binaryOp
&& binaryOp
->getOpcode() == BO_Assign
)
348 if (auto const opCall
= dyn_cast
<CXXOperatorCallExpr
>(parenExpr
->getSubExpr())) {
349 if (opCall
->getOperator() == OO_Equal
) {
354 DiagnosticsEngine::Warning
, "parentheses immediately inside %0 statement",
355 compat::getBeginLoc(parenExpr
))
357 << parenExpr
->getSourceRange();
358 handled_
.insert(parenExpr
);
362 bool UnnecessaryParen::VisitCallExpr(const CallExpr
* callExpr
)
364 if (ignoreLocation(callExpr
))
366 if (callExpr
->getNumArgs() != 1 || isa
<CXXOperatorCallExpr
>(callExpr
))
369 auto parenExpr
= dyn_cast
<ParenExpr
>(ignoreAllImplicit(callExpr
->getArg(0)));
372 if (compat::getBeginLoc(parenExpr
).isMacroID())
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
)
379 DiagnosticsEngine::Warning
, "parentheses immediately inside single-arg call",
380 compat::getBeginLoc(parenExpr
))
381 << parenExpr
->getSourceRange();
382 handled_
.insert(parenExpr
);
386 bool UnnecessaryParen::VisitCXXDeleteExpr(const CXXDeleteExpr
* deleteExpr
)
388 if (ignoreLocation(deleteExpr
))
391 auto parenExpr
= dyn_cast
<ParenExpr
>(ignoreAllImplicit(deleteExpr
->getArgument()));
394 if (compat::getBeginLoc(parenExpr
).isMacroID())
396 // assignments need extra parentheses or they generate a compiler warning
397 auto binaryOp
= dyn_cast
<BinaryOperator
>(parenExpr
->getSubExpr());
398 if (binaryOp
&& binaryOp
->getOpcode() == BO_Assign
)
401 DiagnosticsEngine::Warning
, "parentheses immediately inside delete expr",
402 compat::getBeginLoc(parenExpr
))
403 << parenExpr
->getSourceRange();
404 handled_
.insert(parenExpr
);
408 bool UnnecessaryParen::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr
* callExpr
)
410 if (ignoreLocation(callExpr
))
412 if (callExpr
->getNumArgs() != 2)
415 // Same logic as CXXOperatorCallExpr::isAssignmentOp(), which our supported clang
417 auto Opc
= callExpr
->getOperator();
418 if (Opc
!= OO_Equal
&& Opc
!= OO_StarEqual
&&
419 Opc
!= OO_SlashEqual
&& Opc
!= OO_PercentEqual
&&
420 Opc
!= OO_PlusEqual
&& Opc
!= OO_MinusEqual
&&
421 Opc
!= OO_LessLessEqual
&& Opc
!= OO_GreaterGreaterEqual
&&
422 Opc
!= OO_AmpEqual
&& Opc
!= OO_CaretEqual
&&
425 auto parenExpr
= dyn_cast
<ParenExpr
>(ignoreAllImplicit(callExpr
->getArg(1)));
428 if (compat::getBeginLoc(parenExpr
).isMacroID())
430 // Sometimes parentheses make the RHS of an assignment easier to read by
431 // visually disambiguating the = from a call to ==
432 auto sub
= parenExpr
->getSubExpr();
433 if (auto subBinOp
= dyn_cast
<BinaryOperator
>(sub
))
435 if (!(subBinOp
->isMultiplicativeOp() || subBinOp
->isAdditiveOp() || subBinOp
->isPtrMemOp()))
438 if (auto subOperatorCall
= dyn_cast
<CXXOperatorCallExpr
>(sub
))
440 auto op
= subOperatorCall
->getOperator();
441 if (!((op
>= OO_Plus
&& op
<= OO_Exclaim
) || (op
>= OO_ArrowStar
&& op
<= OO_Subscript
)))
444 if (isa
<ConditionalOperator
>(sub
))
448 DiagnosticsEngine::Warning
, "parentheses immediately inside assignment",
449 compat::getBeginLoc(parenExpr
))
450 << parenExpr
->getSourceRange();
451 handled_
.insert(parenExpr
);
455 bool UnnecessaryParen::VisitVarDecl(const VarDecl
* varDecl
)
457 if (ignoreLocation(varDecl
))
459 if (!varDecl
->getInit())
462 auto parenExpr
= dyn_cast
<ParenExpr
>(ignoreAllImplicit(varDecl
->getInit()));
465 if (compat::getBeginLoc(parenExpr
).isMacroID())
468 // Sometimes parentheses make the RHS of an assignment easier to read by
469 // visually disambiguating the = from a call to ==
470 auto sub
= parenExpr
->getSubExpr();
471 #if CLANG_VERSION >= 100000
472 if (auto const e
= dyn_cast
<CXXRewrittenBinaryOperator
>(sub
)) {
473 sub
= e
->getDecomposedForm().InnerBinOp
;
476 if (auto subBinOp
= dyn_cast
<BinaryOperator
>(sub
))
478 if (!(subBinOp
->isMultiplicativeOp() || subBinOp
->isAdditiveOp() || subBinOp
->isPtrMemOp()))
481 if (auto subOperatorCall
= dyn_cast
<CXXOperatorCallExpr
>(sub
))
483 auto op
= subOperatorCall
->getOperator();
484 if (!((op
>= OO_Plus
&& op
<= OO_Exclaim
) || (op
>= OO_ArrowStar
&& op
<= OO_Subscript
)))
487 if (isa
<ConditionalOperator
>(sub
))
490 // these two are for "parentheses were disambiguated as a function declaration [-Werror,-Wvexing-parse]"
491 auto const sub2
= sub
->IgnoreImplicit();
492 if (isa
<CXXTemporaryObjectExpr
>(sub2
)
493 || isa
<CXXFunctionalCastExpr
>(sub2
))
497 DiagnosticsEngine::Warning
, "parentheses immediately inside vardecl statement",
498 compat::getBeginLoc(parenExpr
))
499 << parenExpr
->getSourceRange();
500 handled_
.insert(parenExpr
);
504 bool UnnecessaryParen::VisitMemberExpr(const MemberExpr
* memberExpr
)
506 if (ignoreLocation(memberExpr
))
509 auto parenExpr
= dyn_cast
<ParenExpr
>(ignoreAllImplicit(memberExpr
->getBase()));
512 if (handled_
.find(parenExpr
) != handled_
.end())
514 if (compat::getBeginLoc(parenExpr
).isMacroID())
517 auto sub
= parenExpr
->getSubExpr();
518 if (isa
<CallExpr
>(sub
)) {
519 if (isa
<CXXOperatorCallExpr
>(sub
))
521 } else if (isa
<CXXConstructExpr
>(sub
)) {
523 } else if (isa
<MemberExpr
>(sub
)) {
525 } else if (isa
<DeclRefExpr
>(sub
)) {
531 DiagnosticsEngine::Warning
, "unnecessary parentheses around member expr",
532 compat::getBeginLoc(parenExpr
))
533 << parenExpr
->getSourceRange();
534 handled_
.insert(parenExpr
);
538 // Conservatively assume any parenthesised integer or Boolean (incl. Objective-C ones) literal in
539 // certain condition expressions (i.e., those for which handleUnreachableCodeConditionParens is
540 // called) to be parenthesised to silence Clang -Wunreachable-code, if that is either the whole
541 // condition expression or appears as a certain sub-expression (looking at what isConfigurationValue
542 // in Clang's lib/Analysis/ReachableCode.cpp looks for, descending into certain unary and binary
544 void UnnecessaryParen::handleUnreachableCodeConditionParens(Expr
const * expr
) {
546 auto const e
= ignoreAllImplicit(expr
);
547 if (auto const e1
= dyn_cast
<ParenExpr
>(e
)) {
548 auto const sub
= e1
->getSubExpr();
549 if (isa
<IntegerLiteral
>(sub
) || isa
<CXXBoolLiteralExpr
>(sub
)
550 || isa
<ObjCBoolLiteralExpr
>(sub
))
554 } else if (auto const e1
= dyn_cast
<UnaryOperator
>(e
)) {
555 if (e1
->getOpcode() == UO_LNot
) {
556 handleUnreachableCodeConditionParens(e1
->getSubExpr());
558 } else if (auto const e1
= dyn_cast
<BinaryOperator
>(e
)) {
559 if (e1
->isLogicalOp() || e1
->isComparisonOp()) {
560 handleUnreachableCodeConditionParens(e1
->getLHS());
561 handleUnreachableCodeConditionParens(e1
->getRHS());
566 bool UnnecessaryParen::isPrecededBy_BAD_CAST(Expr
const * expr
) {
567 if (compat::getBeginLoc(expr
).isMacroID()) {
570 SourceManager
& SM
= compiler
.getSourceManager();
571 const char *p1
= SM
.getCharacterData( compat::getBeginLoc(expr
).getLocWithOffset(-10) );
572 const char *p2
= SM
.getCharacterData( compat::getBeginLoc(expr
) );
573 return std::string(p1
, p2
- p1
).find("BAD_CAST") != std::string::npos
;
578 bool badCombinationChar(char c
) {
579 return (c
>= 'A' && c
<= 'Z') || (c
>= 'a' && c
<= 'z') || (c
>= '0' && c
<= '9') || c
== '_'
580 || c
== '+' || c
== '-' || c
== '\'' || c
== '"';
585 bool UnnecessaryParen::badCombination(SourceLocation loc
, int prevOffset
, int nextOffset
) {
586 //TODO: check for start/end of file; take backslash-newline line concatenation into account
588 = compiler
.getSourceManager().getCharacterData(loc
.getLocWithOffset(prevOffset
))[0];
590 = compiler
.getSourceManager().getCharacterData(loc
.getLocWithOffset(nextOffset
))[0];
591 // An approximation of avoiding whatever combinations that would cause two adjacent tokens to be
592 // lexed differently, using, for now, letters (TODO: non-ASCII ones) and digits and '_'; '+' and
593 // '-' (to avoid ++, etc.); '\'' and '"' (to avoid u'x' or "foo"bar, etc.):
594 return badCombinationChar(c1
) && badCombinationChar(c2
);
597 bool UnnecessaryParen::removeParens(ParenExpr
const * expr
) {
598 if (rewriter
== nullptr) {
601 auto const firstBegin
= compat::getBeginLoc(expr
);
602 auto secondBegin
= compat::getEndLoc(expr
);
603 if (firstBegin
.isMacroID() || secondBegin
.isMacroID()) {
606 unsigned firstLen
= Lexer::MeasureTokenLength(
607 firstBegin
, compiler
.getSourceManager(), compiler
.getLangOpts());
608 for (auto l
= firstBegin
.getLocWithOffset(std::max
<unsigned>(firstLen
, 1));;
609 l
= l
.getLocWithOffset(1))
611 unsigned n
= Lexer::MeasureTokenLength(
612 l
, compiler
.getSourceManager(), compiler
.getLangOpts());
618 unsigned secondLen
= Lexer::MeasureTokenLength(
619 secondBegin
, compiler
.getSourceManager(), compiler
.getLangOpts());
621 auto l
= secondBegin
.getLocWithOffset(-1);
622 auto const c
= compiler
.getSourceManager().getCharacterData(l
)[0];
624 if (compiler
.getSourceManager().getCharacterData(l
.getLocWithOffset(-1))[0] == '\\') {
627 } else if (!(c
== ' ' || c
== '\t' || c
== '\v' || c
== '\f')) {
633 if (!replaceText(firstBegin
, firstLen
, badCombination(firstBegin
, -1, firstLen
) ? " " : "")) {
636 DiagnosticsEngine::Fatal
,
637 "TODO: cannot rewrite opening parenthesis, needs investigation",
640 DiagnosticsEngine::Note
, "when removing these parentheses", expr
->getExprLoc())
641 << expr
->getSourceRange();
645 if (!replaceText(secondBegin
, secondLen
, badCombination(secondBegin
, -1, secondLen
) ? " " : ""))
647 //TODO: roll back first change
650 DiagnosticsEngine::Fatal
,
651 "TODO: cannot rewrite closing parenthesis, needs investigation",
654 DiagnosticsEngine::Note
, "when removing these parentheses", expr
->getExprLoc())
655 << expr
->getSourceRange();
662 loplugin::Plugin::Registration
< UnnecessaryParen
> unnecessaryparen("unnecessaryparen", true);
666 #endif // LO_CLANG_SHARED_PLUGINS
668 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */