clang-tools 23.0.0git
UseStringViewCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "../utils/Matchers.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/AST/ASTDiagnostic.h"
14#include "clang/AST/Stmt.h"
15#include "clang/ASTMatchers/ASTMatchFinder.h"
16#include "clang/ASTMatchers/ASTMatchers.h"
17#include "clang/Basic/Diagnostic.h"
18#include "llvm/ADT/StringMap.h"
19
20using namespace clang::ast_matchers;
21
22namespace clang::tidy::modernize {
23
24static constexpr StringRef StringViewClassKey = "string";
25static constexpr StringRef WStringViewClassKey = "wstring";
26static constexpr StringRef U8StringViewClassKey = "u8string";
27static constexpr StringRef U16StringViewClassKey = "u16string";
28static constexpr StringRef U32StringViewClassKey = "u32string";
29
30static auto getStringTypeMatcher(StringRef CharType) {
31 return hasCanonicalType(hasDeclaration(cxxRecordDecl(hasName(CharType))));
32}
33
34static void fixReturns(const FunctionDecl *FuncDecl, DiagnosticBuilder &Diag,
35 ASTContext &Context) {
36 auto Matches = match(
37 findAll(returnStmt(hasReturnValue(ignoringParenImpCasts(
38 cxxTemporaryObjectExpr(argumentCountIs(0)).bind("temp_obj_expr"))))),
39 *FuncDecl->getBody(), Context);
40
41 for (const auto &Match : Matches)
42 if (const auto *TempObjExpr =
43 Match.getNodeAs<CXXTemporaryObjectExpr>("temp_obj_expr");
44 TempObjExpr && TempObjExpr->getSourceRange().isValid())
45 Diag << FixItHint::CreateReplacement(TempObjExpr->getSourceRange(), "{}");
46}
47
49 ClangTidyContext *Context)
50 : ClangTidyCheck(Name, Context),
51 IgnoredFunctions(utils::options::parseStringList(
52 Options.get("IgnoredFunctions", "toString$;ToString$;to_string$"))) {
53 parseReplacementStringViewClass(
54 Options.get("ReplacementStringViewClass", ""));
55}
56
58 Options.store(Opts, "IgnoredFunctions",
59 utils::options::serializeStringList(IgnoredFunctions));
60 Options.store(Opts, "ReplacementStringViewClass",
61 (Twine("") + StringViewClassKey + "=" + StringViewClass + ";" +
62 WStringViewClassKey + "=" + WStringViewClass + ";" +
63 U8StringViewClassKey + "=" + U8StringViewClass + ";" +
64 U16StringViewClassKey + "=" + U16StringViewClass + ";" +
65 U32StringViewClassKey + "=" + U32StringViewClass)
66 .str());
67}
68
69void UseStringViewCheck::registerMatchers(MatchFinder *Finder) {
70 const auto IsStdString = getStringTypeMatcher("::std::basic_string");
71 // TODO: also consider *StringViewClass types
72 const auto IsStdStringView = getStringTypeMatcher("::std::basic_string_view");
73 const auto IgnoredFunctionsMatcher =
75 const auto TernaryOperator = conditionalOperator(
76 hasTrueExpression(ignoringParenImpCasts(stringLiteral())),
77 hasFalseExpression(ignoringParenImpCasts(stringLiteral())));
78 const auto VirtualOrOperator =
79 cxxMethodDecl(anyOf(cxxConversionDecl(), isVirtual()));
80 Finder->addMatcher(
81 functionDecl(
82 isDefinition(),
83 unless(anyOf(VirtualOrOperator, IgnoredFunctionsMatcher,
84 ast_matchers::isExplicitTemplateSpecialization())),
85 returns(IsStdString), hasDescendant(returnStmt()),
86 unless(hasDescendant(returnStmt(hasReturnValue(unless(
87 anyOf(stringLiteral(), hasType(IsStdStringView), TernaryOperator,
88 cxxConstructExpr(anyOf(
89 allOf(hasType(IsStdString), argumentCountIs(0)),
90 allOf(isListInitialization(),
91 unless(cxxTemporaryObjectExpr()),
92 hasArgument(0, ignoringParenImpCasts(
93 stringLiteral()))))))))))))
94 .bind("func"),
95 this);
96}
97
98void UseStringViewCheck::check(const MatchFinder::MatchResult &Result) {
99 const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("func");
100 assert(MatchedDecl);
101 bool ShouldAKA = false;
102 const std::string DesugaredTypeStr =
103 clang::desugarForDiagnostic(
104 *Result.Context, QualType(MatchedDecl->getReturnType()), ShouldAKA)
105 .getAsString();
106 const StringRef DestReturnTypeStr = toStringViewTypeStr(DesugaredTypeStr);
107
108 auto Diag =
109 diag(MatchedDecl->getTypeSpecStartLoc(),
110 "consider using '%0' to avoid unnecessary copying and allocations")
111 << DestReturnTypeStr;
112
113 fixReturns(MatchedDecl, Diag, *Result.Context);
114
115 for (const auto *FuncDecl : MatchedDecl->redecls())
116 if (const SourceRange ReturnTypeRange =
117 FuncDecl->getReturnTypeSourceRange();
118 ReturnTypeRange.isValid())
119 Diag << FixItHint::CreateReplacement(ReturnTypeRange, DestReturnTypeStr);
120}
121
122StringRef UseStringViewCheck::toStringViewTypeStr(StringRef Type) const {
123 if (Type.contains("wchar_t"))
124 return WStringViewClass;
125 if (Type.contains("char8_t"))
126 return U8StringViewClass;
127 if (Type.contains("char16_t"))
128 return U16StringViewClass;
129 if (Type.contains("char32_t"))
130 return U32StringViewClass;
131 return StringViewClass;
132}
133
134void UseStringViewCheck::parseReplacementStringViewClass(StringRef Options) {
135 if (Options.empty())
136 return;
137 const llvm::StringMap<StringRef *> StringClassesMap{
138 {StringViewClassKey, &StringViewClass},
139 {WStringViewClassKey, &WStringViewClass},
140 {U8StringViewClassKey, &U8StringViewClass},
141 {U16StringViewClassKey, &U16StringViewClass},
142 {U32StringViewClassKey, &U32StringViewClass}};
143 for (const auto &Option : utils::options::parseStringList(Options)) {
144 const auto Split = Option.split('=');
145 if (auto It = StringClassesMap.find(Split.first);
146 It != StringClassesMap.end())
147 *It->second = Split.second;
148 }
149}
150
151} // namespace clang::tidy::modernize
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
UseStringViewCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
inline ::clang::ast_matchers::internal::Matcher< NamedDecl > matchesAnyListedRegexName(llvm::ArrayRef< StringRef > NameList)
static auto getStringTypeMatcher(StringRef CharType)
static constexpr StringRef WStringViewClassKey
static constexpr StringRef U32StringViewClassKey
static constexpr StringRef U8StringViewClassKey
static void fixReturns(const FunctionDecl *FuncDecl, DiagnosticBuilder &Diag, ASTContext &Context)
static constexpr StringRef StringViewClassKey
static constexpr StringRef U16StringViewClassKey
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
llvm::StringMap< ClangTidyValue > OptionMap
static constexpr const char FuncDecl[]