10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/ASTMatchers/ASTMatchers.h"
13#include "clang/Basic/CharInfo.h"
14#include "clang/Tooling/FixIt.h"
18using namespace clang::ast_matchers::internal;
23const char IteratorDeclStmtId[] =
"iterator_decl";
24const char DeclWithNewId[] =
"decl_new";
25const char DeclWithCastId[] =
"decl_cast";
26const char DeclWithTemplateCastId[] =
"decl_template";
28size_t getTypeNameLength(
bool RemoveStars, StringRef
Text) {
32 int TemplateTypenameCntr = 0;
33 for (
const unsigned char C :
Text) {
35 ++TemplateTypenameCntr;
37 --TemplateTypenameCntr;
42 (!RemoveStars && TemplateTypenameCntr == 0 &&
C ==
'*'))
45 if (NextChar != Space) {
47 if (LastChar == Space && NextChar == Alpha && BeforeSpace == Alpha)
49 BeforeSpace = NextChar;
70 const Expr *Init =
Node.getAnyInitializer();
74 Init = Init->IgnoreImplicit();
78 if (
const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
79 return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
80 !Construct->getArg(0)->isDefaultArgument();
82 return Node.getInitStyle() != VarDecl::ListInit;
97AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
100 if (SugarMatcher.matches(QT, Finder,
Builder))
103 QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
120Matcher<NamedDecl> hasStdIteratorName() {
121 static const StringRef IteratorNames[] = {
"iterator",
"reverse_iterator",
123 "const_reverse_iterator"};
124 return hasAnyName(IteratorNames);
139Matcher<NamedDecl> hasStdContainerName() {
140 static StringRef ContainerNames[] = {
"array",
"deque",
141 "forward_list",
"list",
147 "unordered_map",
"unordered_multimap",
148 "unordered_set",
"unordered_multiset",
150 "queue",
"priority_queue",
153 return hasAnyName(ContainerNames);
159 AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
161 return Node.hasExplicitTemplateArgs();
166DeclarationMatcher standardIterator() {
168 namedDecl(hasStdIteratorName()),
169 hasDeclContext(recordDecl(hasStdContainerName(), isInStdNamespace())));
174TypeMatcher typedefIterator() {
175 return typedefType(hasDeclaration(standardIterator()));
180TypeMatcher nestedIterator() {
181 return recordType(hasDeclaration(standardIterator()));
186TypeMatcher iteratorFromUsingDeclaration() {
187 auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
189 return elaboratedType(
192 hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
193 namedDecl(hasStdContainerName(), isInStdNamespace()))))),
197 anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl))));
202StatementMatcher makeIteratorDeclMatcher() {
203 return declStmt(unless(has(
204 varDecl(anyOf(unless(hasWrittenNonListInitializer()),
205 unless(hasType(isSugarFor(anyOf(
206 typedefIterator(), nestedIterator(),
207 iteratorFromUsingDeclaration())))))))))
208 .bind(IteratorDeclStmtId);
211StatementMatcher makeDeclWithNewMatcher() {
213 unless(has(varDecl(anyOf(
214 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
219 pointee(hasCanonicalType(hasLocalQualifiers())))),
225 pointsTo(parenType(innerType(functionType()))))))))))
226 .bind(DeclWithNewId);
229StatementMatcher makeDeclWithCastMatcher() {
231 unless(has(varDecl(unless(hasInitializer(explicitCastExpr()))))))
232 .bind(DeclWithCastId);
235StatementMatcher makeDeclWithTemplateCastMatcher() {
237 substTemplateTypeParmType(hasReplacementType(equalsBoundNode(
"arg")));
240 anyOf(has(memberExpr(hasExplicitTemplateArgs())),
241 has(ignoringImpCasts(declRefExpr(hasExplicitTemplateArgs()))));
244 hasTemplateArgument(0, refersToType(qualType().bind(
"arg")));
246 auto TemplateCall = callExpr(
248 callee(functionDecl(TemplateArg,
249 returns(anyOf(ST, pointsTo(ST), references(ST))))));
251 return declStmt(unless(has(varDecl(
252 unless(hasInitializer(ignoringImplicit(TemplateCall)))))))
253 .bind(DeclWithTemplateCastId);
256StatementMatcher makeCombinedMatcher() {
261 has(varDecl(unless(isImplicit()))),
263 unless(has(varDecl(anyOf(hasType(autoType()),
264 hasType(qualType(hasDescendant(autoType()))))))),
265 anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(),
266 makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher()));
273 MinTypeNameLength(Options.get(
"MinTypeNameLength", 5)),
274 RemoveStars(Options.get(
"RemoveStars", false)) {}
277 Options.
store(Opts,
"MinTypeNameLength", MinTypeNameLength);
282 Finder->addMatcher(traverse(TK_AsIs, makeCombinedMatcher()),
this);
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 (!Context->hasSameType(V->getType(),
E->getType()))
325 const auto *V = cast<VarDecl>(*D->decl_begin());
331 SourceRange
Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
332 diag(
Range.getBegin(),
"use auto when declaring iterators")
333 << FixItHint::CreateReplacement(Range,
"auto");
336void UseAutoCheck::replaceExpr(
337 const DeclStmt *D, ASTContext *Context,
338 llvm::function_ref<QualType(
const Expr *)> GetType, StringRef
Message) {
339 const auto *FirstDecl = dyn_cast<VarDecl>(*
D->decl_begin());
344 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
346 std::vector<FixItHint> StarRemovals;
347 for (
const auto *Dec :
D->decls()) {
348 const auto *V = cast<VarDecl>(Dec);
353 const auto *Expr = V->getInit()->IgnoreParenImpCasts();
359 if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr)))
365 if (FirstDeclType != V->getType().getCanonicalType())
371 if (Dec == *
D->decl_begin())
374 auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
375 while (!Q.isNull()) {
376 StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
377 Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
386 TypeLoc
Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
388 while (
Loc.getTypeLocClass() == TypeLoc::Pointer ||
389 Loc.getTypeLocClass() == TypeLoc::Qualified)
390 Loc =
Loc.getNextTypeLoc();
392 while (
Loc.getTypeLocClass() == TypeLoc::LValueReference ||
393 Loc.getTypeLocClass() == TypeLoc::RValueReference ||
394 Loc.getTypeLocClass() == TypeLoc::Qualified) {
395 Loc =
Loc.getNextTypeLoc();
397 SourceRange
Range(
Loc.getSourceRange());
399 if (MinTypeNameLength != 0 &&
400 getTypeNameLength(RemoveStars,
401 tooling::fixit::getText(
Loc.getSourceRange(),
402 FirstDecl->getASTContext())) <
412 Diag << FixItHint::CreateReplacement(Range, RemoveStars ?
"auto " :
"auto")
417 if (
const auto *
Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
418 replaceIterators(
Decl, Result.Context);
419 }
else if (
const auto *
Decl =
420 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
421 replaceExpr(
Decl, Result.Context,
422 [](
const Expr *Expr) { return Expr->getType(); },
423 "use auto when initializing with new to avoid "
424 "duplicating the type name");
425 }
else if (
const auto *
Decl =
426 Result.Nodes.getNodeAs<DeclStmt>(DeclWithCastId)) {
428 Decl, Result.Context,
429 [](
const Expr *Expr) {
430 return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
432 "use auto when initializing with a cast to avoid duplicating the type "
434 }
else if (
const auto *
Decl =
435 Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) {
437 Decl, Result.Context,
438 [](
const Expr *Expr) {
439 return cast<CallExpr>(Expr->IgnoreImplicit())
443 "use auto when initializing with a template cast to avoid duplicating "
446 llvm_unreachable(
"Bad Callback. No node provided.");
const FunctionDecl * Decl
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
constexpr llvm::StringLiteral Message
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::StringMap< ClangTidyValue > OptionMap