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;
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", 0)) {}
323 Options.
store(Opts,
"MinTypeNameLength", MinTypeNameLength);
324 Options.
store(Opts,
"RemoveStars", RemoveStars ? 1 : 0);
327 void UseAutoCheck::registerMatchers(MatchFinder *Finder) {
331 Finder->addMatcher(makeCombinedMatcher(),
this);
335 void UseAutoCheck::replaceIterators(
const DeclStmt *D, ASTContext *Context) {
336 for (
const auto *Dec : D->decls()) {
337 const auto *V = cast<VarDecl>(Dec);
338 const Expr *ExprInit = V->getInit();
341 if (
const auto *
E = dyn_cast<ExprWithCleanups>(ExprInit))
342 ExprInit =
E->getSubExpr();
344 const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
349 if (Construct->getNumArgs() != 1)
353 const Expr *
E = (*Construct->arg_begin())->IgnoreParenImpCasts();
354 if (E != E->IgnoreConversionOperator()) {
361 if (
const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
367 if (NestedConstruct->getConstructor()->isConvertingConstructor(
false))
370 if (!Context->hasSameType(V->getType(), E->getType()))
375 const auto *V = cast<VarDecl>(*D->decl_begin());
381 SourceRange
Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
382 diag(
Range.getBegin(),
"use auto when declaring iterators")
383 << FixItHint::CreateReplacement(
Range,
"auto");
386 void UseAutoCheck::replaceExpr(
387 const DeclStmt *D, ASTContext *Context,
388 llvm::function_ref<QualType(
const Expr *)> GetType, StringRef
Message) {
389 const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
394 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
396 std::vector<FixItHint> StarRemovals;
397 for (
const auto *Dec : D->decls()) {
398 const auto *V = cast<VarDecl>(Dec);
403 const auto *Expr = V->getInit()->IgnoreParenImpCasts();
409 if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr)))
415 if (FirstDeclType != V->getType().getCanonicalType())
421 if (Dec == *D->decl_begin())
424 auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
425 while (!Q.isNull()) {
426 StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
427 Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
436 TypeLoc
Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
438 while (Loc.getTypeLocClass() == TypeLoc::Pointer ||
439 Loc.getTypeLocClass() == TypeLoc::Qualified)
440 Loc = Loc.getNextTypeLoc();
442 while (Loc.getTypeLocClass() == TypeLoc::LValueReference ||
443 Loc.getTypeLocClass() == TypeLoc::RValueReference ||
444 Loc.getTypeLocClass() == TypeLoc::Qualified) {
445 Loc = Loc.getNextTypeLoc();
447 SourceRange
Range(Loc.getSourceRange());
449 if (MinTypeNameLength != 0 &&
450 GetTypeNameLength(RemoveStars,
451 tooling::fixit::getText(Loc.getSourceRange(),
452 FirstDecl->getASTContext())) <
462 Diag << FixItHint::CreateReplacement(
Range, RemoveStars ?
"auto " :
"auto")
466 void UseAutoCheck::check(
const MatchFinder::MatchResult &Result) {
467 if (
const auto *
Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
468 replaceIterators(
Decl, Result.Context);
469 }
else if (
const auto *
Decl =
470 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
471 replaceExpr(
Decl, Result.Context,
472 [](
const Expr *Expr) { return Expr->getType(); },
473 "use auto when initializing with new to avoid " 474 "duplicating the type name");
475 }
else if (
const auto *
Decl =
476 Result.Nodes.getNodeAs<DeclStmt>(DeclWithCastId)) {
478 Decl, Result.Context,
479 [](
const Expr *Expr) {
480 return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
482 "use auto when initializing with a cast to avoid duplicating the type " 484 }
else if (
const auto *
Decl =
485 Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) {
487 Decl, Result.Context,
488 [](
const Expr *Expr) {
489 return cast<CallExpr>(Expr->IgnoreImplicit())
493 "use auto when initializing with a template cast to avoid duplicating " 496 llvm_unreachable(
"Bad Callback. No node provided.");
SourceLocation Loc
'#' location in the include directive
const FunctionDecl * Decl
AST_MATCHER(Expr, isMacroID)
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
constexpr llvm::StringLiteral Message
const LangOptions & getLangOpts() const
Returns the language options from the context.
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.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
CodeCompletionBuilder Builder
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
AST_MATCHER_P(CXXMethodDecl, hasCanonicalDecl, ast_matchers::internal::Matcher< CXXMethodDecl >, InnerMatcher)