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;
41 isAlphanumeric(C) ? Alpha
43 (!RemoveStars && TemplateTypenameCntr == 0 &&
C ==
'*'))
46 if (NextChar != Space) {
48 if (LastChar == Space && NextChar == Alpha && BeforeSpace == Alpha)
50 BeforeSpace = NextChar;
71 const Expr *Init = Node.getAnyInitializer();
75 Init = Init->IgnoreImplicit();
79 if (
const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
80 return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
81 !Construct->getArg(0)->isDefaultArgument();
83 return Node.getInitStyle() != VarDecl::ListInit;
98AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
101 if (SugarMatcher.matches(QT, Finder, Builder))
104 QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
121Matcher<NamedDecl> hasStdIteratorName() {
122 static const StringRef IteratorNames[] = {
"iterator",
"reverse_iterator",
124 "const_reverse_iterator"};
125 return hasAnyName(IteratorNames);
140Matcher<NamedDecl> hasStdContainerName() {
141 static StringRef ContainerNames[] = {
"array",
"deque",
142 "forward_list",
"list",
148 "unordered_map",
"unordered_multimap",
149 "unordered_set",
"unordered_multiset",
151 "queue",
"priority_queue",
154 return hasAnyName(ContainerNames);
160 AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
162 return Node.hasExplicitTemplateArgs();
167DeclarationMatcher standardIterator() {
169 namedDecl(hasStdIteratorName()),
170 hasDeclContext(recordDecl(hasStdContainerName(), isInStdNamespace())));
175TypeMatcher typedefIterator() {
176 return typedefType(hasDeclaration(standardIterator()));
181TypeMatcher nestedIterator() {
182 return recordType(hasDeclaration(standardIterator()));
187TypeMatcher iteratorFromUsingDeclaration() {
188 auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
191 auto Qualifier = hasQualifier(specifiesType(templateSpecializationType(
192 hasDeclaration(namedDecl(hasStdContainerName(), isInStdNamespace())))));
195 return anyOf(typedefType(HasIteratorDecl, Qualifier),
196 recordType(HasIteratorDecl, Qualifier));
201StatementMatcher makeIteratorDeclMatcher() {
202 return declStmt(unless(has(
203 varDecl(anyOf(unless(hasWrittenNonListInitializer()),
204 unless(hasType(isSugarFor(anyOf(
205 typedefIterator(), nestedIterator(),
206 iteratorFromUsingDeclaration())))))))))
207 .bind(IteratorDeclStmtId);
210StatementMatcher makeDeclWithNewMatcher() {
212 unless(has(varDecl(anyOf(
213 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
218 pointee(hasCanonicalType(hasLocalQualifiers())))),
224 pointsTo(parenType(innerType(functionType()))))))))))
225 .bind(DeclWithNewId);
228StatementMatcher makeDeclWithCastMatcher() {
230 unless(has(varDecl(unless(hasInitializer(explicitCastExpr()))))))
231 .bind(DeclWithCastId);
234StatementMatcher makeDeclWithTemplateCastMatcher() {
236 substTemplateTypeParmType(hasReplacementType(equalsBoundNode(
"arg")));
239 anyOf(has(memberExpr(hasExplicitTemplateArgs())),
240 has(ignoringImpCasts(declRefExpr(hasExplicitTemplateArgs()))));
243 hasTemplateArgument(0, refersToType(qualType().bind(
"arg")));
245 auto TemplateCall = callExpr(
247 callee(functionDecl(TemplateArg,
248 returns(anyOf(ST, pointsTo(ST), references(ST))))));
250 return declStmt(unless(has(varDecl(
251 unless(hasInitializer(ignoringImplicit(TemplateCall)))))))
252 .bind(DeclWithTemplateCastId);
255StatementMatcher makeCombinedMatcher() {
260 has(varDecl(unless(isImplicit()))),
262 unless(has(varDecl(anyOf(hasType(autoType()),
263 hasType(qualType(hasDescendant(autoType()))))))),
264 anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(),
265 makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher()));
272 MinTypeNameLength(Options.get(
"MinTypeNameLength", 5)),
273 RemoveStars(Options.get(
"RemoveStars", false)) {}
276 Options.store(Opts,
"MinTypeNameLength", MinTypeNameLength);
277 Options.store(Opts,
"RemoveStars", RemoveStars);
281 Finder->addMatcher(traverse(TK_AsIs, makeCombinedMatcher()),
this);
284void UseAutoCheck::replaceIterators(
const DeclStmt *D, ASTContext *Context) {
285 for (
const auto *Dec : D->decls()) {
286 const auto *V = cast<VarDecl>(Dec);
287 const Expr *ExprInit = V->getInit();
290 if (
const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
291 ExprInit = E->getSubExpr();
293 const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
298 if (Construct->getNumArgs() != 1)
302 const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
303 if (E != E->IgnoreConversionOperatorSingleStep()) {
310 if (
const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
316 if (NestedConstruct->getConstructor()->isConvertingConstructor(
false))
319 if (!Context->hasSameType(V->getType(), E->getType()))
324 const auto *V = cast<VarDecl>(*D->decl_begin());
330 SourceRange Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
331 diag(Range.getBegin(),
"use auto when declaring iterators")
332 << FixItHint::CreateReplacement(Range,
"auto");
337 const std::initializer_list<TypeLoc::TypeLocClass> &LocClasses) {
338 while (llvm::is_contained(LocClasses, Loc.getTypeLocClass()))
339 Loc = Loc.getNextTypeLoc();
344 const std::initializer_list<TypeLoc::TypeLocClass> &LocClasses) {
346 TypeLoc::TypeLocClass TLC = Loc.getTypeLocClass();
347 if (TLC != TypeLoc::Pointer && TLC != TypeLoc::MemberPointer)
350 TypeLoc::Pointer, TypeLoc::MemberPointer});
351 return llvm::is_contained(LocClasses, Loc.getTypeLocClass());
354void UseAutoCheck::replaceExpr(
355 const DeclStmt *D, ASTContext *Context,
356 llvm::function_ref<QualType(
const Expr *)> GetType, StringRef
Message) {
357 const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
362 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
363 TypeSourceInfo *TSI = FirstDecl->getTypeSourceInfo();
368 std::vector<FixItHint> StarRemovals;
369 for (
const auto *Dec : D->decls()) {
370 const auto *V = cast<VarDecl>(Dec);
375 const auto *Expr = V->getInit()->IgnoreParenImpCasts();
381 if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr)))
387 if (FirstDeclType != V->getType().getCanonicalType())
393 if (Dec == *D->decl_begin())
396 auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
397 while (!Q.isNull()) {
398 StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
399 Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
408 TypeLoc Loc = TSI->getTypeLoc();
412 TypeLoc::Qualified});
413 SourceRange Range(Loc.getSourceRange());
415 if (MinTypeNameLength != 0 &&
416 getTypeNameLength(RemoveStars,
417 tooling::fixit::getText(Loc.getSourceRange(),
418 FirstDecl->getASTContext())) <
422 auto Diag = diag(Range.getBegin(),
Message);
425 TSI->getTypeLoc(), {TypeLoc::FunctionProto, TypeLoc::ConstantArray});
429 llvm::StringRef
Auto = ShouldReplenishVariableName
430 ? (RemoveStars ?
"auto " :
"auto *")
431 : (RemoveStars ?
"auto " :
"auto");
432 std::string ReplenishedVariableName =
433 ShouldReplenishVariableName ? FirstDecl->getQualifiedNameAsString() :
"";
434 std::string Replacement =
435 (
Auto + llvm::StringRef{ReplenishedVariableName}).str();
436 Diag << FixItHint::CreateReplacement(Range, Replacement) << StarRemovals;
440 if (
const auto *Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
441 replaceIterators(Decl, Result.Context);
442 }
else if (
const auto *Decl =
443 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
445 Decl, Result.Context, [](
const Expr *Expr) { return Expr->getType(); },
446 "use auto when initializing with new to avoid "
447 "duplicating the type name");
448 }
else if (
const auto *Decl =
449 Result.Nodes.getNodeAs<DeclStmt>(DeclWithCastId)) {
451 Decl, Result.Context,
452 [](
const Expr *Expr) {
453 return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
455 "use auto when initializing with a cast to avoid duplicating the type "
457 }
else if (
const auto *Decl =
458 Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) {
460 Decl, Result.Context,
461 [](
const Expr *Expr) {
462 return cast<CallExpr>(Expr->IgnoreImplicit())
466 "use auto when initializing with a template cast to avoid duplicating "
469 llvm_unreachable(
"Bad Callback. No node provided.");
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
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
@ Auto
Diagnostics must not be generated for this snapshot.
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
AST_MATCHER(BinaryOperator, isRelationalOperator)
static const StringRef Message
static void ignoreTypeLocClasses(TypeLoc &Loc, const std::initializer_list< TypeLoc::TypeLocClass > &LocClasses)
static bool isMultiLevelPointerToTypeLocClasses(TypeLoc Loc, const std::initializer_list< TypeLoc::TypeLocClass > &LocClasses)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::StringMap< ClangTidyValue > OptionMap