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"
16 using namespace clang;
18 using namespace clang::ast_matchers::internal;
25 const char IteratorDeclStmtId[] =
"iterator_decl";
26 const char DeclWithNewId[] =
"decl_new";
27 const char DeclWithCastId[] =
"decl_cast";
28 const char DeclWithTemplateCastId[] =
"decl_template";
30 size_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;
71 AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
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;
99 AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
102 if (SugarMatcher.matches(QT, Finder,
Builder))
105 QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
123 static const char *
const IteratorNames[] = {
"iterator",
"reverse_iterator",
125 "const_reverse_iterator"};
127 for (
const char *
Name : IteratorNames) {
147 static const char *
const ContainerNames[] = {
149 "forward_list",
"list",
155 "unordered_map",
"unordered_multimap",
156 "unordered_set",
"unordered_multiset",
158 "queue",
"priority_queue",
161 for (
const char *
Name : ContainerNames) {
189 const DeclContext *D = Node.getDeclContext();
191 while (D->isInlineNamespace())
194 if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
197 const IdentifierInfo *
Info = cast<NamespaceDecl>(D)->getIdentifier();
199 return (Info &&
Info->isStr(
"std"));
205 AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
207 return Node.hasExplicitTemplateArgs();
212 DeclarationMatcher standardIterator() {
214 namedDecl(hasStdIteratorName()),
215 hasDeclContext(recordDecl(hasStdContainerName(), isFromStdNamespace())));
220 TypeMatcher typedefIterator() {
221 return typedefType(hasDeclaration(standardIterator()));
226 TypeMatcher nestedIterator() {
227 return recordType(hasDeclaration(standardIterator()));
232 TypeMatcher iteratorFromUsingDeclaration() {
233 auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
235 return elaboratedType(
238 hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
239 namedDecl(hasStdContainerName(), isFromStdNamespace()))))),
243 anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl))));
248 StatementMatcher makeIteratorDeclMatcher() {
249 return declStmt(unless(has(
250 varDecl(anyOf(unless(hasWrittenNonListInitializer()),
251 unless(hasType(isSugarFor(anyOf(
252 typedefIterator(), nestedIterator(),
253 iteratorFromUsingDeclaration())))))))))
254 .bind(IteratorDeclStmtId);
257 StatementMatcher makeDeclWithNewMatcher() {
259 unless(has(varDecl(anyOf(
260 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
265 pointee(hasCanonicalType(hasLocalQualifiers())))),
271 pointsTo(parenType(innerType(functionType()))))))))))
272 .bind(DeclWithNewId);
275 StatementMatcher makeDeclWithCastMatcher() {
277 unless(has(varDecl(unless(hasInitializer(explicitCastExpr()))))))
278 .bind(DeclWithCastId);
281 StatementMatcher makeDeclWithTemplateCastMatcher() {
283 substTemplateTypeParmType(hasReplacementType(equalsBoundNode(
"arg")));
286 anyOf(has(memberExpr(hasExplicitTemplateArgs())),
287 has(ignoringImpCasts(declRefExpr(hasExplicitTemplateArgs()))));
290 hasTemplateArgument(0, refersToType(qualType().bind(
"arg")));
292 auto TemplateCall = callExpr(
294 callee(functionDecl(TemplateArg,
295 returns(anyOf(ST, pointsTo(ST), references(ST))))));
297 return declStmt(unless(has(varDecl(
298 unless(hasInitializer(ignoringImplicit(TemplateCall)))))))
299 .bind(DeclWithTemplateCastId);
302 StatementMatcher makeCombinedMatcher() {
307 has(varDecl(unless(isImplicit()))),
309 unless(has(varDecl(anyOf(hasType(autoType()),
310 hasType(qualType(hasDescendant(autoType()))))))),
311 anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(),
312 makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher()));
317 UseAutoCheck::UseAutoCheck(StringRef
Name, ClangTidyContext *Context)
318 : ClangTidyCheck(
Name, Context),
319 MinTypeNameLength(Options.get(
"MinTypeNameLength", 5)),
320 RemoveStars(Options.get(
"RemoveStars", false)) {}
323 Options.store(Opts,
"MinTypeNameLength", MinTypeNameLength);
324 Options.store(Opts,
"RemoveStars", RemoveStars);
327 void UseAutoCheck::registerMatchers(MatchFinder *Finder) {
328 Finder->addMatcher(traverse(ast_type_traits::TK_AsIs, makeCombinedMatcher()),
332 void UseAutoCheck::replaceIterators(
const DeclStmt *D, ASTContext *Context) {
333 for (
const auto *Dec : D->decls()) {
334 const auto *V = cast<VarDecl>(Dec);
335 const Expr *ExprInit = V->getInit();
338 if (
const auto *
E = dyn_cast<ExprWithCleanups>(ExprInit))
339 ExprInit =
E->getSubExpr();
341 const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
346 if (Construct->getNumArgs() != 1)
350 const Expr *
E = (*Construct->arg_begin())->IgnoreParenImpCasts();
351 if (
E !=
E->IgnoreConversionOperator()) {
358 if (
const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(
E)) {
364 if (NestedConstruct->getConstructor()->isConvertingConstructor(
false))
367 if (!Context->hasSameType(V->getType(),
E->getType()))
372 const auto *V = cast<VarDecl>(*D->decl_begin());
378 SourceRange
Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
379 diag(
Range.getBegin(),
"use auto when declaring iterators")
380 << FixItHint::CreateReplacement(
Range,
"auto");
383 void UseAutoCheck::replaceExpr(
384 const DeclStmt *D, ASTContext *Context,
385 llvm::function_ref<QualType(
const Expr *)> GetType, StringRef
Message) {
386 const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
391 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
393 std::vector<FixItHint> StarRemovals;
394 for (
const auto *Dec : D->decls()) {
395 const auto *V = cast<VarDecl>(Dec);
400 const auto *Expr = V->getInit()->IgnoreParenImpCasts();
406 if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr)))
412 if (FirstDeclType != V->getType().getCanonicalType())
418 if (Dec == *D->decl_begin())
421 auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
422 while (!Q.isNull()) {
423 StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
424 Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
433 TypeLoc
Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
435 while (
Loc.getTypeLocClass() == TypeLoc::Pointer ||
436 Loc.getTypeLocClass() == TypeLoc::Qualified)
437 Loc =
Loc.getNextTypeLoc();
439 while (
Loc.getTypeLocClass() == TypeLoc::LValueReference ||
440 Loc.getTypeLocClass() == TypeLoc::RValueReference ||
441 Loc.getTypeLocClass() == TypeLoc::Qualified) {
442 Loc =
Loc.getNextTypeLoc();
444 SourceRange
Range(
Loc.getSourceRange());
446 if (MinTypeNameLength != 0 &&
447 GetTypeNameLength(RemoveStars,
448 tooling::fixit::getText(
Loc.getSourceRange(),
449 FirstDecl->getASTContext())) <
459 Diag << FixItHint::CreateReplacement(
Range, RemoveStars ?
"auto " :
"auto")
463 void UseAutoCheck::check(
const MatchFinder::MatchResult &Result) {
464 if (
const auto *
Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
465 replaceIterators(
Decl, Result.Context);
466 }
else if (
const auto *
Decl =
467 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
468 replaceExpr(
Decl, Result.Context,
469 [](
const Expr *Expr) { return Expr->getType(); },
470 "use auto when initializing with new to avoid "
471 "duplicating the type name");
472 }
else if (
const auto *
Decl =
473 Result.Nodes.getNodeAs<DeclStmt>(DeclWithCastId)) {
475 Decl, Result.Context,
476 [](
const Expr *Expr) {
477 return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
479 "use auto when initializing with a cast to avoid duplicating the type "
481 }
else if (
const auto *
Decl =
482 Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) {
484 Decl, Result.Context,
485 [](
const Expr *Expr) {
486 return cast<CallExpr>(Expr->IgnoreImplicit())
490 "use auto when initializing with a template cast to avoid duplicating "
493 llvm_unreachable(
"Bad Callback. No node provided.");