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"
26 const auto Matches = [](
const auto &Self,
const Expr &Expression) ->
bool {
27 const Expr *Unwrapped = Expression.IgnoreParenImpCasts();
28 if (
const auto *Ternary = dyn_cast<ConditionalOperator>(Unwrapped))
29 return Self(Self, *Ternary->getTrueExpr()) &&
30 Self(Self, *Ternary->getFalseExpr());
31 return isa<StringLiteral>(Unwrapped);
33 return Matches(Matches, Node);
37 const DeclarationName Name = Node.getDeclName();
41 const DeclContext *DC = Node.getDeclContext();
42 auto LookupResult = DC->lookup(Name);
43 size_t UniqueSignatures = 0;
44 llvm::SmallPtrSet<const FunctionDecl *, 2> SeenFunctions;
45 for (NamedDecl *ND : LookupResult) {
46 const FunctionDecl *FD =
nullptr;
47 if (
const auto *Func = dyn_cast<FunctionDecl>(ND)) {
50 }
else if (
const auto *USD = dyn_cast<UsingShadowDecl>(ND)) {
52 FD = dyn_cast<FunctionDecl>(USD->getTargetDecl());
53 }
else if (
const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND)) {
55 FD = FTD->getTemplatedDecl();
57 if (FD && SeenFunctions.insert(FD->getCanonicalDecl()).second) {
59 if (UniqueSignatures > 1)
74 return hasCanonicalType(hasDeclaration(cxxRecordDecl(hasName(CharType))));
78 const DiagnosticBuilder &Diag, ASTContext &Context) {
80 findAll(returnStmt(hasReturnValue(ignoringParenImpCasts(
81 cxxTemporaryObjectExpr(argumentCountIs(0)).bind(
"temp_obj_expr"))))),
84 for (
const auto &Match : Matches)
85 if (
const auto *TempObjExpr =
86 Match.getNodeAs<CXXTemporaryObjectExpr>(
"temp_obj_expr");
87 TempObjExpr && TempObjExpr->getSourceRange().isValid())
88 Diag << FixItHint::CreateReplacement(TempObjExpr->getSourceRange(),
"{}");
94 CheckOverloadedFunctions(Options.get(
"CheckOverloadedFunctions", false)),
95 IgnoredFunctions(
utils::options::parseStringList(
96 Options.get(
"IgnoredFunctions",
"toString$;ToString$;to_string$"))) {
97 parseReplacementStringViewClass(
98 Options.get(
"ReplacementStringViewClass",
""));
102 Options.store(Opts,
"CheckOverloadedFunctions", CheckOverloadedFunctions);
103 Options.store(Opts,
"IgnoredFunctions",
105 Options.store(Opts,
"ReplacementStringViewClass",
118 const auto IgnoredFunctionsMatcher =
120 const auto VirtualOrOperator =
121 cxxMethodDecl(anyOf(cxxConversionDecl(), isVirtual()));
122 const auto CheckOverloaded =
123 CheckOverloadedFunctions ? unless(anything()) : isOverloaded();
127 unless(anyOf(VirtualOrOperator, IgnoredFunctionsMatcher,
129 ast_matchers::isExplicitTemplateSpecialization())),
130 returns(IsStdString), hasDescendant(returnStmt()),
131 unless(hasDescendant(returnStmt(hasReturnValue(unless(
132 anyOf(stringLiteral(), hasType(IsStdStringView),
133 isStringLiteralOrTernary(),
134 cxxConstructExpr(anyOf(
135 allOf(hasType(IsStdString), argumentCountIs(0)),
136 allOf(isListInitialization(),
137 unless(cxxTemporaryObjectExpr()),
138 hasArgument(0, ignoringParenImpCasts(
139 stringLiteral()))))))))))))
145 const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>(
"func");
147 bool ShouldAKA =
false;
148 const std::string DesugaredTypeStr =
149 desugarForDiagnostic(*Result.Context,
150 QualType(MatchedDecl->getReturnType()), ShouldAKA)
152 const StringRef DestReturnTypeStr = toStringViewTypeStr(DesugaredTypeStr);
155 diag(MatchedDecl->getTypeSpecStartLoc(),
156 "consider using '%0' to avoid unnecessary copying and allocations")
157 << DestReturnTypeStr;
159 fixReturns(MatchedDecl, Diag, *Result.Context);
161 for (
const auto *
FuncDecl : MatchedDecl->redecls())
162 if (
const SourceRange ReturnTypeRange =
163 FuncDecl->getReturnTypeSourceRange();
164 ReturnTypeRange.isValid())
165 Diag << FixItHint::CreateReplacement(ReturnTypeRange, DestReturnTypeStr);
168StringRef UseStringViewCheck::toStringViewTypeStr(StringRef Type)
const {
169 if (Type.contains(
"wchar_t"))
170 return WStringViewClass;
171 if (Type.contains(
"char8_t"))
172 return U8StringViewClass;
173 if (Type.contains(
"char16_t"))
174 return U16StringViewClass;
175 if (Type.contains(
"char32_t"))
176 return U32StringViewClass;
177 return StringViewClass;
180void UseStringViewCheck::parseReplacementStringViewClass(StringRef Options) {
183 const llvm::StringMap<StringRef *> StringClassesMap{
190 const auto Split = Option.split(
'=');
191 if (
auto It = StringClassesMap.find(Split.first);
192 It != StringClassesMap.end())
193 *It->second = Split.second;
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 constexpr StringRef StringViewClassKey
static void fixReturns(const FunctionDecl *FuncDecl, const DiagnosticBuilder &Diag, ASTContext &Context)
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[]