11 #include "clang/AST/ASTContext.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" 17 using namespace clang;
26 const char IteratorDeclStmtId[] =
"iterator_decl";
27 const char DeclWithNewId[] =
"decl_new";
28 const char DeclWithCastId[] =
"decl_cast";
29 const char DeclWithTemplateCastId[] =
"decl_template";
31 size_t GetTypeNameLength(
bool RemoveStars, StringRef Text) {
35 int TemplateTypenameCntr = 0;
36 for (
const unsigned char C : Text) {
38 ++TemplateTypenameCntr;
40 --TemplateTypenameCntr;
45 (!RemoveStars && TemplateTypenameCntr == 0 && C ==
'*'))
48 if (NextChar != Space) {
50 if (LastChar == Space && NextChar == Alpha && BeforeSpace == Alpha)
52 BeforeSpace = NextChar;
72 AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
73 const Expr *Init = Node.getAnyInitializer();
77 Init = Init->IgnoreImplicit();
81 if (
const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
82 return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
83 !Construct->getArg(0)->isDefaultArgument();
85 return Node.getInitStyle() != VarDecl::ListInit;
100 AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
103 if (SugarMatcher.matches(QT, Finder, Builder))
106 QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
124 static const char *
const IteratorNames[] = {
"iterator",
"reverse_iterator",
126 "const_reverse_iterator"};
128 for (
const char *
Name : IteratorNames) {
129 if (hasName(
Name).matches(Node, Finder, Builder))
148 static const char *
const ContainerNames[] = {
150 "forward_list",
"list",
156 "unordered_map",
"unordered_multimap",
157 "unordered_set",
"unordered_multiset",
159 "queue",
"priority_queue",
162 for (
const char *
Name : ContainerNames) {
163 if (hasName(
Name).matches(Node, Finder, Builder))
190 const DeclContext *
D = Node.getDeclContext();
192 while (D->isInlineNamespace())
195 if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
198 const IdentifierInfo *
Info = cast<NamespaceDecl>(
D)->getIdentifier();
200 return (Info && Info->isStr(
"std"));
206 AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
208 return Node.hasExplicitTemplateArgs();
213 DeclarationMatcher standardIterator() {
215 namedDecl(hasStdIteratorName()),
216 hasDeclContext(recordDecl(hasStdContainerName(), isFromStdNamespace())));
221 TypeMatcher typedefIterator() {
222 return typedefType(hasDeclaration(standardIterator()));
227 TypeMatcher nestedIterator() {
228 return recordType(hasDeclaration(standardIterator()));
233 TypeMatcher iteratorFromUsingDeclaration() {
234 auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
236 return elaboratedType(
239 hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
240 namedDecl(hasStdContainerName(), isFromStdNamespace()))))),
244 anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl))));
249 StatementMatcher makeIteratorDeclMatcher() {
250 return declStmt(unless(has(
251 varDecl(anyOf(unless(hasWrittenNonListInitializer()),
252 unless(hasType(isSugarFor(anyOf(
253 typedefIterator(), nestedIterator(),
254 iteratorFromUsingDeclaration())))))))))
255 .bind(IteratorDeclStmtId);
258 StatementMatcher makeDeclWithNewMatcher() {
260 unless(has(varDecl(anyOf(
261 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
266 pointee(hasCanonicalType(hasLocalQualifiers())))),
272 pointsTo(parenType(innerType(functionType()))))))))))
273 .bind(DeclWithNewId);
276 StatementMatcher makeDeclWithCastMatcher() {
278 unless(has(varDecl(unless(hasInitializer(explicitCastExpr()))))))
279 .bind(DeclWithCastId);
282 StatementMatcher makeDeclWithTemplateCastMatcher() {
284 substTemplateTypeParmType(hasReplacementType(equalsBoundNode(
"arg")));
287 anyOf(has(memberExpr(hasExplicitTemplateArgs())),
288 has(ignoringImpCasts(declRefExpr(hasExplicitTemplateArgs()))));
291 hasTemplateArgument(0, refersToType(qualType().bind(
"arg")));
293 auto TemplateCall = callExpr(
295 callee(functionDecl(TemplateArg,
296 returns(anyOf(ST, pointsTo(ST), references(ST))))));
298 return declStmt(unless(has(varDecl(
299 unless(hasInitializer(ignoringImplicit(TemplateCall)))))))
300 .bind(DeclWithTemplateCastId);
303 StatementMatcher makeCombinedMatcher() {
308 has(varDecl(unless(isImplicit()))),
310 unless(has(varDecl(anyOf(hasType(autoType()),
311 hasType(qualType(hasDescendant(autoType()))))))),
312 anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(),
313 makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher()));
318 UseAutoCheck::UseAutoCheck(StringRef
Name, ClangTidyContext *Context)
319 : ClangTidyCheck(Name, Context),
320 MinTypeNameLength(Options.get(
"MinTypeNameLength", 5)),
321 RemoveStars(Options.get(
"RemoveStars", 0)) {}
324 Options.
store(Opts,
"MinTypeNameLength", MinTypeNameLength);
325 Options.
store(Opts,
"RemoveStars", RemoveStars ? 1 : 0);
328 void UseAutoCheck::registerMatchers(MatchFinder *Finder) {
332 Finder->addMatcher(makeCombinedMatcher(),
this);
336 void UseAutoCheck::replaceIterators(
const DeclStmt *D, ASTContext *Context) {
337 for (
const auto *Dec : D->decls()) {
338 const auto *V = cast<VarDecl>(Dec);
339 const Expr *ExprInit = V->getInit();
342 if (
const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
343 ExprInit = E->getSubExpr();
345 const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
350 if (Construct->getNumArgs() != 1)
354 const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
355 if (E != E->IgnoreConversionOperator()) {
362 if (
const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
368 if (NestedConstruct->getConstructor()->isConvertingConstructor(
false))
371 if (!Context->hasSameType(V->getType(), E->getType()))
376 const auto *V = cast<VarDecl>(*D->decl_begin());
382 SourceRange
Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
383 diag(
Range.getBegin(),
"use auto when declaring iterators")
384 << FixItHint::CreateReplacement(
Range,
"auto");
387 void UseAutoCheck::replaceExpr(
388 const DeclStmt *D, ASTContext *Context,
389 llvm::function_ref<QualType(
const Expr *)> GetType, StringRef
Message) {
390 const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
395 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
397 std::vector<FixItHint> StarRemovals;
398 for (
const auto *Dec : D->decls()) {
399 const auto *V = cast<VarDecl>(Dec);
404 const auto *Expr = V->getInit()->IgnoreParenImpCasts();
410 if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr)))
416 if (FirstDeclType != V->getType().getCanonicalType())
422 if (Dec == *D->decl_begin())
425 auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
426 while (!Q.isNull()) {
427 StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
428 Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
437 TypeLoc
Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
439 while (Loc.getTypeLocClass() == TypeLoc::Pointer ||
440 Loc.getTypeLocClass() == TypeLoc::Qualified)
441 Loc = Loc.getNextTypeLoc();
443 while (Loc.getTypeLocClass() == TypeLoc::LValueReference ||
444 Loc.getTypeLocClass() == TypeLoc::RValueReference ||
445 Loc.getTypeLocClass() == TypeLoc::Qualified) {
446 Loc = Loc.getNextTypeLoc();
448 SourceRange
Range(Loc.getSourceRange());
450 if (MinTypeNameLength != 0 &&
451 GetTypeNameLength(RemoveStars,
452 tooling::fixit::getText(Loc.getSourceRange(),
453 FirstDecl->getASTContext())) <
463 Diag << FixItHint::CreateReplacement(
Range, RemoveStars ?
"auto " :
"auto")
467 void UseAutoCheck::check(
const MatchFinder::MatchResult &
Result) {
468 if (
const auto *Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
469 replaceIterators(Decl, Result.Context);
470 }
else if (
const auto *Decl =
471 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
472 replaceExpr(Decl, Result.Context,
473 [](
const Expr *Expr) { return Expr->getType(); },
474 "use auto when initializing with new to avoid " 475 "duplicating the type name");
476 }
else if (
const auto *Decl =
477 Result.Nodes.getNodeAs<DeclStmt>(DeclWithCastId)) {
479 Decl, Result.Context,
480 [](
const Expr *Expr) {
481 return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
483 "use auto when initializing with a cast to avoid duplicating the type " 485 }
else if (
const auto *Decl =
486 Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) {
488 Decl, Result.Context,
489 [](
const Expr *Expr) {
490 return cast<CallExpr>(Expr->IgnoreImplicit())
494 "use auto when initializing with a template cast to avoid duplicating " 497 llvm_unreachable(
"Bad Callback. No node provided.");
SourceLocation Loc
'#' location in the include directive
AST_MATCHER(BinaryOperator, isAssignmentOperator)
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.
LangOptions getLangOpts() const
Returns the language options from the context.
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
static const StringRef Message
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
AST_MATCHER_P(FunctionDecl, throws, internal::Matcher< Type >, InnerMatcher)
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.