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 Finder->addMatcher( 107 cxxConstructExpr(StringConstructorExpr, 108 hasArgument(0, StringCStrCallExpr)), 109 this); 110 111 Finder->addMatcher( 112 cxxConstructExpr( 113 // Implicit constructors of these classes are overloaded 114 // wrt. string types and they internally make a StringRef 115 // referring to the argument. Passing a string directly to 116 // them is preferred to passing a char pointer. 117 hasDeclaration( 118 cxxMethodDecl(anyOf(hasName("::llvm::StringRef::StringRef"), 119 hasName("::llvm::Twine::Twine")))), 120 argumentCountIs(1), 121 // The only argument must have the form x.c_str() or p->c_str() 122 // where the method is string::c_str(). StringRef also has 123 // a constructor from string which is more efficient (avoids 124 // strlen), so we can construct StringRef from the string 125 // directly. 126 hasArgument(0, StringCStrCallExpr)), 127 this); 128 } 129 130 void RedundantStringCStrCheck::check(const MatchFinder::MatchResult &Result) { 131 const auto *Call = Result.Nodes.getStmtAs<CallExpr>("call"); 132 const auto *Arg = Result.Nodes.getStmtAs<Expr>("arg"); 133 bool Arrow = Result.Nodes.getStmtAs<MemberExpr>("member")->isArrow(); 134 // Replace the "call" node with the "arg" node, prefixed with '*' 135 // if the call was using '->' rather than '.'. 136 std::string ArgText = 137 Arrow ? formatDereference(Result, *Arg) : getText(Result, *Arg).str(); 138 if (ArgText.empty()) 139 return; 140 141 diag(Call->getLocStart(), "redundant call to `c_str()`") 142 << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText); 143 } 144 145 } // namespace readability 146 } // namespace tidy 147 } // namespace clang 148