llvm-project/clang-tools-extra/clang-modernize/UseAuto/UseAutoMatchers.cpp

281 lines
7.7 KiB
C++

//===-- UseAutoMatchers.cpp - Matchers for use-auto transform -------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file contains the implementation for matcher-generating
/// functions and custom AST_MATCHERs.
///
//===----------------------------------------------------------------------===//
#include "UseAutoMatchers.h"
#include "Core/CustomMatchers.h"
#include "clang/AST/ASTContext.h"
using namespace clang::ast_matchers;
using namespace clang;
const char *IteratorDeclStmtId = "iterator_decl";
const char *DeclWithNewId = "decl_new";
const char *NewExprId = "new_expr";
namespace clang {
namespace ast_matchers {
/// \brief Matches variable declarations that have explicit initializers that
/// are not initializer lists.
///
/// Given
/// \code
/// iterator I = Container.begin();
/// MyType A(42);
/// MyType B{2};
/// MyType C;
/// \endcode
/// varDecl(hasWrittenNonListInitializer()) matches \c I and \c A but not \c B
/// or \c C.
AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
const Expr *Init = Node.getAnyInitializer();
if (!Init)
return false;
// The following test is based on DeclPrinter::VisitVarDecl() to find if an
// initializer is implicit or not.
bool ImplicitInit = false;
if (const CXXConstructExpr *Construct = dyn_cast<CXXConstructExpr>(Init)) {
if (Construct->isListInitialization())
return false;
ImplicitInit = Construct->getNumArgs() == 0 ||
Construct->getArg(0)->isDefaultArgument();
} else
if (Node.getInitStyle() == VarDecl::ListInit)
return false;
return !ImplicitInit;
}
/// \brief Matches QualTypes that are type sugar for QualTypes that match \c
/// SugarMatcher.
///
/// Given
/// \code
/// class C {};
/// typedef C my_type
/// typedef my_type my_other_type;
/// \endcode
///
/// \c qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C"))))))
/// matches \c my_type and \c my_other_type.
AST_MATCHER_P(QualType, isSugarFor, internal::Matcher<QualType>, SugarMatcher) {
QualType QT = Node;
for (;;) {
if (SugarMatcher.matches(QT, Finder, Builder))
return true;
QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
if (NewQT == QT)
break;
QT = NewQT;
}
return false;
}
/// \brief Matches named declarations that have one of the standard iterator
/// names: iterator, reverse_iterator, const_iterator, const_reverse_iterator.
///
/// Given
/// \code
/// iterator I;
/// const_iterator CI;
/// \endcode
///
/// \c namedDecl(hasStdIteratorName()) matches \c I and \c CI.
AST_MATCHER(NamedDecl, hasStdIteratorName) {
static const char *IteratorNames[] = {
"iterator",
"reverse_iterator",
"const_iterator",
"const_reverse_iterator"
};
for (unsigned int i = 0;
i < llvm::array_lengthof(IteratorNames);
++i) {
if (hasName(IteratorNames[i]).matches(Node, Finder, Builder))
return true;
}
return false;
}
/// \brief Matches named declarations that have one of the standard container
/// names.
///
/// Given
/// \code
/// class vector {};
/// class forward_list {};
/// class my_vec {};
/// \endcode
///
/// \c recordDecl(hasStdContainerName()) matches \c vector and \c forward_list
/// but not \c my_vec.
AST_MATCHER(NamedDecl, hasStdContainerName) {
static const char *ContainerNames[] = {
"array",
"deque",
"forward_list",
"list",
"vector",
"map",
"multimap",
"set",
"multiset",
"unordered_map",
"unordered_multimap",
"unordered_set",
"unordered_multiset",
"queue",
"priority_queue",
"stack"
};
for (unsigned int i = 0; i < llvm::array_lengthof(ContainerNames); ++i) {
if (hasName(ContainerNames[i]).matches(Node, Finder, Builder))
return true;
}
return false;
}
} // namespace ast_matchers
} // namespace clang
namespace {
// \brief Returns a TypeMatcher that matches typedefs for standard iterators
// inside records with a standard container name.
TypeMatcher typedefIterator() {
return typedefType(
hasDeclaration(
allOf(
namedDecl(hasStdIteratorName()),
hasDeclContext(
recordDecl(hasStdContainerName(), isFromStdNamespace())
)
)
)
);
}
// \brief Returns a TypeMatcher that matches records named for standard
// iterators nested inside records named for standard containers.
TypeMatcher nestedIterator() {
return recordType(
hasDeclaration(
allOf(
namedDecl(hasStdIteratorName()),
hasDeclContext(
recordDecl(hasStdContainerName(), isFromStdNamespace())
)
)
)
);
}
// \brief Returns a TypeMatcher that matches types declared with using
// declarations and which name standard iterators for standard containers.
TypeMatcher iteratorFromUsingDeclaration() {
// Types resulting from using declarations are
// represented by ElaboratedType.
return elaboratedType(
allOf(
// Unwrap the nested name specifier to test for
// one of the standard containers.
hasQualifier(
specifiesType(
templateSpecializationType(
hasDeclaration(
namedDecl(hasStdContainerName(), isFromStdNamespace())
)
)
)
),
// The named type is what comes after the final
// '::' in the type. It should name one of the
// standard iterator names.
namesType(anyOf(
typedefType(
hasDeclaration(
namedDecl(hasStdIteratorName())
)
),
recordType(
hasDeclaration(
namedDecl(hasStdIteratorName())
)
)
))
)
);
}
} // namespace
// \brief This matcher returns delaration statements that contain variable
// declarations with written non-list initializer for standard iterators.
StatementMatcher makeIteratorDeclMatcher() {
return declStmt(
// At least one varDecl should be a child of the declStmt to ensure it's a
// declaration list and avoid matching other declarations
// e.g. using directives.
has(varDecl()),
unless(has(varDecl(
anyOf(
unless(hasWrittenNonListInitializer()),
hasType(autoType()),
unless(hasType(
isSugarFor(
anyOf(
typedefIterator(),
nestedIterator(),
iteratorFromUsingDeclaration()
)
)
))
)
)))
).bind(IteratorDeclStmtId);
}
StatementMatcher makeDeclWithNewMatcher() {
return declStmt(
has(varDecl()),
unless(has(varDecl(
anyOf(
unless(hasInitializer(
ignoringParenImpCasts(newExpr())
)),
// FIXME: TypeLoc information is not reliable where CV qualifiers are
// concerned so these types can't be handled for now.
hasType(pointerType(pointee(hasCanonicalType(hasLocalQualifiers())))),
// FIXME: Handle function pointers. For now we ignore them because
// the replacement replaces the entire type specifier source range
// which includes the identifier.
hasType(
pointsTo(
pointsTo(
parenType(innerType(functionType()))
)
)
)
)
)))
).bind(DeclWithNewId);
}