diff options
Diffstat (limited to 'clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp')
-rw-r--r-- | clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp new file mode 100644 index 00000000000..6522f4f10bd --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp @@ -0,0 +1,247 @@ +//===-- 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 "clang/AST/ASTContext.h" + +using namespace clang::ast_matchers; +using namespace clang; + +const char *DeclNodeId = "decl"; + +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; +/// \code +/// +/// \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; +/// \code +/// +/// \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 {}; +/// \code +/// +/// \c recordDecl(hasStdContainerName()) matches \c vector and \c forward_list +/// but not \c my_vec. +AST_MATCHER_P(NamedDecl, hasStdContainerName, bool, WithStd) { + 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) { + std::string Name(ContainerNames[i]); + if (WithStd) + Name = "std::" + Name; + if (hasName(Name).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(true)) + ) + ) + ) + ); +} + +// \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(true)) + ) + ) + ) + ); +} + +// \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(allOf( + specifiesType( + templateSpecializationType( + hasDeclaration( + namedDecl(hasStdContainerName(false)) + ) + ) + ), + hasPrefix( + specifiesNamespace(hasName("std")) + ) + )), + // 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 + +DeclarationMatcher makeIteratorMatcher() { + return varDecl(allOf( + hasWrittenNonListInitializer(), + unless(hasType(autoType())), + hasType( + isSugarFor( + anyOf( + typedefIterator(), + nestedIterator(), + iteratorFromUsingDeclaration() + ) + ) + ) + )).bind(DeclNodeId); +} |