nss: upgrade to release 3.73
[LibreOffice.git] / compilerplugins / clang / unnecessaryparen.cxx
blobe93dfa64b5ec1cabb68279eab630fe26f7879628
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 = 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();
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 *);
111 bool VisitImplicitCastExpr(ImplicitCastExpr const * expr) {
112 if (ignoreLocation(expr)) {
113 return true;
115 if (expr->getCastKind() != CK_UserDefinedConversion) {
116 return true;
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()))
126 handled_.insert(e2);
129 return true;
132 private:
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()))) {
153 handled_.insert(e);
156 return true;
159 bool UnnecessaryParen::VisitConditionalOperator(ConditionalOperator const * expr) {
160 handleUnreachableCodeConditionParens(expr->getCond());
161 return true;
164 bool UnnecessaryParen::VisitBinaryConditionalOperator(BinaryConditionalOperator const * expr) {
165 handleUnreachableCodeConditionParens(expr->getCond());
166 return true;
169 bool UnnecessaryParen::VisitParenExpr(const ParenExpr* parenExpr)
171 if (ignoreLocation(parenExpr))
172 return true;
173 if (compat::getBeginLoc(parenExpr).isMacroID())
174 return true;
175 if (handled_.find(parenExpr) != handled_.end())
176 return true;
178 auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
180 if (auto subParenExpr = dyn_cast<ParenExpr>(subExpr))
182 if (compat::getBeginLoc(subParenExpr).isMacroID())
183 return true;
184 report(
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)) {
197 report(
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
212 // parentheses here
213 return true;
215 report(
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)) {
222 report(
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)) {
234 report(
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)) {
244 report(
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()))) {
252 report(
253 DiagnosticsEngine::Warning, "unnecessary parentheses around member expr",
254 compat::getBeginLoc(parenExpr))
255 << parenExpr->getSourceRange();
256 handled_.insert(parenExpr);
260 return true;
263 bool UnnecessaryParen::VisitIfStmt(const IfStmt* ifStmt)
265 handleUnreachableCodeConditionParens(ifStmt->getCond());
266 VisitSomeStmt(ifStmt, ifStmt->getCond(), "if");
267 return true;
270 bool UnnecessaryParen::VisitDoStmt(const DoStmt* doStmt)
272 VisitSomeStmt(doStmt, doStmt->getCond(), "do");
273 return true;
276 bool UnnecessaryParen::VisitWhileStmt(const WhileStmt* whileStmt)
278 handleUnreachableCodeConditionParens(whileStmt->getCond());
279 VisitSomeStmt(whileStmt, whileStmt->getCond(), "while");
280 return true;
283 bool UnnecessaryParen::VisitForStmt(ForStmt const * stmt) {
284 if (auto const cond = stmt->getCond()) {
285 handleUnreachableCodeConditionParens(cond);
287 return true;
290 bool UnnecessaryParen::VisitSwitchStmt(const SwitchStmt* switchStmt)
292 VisitSomeStmt(switchStmt, switchStmt->getCond(), "switch");
293 return true;
296 bool UnnecessaryParen::VisitCaseStmt(const CaseStmt* caseStmt)
298 VisitSomeStmt(caseStmt, caseStmt->getLHS(), "case");
299 return true;
302 bool UnnecessaryParen::VisitReturnStmt(const ReturnStmt* returnStmt)
304 if (ignoreLocation(returnStmt))
305 return true;
307 if (!returnStmt->getRetValue())
308 return true;
309 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(returnStmt->getRetValue()));
310 if (!parenExpr)
311 return true;
312 if (compat::getBeginLoc(parenExpr).isMacroID())
313 return true;
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)
317 return true;
319 // only non-operator-calls for now
320 auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
321 if (isa<CallExpr>(subExpr) && !isa<CXXOperatorCallExpr>(subExpr))
323 report(
324 DiagnosticsEngine::Warning, "parentheses immediately inside return statement",
325 compat::getBeginLoc(parenExpr))
326 << parenExpr->getSourceRange();
327 handled_.insert(parenExpr);
329 return true;
332 void UnnecessaryParen::VisitSomeStmt(const Stmt * stmt, const Expr* cond, StringRef stmtName)
334 if (ignoreLocation(stmt))
335 return;
337 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(cond));
338 if (parenExpr) {
339 if (handled_.find(parenExpr) != handled_.end()) {
340 return;
342 if (compat::getBeginLoc(parenExpr).isMacroID())
343 return;
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)
347 return;
348 if (auto const opCall = dyn_cast<CXXOperatorCallExpr>(parenExpr->getSubExpr())) {
349 if (opCall->getOperator() == OO_Equal) {
350 return;
353 report(
354 DiagnosticsEngine::Warning, "parentheses immediately inside %0 statement",
355 compat::getBeginLoc(parenExpr))
356 << stmtName
357 << parenExpr->getSourceRange();
358 handled_.insert(parenExpr);
362 bool UnnecessaryParen::VisitCallExpr(const CallExpr* callExpr)
364 if (ignoreLocation(callExpr))
365 return true;
366 if (callExpr->getNumArgs() != 1 || isa<CXXOperatorCallExpr>(callExpr))
367 return true;
369 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(callExpr->getArg(0)));
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 single-arg call",
380 compat::getBeginLoc(parenExpr))
381 << parenExpr->getSourceRange();
382 handled_.insert(parenExpr);
383 return true;
386 bool UnnecessaryParen::VisitCXXDeleteExpr(const CXXDeleteExpr* deleteExpr)
388 if (ignoreLocation(deleteExpr))
389 return true;
391 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(deleteExpr->getArgument()));
392 if (!parenExpr)
393 return true;
394 if (compat::getBeginLoc(parenExpr).isMacroID())
395 return true;
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)
399 return true;
400 report(
401 DiagnosticsEngine::Warning, "parentheses immediately inside delete expr",
402 compat::getBeginLoc(parenExpr))
403 << parenExpr->getSourceRange();
404 handled_.insert(parenExpr);
405 return true;
408 bool UnnecessaryParen::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr* callExpr)
410 if (ignoreLocation(callExpr))
411 return true;
412 if (callExpr->getNumArgs() != 2)
413 return true;
415 // Same logic as CXXOperatorCallExpr::isAssignmentOp(), which our supported clang
416 // doesn't have yet.
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 &&
423 Opc != OO_PipeEqual)
424 return true;
425 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(callExpr->getArg(1)));
426 if (!parenExpr)
427 return true;
428 if (compat::getBeginLoc(parenExpr).isMacroID())
429 return true;
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()))
436 return true;
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)))
442 return true;
444 if (isa<ConditionalOperator>(sub))
445 return true;
447 report(
448 DiagnosticsEngine::Warning, "parentheses immediately inside assignment",
449 compat::getBeginLoc(parenExpr))
450 << parenExpr->getSourceRange();
451 handled_.insert(parenExpr);
452 return true;
455 bool UnnecessaryParen::VisitVarDecl(const VarDecl* varDecl)
457 if (ignoreLocation(varDecl))
458 return true;
459 if (!varDecl->getInit())
460 return true;
462 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(varDecl->getInit()));
463 if (!parenExpr)
464 return true;
465 if (compat::getBeginLoc(parenExpr).isMacroID())
466 return true;
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;
475 #endif
476 if (auto subBinOp = dyn_cast<BinaryOperator>(sub))
478 if (!(subBinOp->isMultiplicativeOp() || subBinOp->isAdditiveOp() || subBinOp->isPtrMemOp()))
479 return true;
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)))
485 return true;
487 if (isa<ConditionalOperator>(sub))
488 return true;
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))
494 return true;
496 report(
497 DiagnosticsEngine::Warning, "parentheses immediately inside vardecl statement",
498 compat::getBeginLoc(parenExpr))
499 << parenExpr->getSourceRange();
500 handled_.insert(parenExpr);
501 return true;
504 bool UnnecessaryParen::VisitMemberExpr(const MemberExpr* memberExpr)
506 if (ignoreLocation(memberExpr))
507 return true;
509 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(memberExpr->getBase()));
510 if (!parenExpr)
511 return true;
512 if (handled_.find(parenExpr) != handled_.end())
513 return true;
514 if (compat::getBeginLoc(parenExpr).isMacroID())
515 return true;
517 auto sub = parenExpr->getSubExpr();
518 if (isa<CallExpr>(sub)) {
519 if (isa<CXXOperatorCallExpr>(sub))
520 return true;
521 } else if (isa<CXXConstructExpr>(sub)) {
522 // warn
523 } else if (isa<MemberExpr>(sub)) {
524 // warn
525 } else if (isa<DeclRefExpr>(sub)) {
526 // warn
527 } else
528 return true;
530 report(
531 DiagnosticsEngine::Warning, "unnecessary parentheses around member expr",
532 compat::getBeginLoc(parenExpr))
533 << parenExpr->getSourceRange();
534 handled_.insert(parenExpr);
535 return true;
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
543 // operators):
544 void UnnecessaryParen::handleUnreachableCodeConditionParens(Expr const * expr) {
545 // Cf. :
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))
552 handled_.insert(e1);
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()) {
568 return false;
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;
576 namespace {
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
587 auto const c1
588 = compiler.getSourceManager().getCharacterData(loc.getLocWithOffset(prevOffset))[0];
589 auto const c2
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) {
599 return false;
601 auto const firstBegin = compat::getBeginLoc(expr);
602 auto secondBegin = compat::getEndLoc(expr);
603 if (firstBegin.isMacroID() || secondBegin.isMacroID()) {
604 return false;
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());
613 if (n != 0) {
614 break;
616 ++firstLen;
618 unsigned secondLen = Lexer::MeasureTokenLength(
619 secondBegin, compiler.getSourceManager(), compiler.getLangOpts());
620 for (;;) {
621 auto l = secondBegin.getLocWithOffset(-1);
622 auto const c = compiler.getSourceManager().getCharacterData(l)[0];
623 if (c == '\n') {
624 if (compiler.getSourceManager().getCharacterData(l.getLocWithOffset(-1))[0] == '\\') {
625 break;
627 } else if (!(c == ' ' || c == '\t' || c == '\v' || c == '\f')) {
628 break;
630 secondBegin = l;
631 ++secondLen;
633 if (!replaceText(firstBegin, firstLen, badCombination(firstBegin, -1, firstLen) ? " " : "")) {
634 if (isDebugMode()) {
635 report(
636 DiagnosticsEngine::Fatal,
637 "TODO: cannot rewrite opening parenthesis, needs investigation",
638 firstBegin);
639 report(
640 DiagnosticsEngine::Note, "when removing these parentheses", expr->getExprLoc())
641 << expr->getSourceRange();
643 return false;
645 if (!replaceText(secondBegin, secondLen, badCombination(secondBegin, -1, secondLen) ? " " : ""))
647 //TODO: roll back first change
648 if (isDebugMode()) {
649 report(
650 DiagnosticsEngine::Fatal,
651 "TODO: cannot rewrite closing parenthesis, needs investigation",
652 secondBegin);
653 report(
654 DiagnosticsEngine::Note, "when removing these parentheses", expr->getExprLoc())
655 << expr->getSourceRange();
657 return false;
659 return true;
662 loplugin::Plugin::Registration< UnnecessaryParen > unnecessaryparen("unnecessaryparen", true);
666 #endif // LO_CLANG_SHARED_PLUGINS
668 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */