14#include "../utils/Matchers.h"
15#include "../utils/OptionsUtils.h"
16#include "clang/Lex/Lexer.h"
17#include "clang/Tooling/FixIt.h"
28bool needParensAfterUnaryOperator(
const Expr &ExprNode) {
29 if (isa<clang::BinaryOperator>(&ExprNode) ||
30 isa<clang::ConditionalOperator>(&ExprNode)) {
33 if (
const auto *Op = dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
34 return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
35 Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
36 Op->getOperator() != OO_Subscript;
44formatDereference(
const ast_matchers::MatchFinder::MatchResult &Result,
45 const Expr &ExprNode) {
46 if (
const auto *Op = dyn_cast<clang::UnaryOperator>(&ExprNode)) {
47 if (Op->getOpcode() == UO_AddrOf) {
49 return std::string(tooling::fixit::getText(
50 *Op->getSubExpr()->IgnoreParens(), *Result.Context));
53 StringRef
Text = tooling::fixit::getText(ExprNode, *Result.Context);
59 Text.consume_back(
"->");
62 if (needParensAfterUnaryOperator(ExprNode)) {
63 return (llvm::Twine(
"*(") +
Text +
")").str();
65 return (llvm::Twine(
"*") +
Text).str();
68AST_MATCHER(MaterializeTemporaryExpr, isBoundToLValue) {
69 return Node.isBoundToLvalueReference();
77 StringParameterFunctions(utils::options::parseStringList(
78 Options.get(
"StringParameterFunctions",
""))) {
80 StringParameterFunctions.push_back(
"::std::format");
82 StringParameterFunctions.push_back(
"::std::print");
86 ast_matchers::MatchFinder *Finder) {
88 const auto StringDecl = type(hasUnqualifiedDesugaredType(recordType(
89 hasDeclaration(cxxRecordDecl(hasName(
"::std::basic_string"))))));
90 const auto StringExpr =
91 expr(anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));
94 const auto StringConstructorExpr = expr(anyOf(
95 cxxConstructExpr(argumentCountIs(1),
96 hasDeclaration(cxxMethodDecl(hasName(
"basic_string")))),
99 hasDeclaration(cxxMethodDecl(hasName(
"basic_string"))),
102 hasArgument(1, cxxDefaultArgExpr()))));
105 const auto StringViewConstructorExpr = cxxConstructExpr(
107 hasDeclaration(cxxMethodDecl(hasName(
"basic_string_view"))));
110 const auto StringCStrCallExpr =
111 cxxMemberCallExpr(on(StringExpr.bind(
"arg")),
112 callee(memberExpr().bind(
"member")),
113 callee(cxxMethodDecl(hasAnyName(
"c_str",
"data"))))
115 const auto HasRValueTempParent =
116 hasParent(materializeTemporaryExpr(unless(isBoundToLValue())));
124 anyOf(StringConstructorExpr, StringViewConstructorExpr),
125 hasArgument(0, StringCStrCallExpr),
126 unless(anyOf(HasRValueTempParent, hasParent(cxxBindTemporaryExpr(
127 HasRValueTempParent)))))),
133 hasAnyOverloadedOperatorName(
"<",
">",
">=",
"<=",
"!=",
"==",
"+"),
134 anyOf(allOf(hasArgument(0, StringExpr),
135 hasArgument(1, StringCStrCallExpr)),
136 allOf(hasArgument(0, StringCStrCallExpr),
137 hasArgument(1, StringExpr)))),
143 cxxOperatorCallExpr(hasAnyOverloadedOperatorName(
"=",
"+="),
144 hasArgument(0, StringExpr),
145 hasArgument(1, StringCStrCallExpr)),
150 cxxMemberCallExpr(on(StringExpr), callee(decl(cxxMethodDecl(hasAnyName(
151 "append",
"assign",
"compare")))),
152 argumentCountIs(1), hasArgument(0, StringCStrCallExpr)),
157 cxxMemberCallExpr(on(StringExpr),
158 callee(decl(cxxMethodDecl(hasName(
"compare")))),
159 argumentCountIs(3), hasArgument(2, StringCStrCallExpr)),
164 cxxMemberCallExpr(on(StringExpr),
165 callee(decl(cxxMethodDecl(hasAnyName(
166 "find",
"find_first_not_of",
"find_first_of",
167 "find_last_not_of",
"find_last_of",
"rfind")))),
168 anyOf(argumentCountIs(1), argumentCountIs(2)),
169 hasArgument(0, StringCStrCallExpr)),
174 cxxMemberCallExpr(on(StringExpr),
175 callee(decl(cxxMethodDecl(hasName(
"insert")))),
176 argumentCountIs(2), hasArgument(1, StringCStrCallExpr)),
188 hasDeclaration(cxxMethodDecl(hasAnyName(
189 "::llvm::StringRef::StringRef",
"::llvm::Twine::Twine"))),
196 hasArgument(0, StringCStrCallExpr))),
199 if (!StringParameterFunctions.empty()) {
205 StringParameterFunctions))),
206 forEachArgumentWithParam(StringCStrCallExpr,
213 const auto *Call = Result.Nodes.getNodeAs<CallExpr>(
"call");
214 const auto *Arg = Result.Nodes.getNodeAs<Expr>(
"arg");
215 const auto *Member = Result.Nodes.getNodeAs<MemberExpr>(
"member");
216 bool Arrow = Member->isArrow();
219 std::string ArgText =
220 Arrow ? formatDereference(Result, *Arg)
221 : tooling::fixit::getText(*Arg, *Result.Context).str();
225 diag(Call->getBeginLoc(),
"redundant call to %0")
226 << Member->getMemberDecl()
227 << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
RedundantStringCStrCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
inline ::clang::ast_matchers::internal::Matcher< NamedDecl > matchesAnyListedName(llvm::ArrayRef< StringRef > NameList)
AST_MATCHER(CXXMethodDecl, isStatic)