Mercurial > hg > CbC > CbC_llvm
view clang-tools-extra/clang-tidy/readability/RedundantStringCStrCheck.cpp @ 252:1f2b6ac9f198 llvm-original
LLVM16-1
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 18 Aug 2023 09:04:13 +0900 |
parents | 79ff65ed7e25 |
children |
line wrap: on
line source
//===- RedundantStringCStrCheck.cpp - Check for redundant c_str calls -----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements a check for redundant calls of c_str() on strings. // //===----------------------------------------------------------------------===// #include "RedundantStringCStrCheck.h" #include "../utils/FixItHintUtils.h" #include "../utils/Matchers.h" #include "../utils/OptionsUtils.h" #include "clang/Lex/Lexer.h" #include "clang/Tooling/FixIt.h" using namespace clang::ast_matchers; namespace clang::tidy::readability { namespace { AST_MATCHER(MaterializeTemporaryExpr, isBoundToLValue) { return Node.isBoundToLvalueReference(); } } // end namespace RedundantStringCStrCheck::RedundantStringCStrCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), StringParameterFunctions(utils::options::parseStringList( Options.get("StringParameterFunctions", ""))) { if (getLangOpts().CPlusPlus20) StringParameterFunctions.push_back("::std::format"); if (getLangOpts().CPlusPlus23) StringParameterFunctions.push_back("::std::print"); } void RedundantStringCStrCheck::registerMatchers( ast_matchers::MatchFinder *Finder) { // Match expressions of type 'string' or 'string*'. const auto StringDecl = type(hasUnqualifiedDesugaredType(recordType( hasDeclaration(cxxRecordDecl(hasName("::std::basic_string")))))); const auto StringExpr = expr(anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl))))); // Match string constructor. const auto StringConstructorExpr = expr(anyOf( cxxConstructExpr(argumentCountIs(1), hasDeclaration(cxxMethodDecl(hasName("basic_string")))), cxxConstructExpr( argumentCountIs(2), hasDeclaration(cxxMethodDecl(hasName("basic_string"))), // If present, the second argument is the alloc object which must not // be present explicitly. hasArgument(1, cxxDefaultArgExpr())))); // Match string constructor. const auto StringViewConstructorExpr = cxxConstructExpr( argumentCountIs(1), hasDeclaration(cxxMethodDecl(hasName("basic_string_view")))); // Match a call to the string 'c_str()' method. const auto StringCStrCallExpr = cxxMemberCallExpr(on(StringExpr.bind("arg")), callee(memberExpr().bind("member")), callee(cxxMethodDecl(hasAnyName("c_str", "data")))) .bind("call"); const auto HasRValueTempParent = hasParent(materializeTemporaryExpr(unless(isBoundToLValue()))); // Detect redundant 'c_str()' calls through a string constructor. // If CxxConstructExpr is the part of some CallExpr we need to // check that matched ParamDecl of the ancestor CallExpr is not rvalue. Finder->addMatcher( traverse( TK_AsIs, cxxConstructExpr( anyOf(StringConstructorExpr, StringViewConstructorExpr), hasArgument(0, StringCStrCallExpr), unless(anyOf(HasRValueTempParent, hasParent(cxxBindTemporaryExpr( HasRValueTempParent)))))), this); // Detect: 's == str.c_str()' -> 's == str' Finder->addMatcher( cxxOperatorCallExpr( hasAnyOverloadedOperatorName("<", ">", ">=", "<=", "!=", "==", "+"), anyOf(allOf(hasArgument(0, StringExpr), hasArgument(1, StringCStrCallExpr)), allOf(hasArgument(0, StringCStrCallExpr), hasArgument(1, StringExpr)))), this); // Detect: 'dst += str.c_str()' -> 'dst += str' // Detect: 's = str.c_str()' -> 's = str' Finder->addMatcher( cxxOperatorCallExpr(hasAnyOverloadedOperatorName("=", "+="), hasArgument(0, StringExpr), hasArgument(1, StringCStrCallExpr)), this); // Detect: 'dst.append(str.c_str())' -> 'dst.append(str)' Finder->addMatcher( cxxMemberCallExpr(on(StringExpr), callee(decl(cxxMethodDecl(hasAnyName( "append", "assign", "compare")))), argumentCountIs(1), hasArgument(0, StringCStrCallExpr)), this); // Detect: 'dst.compare(p, n, str.c_str())' -> 'dst.compare(p, n, str)' Finder->addMatcher( cxxMemberCallExpr(on(StringExpr), callee(decl(cxxMethodDecl(hasName("compare")))), argumentCountIs(3), hasArgument(2, StringCStrCallExpr)), this); // Detect: 'dst.find(str.c_str())' -> 'dst.find(str)' Finder->addMatcher( cxxMemberCallExpr(on(StringExpr), callee(decl(cxxMethodDecl(hasAnyName( "find", "find_first_not_of", "find_first_of", "find_last_not_of", "find_last_of", "rfind")))), anyOf(argumentCountIs(1), argumentCountIs(2)), hasArgument(0, StringCStrCallExpr)), this); // Detect: 'dst.insert(pos, str.c_str())' -> 'dst.insert(pos, str)' Finder->addMatcher( cxxMemberCallExpr(on(StringExpr), callee(decl(cxxMethodDecl(hasName("insert")))), argumentCountIs(2), hasArgument(1, StringCStrCallExpr)), this); // Detect redundant 'c_str()' calls through a StringRef constructor. Finder->addMatcher( traverse( TK_AsIs, cxxConstructExpr( // Implicit constructors of these classes are overloaded // wrt. string types and they internally make a StringRef // referring to the argument. Passing a string directly to // them is preferred to passing a char pointer. hasDeclaration(cxxMethodDecl(hasAnyName( "::llvm::StringRef::StringRef", "::llvm::Twine::Twine"))), argumentCountIs(1), // The only argument must have the form x.c_str() or p->c_str() // where the method is string::c_str(). StringRef also has // a constructor from string which is more efficient (avoids // strlen), so we can construct StringRef from the string // directly. hasArgument(0, StringCStrCallExpr))), this); if (!StringParameterFunctions.empty()) { // Detect redundant 'c_str()' calls in parameters passed to std::format in // C++20 onwards and std::print in C++23 onwards. Finder->addMatcher( traverse(TK_AsIs, callExpr(callee(functionDecl(matchers::matchesAnyListedName( StringParameterFunctions))), forEachArgumentWithParam(StringCStrCallExpr, parmVarDecl()))), this); } } void RedundantStringCStrCheck::check(const MatchFinder::MatchResult &Result) { const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call"); const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg"); const auto *Member = Result.Nodes.getNodeAs<MemberExpr>("member"); bool Arrow = Member->isArrow(); // Replace the "call" node with the "arg" node, prefixed with '*' // if the call was using '->' rather than '.'. std::string ArgText = Arrow ? utils::fixit::formatDereference(*Arg, *Result.Context) : tooling::fixit::getText(*Arg, *Result.Context).str(); if (ArgText.empty()) return; diag(Call->getBeginLoc(), "redundant call to %0") << Member->getMemberDecl() << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText); } } // namespace clang::tidy::readability