forked from OSchip/llvm-project
281 lines
7.7 KiB
C++
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);
|
|
}
|