summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/unnecessaryparen.cxx
diff options
context:
space:
mode:
authorStephan Bergmann <sbergman@redhat.com>2017-11-27 09:46:06 +0100
committerStephan Bergmann <sbergman@redhat.com>2017-11-27 14:04:40 +0100
commita2656563f1a7639a99276fda90b6c8f675d20fc5 (patch)
treecc9eb4f064b1e8cdfce9ac42c52dfa6ae54f2b70 /compilerplugins/clang/unnecessaryparen.cxx
parent0093ae5f2ca1141f199aa9ae3b34b05a33def928 (diff)
loplugin:unnecessaryparen: Warn about parentheses around literals
...that are not composed of multiple tokens, like ("foo" "bar"). Also don't yet warn about Boolean literals, which are sometimes wrapped in parentheses to silence unreachable-code warnings. To avoid multiple warnings about code like f((0)) switch to generally using a set of ParenExpr to keep track of which occurrences have already been handled. Change-Id: I036a25a92836ec6ab6c56ea848f71bc6d63822bc Reviewed-on: https://gerrit.libreoffice.org/45317 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
Diffstat (limited to 'compilerplugins/clang/unnecessaryparen.cxx')
-rw-r--r--compilerplugins/clang/unnecessaryparen.cxx115
1 files changed, 79 insertions, 36 deletions
diff --git a/compilerplugins/clang/unnecessaryparen.cxx b/compilerplugins/clang/unnecessaryparen.cxx
index dcf65f0c0234..046176a64150 100644
--- a/compilerplugins/clang/unnecessaryparen.cxx
+++ b/compilerplugins/clang/unnecessaryparen.cxx
@@ -12,6 +12,7 @@
#include <iostream>
#include <fstream>
#include <set>
+#include <unordered_set>
#include <clang/AST/CXXInheritance.h>
#include "compat.hxx"
@@ -81,33 +82,37 @@ public:
bool VisitCallExpr(const CallExpr *);
bool VisitVarDecl(const VarDecl *);
bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *);
- bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *);
- bool TraverseCaseStmt(CaseStmt *);
+ bool VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr const *);
+ bool VisitConditionalOperator(ConditionalOperator const * expr);
bool VisitMemberExpr(const MemberExpr *f);
private:
void VisitSomeStmt(Stmt const * stmt, const Expr* cond, StringRef stmtName);
- Expr const * insideSizeof = nullptr;
- Expr const * insideCaseStmt = nullptr;
+
+ // Hack for libxml2's BAD_CAST object-like macro (expanding to "(xmlChar *)"), which is
+ // typically used as if it were a function-like macro, e.g., as "BAD_CAST(pName)" in
+ // SwNode::dumpAsXml (sw/source/core/docnode/node.cxx):
+ bool isPrecededBy_BAD_CAST(Expr const * expr);
+
+ std::unordered_set<ParenExpr const *> handled_;
};
-bool UnnecessaryParen::TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr * expr)
+bool UnnecessaryParen::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr const * expr)
{
- auto old = insideSizeof;
if (expr->getKind() == UETT_SizeOf && !expr->isArgumentType()) {
- insideSizeof = ignoreAllImplicit(expr->getArgumentExpr());
+ if (auto const e = dyn_cast<ParenExpr>(ignoreAllImplicit(expr->getArgumentExpr()))) {
+ handled_.insert(e);
+ }
}
- bool ret = RecursiveASTVisitor::TraverseUnaryExprOrTypeTraitExpr(expr);
- insideSizeof = old;
- return ret;
+ return true;
}
-bool UnnecessaryParen::TraverseCaseStmt(CaseStmt * caseStmt)
-{
- auto old = insideCaseStmt;
- insideCaseStmt = ignoreAllImplicit(caseStmt->getLHS());
- bool ret = RecursiveASTVisitor::TraverseCaseStmt(caseStmt);
- insideCaseStmt = old;
- return ret;
+bool UnnecessaryParen::VisitConditionalOperator(ConditionalOperator const * expr) {
+ if (auto const e = dyn_cast<ParenExpr>(ignoreAllImplicit(expr->getCond()))) {
+ if (isa<CXXBoolLiteralExpr>(e->getSubExpr())) {
+ handled_.insert(e);
+ }
+ }
+ return true;
}
bool UnnecessaryParen::VisitParenExpr(const ParenExpr* parenExpr)
@@ -116,9 +121,7 @@ bool UnnecessaryParen::VisitParenExpr(const ParenExpr* parenExpr)
return true;
if (parenExpr->getLocStart().isMacroID())
return true;
- if (insideSizeof && parenExpr == insideSizeof)
- return true;
- if (insideCaseStmt && parenExpr == insideCaseStmt)
+ if (handled_.find(parenExpr) != handled_.end())
return true;
auto subExpr = ignoreAllImplicit(parenExpr->getSubExpr());
@@ -131,32 +134,53 @@ bool UnnecessaryParen::VisitParenExpr(const ParenExpr* parenExpr)
DiagnosticsEngine::Warning, "parentheses around parentheses",
parenExpr->getLocStart())
<< parenExpr->getSourceRange();
+ handled_.insert(subParenExpr);
}
- if (auto declRefExpr = dyn_cast<DeclRefExpr>(subExpr)) {
- // hack for libxml2's BAD_CAST object-like macro (expanding to "(xmlChar *)"), which is
- // typically used as if it were a function-like macro, e.g., as "BAD_CAST(pName)" in
- // SwNode::dumpAsXml (sw/source/core/docnode/node.cxx)
- if (!declRefExpr->getLocStart().isMacroID()) {
- SourceManager& SM = compiler.getSourceManager();
- const char *p1 = SM.getCharacterData( declRefExpr->getLocStart().getLocWithOffset(-10) );
- const char *p2 = SM.getCharacterData( declRefExpr->getLocStart() );
- if ( std::string(p1, p2 - p1).find("BAD_CAST") != std::string::npos )
- return true;
+ // Somewhat redundantly add parenExpr to handled_, so that issues within InitListExpr don't get
+ // reported twice (without having to change TraverseInitListExpr to only either traverse the
+ // syntactic or semantic form, as other plugins do):
+
+ if (isa<DeclRefExpr>(subExpr)) {
+ if (!isPrecededBy_BAD_CAST(parenExpr)) {
+ report(
+ DiagnosticsEngine::Warning, "unnecessary parentheses around identifier",
+ parenExpr->getLocStart())
+ << parenExpr->getSourceRange();
+ handled_.insert(parenExpr);
+ }
+ } else if (isa<IntegerLiteral>(subExpr) || isa<CharacterLiteral>(subExpr)
+ || isa<FloatingLiteral>(subExpr) || isa<ImaginaryLiteral>(subExpr)
+ || isa<CXXNullPtrLiteralExpr>(subExpr))
+ //TODO: isa<CXXBoolLiteralExpr>(subExpr) || isa<ObjCBoolLiteralExpr>(subExpr)
+ {
+ auto const loc = subExpr->getLocStart();
+ if (loc.isMacroID() && compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(loc))
+ {
+ // just in case the macro could also expand to something that /would/ require
+ // parentheses here
+ return true;
}
-
report(
- DiagnosticsEngine::Warning, "unnecessary parentheses around identifier",
+ DiagnosticsEngine::Warning, "unnecessary parentheses around literal",
parenExpr->getLocStart())
<< parenExpr->getSourceRange();
- }
-
-
- if (isa<CXXNamedCastExpr>(subExpr)) {
+ handled_.insert(parenExpr);
+ } else if (auto const e = dyn_cast<clang::StringLiteral>(subExpr)) {
+ if (e->getNumConcatenated() == 1 && !isPrecededBy_BAD_CAST(parenExpr)) {
+ report(
+ DiagnosticsEngine::Warning,
+ "unnecessary parentheses around single-token string literal",
+ parenExpr->getLocStart())
+ << parenExpr->getSourceRange();
+ handled_.insert(parenExpr);
+ }
+ } else if (isa<CXXNamedCastExpr>(subExpr)) {
report(
DiagnosticsEngine::Warning, "unnecessary parentheses around cast",
parenExpr->getLocStart())
<< parenExpr->getSourceRange();
+ handled_.insert(parenExpr);
}
return true;
@@ -217,6 +241,7 @@ bool UnnecessaryParen::VisitReturnStmt(const ReturnStmt* returnStmt)
DiagnosticsEngine::Warning, "parentheses immediately inside return statement",
parenExpr->getLocStart())
<< parenExpr->getSourceRange();
+ handled_.insert(parenExpr);
}
return true;
}
@@ -234,6 +259,7 @@ void UnnecessaryParen::VisitSomeStmt(const Stmt * stmt, const Expr* cond, String
if (isa<CXXBoolLiteralExpr>(parenExpr->getSubExpr())
&& stmtName == "if")
{
+ handled_.insert(parenExpr);
return;
}
// assignments need extra parentheses or they generate a compiler warning
@@ -250,6 +276,7 @@ void UnnecessaryParen::VisitSomeStmt(const Stmt * stmt, const Expr* cond, String
parenExpr->getLocStart())
<< stmtName
<< parenExpr->getSourceRange();
+ handled_.insert(parenExpr);
}
}
@@ -273,6 +300,7 @@ bool UnnecessaryParen::VisitCallExpr(const CallExpr* callExpr)
DiagnosticsEngine::Warning, "parentheses immediately inside single-arg call",
parenExpr->getLocStart())
<< parenExpr->getSourceRange();
+ handled_.insert(parenExpr);
return true;
}
@@ -311,6 +339,7 @@ bool UnnecessaryParen::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr* callE
DiagnosticsEngine::Warning, "parentheses immediately inside assignment",
parenExpr->getLocStart())
<< parenExpr->getSourceRange();
+ handled_.insert(parenExpr);
return true;
}
@@ -339,6 +368,7 @@ bool UnnecessaryParen::VisitVarDecl(const VarDecl* varDecl)
DiagnosticsEngine::Warning, "parentheses immediately inside vardecl statement",
parenExpr->getLocStart())
<< parenExpr->getSourceRange();
+ handled_.insert(parenExpr);
return true;
}
@@ -350,6 +380,8 @@ bool UnnecessaryParen::VisitMemberExpr(const MemberExpr* memberExpr)
auto parenExpr = dyn_cast<ParenExpr>(ignoreAllImplicit(memberExpr->getBase()));
if (!parenExpr)
return true;
+ if (handled_.find(parenExpr) != handled_.end())
+ return true;
if (parenExpr->getLocStart().isMacroID())
return true;
@@ -370,9 +402,20 @@ bool UnnecessaryParen::VisitMemberExpr(const MemberExpr* memberExpr)
DiagnosticsEngine::Warning, "unnecessary parentheses around member expr",
parenExpr->getLocStart())
<< parenExpr->getSourceRange();
+ handled_.insert(parenExpr);
return true;
}
+bool UnnecessaryParen::isPrecededBy_BAD_CAST(Expr const * expr) {
+ if (expr->getLocStart().isMacroID()) {
+ return false;
+ }
+ SourceManager& SM = compiler.getSourceManager();
+ const char *p1 = SM.getCharacterData( expr->getLocStart().getLocWithOffset(-10) );
+ const char *p2 = SM.getCharacterData( expr->getLocStart() );
+ return std::string(p1, p2 - p1).find("BAD_CAST") != std::string::npos;
+}
+
loplugin::Plugin::Registration< UnnecessaryParen > X("unnecessaryparen", true);
}