1 //===- RedundantStringCStrCheck.cpp - Check for redundant c_str calls -----===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file implements a check for redundant calls of c_str() on strings. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "RedundantStringCStrCheck.h" 15 #include "clang/Lex/Lexer.h" 16 17 namespace clang { 18 19 using namespace ast_matchers; 20 21 namespace { 22 23 template <typename T> 24 StringRef getText(const ast_matchers::MatchFinder::MatchResult &Result, 25 T const &Node) { 26 return Lexer::getSourceText( 27 CharSourceRange::getTokenRange(Node.getSourceRange()), 28 *Result.SourceManager, Result.Context->getLangOpts()); 29 } 30 31 // Return true if expr needs to be put in parens when it is an argument of a 32 // prefix unary operator, e.g. when it is a binary or ternary operator 33 // syntactically. 34 bool needParensAfterUnaryOperator(const Expr &ExprNode) { 35 if (isa<clang::BinaryOperator>(&ExprNode) || 36 isa<clang::ConditionalOperator>(&ExprNode)) { 37 return true; 38 } 39 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(&ExprNode)) { 40 return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus && 41 Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call && 42 Op->getOperator() != OO_Subscript; 43 } 44 return false; 45 } 46 47 // Format a pointer to an expression: prefix with '*' but simplify 48 // when it already begins with '&'. Return empty string on failure. 49 std::string 50 formatDereference(const ast_matchers::MatchFinder::MatchResult &Result, 51 const Expr &ExprNode) { 52 if (const auto *Op = dyn_cast<clang::UnaryOperator>(&ExprNode)) { 53 if (Op->getOpcode() == UO_AddrOf) { 54 // Strip leading '&'. 55 return getText(Result, *Op->getSubExpr()->IgnoreParens()); 56 } 57 } 58 StringRef Text = getText(Result, ExprNode); 59 if (Text.empty()) 60 return std::string(); 61 // Add leading '*'. 62 if (needParensAfterUnaryOperator(ExprNode)) { 63 return (llvm::Twine("*(") + Text + ")").str(); 64 } 65 return (llvm::Twine("*") + Text).str(); 66 } 67 68 } // end namespace 69 70 namespace tidy { 71 namespace readability { 72 73 void RedundantStringCStrCheck::registerMatchers( 74 ast_matchers::MatchFinder *Finder) { 75 // Only register the matchers for C++; the functionality currently does not 76 // provide any benefit to other languages, despite being benign. 77 if (!getLangOpts().CPlusPlus) 78 return; 79 80 // Match expressions of type 'string' or 'string*'. 81 const auto StringDecl = 82 cxxRecordDecl(hasName("::std::basic_string")); 83 const auto StringExpr = 84 expr(anyOf(hasType(StringDecl), 85 hasType(qualType(pointsTo(StringDecl))))); 86 87 // Match string constructor. 88 const auto StringConstructorExpr = expr(anyOf( 89 cxxConstructExpr( 90 argumentCountIs(1), 91 hasDeclaration(cxxMethodDecl(hasName("basic_string")))), 92 cxxConstructExpr( 93 argumentCountIs(2), 94 hasDeclaration(cxxMethodDecl(hasName("basic_string"))), 95 // If present, the second argument is the alloc object which must not 96 // be present explicitly. 97 hasArgument(1, cxxDefaultArgExpr())))); 98 99 // Match a call to the string 'c_str()' method. 100 const auto StringCStrCallExpr = 101 cxxMemberCallExpr(on(StringExpr.bind("arg")), 102 callee(memberExpr().bind("member")), 103 callee(cxxMethodDecl(hasName("c_str")))) 104 .bind("call"); 105 106 // Detect redundant 'c_str()' calls through a string constructor. 107 Finder->addMatcher( 108 cxxConstructExpr(StringConstructorExpr, 109 hasArgument(0, StringCStrCallExpr)), 110 this); 111 112 // Detect: 's == str.c_str()' -> 's == str' 113 Finder->addMatcher( 114 cxxOperatorCallExpr( 115 anyOf(hasOverloadedOperatorName("<"), 116 hasOverloadedOperatorName(">"), 117 hasOverloadedOperatorName(">="), 118 hasOverloadedOperatorName("<="), 119 hasOverloadedOperatorName("!="), 120 hasOverloadedOperatorName("=="), 121 hasOverloadedOperatorName("+")), 122 anyOf(allOf(hasArgument(0, StringExpr), 123 hasArgument(1, StringCStrCallExpr)), 124 allOf(hasArgument(0, StringCStrCallExpr), 125 hasArgument(1, StringExpr)))), 126 this); 127 128 // Detect: 'dst += str.c_str()' -> 'dst += str' 129 // Detect: 's = str.c_str()' -> 's = str' 130 Finder->addMatcher( 131 cxxOperatorCallExpr( 132 anyOf(hasOverloadedOperatorName("="), 133 hasOverloadedOperatorName("+=")), 134 hasArgument(0, StringExpr), 135 hasArgument(1, StringCStrCallExpr)), 136 this); 137 138 // Detect: 'dst.append(str.c_str())' -> 'dst.append(str)' 139 Finder->addMatcher( 140 cxxMemberCallExpr(on(StringExpr), 141 callee(decl(cxxMethodDecl( 142 hasAnyName("append", "assign", "compare")))), 143 argumentCountIs(1), 144 hasArgument(0, StringCStrCallExpr)), 145 this); 146 147 // Detect: 'dst.compare(p, n, str.c_str())' -> 'dst.compare(p, n, str)' 148 Finder->addMatcher( 149 cxxMemberCallExpr(on(StringExpr), 150 callee(decl(cxxMethodDecl(hasName("compare")))), 151 argumentCountIs(3), 152 hasArgument(2, StringCStrCallExpr)), 153 this); 154 155 // Detect: 'dst.find(str.c_str())' -> 'dst.find(str)' 156 Finder->addMatcher( 157 cxxMemberCallExpr(on(StringExpr), 158 callee(decl(cxxMethodDecl( 159 hasAnyName("find", "find_first_not_of", "find_first_of", 160 "find_last_not_of", "find_last_of", "rfind")))), 161 anyOf(argumentCountIs(1), argumentCountIs(2)), 162 hasArgument(0, StringCStrCallExpr)), 163 this); 164 165 // Detect: 'dst.insert(pos, str.c_str())' -> 'dst.insert(pos, str)' 166 Finder->addMatcher( 167 cxxMemberCallExpr(on(StringExpr), 168 callee(decl(cxxMethodDecl(hasName("insert")))), 169 argumentCountIs(2), 170 hasArgument(1, StringCStrCallExpr)), 171 this); 172 173 // Detect redundant 'c_str()' calls through a StringRef constructor. 174 Finder->addMatcher( 175 cxxConstructExpr( 176 // Implicit constructors of these classes are overloaded 177 // wrt. string types and they internally make a StringRef 178 // referring to the argument. Passing a string directly to 179 // them is preferred to passing a char pointer. 180 hasDeclaration( 181 cxxMethodDecl(hasAnyName("::llvm::StringRef::StringRef", 182 "::llvm::Twine::Twine"))), 183 argumentCountIs(1), 184 // The only argument must have the form x.c_str() or p->c_str() 185 // where the method is string::c_str(). StringRef also has 186 // a constructor from string which is more efficient (avoids 187 // strlen), so we can construct StringRef from the string 188 // directly. 189 hasArgument(0, StringCStrCallExpr)), 190 this); 191 } 192 193 void RedundantStringCStrCheck::check(const MatchFinder::MatchResult &Result) { 194 const auto *Call = Result.Nodes.getStmtAs<CallExpr>("call"); 195 const auto *Arg = Result.Nodes.getStmtAs<Expr>("arg"); 196 bool Arrow = Result.Nodes.getStmtAs<MemberExpr>("member")->isArrow(); 197 // Replace the "call" node with the "arg" node, prefixed with '*' 198 // if the call was using '->' rather than '.'. 199 std::string ArgText = 200 Arrow ? formatDereference(Result, *Arg) : getText(Result, *Arg).str(); 201 if (ArgText.empty()) 202 return; 203 204 diag(Call->getLocStart(), "redundant call to `c_str()`") 205 << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText); 206 } 207 208 } // namespace readability 209 } // namespace tidy 210 } // namespace clang 211