Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / compilerplugins / clang / unnecessaryparen.cxx
blobcaeadcac0072d6b0c5bf9cc8bbf06ab259d2d459
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 #include <cassert>
11 #include <string>
12 #include <iostream>
13 #include <fstream>
14 #include <set>
15 #include <unordered_set>
17 #include <clang/AST/CXXInheritance.h>
18 #include "compat.hxx"
19 #include "plugin.hxx"
21 /**
22 look for unnecessary parentheses
25 namespace {
27 // Like clang::Stmt::IgnoreImplicit (lib/AST/Stmt.cpp), but also ignoring CXXConstructExpr and
28 // looking through implicit UserDefinedConversion's member function call:
29 Expr const * ignoreAllImplicit(Expr const * expr) {
30 while (true)
32 auto oldExpr = expr;
33 if (auto const e = dyn_cast<ExprWithCleanups>(expr)) {
34 expr = e->getSubExpr();
36 else if (auto const e = dyn_cast<CXXConstructExpr>(expr)) {
37 if (e->getNumArgs() == 1) {
38 expr = e->getArg(0);
41 else if (auto const e = dyn_cast<MaterializeTemporaryExpr>(expr)) {
42 expr = e->GetTemporaryExpr();
44 else if (auto const e = dyn_cast<CXXBindTemporaryExpr>(expr)) {
45 expr = e->getSubExpr();
47 else if (auto const e = dyn_cast<ImplicitCastExpr>(expr)) {
48 expr = e->getSubExpr();
49 if (e->getCastKind() == CK_UserDefinedConversion) {
50 auto const ce = cast<CXXMemberCallExpr>(expr);
51 assert(ce->getNumArgs() == 0);
52 expr = ce->getImplicitObjectArgument();
55 if (expr == oldExpr)
56 return expr;
58 return expr;
61 class UnnecessaryParen:
62 public RecursiveASTVisitor<UnnecessaryParen>, public loplugin::RewritePlugin
64 public:
65 explicit UnnecessaryParen(loplugin::InstantiationData const & data):
66 RewritePlugin(data) {}
68 virtual void run() override
70 StringRef fn( compiler.getSourceManager().getFileEntryForID(
71 compiler.getSourceManager().getMainFileID())->getName() );
72 // fixing this, makes the source in the .y files look horrible
73 if (loplugin::isSamePathname(fn, WORKDIR "/YaccTarget/unoidl/source/sourceprovider-parser.cxx"))
74 return;
75 if (loplugin::isSamePathname(fn, WORKDIR "/YaccTarget/idlc/source/parser.cxx"))
76 return;
78 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
81 bool VisitParenExpr(const ParenExpr *);
82 bool VisitIfStmt(const IfStmt *);
83 bool VisitDoStmt(const DoStmt *);
84 bool VisitWhileStmt(const WhileStmt *);
85 bool VisitForStmt(ForStmt const * stmt);
86 bool VisitSwitchStmt(const SwitchStmt *);
87 bool VisitCaseStmt(const CaseStmt *);
88 bool VisitReturnStmt(const ReturnStmt* );
89 bool VisitCallExpr(const CallExpr *);
90 bool VisitVarDecl(const VarDecl *);
91 bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *);
92 bool VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr const *);
93 bool VisitConditionalOperator(ConditionalOperator const * expr);
94 bool VisitBinaryConditionalOperator(BinaryConditionalOperator const * expr);
95 bool VisitMemberExpr(const MemberExpr *f);
96 bool VisitCXXDeleteExpr(const CXXDeleteExpr *);
97 private:
98 void VisitSomeStmt(Stmt const * stmt, const Expr* cond, StringRef stmtName);
100 void handleUnreachableCodeConditionParens(Expr const * expr);
102 // Hack for libxml2's BAD_CAST object-like macro (expanding to "(xmlChar *)"), which is
103 // typically used as if it were a function-like macro, e.g., as "BAD_CAST(pName)" in
104 // SwNode::dumpAsXml (sw/source/core/docnode/node.cxx):
105 bool isPrecededBy_BAD_CAST(Expr const * expr);
107 bool badCombination(SourceLocation loc, int prevOffset, int nextOffset);
109 bool removeParens(ParenExpr const * expr);
111 std::unordered_set<ParenExpr const *> handled_;
114 bool UnnecessaryParen::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr const * expr)
116 if (expr->getKind() == UETT_SizeOf && !expr->isArgumentType()) {
117 if (auto const e = dyn_cast<ParenExpr>(ignoreAllImplicit(expr->getArgumentExpr()))) {
118 handled_.insert(e);
121 return true;
124 bool UnnecessaryParen::VisitConditionalOperator(ConditionalOperator const * expr) {
125 handleUnreachableCodeConditionParens(expr->getCond());
126 return true;
129 bool UnnecessaryParen::VisitBinaryConditionalOperator(BinaryConditionalOperator const * expr) {
130 handleUnreachableCodeConditionParens(expr->getCond());
131 return true;
134 bool UnnecessaryParen::VisitParenExpr(const ParenExpr* parenExpr)
136 if (ignoreLocation(parenExpr))
137 return true;
138 if (parenExpr->getLocStart().isMacroID())
139 return true;
140 if (handled_.find(parenExpr) != handled_.end())
141 return true;
143 auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
145 if (auto subParenExpr = dyn_cast<ParenExpr>(subExpr))
147 if (subParenExpr->getLocStart().isMacroID())
148 return true;
149 report(
150 DiagnosticsEngine::Warning, "parentheses around parentheses",
151 parenExpr->getLocStart())
152 << parenExpr->getSourceRange();
153 handled_.insert(subParenExpr);
156 // Somewhat redundantly add parenExpr to handled_, so that issues within InitListExpr don't get
157 // reported twice (without having to change TraverseInitListExpr to only either traverse the
158 // syntactic or semantic form, as other plugins do):
160 if (isa<DeclRefExpr>(subExpr)) {
161 if (!isPrecededBy_BAD_CAST(parenExpr)) {
162 report(
163 DiagnosticsEngine::Warning, "unnecessary parentheses around identifier",
164 parenExpr->getLocStart())
165 << parenExpr->getSourceRange();
166 handled_.insert(parenExpr);
168 } else if (isa<IntegerLiteral>(subExpr) || isa<CharacterLiteral>(subExpr)
169 || isa<FloatingLiteral>(subExpr) || isa<ImaginaryLiteral>(subExpr)
170 || isa<CXXBoolLiteralExpr>(subExpr) || isa<CXXNullPtrLiteralExpr>(subExpr)
171 || isa<ObjCBoolLiteralExpr>(subExpr))
173 auto const loc = subExpr->getLocStart();
174 if (loc.isMacroID() && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(loc))
176 // just in case the macro could also expand to something that /would/ require
177 // parentheses here
178 return true;
180 report(
181 DiagnosticsEngine::Warning, "unnecessary parentheses around literal",
182 parenExpr->getLocStart())
183 << parenExpr->getSourceRange();
184 handled_.insert(parenExpr);
185 } else if (auto const e = dyn_cast<clang::StringLiteral>(subExpr)) {
186 if (e->getNumConcatenated() == 1 && !isPrecededBy_BAD_CAST(parenExpr)) {
187 report(
188 DiagnosticsEngine::Warning,
189 "unnecessary parentheses around single-token string literal",
190 parenExpr->getLocStart())
191 << parenExpr->getSourceRange();
192 handled_.insert(parenExpr);
194 } else if (auto const e = dyn_cast<UnaryOperator>(subExpr)) {
195 auto const op = e->getOpcode();
196 if (op == UO_Plus || op == UO_Minus) {
197 auto const e2 = e->getSubExpr();
198 if (isa<IntegerLiteral>(e2) || isa<FloatingLiteral>(e2) || isa<ImaginaryLiteral>(e2)) {
199 report(
200 DiagnosticsEngine::Warning,
201 "unnecessary parentheses around signed numeric literal",
202 parenExpr->getLocStart())
203 << parenExpr->getSourceRange();
204 handled_.insert(parenExpr);
207 } else if (isa<CXXNamedCastExpr>(subExpr)) {
208 if (!removeParens(parenExpr)) {
209 report(
210 DiagnosticsEngine::Warning, "unnecessary parentheses around cast",
211 parenExpr->getLocStart())
212 << parenExpr->getSourceRange();
214 handled_.insert(parenExpr);
217 return true;
220 bool UnnecessaryParen::VisitIfStmt(const IfStmt* ifStmt)
222 handleUnreachableCodeConditionParens(ifStmt->getCond());
223 VisitSomeStmt(ifStmt, ifStmt->getCond(), "if");
224 return true;
227 bool UnnecessaryParen::VisitDoStmt(const DoStmt* doStmt)
229 VisitSomeStmt(doStmt, doStmt->getCond(), "do");
230 return true;
233 bool UnnecessaryParen::VisitWhileStmt(const WhileStmt* whileStmt)
235 handleUnreachableCodeConditionParens(whileStmt->getCond());
236 VisitSomeStmt(whileStmt, whileStmt->getCond(), "while");
237 return true;
240 bool UnnecessaryParen::VisitForStmt(ForStmt const * stmt) {
241 if (auto const cond = stmt->getCond()) {
242 handleUnreachableCodeConditionParens(cond);
244 return true;
247 bool UnnecessaryParen::VisitSwitchStmt(const SwitchStmt* switchStmt)
249 VisitSomeStmt(switchStmt, switchStmt->getCond(), "switch");
250 return true;
253 bool UnnecessaryParen::VisitCaseStmt(const CaseStmt* caseStmt)
255 VisitSomeStmt(caseStmt, caseStmt->getLHS(), "case");
256 return true;
259 bool UnnecessaryParen::VisitReturnStmt(const ReturnStmt* returnStmt)
261 if (ignoreLocation(returnStmt))
262 return true;
264 if (!returnStmt->getRetValue())
265 return true;
266 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(returnStmt->getRetValue()));
267 if (!parenExpr)
268 return true;
269 if (parenExpr->getLocStart().isMacroID())
270 return true;
271 // assignments need extra parentheses or they generate a compiler warning
272 auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
273 if (binaryOp && binaryOp->getOpcode() == BO_Assign)
274 return true;
276 // only non-operator-calls for now
277 auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
278 if (isa<CallExpr>(subExpr) && !isa<CXXOperatorCallExpr>(subExpr))
280 report(
281 DiagnosticsEngine::Warning, "parentheses immediately inside return statement",
282 parenExpr->getLocStart())
283 << parenExpr->getSourceRange();
284 handled_.insert(parenExpr);
286 return true;
289 void UnnecessaryParen::VisitSomeStmt(const Stmt * stmt, const Expr* cond, StringRef stmtName)
291 if (ignoreLocation(stmt))
292 return;
294 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(cond));
295 if (parenExpr) {
296 if (handled_.find(parenExpr) != handled_.end()) {
297 return;
299 if (parenExpr->getLocStart().isMacroID())
300 return;
301 // assignments need extra parentheses or they generate a compiler warning
302 auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
303 if (binaryOp && binaryOp->getOpcode() == BO_Assign)
304 return;
305 if (auto const opCall = dyn_cast<CXXOperatorCallExpr>(parenExpr->getSubExpr())) {
306 if (opCall->getOperator() == OO_Equal) {
307 return;
310 report(
311 DiagnosticsEngine::Warning, "parentheses immediately inside %0 statement",
312 parenExpr->getLocStart())
313 << stmtName
314 << parenExpr->getSourceRange();
315 handled_.insert(parenExpr);
319 bool UnnecessaryParen::VisitCallExpr(const CallExpr* callExpr)
321 if (ignoreLocation(callExpr))
322 return true;
323 if (callExpr->getNumArgs() != 1 || isa<CXXOperatorCallExpr>(callExpr))
324 return true;
326 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(callExpr->getArg(0)));
327 if (!parenExpr)
328 return true;
329 if (parenExpr->getLocStart().isMacroID())
330 return true;
331 // assignments need extra parentheses or they generate a compiler warning
332 auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
333 if (binaryOp && binaryOp->getOpcode() == BO_Assign)
334 return true;
335 report(
336 DiagnosticsEngine::Warning, "parentheses immediately inside single-arg call",
337 parenExpr->getLocStart())
338 << parenExpr->getSourceRange();
339 handled_.insert(parenExpr);
340 return true;
343 bool UnnecessaryParen::VisitCXXDeleteExpr(const CXXDeleteExpr* deleteExpr)
345 if (ignoreLocation(deleteExpr))
346 return true;
348 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(deleteExpr->getArgument()));
349 if (!parenExpr)
350 return true;
351 if (parenExpr->getLocStart().isMacroID())
352 return true;
353 // assignments need extra parentheses or they generate a compiler warning
354 auto binaryOp = dyn_cast<BinaryOperator>(parenExpr->getSubExpr());
355 if (binaryOp && binaryOp->getOpcode() == BO_Assign)
356 return true;
357 report(
358 DiagnosticsEngine::Warning, "parentheses immediately inside delete expr",
359 parenExpr->getLocStart())
360 << parenExpr->getSourceRange();
361 handled_.insert(parenExpr);
362 return true;
365 bool UnnecessaryParen::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr* callExpr)
367 if (ignoreLocation(callExpr))
368 return true;
369 if (callExpr->getNumArgs() != 2)
370 return true;
372 // Same logic as CXXOperatorCallExpr::isAssignmentOp(), which our supported clang
373 // doesn't have yet.
374 auto Opc = callExpr->getOperator();
375 if (Opc != OO_Equal && Opc != OO_StarEqual &&
376 Opc != OO_SlashEqual && Opc != OO_PercentEqual &&
377 Opc != OO_PlusEqual && Opc != OO_MinusEqual &&
378 Opc != OO_LessLessEqual && Opc != OO_GreaterGreaterEqual &&
379 Opc != OO_AmpEqual && Opc != OO_CaretEqual &&
380 Opc != OO_PipeEqual)
381 return true;
383 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(callExpr->getArg(1)));
384 if (!parenExpr)
385 return true;
386 if (parenExpr->getLocStart().isMacroID())
387 return true;
388 // Sometimes parentheses make the RHS of an assignment easier to read by
389 // visually disambiguating the = from a call to ==
390 auto sub = parenExpr->getSubExpr();
391 if (isa<BinaryOperator>(sub)
392 || isa<CXXOperatorCallExpr>(sub)
393 || isa<ConditionalOperator>(sub))
394 return true;
396 report(
397 DiagnosticsEngine::Warning, "parentheses immediately inside assignment",
398 parenExpr->getLocStart())
399 << parenExpr->getSourceRange();
400 handled_.insert(parenExpr);
401 return true;
404 bool UnnecessaryParen::VisitVarDecl(const VarDecl* varDecl)
406 if (ignoreLocation(varDecl))
407 return true;
408 if (!varDecl->getInit())
409 return true;
411 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(varDecl->getInit()));
412 if (!parenExpr)
413 return true;
414 if (parenExpr->getLocStart().isMacroID())
415 return true;
416 auto sub = parenExpr->getSubExpr();
417 if (isa<BinaryOperator>(sub)
418 || isa<CXXOperatorCallExpr>(sub)
419 || isa<ConditionalOperator>(sub)
420 // these two are for "parentheses were disambiguated as a function declaration [-Werror,-Wvexing-parse]"
421 || isa<CXXBindTemporaryExpr>(sub)
422 || isa<CXXFunctionalCastExpr>(sub))
423 return true;
425 report(
426 DiagnosticsEngine::Warning, "parentheses immediately inside vardecl statement",
427 parenExpr->getLocStart())
428 << parenExpr->getSourceRange();
429 handled_.insert(parenExpr);
430 return true;
433 bool UnnecessaryParen::VisitMemberExpr(const MemberExpr* memberExpr)
435 if (ignoreLocation(memberExpr))
436 return true;
438 auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(memberExpr->getBase()));
439 if (!parenExpr)
440 return true;
441 if (handled_.find(parenExpr) != handled_.end())
442 return true;
443 if (parenExpr->getLocStart().isMacroID())
444 return true;
446 auto sub = parenExpr->getSubExpr();
447 if (isa<CallExpr>(sub)) {
448 if (isa<CXXOperatorCallExpr>(sub))
449 return true;
450 } else if (isa<CXXConstructExpr>(sub)) {
451 // warn
452 } else if (isa<MemberExpr>(sub)) {
453 // warn
454 } else if (isa<DeclRefExpr>(sub)) {
455 // warn
456 } else
457 return true;
459 report(
460 DiagnosticsEngine::Warning, "unnecessary parentheses around member expr",
461 parenExpr->getLocStart())
462 << parenExpr->getSourceRange();
463 handled_.insert(parenExpr);
464 return true;
467 // Conservatively assume any parenthesised integer or Boolean (incl. Objective-C ones) literal in
468 // certain condition expressions (i.e., those for which handleUnreachableCodeConditionParens is
469 // called) to be parenthesised to silence Clang -Wunreachable-code, if that is either the whole
470 // condition expression or appears as a certain sub-expression (looking at what isConfigurationValue
471 // in Clang's lib/Analysis/ReachableCode.cpp looks for, descending into certain unary and binary
472 // operators):
473 void UnnecessaryParen::handleUnreachableCodeConditionParens(Expr const * expr) {
474 // Cf. :
475 auto const e = ignoreAllImplicit(expr);
476 if (auto const e1 = dyn_cast<ParenExpr>(e)) {
477 auto const sub = e1->getSubExpr();
478 if (isa<IntegerLiteral>(sub) || isa<CXXBoolLiteralExpr>(sub)
479 || isa<ObjCBoolLiteralExpr>(sub))
481 handled_.insert(e1);
483 } else if (auto const e1 = dyn_cast<UnaryOperator>(e)) {
484 if (e1->getOpcode() == UO_LNot) {
485 handleUnreachableCodeConditionParens(e1->getSubExpr());
487 } else if (auto const e1 = dyn_cast<BinaryOperator>(e)) {
488 if (e1->isLogicalOp() || e1->isComparisonOp()) {
489 handleUnreachableCodeConditionParens(e1->getLHS());
490 handleUnreachableCodeConditionParens(e1->getRHS());
495 bool UnnecessaryParen::isPrecededBy_BAD_CAST(Expr const * expr) {
496 if (expr->getLocStart().isMacroID()) {
497 return false;
499 SourceManager& SM = compiler.getSourceManager();
500 const char *p1 = SM.getCharacterData( expr->getLocStart().getLocWithOffset(-10) );
501 const char *p2 = SM.getCharacterData( expr->getLocStart() );
502 return std::string(p1, p2 - p1).find("BAD_CAST") != std::string::npos;
505 namespace {
507 bool badCombinationChar(char c) {
508 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'
509 || c == '+' || c == '-' || c == '\'' || c == '"';
514 bool UnnecessaryParen::badCombination(SourceLocation loc, int prevOffset, int nextOffset) {
515 //TODO: check for start/end of file; take backslash-newline line concatenation into account
516 auto const c1
517 = compiler.getSourceManager().getCharacterData(loc.getLocWithOffset(prevOffset))[0];
518 auto const c2
519 = compiler.getSourceManager().getCharacterData(loc.getLocWithOffset(nextOffset))[0];
520 // An approximation of avoiding whatever combinations that would cause two adjacent tokens to be
521 // lexed differently, using, for now, letters (TODO: non-ASCII ones) and digits and '_'; '+' and
522 // '-' (to avoid ++, etc.); '\'' and '"' (to avoid u'x' or "foo"bar, etc.):
523 return badCombinationChar(c1) && badCombinationChar(c2);
526 bool UnnecessaryParen::removeParens(ParenExpr const * expr) {
527 if (rewriter == nullptr) {
528 return false;
530 auto const firstBegin = expr->getLocStart();
531 auto secondBegin = expr->getLocEnd();
532 if (firstBegin.isMacroID() || secondBegin.isMacroID()) {
533 return false;
535 unsigned firstLen = Lexer::MeasureTokenLength(
536 firstBegin, compiler.getSourceManager(), compiler.getLangOpts());
537 for (auto l = firstBegin.getLocWithOffset(std::max<unsigned>(firstLen, 1));;
538 l = l.getLocWithOffset(1))
540 unsigned n = Lexer::MeasureTokenLength(
541 l, compiler.getSourceManager(), compiler.getLangOpts());
542 if (n != 0) {
543 break;
545 ++firstLen;
547 unsigned secondLen = Lexer::MeasureTokenLength(
548 secondBegin, compiler.getSourceManager(), compiler.getLangOpts());
549 for (;;) {
550 auto l = secondBegin.getLocWithOffset(-1);
551 auto const c = compiler.getSourceManager().getCharacterData(l)[0];
552 if (c == '\n') {
553 if (compiler.getSourceManager().getCharacterData(l.getLocWithOffset(-1))[0] == '\\') {
554 break;
556 } else if (!(c == ' ' || c == '\t' || c == '\v' || c == '\f')) {
557 break;
559 secondBegin = l;
560 ++secondLen;
562 if (!replaceText(firstBegin, firstLen, badCombination(firstBegin, -1, firstLen) ? " " : "")) {
563 if (isDebugMode()) {
564 report(
565 DiagnosticsEngine::Fatal,
566 "TODO: cannot rewrite opening parenthesis, needs investigation",
567 firstBegin);
568 report(
569 DiagnosticsEngine::Note, "when removing these parentheses", expr->getExprLoc())
570 << expr->getSourceRange();
572 return false;
574 if (!replaceText(secondBegin, secondLen, badCombination(secondBegin, -1, secondLen) ? " " : ""))
576 //TODO: roll back first change
577 if (isDebugMode()) {
578 report(
579 DiagnosticsEngine::Fatal,
580 "TODO: cannot rewrite closing parenthesis, needs investigation",
581 secondBegin);
582 report(
583 DiagnosticsEngine::Note, "when removing these parentheses", expr->getExprLoc())
584 << expr->getSourceRange();
586 return false;
588 return true;
591 loplugin::Plugin::Registration< UnnecessaryParen > X("unnecessaryparen", true);
595 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */