11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 using namespace clang;
16 using namespace clang::ast_matchers;
17 using namespace clang::ast_matchers::internal;
24 const char IteratorDeclStmtId[] =
"iterator_decl";
25 const char DeclWithNewId[] =
"decl_new";
40 AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
41 const Expr *Init = Node.getAnyInitializer();
47 if (
const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
48 return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
49 !Construct->getArg(0)->isDefaultArgument();
51 return Node.getInitStyle() != VarDecl::ListInit;
66 AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
69 if (SugarMatcher.matches(QT,
Finder, Builder))
72 QualType NewQT = QT.getSingleStepDesugaredType(
Finder->getASTContext());
90 static const char *
const IteratorNames[] = {
"iterator",
"reverse_iterator",
92 "const_reverse_iterator"};
94 for (
const char *
Name : IteratorNames) {
95 if (hasName(
Name).matches(Node,
Finder, Builder))
114 static const char *
const ContainerNames[] = {
"array",
"deque",
115 "forward_list",
"list",
122 "unordered_multimap",
124 "unordered_multiset",
126 "queue",
"priority_queue",
129 for (
const char *
Name : ContainerNames) {
130 if (hasName(
Name).matches(Node,
Finder, Builder))
157 const DeclContext *D = Node.getDeclContext();
159 while (D->isInlineNamespace())
162 if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
165 const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
167 return (Info && Info->isStr(
"std"));
172 DeclarationMatcher standardIterator() {
174 namedDecl(hasStdIteratorName()),
175 hasDeclContext(recordDecl(hasStdContainerName(), isFromStdNamespace())));
180 TypeMatcher typedefIterator() {
181 return typedefType(hasDeclaration(standardIterator()));
186 TypeMatcher nestedIterator() {
187 return recordType(hasDeclaration(standardIterator()));
192 TypeMatcher iteratorFromUsingDeclaration() {
193 auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
195 return elaboratedType(allOf(
198 hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
199 namedDecl(hasStdContainerName(), isFromStdNamespace()))))),
203 anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl)))));
208 StatementMatcher makeIteratorDeclMatcher() {
214 unless(has(varDecl(anyOf(
215 unless(hasWrittenNonListInitializer()), hasType(autoType()),
217 isSugarFor(anyOf(typedefIterator(), nestedIterator(),
218 iteratorFromUsingDeclaration())))))))))
219 .bind(IteratorDeclStmtId);
222 StatementMatcher makeDeclWithNewMatcher() {
225 unless(has(varDecl(anyOf(
226 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
228 anyOf(hasType(autoType()),
229 hasType(pointerType(pointee(autoType())))),
234 pointee(hasCanonicalType(hasLocalQualifiers())))),
240 pointsTo(parenType(innerType(functionType()))))))))))
241 .bind(DeclWithNewId);
246 void UseAutoCheck::registerMatchers(MatchFinder *
Finder) {
249 if (getLangOpts().CPlusPlus) {
250 Finder->addMatcher(makeIteratorDeclMatcher(),
this);
251 Finder->addMatcher(makeDeclWithNewMatcher(),
this);
255 void UseAutoCheck::replaceIterators(
const DeclStmt *D, ASTContext *
Context) {
256 for (
const auto *Dec : D->decls()) {
257 const auto *V = cast<VarDecl>(Dec);
258 const Expr *ExprInit = V->getInit();
261 if (
const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
262 ExprInit = E->getSubExpr();
264 const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
269 if (Construct->getNumArgs() != 1)
273 const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
274 if (E != E->IgnoreConversionOperator()) {
281 if (
const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
287 if (NestedConstruct->getConstructor()->isConvertingConstructor(
false))
290 if (!Context->hasSameType(V->getType(), E->getType()))
295 const auto *V = cast<VarDecl>(*D->decl_begin());
301 SourceRange
Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
302 diag(
Range.getBegin(),
"use auto when declaring iterators")
303 << FixItHint::CreateReplacement(
Range,
"auto");
306 void UseAutoCheck::replaceNew(
const DeclStmt *D, ASTContext *Context) {
307 const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
312 const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
314 std::vector<SourceLocation> StarLocations;
315 for (
const auto *Dec : D->decls()) {
316 const auto *V = cast<VarDecl>(Dec);
321 const auto *NewExpr = cast<CXXNewExpr>(V->getInit()->IgnoreParenImpCasts());
327 if (!Context->hasSameUnqualifiedType(V->getType(), NewExpr->getType()))
332 if (Dec == *D->decl_begin())
336 if (FirstDeclType != V->getType().getCanonicalType())
339 auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
340 while (!Q.isNull()) {
341 StarLocations.push_back(Q.getStarLoc());
342 Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
351 FirstDecl->getTypeSourceInfo()->getTypeLoc().getSourceRange());
352 auto Diag = diag(
Range.getBegin(),
"use auto when initializing with new"
353 " to avoid duplicating the type name");
357 Diag << FixItHint::CreateReplacement(
Range,
"auto ");
360 for (
const auto &
Loc : StarLocations) {
361 Diag << FixItHint::CreateReplacement(
Loc,
"");
365 void UseAutoCheck::check(
const MatchFinder::MatchResult &
Result) {
366 if (
const auto *Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
367 replaceIterators(Decl, Result.Context);
368 }
else if (
const auto *Decl =
369 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
370 replaceNew(Decl, Result.Context);
372 llvm_unreachable(
"Bad Callback. No node provided.");
SourceLocation Loc
'#' location in the include directive
std::unique_ptr< ast_matchers::MatchFinder > Finder
AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt)
AST_MATCHER_P(CXXForRangeStmt, hasRangeBeginEndStmt, ast_matchers::internal::Matcher< DeclStmt >, InnerMatcher)
CharSourceRange Range
SourceRange for the file name.
ClangTidyContext & Context