10#include "clang/AST/ASTContext.h"
11#include "clang/AST/TypeLoc.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
14#include "clang/Basic/CharInfo.h"
15#include "clang/Tooling/FixIt.h"
16#include "llvm/ADT/STLExtras.h"
20using namespace clang::ast_matchers::internal;
25const char IteratorDeclStmtId[] =
"iterator_decl";
26const char DeclWithNewId[] =
"decl_new";
27const char DeclWithCastId[] =
"decl_cast";
28const char DeclWithTemplateCastId[] =
"decl_template";
30size_t getTypeNameLength(
bool RemoveStars, StringRef
Text) {
34 int TemplateTypenameCntr = 0;
35 for (
const unsigned char C :
Text) {
37 ++TemplateTypenameCntr;
39 --TemplateTypenameCntr;
44 (!RemoveStars && TemplateTypenameCntr == 0 &&
C ==
'*'))
47 if (NextChar != Space) {
49 if (LastChar == Space && NextChar == Alpha && BeforeSpace == Alpha)
51 BeforeSpace = NextChar;
72 const Expr *Init =
Node.getAnyInitializer();
76 Init = Init->IgnoreImplicit();
80 if (
const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
81 return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
82 !Construct->getArg(0)->isDefaultArgument();
84 return Node.getInitStyle() != VarDecl::ListInit;
99AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
102 if (SugarMatcher.matches(QT, Finder,
Builder))
105 QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
122Matcher<NamedDecl> hasStdIteratorName() {
123 static const StringRef IteratorNames[] = {
"iterator",
"reverse_iterator",
125 "const_reverse_iterator"};
126 return hasAnyName(IteratorNames);
141Matcher<NamedDecl> hasStdContainerName() {
142 static StringRef ContainerNames[] = {
"array",
"deque",
143 "forward_list",
"list",
149 "unordered_map",
"unordered_multimap",
150 "unordered_set",
"unordered_multiset",
152 "queue",
"priority_queue",
155 return hasAnyName(ContainerNames);
161 AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
163 return Node.hasExplicitTemplateArgs();
168DeclarationMatcher standardIterator() {
170 namedDecl(hasStdIteratorName()),
171 hasDeclContext(recordDecl(hasStdContainerName(), isInStdNamespace())));
176TypeMatcher typedefIterator() {
177 return typedefType(hasDeclaration(standardIterator()));
182TypeMatcher nestedIterator() {
183 return recordType(hasDeclaration(standardIterator()));
188TypeMatcher iteratorFromUsingDeclaration() {
189 auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
191 return elaboratedType(
194 hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
195 namedDecl(hasStdContainerName(), isInStdNamespace()))))),
199 anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl))));
204StatementMatcher makeIteratorDeclMatcher() {
205 return declStmt(unless(has(
206 varDecl(anyOf(unless(hasWrittenNonListInitializer()),
207 unless(hasType(isSugarFor(anyOf(
208 typedefIterator(), nestedIterator(),
209 iteratorFromUsingDeclaration())))))))))
210 .bind(IteratorDeclStmtId);
213StatementMatcher makeDeclWithNewMatcher() {
215 unless(has(varDecl(anyOf(
216 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
221 pointee(hasCanonicalType(hasLocalQualifiers())))),
227 pointsTo(parenType(innerType(functionType()))))))))))
228 .bind(DeclWithNewId);
231StatementMatcher makeDeclWithCastMatcher() {
233 unless(has(varDecl(unless(hasInitializer(explicitCastExpr()))))))
234 .bind(DeclWithCastId);
237StatementMatcher makeDeclWithTemplateCastMatcher() {
239 substTemplateTypeParmType(hasReplacementType(equalsBoundNode(
"arg")));
242 anyOf(has(memberExpr(hasExplicitTemplateArgs())),
243 has(ignoringImpCasts(declRefExpr(hasExplicitTemplateArgs()))));
246 hasTemplateArgument(0, refersToType(qualType().bind(
"arg")));
248 auto TemplateCall = callExpr(
250 callee(functionDecl(TemplateArg,
251 returns(anyOf(ST, pointsTo(ST), references(ST))))));
253 return declStmt(unless(has(varDecl(
254 unless(hasInitializer(ignoringImplicit(TemplateCall)))))))
255 .bind(DeclWithTemplateCastId);
258StatementMatcher makeCombinedMatcher() {
263 has(varDecl(unless(isImplicit()))),
265 unless(has(varDecl(anyOf(hasType(autoType()),
266 hasType(qualType(hasDescendant(autoType()))))))),
267 anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(),
268 makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher()));
275 MinTypeNameLength(Options.get(
"MinTypeNameLength", 5)),
276 RemoveStars(Options.get(
"RemoveStars", false)) {}
279 Options.
store(Opts,
"MinTypeNameLength", MinTypeNameLength);
284 Finder->addMatcher(traverse(TK_AsIs, makeCombinedMatcher()),
this);
287void UseAutoCheck::replaceIterators(
const DeclStmt *D, ASTContext *Context) {
288 for (
const auto *Dec : D->decls()) {
289 const auto *V = cast<VarDecl>(Dec);
290 const Expr *ExprInit = V->getInit();
293 if (
const auto *
E = dyn_cast<ExprWithCleanups>(ExprInit))
294 ExprInit =
E->getSubExpr();
296 const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
301 if (Construct->getNumArgs() != 1)
305 const Expr *
E = (*Construct->arg_begin())->IgnoreParenImpCasts();
306 if (
E !=
E->IgnoreConversionOperatorSingleStep()) {
313 if (
const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(
E)) {
319 if (NestedConstruct->getConstructor()->isConvertingConstructor(
false))
322 if (!Context->hasSameType(V->getType(),
E->getType()))
327 const auto *V = cast<VarDecl>(*D->decl_begin());
333 SourceRange
Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
334 diag(
Range.getBegin(),
"use auto when declaring iterators")
335 << FixItHint::CreateReplacement(Range,
"auto");
340 std::initializer_list<TypeLoc::TypeLocClass>
const &LocClasses) {
341 while (llvm::is_contained(LocClasses,
Loc.getTypeLocClass()))
342 Loc =
Loc.getNextTypeLoc();
347 std::initializer_list<TypeLoc::TypeLocClass>
const &LocClasses) {
349 TypeLoc::TypeLocClass TLC =
Loc.getTypeLocClass();
350 if (TLC != TypeLoc::Pointer && TLC != TypeLoc::MemberPointer)
353 TypeLoc::Pointer, TypeLoc::MemberPointer});
354 return llvm::is_contained(LocClasses,
Loc.getTypeLocClass());
357void UseAutoCheck::replaceExpr(
358 const DeclStmt *D, ASTContext *Context,
359 llvm::function_ref<QualType(
const Expr *)> GetType, StringRef
Message) {
360 const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
365 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
366 TypeSourceInfo *TSI = FirstDecl->getTypeSourceInfo();
371 std::vector<FixItHint> StarRemovals;
372 for (
const auto *Dec : D->decls()) {
373 const auto *V = cast<VarDecl>(Dec);
378 const auto *Expr = V->getInit()->IgnoreParenImpCasts();
384 if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr)))
390 if (FirstDeclType != V->getType().getCanonicalType())
396 if (Dec == *D->decl_begin())
399 auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
400 while (!Q.isNull()) {
401 StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
402 Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
411 TypeLoc
Loc = TSI->getTypeLoc();
415 TypeLoc::Qualified});
416 SourceRange
Range(
Loc.getSourceRange());
418 if (MinTypeNameLength != 0 &&
419 getTypeNameLength(RemoveStars,
420 tooling::fixit::getText(
Loc.getSourceRange(),
421 FirstDecl->getASTContext())) <
428 TSI->getTypeLoc(), {TypeLoc::FunctionProto, TypeLoc::ConstantArray});
432 llvm::StringRef
Auto = ShouldReplenishVariableName
433 ? (RemoveStars ?
"auto " :
"auto *")
434 : (RemoveStars ?
"auto " :
"auto");
435 std::string ReplenishedVariableName =
436 ShouldReplenishVariableName ? FirstDecl->getQualifiedNameAsString() :
"";
437 std::string Replacement =
438 (
Auto + llvm::StringRef{ReplenishedVariableName}).str();
439 Diag << FixItHint::CreateReplacement(Range, Replacement) << StarRemovals;
443 if (
const auto *
Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
444 replaceIterators(
Decl, Result.Context);
445 }
else if (
const auto *
Decl =
446 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
447 replaceExpr(
Decl, Result.Context,
448 [](
const Expr *Expr) { return Expr->getType(); },
449 "use auto when initializing with new to avoid "
450 "duplicating the type name");
451 }
else if (
const auto *
Decl =
452 Result.Nodes.getNodeAs<DeclStmt>(DeclWithCastId)) {
454 Decl, Result.Context,
455 [](
const Expr *Expr) {
456 return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
458 "use auto when initializing with a cast to avoid duplicating the type "
460 }
else if (
const auto *
Decl =
461 Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) {
463 Decl, Result.Context,
464 [](
const Expr *Expr) {
465 return cast<CallExpr>(Expr->IgnoreImplicit())
469 "use auto when initializing with a template cast to avoid duplicating "
472 llvm_unreachable(
"Bad Callback. No node provided.");
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
CodeCompletionBuilder Builder
CharSourceRange Range
SourceRange for the file name.
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
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.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
UseAutoCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
AST_MATCHER_P(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
@ Auto
Diagnostics must not be generated for this snapshot.
static void ignoreTypeLocClasses(TypeLoc &Loc, std::initializer_list< TypeLoc::TypeLocClass > const &LocClasses)
static bool isMutliLevelPointerToTypeLocClasses(TypeLoc Loc, std::initializer_list< TypeLoc::TypeLocClass > const &LocClasses)
constexpr llvm::StringLiteral Message
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::StringMap< ClangTidyValue > OptionMap