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
24namespace {
25AST_MATCHER(FunctionDecl, isOverloaded) {
26 const DeclarationName Name = Node.getDeclName();
27 // Sanity check
28 if (Name.isEmpty())
29 return false;
30 const DeclContext *DC = Node.getDeclContext();
31 auto LookupResult = DC->lookup(Name);
32 size_t UniqueSignatures = 0;
33 llvm::SmallPtrSet<const FunctionDecl *, 2> SeenFunctions;
34 for (NamedDecl *ND : LookupResult) {
35 const FunctionDecl *FD = nullptr;
36 if (const auto *Func = dyn_cast<FunctionDecl>(ND)) {
37 // Regular functions
38 FD = Func;
39 } else if (const auto *USD = dyn_cast<UsingShadowDecl>(ND)) {
40 // Overloads via "using ns::func_name"
41 FD = dyn_cast<FunctionDecl>(USD->getTargetDecl());
42 } else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND)) {
43 // Templated functions
44 FD = FTD->getTemplatedDecl();
45 }
46 if (FD && SeenFunctions.insert(FD->getCanonicalDecl()).second) {
47 UniqueSignatures++;
48 if (UniqueSignatures > 1)
49 return true;
50 }
51 }
52 return false;
53}
54} // namespace
55
56static constexpr StringRef StringViewClassKey = "string";
57static constexpr StringRef WStringViewClassKey = "wstring";
58static constexpr StringRef U8StringViewClassKey = "u8string";
59static constexpr StringRef U16StringViewClassKey = "u16string";
60static constexpr StringRef U32StringViewClassKey = "u32string";
61
62static auto getStringTypeMatcher(StringRef CharType) {
63 return hasCanonicalType(hasDeclaration(cxxRecordDecl(hasName(CharType))));
64}
65
66static void fixReturns(const FunctionDecl *FuncDecl, DiagnosticBuilder &Diag,
67 ASTContext &Context) {
68 auto Matches = match(
69 findAll(returnStmt(hasReturnValue(ignoringParenImpCasts(
70 cxxTemporaryObjectExpr(argumentCountIs(0)).bind("temp_obj_expr"))))),
71 *FuncDecl->getBody(), Context);
72
73 for (const auto &Match : Matches)
74 if (const auto *TempObjExpr =
75 Match.getNodeAs<CXXTemporaryObjectExpr>("temp_obj_expr");
76 TempObjExpr && TempObjExpr->getSourceRange().isValid())
77 Diag << FixItHint::CreateReplacement(TempObjExpr->getSourceRange(), "{}");
78}
79
81 ClangTidyContext *Context)
82 : ClangTidyCheck(Name, Context),
83 IgnoredFunctions(utils::options::parseStringList(
84 Options.get("IgnoredFunctions", "toString$;ToString$;to_string$"))) {
85 parseReplacementStringViewClass(
86 Options.get("ReplacementStringViewClass", ""));
87}
88
90 Options.store(Opts, "IgnoredFunctions",
91 utils::options::serializeStringList(IgnoredFunctions));
92 Options.store(Opts, "ReplacementStringViewClass",
93 (Twine("") + StringViewClassKey + "=" + StringViewClass + ";" +
94 WStringViewClassKey + "=" + WStringViewClass + ";" +
95 U8StringViewClassKey + "=" + U8StringViewClass + ";" +
96 U16StringViewClassKey + "=" + U16StringViewClass + ";" +
97 U32StringViewClassKey + "=" + U32StringViewClass)
98 .str());
99}
100
101void UseStringViewCheck::registerMatchers(MatchFinder *Finder) {
102 const auto IsStdString = getStringTypeMatcher("::std::basic_string");
103 // TODO: also consider *StringViewClass types
104 const auto IsStdStringView = getStringTypeMatcher("::std::basic_string_view");
105 const auto IgnoredFunctionsMatcher =
106 matchers::matchesAnyListedRegexName(IgnoredFunctions);
107 const auto TernaryOperator = conditionalOperator(
108 hasTrueExpression(ignoringParenImpCasts(stringLiteral())),
109 hasFalseExpression(ignoringParenImpCasts(stringLiteral())));
110 const auto VirtualOrOperator =
111 cxxMethodDecl(anyOf(cxxConversionDecl(), isVirtual()));
112 Finder->addMatcher(
113 functionDecl(
114 isDefinition(),
115 unless(anyOf(VirtualOrOperator, IgnoredFunctionsMatcher,
116 isOverloaded(),
117 ast_matchers::isExplicitTemplateSpecialization())),
118 returns(IsStdString), hasDescendant(returnStmt()),
119 unless(hasDescendant(returnStmt(hasReturnValue(unless(
120 anyOf(stringLiteral(), hasType(IsStdStringView), TernaryOperator,
121 cxxConstructExpr(anyOf(
122 allOf(hasType(IsStdString), argumentCountIs(0)),
123 allOf(isListInitialization(),
124 unless(cxxTemporaryObjectExpr()),
125 hasArgument(0, ignoringParenImpCasts(
126 stringLiteral()))))))))))))
127 .bind("func"),
128 this);
129}
130
131void UseStringViewCheck::check(const MatchFinder::MatchResult &Result) {
132 const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("func");
133 assert(MatchedDecl);
134 bool ShouldAKA = false;
135 const std::string DesugaredTypeStr =
136 desugarForDiagnostic(*Result.Context,
137 QualType(MatchedDecl->getReturnType()), ShouldAKA)
138 .getAsString();
139 const StringRef DestReturnTypeStr = toStringViewTypeStr(DesugaredTypeStr);
140
141 auto Diag =
142 diag(MatchedDecl->getTypeSpecStartLoc(),
143 "consider using '%0' to avoid unnecessary copying and allocations")
144 << DestReturnTypeStr;
145
146 fixReturns(MatchedDecl, Diag, *Result.Context);
147
148 for (const auto *FuncDecl : MatchedDecl->redecls())
149 if (const SourceRange ReturnTypeRange =
150 FuncDecl->getReturnTypeSourceRange();
151 ReturnTypeRange.isValid())
152 Diag << FixItHint::CreateReplacement(ReturnTypeRange, DestReturnTypeStr);
153}
154
155StringRef UseStringViewCheck::toStringViewTypeStr(StringRef Type) const {
156 if (Type.contains("wchar_t"))
157 return WStringViewClass;
158 if (Type.contains("char8_t"))
159 return U8StringViewClass;
160 if (Type.contains("char16_t"))
161 return U16StringViewClass;
162 if (Type.contains("char32_t"))
163 return U32StringViewClass;
164 return StringViewClass;
165}
166
167void UseStringViewCheck::parseReplacementStringViewClass(StringRef Options) {
168 if (Options.empty())
169 return;
170 const llvm::StringMap<StringRef *> StringClassesMap{
171 {StringViewClassKey, &StringViewClass},
172 {WStringViewClassKey, &WStringViewClass},
173 {U8StringViewClassKey, &U8StringViewClass},
174 {U16StringViewClassKey, &U16StringViewClass},
175 {U32StringViewClassKey, &U32StringViewClass}};
176 for (const auto &Option : utils::options::parseStringList(Options)) {
177 const auto Split = Option.split('=');
178 if (auto It = StringClassesMap.find(Split.first);
179 It != StringClassesMap.end())
180 *It->second = Split.second;
181 }
182}
183
184} // 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
AST_MATCHER(BinaryOperator, isRelationalOperator)
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[]