LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / compilerplugins / clang / unnecessaryparen.cxx
blobeb53c449cd77b94eb114b5382899971b0ce7a704
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 bool isParenWorthyOpcode(BinaryOperatorKind op) {
72 return !(BinaryOperator::isMultiplicativeOp(op) || BinaryOperator::isAdditiveOp(op)
73 || compat::isPtrMemOp(op));
76 class UnnecessaryParen:
77 public loplugin::FilteringRewritePlugin<UnnecessaryParen>
79 public:
80 explicit UnnecessaryParen(loplugin::InstantiationData const & data):
81 FilteringRewritePlugin(data) {}
83 virtual bool preRun() override
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;
93 virtual void run() override
95 if( preRun())
96 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
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 *);
116 bool VisitImplicitCastExpr(ImplicitCastExpr const * expr) {
117 if (ignoreLocation(expr)) {
118 return true;
120 if (expr->getCastKind() != CK_UserDefinedConversion) {
121 return true;
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()))
131 handled_.insert(e2);
134 return true;
137 private:
138 void VisitSomeStmt(Stmt const * stmt, const Expr* cond, StringRef stmtName);
140 void handleUnreachableCodeConditionParens(Expr const * expr);
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);
147 bool badCombination(SourceLocation loc, int prevOffset, int nextOffset);
149 bool removeParens(ParenExpr const * expr);
151 std::unordered_set<ParenExpr const *> handled_;
154 bool UnnecessaryParen::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr const * expr)
156 if (expr->getKind() == UETT_SizeOf && !expr->isArgumentType()) {
157 if (auto const e = dyn_cast<ParenExpr>(ignoreAllImplicit(expr->getArgumentExpr()))) {
158 handled_.insert(e);
161 return true;
164 bool UnnecessaryParen::VisitConditionalOperator(ConditionalOperator const * expr) {
165 handleUnreachableCodeConditionParens(expr->getCond());
166 return true;
169 bool UnnecessaryParen::VisitBinaryConditionalOperator(BinaryConditionalOperator const * expr) {
170 handleUnreachableCodeConditionParens(expr->getCond());
171 return true;
174 bool UnnecessaryParen::VisitParenExpr(const ParenExpr* parenExpr)
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;
183 auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
185 if (auto subParenExpr = dyn_cast<ParenExpr>(subExpr))
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);
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):
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);
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))
213 auto const loc = compat::getBeginLoc(subExpr);
214 if (loc.isMacroID() && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(loc))
216 // just in case the macro could also expand to something that /would/ require
217 // parentheses here
218 return true;
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);
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);
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();
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);
265 return true;
268 bool UnnecessaryParen::VisitIfStmt(const IfStmt* ifStmt)
270 handleUnreachableCodeConditionParens(ifStmt->getCond());
271 VisitSomeStmt(ifStmt, ifStmt->getCond(), "if");
272 return true;
275 bool UnnecessaryParen::VisitDoStmt(const DoStmt* doStmt)
277 VisitSomeStmt(doStmt, doStmt->getCond(), "do");
278 return true;
281 bool UnnecessaryParen::VisitWhileStmt(const WhileStmt* whileStmt)
283 handleUnreachableCodeConditionParens(whileStmt->getCond());
284 VisitSomeStmt(whileStmt, whileStmt->getCond(), "while");
285 return true;
288 bool UnnecessaryParen::VisitForStmt(ForStmt const * stmt) {
289 if (auto const cond = stmt->getCond()) {
290 handleUnreachableCodeConditionParens(cond);
292 return true;
295 bool UnnecessaryParen::VisitSwitchStmt(const SwitchStmt* switchStmt)
297 VisitSomeStmt(switchStmt, switchStmt->getCond(), "switch");
298 return true;
301 bool UnnecessaryParen::VisitCaseStmt(const CaseStmt* caseStmt)
303 VisitSomeStmt(caseStmt, caseStmt->getLHS(), "case");
304 return true;
307 bool UnnecessaryParen::VisitReturnStmt(const ReturnStmt* returnStmt)
309 if (ignoreLocation(returnStmt))
310 return true;
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;
324 // only non-operator-calls for now
325 auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
326 if (isa<CallExpr>(subExpr) && !isa<CXXOperatorCallExpr>(subExpr))
328 report(
329 DiagnosticsEngine::Warning, "parentheses immediately inside return statement",
330 compat::getBeginLoc(parenExpr))
331 << parenExpr->getSourceRange();
332 handled_.insert(parenExpr);
334 return true;
337 void UnnecessaryParen::VisitSomeStmt(const Stmt * stmt, const Expr* cond, StringRef stmtName)
339 if (ignoreLocation(stmt))
340 return;
342 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(cond));
343 if (parenExpr) {
344 if (handled_.find(parenExpr) != handled_.end()) {
345 return;
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;
358 report(
359 DiagnosticsEngine::Warning, "parentheses immediately inside %0 statement",
360 compat::getBeginLoc(parenExpr))
361 << stmtName
362 << parenExpr->getSourceRange();
363 handled_.insert(parenExpr);
367 bool UnnecessaryParen::VisitCallExpr(const CallExpr* callExpr)
369 if (ignoreLocation(callExpr))
370 return true;
371 if (callExpr->getNumArgs() == 0 || isa<CXXOperatorCallExpr>(callExpr))
372 return true;
374 // if we are calling a >1 arg method, are we using the defaults?
375 if (callExpr->getNumArgs() > 1)
377 if (!isa<CXXDefaultArgExpr>(callExpr->getArg(1)))
378 return true;
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;
398 bool UnnecessaryParen::VisitCXXDeleteExpr(const CXXDeleteExpr* deleteExpr)
400 if (ignoreLocation(deleteExpr))
401 return true;
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;
420 bool UnnecessaryParen::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr* callExpr)
422 if (ignoreLocation(callExpr))
423 return true;
424 if (callExpr->getNumArgs() != 2)
425 return true;
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;
451 #endif
452 if (auto subBinOp = dyn_cast<BinaryOperator>(sub))
454 if (isParenWorthyOpcode(subBinOp->getOpcode()))
455 return true;
457 if (auto subOperatorCall = dyn_cast<CXXOperatorCallExpr>(sub))
459 auto op = subOperatorCall->getOperator();
460 if (!((op >= OO_Plus && op <= OO_Exclaim) || (op >= OO_ArrowStar && op <= OO_Subscript)))
461 return true;
463 if (isa<ConditionalOperator>(sub))
464 return true;
466 report(
467 DiagnosticsEngine::Warning, "parentheses immediately inside assignment",
468 compat::getBeginLoc(parenExpr))
469 << parenExpr->getSourceRange();
470 handled_.insert(parenExpr);
471 return true;
474 bool UnnecessaryParen::VisitVarDecl(const VarDecl* varDecl)
476 if (ignoreLocation(varDecl))
477 return true;
478 if (!varDecl->getInit())
479 return true;
481 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(varDecl->getInit()));
482 if (!parenExpr)
483 return true;
484 if (compat::getBeginLoc(parenExpr).isMacroID())
485 return true;
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;
494 #endif
495 if (auto subBinOp = dyn_cast<BinaryOperator>(sub))
497 if (!(subBinOp->isMultiplicativeOp() || subBinOp->isAdditiveOp() || subBinOp->isPtrMemOp()))
498 return true;
500 if (auto subOperatorCall = dyn_cast<CXXOperatorCallExpr>(sub))
502 auto op = subOperatorCall->getOperator();
503 if (!((op >= OO_Plus && op <= OO_Exclaim) || (op >= OO_ArrowStar && op <= OO_Subscript)))
504 return true;
506 if (isa<ConditionalOperator>(sub))
507 return true;
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;
515 report(
516 DiagnosticsEngine::Warning, "parentheses immediately inside vardecl statement",
517 compat::getBeginLoc(parenExpr))
518 << parenExpr->getSourceRange();
519 handled_.insert(parenExpr);
520 return true;
523 bool UnnecessaryParen::VisitMemberExpr(const MemberExpr* memberExpr)
525 if (ignoreLocation(memberExpr))
526 return true;
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;
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;
549 report(
550 DiagnosticsEngine::Warning, "unnecessary parentheses around member expr",
551 compat::getBeginLoc(parenExpr))
552 << parenExpr->getSourceRange();
553 handled_.insert(parenExpr);
554 return true;
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):
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))
571 handled_.insert(e1);
573 } else if (auto const e1 = dyn_cast<UnaryOperator>(e)) {
574 if (e1->getOpcode() == UO_LNot) {
575 handleUnreachableCodeConditionParens(e1->getSubExpr());
577 } else if (auto const e1 = dyn_cast<BinaryOperator>(e)) {
578 if (e1->isLogicalOp() || e1->isComparisonOp()) {
579 handleUnreachableCodeConditionParens(e1->getLHS());
580 handleUnreachableCodeConditionParens(e1->getRHS());
585 bool UnnecessaryParen::isPrecededBy_BAD_CAST(Expr const * expr) {
586 if (compat::getBeginLoc(expr).isMacroID()) {
587 return false;
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;
595 namespace {
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 == '"';
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);
616 bool UnnecessaryParen::removeParens(ParenExpr const * expr) {
617 if (rewriter == nullptr) {
618 return false;
620 auto const firstBegin = compat::getBeginLoc(expr);
621 auto secondBegin = compat::getEndLoc(expr);
622 if (firstBegin.isMacroID() || secondBegin.isMacroID()) {
623 return false;
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))
630 unsigned n = Lexer::MeasureTokenLength(
631 l, compiler.getSourceManager(), compiler.getLangOpts());
632 if (n != 0) {
633 break;
635 ++firstLen;
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;
646 } else if (!(c == ' ' || c == '\t' || c == '\v' || c == '\f')) {
647 break;
649 secondBegin = l;
650 ++secondLen;
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();
662 return false;
664 if (!replaceText(secondBegin, secondLen, badCombination(secondBegin, -1, secondLen) ? " " : ""))
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();
676 return false;
678 return true;
681 loplugin::Plugin::Registration< UnnecessaryParen > unnecessaryparen("unnecessaryparen", true);
685 #endif // LO_CLANG_SHARED_PLUGINS
687 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */