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 Finder->addMatcher( 84 constructExpr( 85 hasDeclaration(methodDecl(hasName(StringConstructor))), 86 argumentCountIs(2), 87 // The first argument must have the form x.c_str() or p->c_str() 88 // where the method is string::c_str(). We can use the copy 89 // constructor of string instead (or the compiler might share 90 // the string object). 91 hasArgument( 92 0, memberCallExpr(callee(memberExpr().bind("member")), 93 callee(methodDecl(hasName(StringCStrMethod))), 94 on(expr().bind("arg"))).bind("call")), 95 // The second argument is the alloc object which must not be 96 // present explicitly. 97 hasArgument(1, defaultArgExpr())), 98 this); 99 Finder->addMatcher( 100 constructExpr( 101 // Implicit constructors of these classes are overloaded 102 // wrt. string types and they internally make a StringRef 103 // referring to the argument. Passing a string directly to 104 // them is preferred to passing a char pointer. 105 hasDeclaration( 106 methodDecl(anyOf(hasName("::llvm::StringRef::StringRef"), 107 hasName("::llvm::Twine::Twine")))), 108 argumentCountIs(1), 109 // The only argument must have the form x.c_str() or p->c_str() 110 // where the method is string::c_str(). StringRef also has 111 // a constructor from string which is more efficient (avoids 112 // strlen), so we can construct StringRef from the string 113 // directly. 114 hasArgument( 115 0, memberCallExpr(callee(memberExpr().bind("member")), 116 callee(methodDecl(hasName(StringCStrMethod))), 117 on(expr().bind("arg"))).bind("call"))), 118 this); 119 } 120 121 void RedundantStringCStrCheck::check(const MatchFinder::MatchResult &Result) { 122 const auto *Call = Result.Nodes.getStmtAs<CallExpr>("call"); 123 const auto *Arg = Result.Nodes.getStmtAs<Expr>("arg"); 124 bool Arrow = Result.Nodes.getStmtAs<MemberExpr>("member")->isArrow(); 125 // Replace the "call" node with the "arg" node, prefixed with '*' 126 // if the call was using '->' rather than '.'. 127 std::string ArgText = 128 Arrow ? formatDereference(Result, *Arg) : getText(Result, *Arg).str(); 129 if (ArgText.empty()) 130 return; 131 132 diag(Call->getLocStart(), "redundant call to `c_str()`") 133 << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText); 134 } 135 136 } // namespace readability 137 } // namespace tidy 138 } // namespace clang 139