14 #include "clang/Lex/Lexer.h"
15 #include "clang/Tooling/FixIt.h"
21 namespace readability {
28 bool needParensAfterUnaryOperator(
const Expr &ExprNode) {
29 if (isa<clang::BinaryOperator>(&ExprNode) ||
30 isa<clang::ConditionalOperator>(&ExprNode)) {
33 if (
const auto *Op = dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
34 return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
35 Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
36 Op->getOperator() != OO_Subscript;
44 formatDereference(
const ast_matchers::MatchFinder::MatchResult &Result,
45 const Expr &ExprNode) {
46 if (
const auto *Op = dyn_cast<clang::UnaryOperator>(&ExprNode)) {
47 if (Op->getOpcode() == UO_AddrOf) {
49 return std::string(tooling::fixit::getText(
50 *Op->getSubExpr()->IgnoreParens(), *Result.Context));
53 StringRef
Text = tooling::fixit::getText(ExprNode, *Result.Context);
58 if (needParensAfterUnaryOperator(ExprNode)) {
59 return (llvm::Twine(
"*(") +
Text +
")").str();
61 return (llvm::Twine(
"*") +
Text).str();
64 AST_MATCHER(MaterializeTemporaryExpr, isBoundToLValue) {
65 return Node.isBoundToLvalueReference();
70 void RedundantStringCStrCheck::registerMatchers(
71 ast_matchers::MatchFinder *Finder) {
73 const auto StringDecl = type(hasUnqualifiedDesugaredType(recordType(
74 hasDeclaration(cxxRecordDecl(hasName(
"::std::basic_string"))))));
75 const auto StringExpr =
76 expr(anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));
79 const auto StringConstructorExpr = expr(anyOf(
80 cxxConstructExpr(argumentCountIs(1),
81 hasDeclaration(cxxMethodDecl(hasName(
"basic_string")))),
84 hasDeclaration(cxxMethodDecl(hasName(
"basic_string"))),
87 hasArgument(1, cxxDefaultArgExpr()))));
90 const auto StringCStrCallExpr =
91 cxxMemberCallExpr(on(StringExpr.bind(
"arg")),
92 callee(memberExpr().bind(
"member")),
93 callee(cxxMethodDecl(hasAnyName(
"c_str",
"data"))))
100 traverse(ast_type_traits::TK_AsIs,
101 cxxConstructExpr(StringConstructorExpr,
102 hasArgument(0, StringCStrCallExpr),
103 unless(hasParent(materializeTemporaryExpr(
104 unless(isBoundToLValue())))))),
110 hasAnyOverloadedOperatorName(
"<",
">",
">=",
"<=",
"!=",
"==",
"+"),
111 anyOf(allOf(hasArgument(0, StringExpr),
112 hasArgument(1, StringCStrCallExpr)),
113 allOf(hasArgument(0, StringCStrCallExpr),
114 hasArgument(1, StringExpr)))),
120 cxxOperatorCallExpr(hasAnyOverloadedOperatorName(
"=",
"+="),
121 hasArgument(0, StringExpr),
122 hasArgument(1, StringCStrCallExpr)),
127 cxxMemberCallExpr(on(StringExpr), callee(decl(cxxMethodDecl(hasAnyName(
128 "append",
"assign",
"compare")))),
129 argumentCountIs(1), hasArgument(0, StringCStrCallExpr)),
134 cxxMemberCallExpr(on(StringExpr),
135 callee(decl(cxxMethodDecl(hasName(
"compare")))),
136 argumentCountIs(3), hasArgument(2, StringCStrCallExpr)),
141 cxxMemberCallExpr(on(StringExpr),
142 callee(decl(cxxMethodDecl(hasAnyName(
143 "find",
"find_first_not_of",
"find_first_of",
144 "find_last_not_of",
"find_last_of",
"rfind")))),
145 anyOf(argumentCountIs(1), argumentCountIs(2)),
146 hasArgument(0, StringCStrCallExpr)),
151 cxxMemberCallExpr(on(StringExpr),
152 callee(decl(cxxMethodDecl(hasName(
"insert")))),
153 argumentCountIs(2), hasArgument(1, StringCStrCallExpr)),
159 ast_type_traits::TK_AsIs,
165 hasDeclaration(cxxMethodDecl(hasAnyName(
166 "::llvm::StringRef::StringRef",
"::llvm::Twine::Twine"))),
173 hasArgument(0, StringCStrCallExpr))),
177 void RedundantStringCStrCheck::check(
const MatchFinder::MatchResult &Result) {
178 const auto *Call = Result.Nodes.getNodeAs<CallExpr>(
"call");
179 const auto *Arg = Result.Nodes.getNodeAs<Expr>(
"arg");
180 const auto *Member = Result.Nodes.getNodeAs<MemberExpr>(
"member");
181 bool Arrow = Member->isArrow();
184 std::string ArgText =
185 Arrow ? formatDereference(Result, *Arg)
186 : tooling::fixit::getText(*Arg, *Result.Context).str();
190 diag(Call->getBeginLoc(),
"redundant call to %0")
191 << Member->getMemberDecl()
192 << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);