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 const char StringConstructor[] = 69 "::std::basic_string<char, std::char_traits<char>, std::allocator<char> >" 70 "::basic_string"; 71 72 const char StringCStrMethod[] = 73 "::std::basic_string<char, std::char_traits<char>, std::allocator<char> >" 74 "::c_str"; 75 76 } // end namespace 77 78 namespace tidy { 79 namespace readability { 80 81 void RedundantStringCStrCheck::registerMatchers( 82 ast_matchers::MatchFinder *Finder) { 83 // Only register the matchers for C++; the functionality currently does not 84 // provide any benefit to other languages, despite being benign. 85 if (!getLangOpts().CPlusPlus) 86 return; 87 88 Finder->addMatcher( 89 cxxConstructExpr( 90 hasDeclaration(cxxMethodDecl(hasName(StringConstructor))), 91 argumentCountIs(2), 92 // The first argument must have the form x.c_str() or p->c_str() 93 // where the method is string::c_str(). We can use the copy 94 // constructor of string instead (or the compiler might share 95 // the string object). 96 hasArgument(0, cxxMemberCallExpr( 97 callee(memberExpr().bind("member")), 98 callee(cxxMethodDecl(hasName(StringCStrMethod))), 99 on(expr().bind("arg"))) 100 .bind("call")), 101 // The second argument is the alloc object which must not be 102 // present explicitly. 103 hasArgument(1, cxxDefaultArgExpr())), 104 this); 105 Finder->addMatcher( 106 cxxConstructExpr( 107 // Implicit constructors of these classes are overloaded 108 // wrt. string types and they internally make a StringRef 109 // referring to the argument. Passing a string directly to 110 // them is preferred to passing a char pointer. 111 hasDeclaration( 112 cxxMethodDecl(anyOf(hasName("::llvm::StringRef::StringRef"), 113 hasName("::llvm::Twine::Twine")))), 114 argumentCountIs(1), 115 // The only argument must have the form x.c_str() or p->c_str() 116 // where the method is string::c_str(). StringRef also has 117 // a constructor from string which is more efficient (avoids 118 // strlen), so we can construct StringRef from the string 119 // directly. 120 hasArgument(0, cxxMemberCallExpr( 121 callee(memberExpr().bind("member")), 122 callee(cxxMethodDecl(hasName(StringCStrMethod))), 123 on(expr().bind("arg"))) 124 .bind("call"))), 125 this); 126 } 127 128 void RedundantStringCStrCheck::check(const MatchFinder::MatchResult &Result) { 129 const auto *Call = Result.Nodes.getStmtAs<CallExpr>("call"); 130 const auto *Arg = Result.Nodes.getStmtAs<Expr>("arg"); 131 bool Arrow = Result.Nodes.getStmtAs<MemberExpr>("member")->isArrow(); 132 // Replace the "call" node with the "arg" node, prefixed with '*' 133 // if the call was using '->' rather than '.'. 134 std::string ArgText = 135 Arrow ? formatDereference(Result, *Arg) : getText(Result, *Arg).str(); 136 if (ArgText.empty()) 137 return; 138 139 diag(Call->getLocStart(), "redundant call to `c_str()`") 140 << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText); 141 } 142 143 } // namespace readability 144 } // namespace tidy 145 } // namespace clang 146