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;
30 enum CharType { Space, Alpha, Punctuation };
31 CharType LastChar = Space, BeforeSpace = Punctuation;
33 int TemplateTypenameCntr = 0;
34 for (
const unsigned char C : Text) {
36 ++TemplateTypenameCntr;
38 --TemplateTypenameCntr;
39 const CharType NextChar =
40 isAlphanumeric(C) ? Alpha
42 (!RemoveStars && TemplateTypenameCntr == 0 && C ==
'*'))
45 if (NextChar != Space) {
47 if (LastChar == Space && NextChar == Alpha && BeforeSpace == Alpha)
49 BeforeSpace = NextChar;
70AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
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 const QualType NewQT =
105 QT.getSingleStepDesugaredType(Finder->getASTContext());
115 AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
117 return Node.hasExplicitTemplateArgs();
132 static const StringRef IteratorNames[] = {
"iterator",
"reverse_iterator",
134 "const_reverse_iterator"};
135 return hasAnyName(IteratorNames);
151 static const StringRef ContainerNames[] = {
153 "forward_list",
"list",
159 "unordered_map",
"unordered_multimap",
160 "unordered_set",
"unordered_multiset",
162 "queue",
"priority_queue",
165 return hasAnyName(ContainerNames);
194 auto Qualifier = hasQualifier(specifiesType(templateSpecializationType(
198 return anyOf(typedefType(HasIteratorDecl, Qualifier),
199 recordType(HasIteratorDecl, Qualifier));
205 return declStmt(unless(has(
206 varDecl(anyOf(unless(hasWrittenNonListInitializer()),
207 unless(hasType(isSugarFor(anyOf(
215 unless(has(varDecl(anyOf(
216 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
221 pointee(hasCanonicalType(hasLocalQualifiers())))),
227 pointsTo(parenType(innerType(functionType()))))))))))
233 unless(has(varDecl(unless(hasInitializer(explicitCastExpr()))))))
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)))))))
263 has(varDecl(unless(isImplicit()))),
265 unless(has(varDecl(anyOf(hasType(autoType()),
266 hasType(qualType(hasDescendant(autoType()))))))),
273 MinTypeNameLength(Options.get(
"MinTypeNameLength", 5)),
274 RemoveStars(Options.get(
"RemoveStars", false)) {}
277 Options.store(Opts,
"MinTypeNameLength", MinTypeNameLength);
278 Options.store(Opts,
"RemoveStars", RemoveStars);
285void UseAutoCheck::replaceIterators(
const DeclStmt *D, ASTContext *Context) {
286 for (
const auto *Dec : D->decls()) {
287 const auto *V = cast<VarDecl>(Dec);
288 const Expr *ExprInit = V->getInit();
291 if (
const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
292 ExprInit = E->getSubExpr();
294 const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
299 if (Construct->getNumArgs() != 1)
303 const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
304 if (E != E->IgnoreConversionOperatorSingleStep()) {
311 if (
const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
317 if (NestedConstruct->getConstructor()->isConvertingConstructor(
false))
320 if (!ASTContext::hasSameType(V->getType(), E->getType()))
325 const auto *V = cast<VarDecl>(*D->decl_begin());
331 const SourceRange Range(
332 V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
333 diag(Range.getBegin(),
"use auto when declaring iterators")
334 << FixItHint::CreateReplacement(Range,
"auto");
339 const std::initializer_list<TypeLoc::TypeLocClass> &LocClasses) {
340 while (llvm::is_contained(LocClasses, Loc.getTypeLocClass()))
341 Loc = Loc.getNextTypeLoc();
346 const std::initializer_list<TypeLoc::TypeLocClass> &LocClasses) {
348 const TypeLoc::TypeLocClass TLC = Loc.getTypeLocClass();
349 if (TLC != TypeLoc::Pointer && TLC != TypeLoc::MemberPointer)
352 TypeLoc::Pointer, TypeLoc::MemberPointer});
353 return llvm::is_contained(LocClasses, Loc.getTypeLocClass());
356void UseAutoCheck::replaceExpr(
357 const DeclStmt *D, ASTContext *Context,
358 llvm::function_ref<QualType(
const Expr *)> GetType, StringRef
Message) {
359 const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
364 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
365 const TypeSourceInfo *TSI = FirstDecl->getTypeSourceInfo();
370 std::vector<FixItHint> StarRemovals;
371 for (
const auto *Dec : D->decls()) {
372 const auto *V = cast<VarDecl>(Dec);
377 const auto *Expr = V->getInit()->IgnoreParenImpCasts();
383 if (!ASTContext::hasSameUnqualifiedType(V->getType(), GetType(Expr)))
389 if (FirstDeclType != V->getType().getCanonicalType())
395 if (Dec == *D->decl_begin())
398 auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
399 while (!Q.isNull()) {
400 StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
401 Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
410 TypeLoc Loc = TSI->getTypeLoc();
414 TypeLoc::Qualified});
415 const SourceRange Range(Loc.getSourceRange());
417 if (MinTypeNameLength != 0 &&
419 tooling::fixit::getText(Loc.getSourceRange(),
420 FirstDecl->getASTContext())) <
424 auto Diag = diag(Range.getBegin(),
Message);
427 TSI->getTypeLoc(), {TypeLoc::FunctionProto, TypeLoc::ConstantArray});
431 const llvm::StringRef
Auto = ShouldReplenishVariableName
432 ? (RemoveStars ?
"auto " :
"auto *")
433 : (RemoveStars ?
"auto " :
"auto");
434 const std::string ReplenishedVariableName =
435 ShouldReplenishVariableName ? FirstDecl->getQualifiedNameAsString() :
"";
436 const std::string Replacement =
437 (
Auto + llvm::StringRef{ReplenishedVariableName}).str();
438 Diag << FixItHint::CreateReplacement(Range, Replacement) << StarRemovals;
443 replaceIterators(Decl, Result.Context);
444 }
else if (
const auto *Decl =
447 Decl, Result.Context, [](
const Expr *Expr) { return Expr->getType(); },
448 "use auto when initializing with new to avoid "
449 "duplicating the type name");
450 }
else if (
const auto *Decl =
453 Decl, Result.Context,
454 [](
const Expr *Expr) {
455 return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
457 "use auto when initializing with a cast to avoid duplicating the type "
459 }
else if (
const auto *Decl =
462 Decl, Result.Context,
463 [](
const Expr *Expr) {
464 return cast<CallExpr>(Expr->IgnoreImplicit())
468 "use auto when initializing with a template cast to avoid duplicating "
471 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.
static StatementMatcher makeDeclWithNewMatcher()
static Matcher< NamedDecl > hasStdIteratorName()
Matches named declarations that have one of the standard iterator names: iterator,...
static const StringRef Message
static TypeMatcher nestedIterator()
Returns a TypeMatcher that matches records named for standard iterators nested inside records named f...
static size_t getTypeNameLength(bool RemoveStars, StringRef Text)
static DeclarationMatcher standardIterator()
Returns a DeclarationMatcher that matches standard iterators nested inside records with a standard co...
static StatementMatcher makeCombinedMatcher()
static StatementMatcher makeDeclWithTemplateCastMatcher()
static Matcher< NamedDecl > hasStdContainerName()
Matches named declarations that have one of the standard container names.
static const char DeclWithTemplateCastId[]
static StatementMatcher makeIteratorDeclMatcher()
This matcher returns declaration statements that contain variable declarations with written non-list ...
static const char DeclWithCastId[]
static TypeMatcher iteratorFromUsingDeclaration()
Returns a TypeMatcher that matches types declared with using declarations and which name standard ite...
static const char DeclWithNewId[]
static TypeMatcher typedefIterator()
Returns a TypeMatcher that matches typedefs for standard iterators inside records with a standard con...
static StatementMatcher makeDeclWithCastMatcher()
static const char IteratorDeclStmtId[]
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