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 DeclarationName Name = Node.getDeclName();
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)) {
39 }
else if (
const auto *USD = dyn_cast<UsingShadowDecl>(ND)) {
41 FD = dyn_cast<FunctionDecl>(USD->getTargetDecl());
42 }
else if (
const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND)) {
44 FD = FTD->getTemplatedDecl();
46 if (FD && SeenFunctions.insert(FD->getCanonicalDecl()).second) {
48 if (UniqueSignatures > 1)
63 return hasCanonicalType(hasDeclaration(cxxRecordDecl(hasName(CharType))));
67 ASTContext &Context) {
69 findAll(returnStmt(hasReturnValue(ignoringParenImpCasts(
70 cxxTemporaryObjectExpr(argumentCountIs(0)).bind(
"temp_obj_expr"))))),
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(),
"{}");
83 IgnoredFunctions(
utils::options::parseStringList(
84 Options.get(
"IgnoredFunctions",
"toString$;ToString$;to_string$"))) {
85 parseReplacementStringViewClass(
86 Options.get(
"ReplacementStringViewClass",
""));
90 Options.store(Opts,
"IgnoredFunctions",
92 Options.store(Opts,
"ReplacementStringViewClass",
105 const auto IgnoredFunctionsMatcher =
107 const auto TernaryOperator = conditionalOperator(
108 hasTrueExpression(ignoringParenImpCasts(stringLiteral())),
109 hasFalseExpression(ignoringParenImpCasts(stringLiteral())));
110 const auto VirtualOrOperator =
111 cxxMethodDecl(anyOf(cxxConversionDecl(), isVirtual()));
115 unless(anyOf(VirtualOrOperator, IgnoredFunctionsMatcher,
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()))))))))))))
132 const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>(
"func");
134 bool ShouldAKA =
false;
135 const std::string DesugaredTypeStr =
136 desugarForDiagnostic(*Result.Context,
137 QualType(MatchedDecl->getReturnType()), ShouldAKA)
139 const StringRef DestReturnTypeStr = toStringViewTypeStr(DesugaredTypeStr);
142 diag(MatchedDecl->getTypeSpecStartLoc(),
143 "consider using '%0' to avoid unnecessary copying and allocations")
144 << DestReturnTypeStr;
146 fixReturns(MatchedDecl, Diag, *Result.Context);
148 for (
const auto *
FuncDecl : MatchedDecl->redecls())
149 if (
const SourceRange ReturnTypeRange =
150 FuncDecl->getReturnTypeSourceRange();
151 ReturnTypeRange.isValid())
152 Diag << FixItHint::CreateReplacement(ReturnTypeRange, DestReturnTypeStr);
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;
167void UseStringViewCheck::parseReplacementStringViewClass(StringRef Options) {
170 const llvm::StringMap<StringRef *> StringClassesMap{
177 const auto Split = Option.split(
'=');
178 if (
auto It = StringClassesMap.find(Split.first);
179 It != StringClassesMap.end())
180 *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 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[]