diff options
author | Chandler Carruth <chandlerc@gmail.com> | 2013-09-04 17:35:07 +0000 |
---|---|---|
committer | Chandler Carruth <chandlerc@gmail.com> | 2013-09-04 17:35:07 +0000 |
commit | d9063c46f59f4bec47bcbeddca8ca2f789348c03 (patch) | |
tree | 76505542df7a05016dc71ffe44ed3ba264fb54be /clang-tools-extra/clang-modernize/LoopConvert/LoopMatchers.cpp | |
parent | 6a23d212897d5402035cfaea82260f6dae1c8f2a (diff) | |
download | bcm5719-llvm-d9063c46f59f4bec47bcbeddca8ca2f789348c03.tar.gz bcm5719-llvm-d9063c46f59f4bec47bcbeddca8ca2f789348c03.zip |
Rename cpp11-migrate to clang-modernize.
There is no reason to expect this tool to be limited to C++11, it seems
very likely to be of on-going interest. It seems likely to be useful for
modernizing even as new libraries come out in TSes and other formats
than a complete standard. Fundamentally, we need something a bit more
general. After some discussion on the list, going with
'clang-modernize'.
I've tried to do a reasonably comprehensive job of fixing up the names,
but I may still have missed some. Feel free to poke me if you spot any
fallout here. Things I've tried reasonably hard to find and fix:
- cpp11-migrate -> clang-modernize
- Migrator -> Modernizer
- Clean up the introductory documentation that was C++11 specific.
I'll also point out that this tool continues to delight me. =] Also,
a huge thanks to those who have so carefully, thoroughly documented the
tool. The docs here are simply phenomenal. Every tool should be this
well documented. I hope I have updated the documentation reasonably
well, but I'm not very good at documentation, so review much
appreciated.
llvm-svn: 189960
Diffstat (limited to 'clang-tools-extra/clang-modernize/LoopConvert/LoopMatchers.cpp')
-rw-r--r-- | clang-tools-extra/clang-modernize/LoopConvert/LoopMatchers.cpp | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-modernize/LoopConvert/LoopMatchers.cpp b/clang-tools-extra/clang-modernize/LoopConvert/LoopMatchers.cpp new file mode 100644 index 00000000000..719c2069fbe --- /dev/null +++ b/clang-tools-extra/clang-modernize/LoopConvert/LoopMatchers.cpp @@ -0,0 +1,346 @@ +//===-- LoopConvert/LoopMatchers.cpp - Matchers for for loops -------------===// +// +// 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 definitions of the matchers for use in migrating +/// C++ for loops. +/// +//===----------------------------------------------------------------------===// + +#include "LoopMatchers.h" + +using namespace clang::ast_matchers; +using namespace clang; + +const char LoopName[] = "forLoop"; +const char ConditionBoundName[] = "conditionBound"; +const char ConditionVarName[] = "conditionVar"; +const char IncrementVarName[] = "incrementVar"; +const char InitVarName[] = "initVar"; +const char BeginCallName[] = "beginCall"; +const char EndCallName[] = "endCall"; +const char ConditionEndVarName[] = "conditionEndVar"; +const char EndVarName[] = "endVar"; +const char DerefByValueResultName[] = "derefByValueResult"; +const char DerefByRefResultName[] = "derefByRefResult"; + +// shared matchers +static const TypeMatcher AnyType = anything(); + +static const StatementMatcher IntegerComparisonMatcher = + expr(ignoringParenImpCasts(declRefExpr(to( + varDecl(hasType(isInteger())).bind(ConditionVarName))))); + +static const DeclarationMatcher InitToZeroMatcher = + varDecl(hasInitializer(ignoringParenImpCasts( + integerLiteral(equals(0))))).bind(InitVarName); + +static const StatementMatcher IncrementVarMatcher = + declRefExpr(to( + varDecl(hasType(isInteger())).bind(IncrementVarName))); + +// FIXME: How best to document complicated matcher expressions? They're fairly +// self-documenting...but there may be some unintuitive parts. + +/// \brief The matcher for loops over arrays. +/// +/// In this general example, assuming 'j' and 'k' are of integral type: +/// \code +/// for (int i = 0; j < 3 + 2; ++k) { ... } +/// \endcode +/// The following string identifers are bound to the parts of the AST: +/// ConditionVarName: 'j' (as a VarDecl) +/// ConditionBoundName: '3 + 2' (as an Expr) +/// InitVarName: 'i' (as a VarDecl) +/// IncrementVarName: 'k' (as a VarDecl) +/// LoopName: The entire for loop (as a ForStmt) +/// +/// Client code will need to make sure that: +/// - The three index variables identified by the matcher are the same +/// VarDecl. +/// - The index variable is only used as an array index. +/// - All arrays indexed by the loop are the same. +StatementMatcher makeArrayLoopMatcher() { + StatementMatcher ArrayBoundMatcher = + expr(hasType(isInteger())).bind(ConditionBoundName); + + return forStmt( + hasLoopInit(declStmt(hasSingleDecl(InitToZeroMatcher))), + hasCondition(anyOf(binaryOperator(hasOperatorName("<"), + hasLHS(IntegerComparisonMatcher), + hasRHS(ArrayBoundMatcher)), + binaryOperator(hasOperatorName(">"), + hasLHS(ArrayBoundMatcher), + hasRHS(IntegerComparisonMatcher)))), + hasIncrement(unaryOperator(hasOperatorName("++"), + hasUnaryOperand(IncrementVarMatcher)))) + .bind(LoopName); +} + +/// \brief The matcher used for iterator-based for loops. +/// +/// This matcher is more flexible than array-based loops. It will match +/// catch loops of the following textual forms (regardless of whether the +/// iterator type is actually a pointer type or a class type): +/// +/// Assuming f, g, and h are of type containerType::iterator, +/// \code +/// for (containerType::iterator it = container.begin(), +/// e = createIterator(); f != g; ++h) { ... } +/// for (containerType::iterator it = container.begin(); +/// f != anotherContainer.end(); ++h) { ... } +/// \endcode +/// The following string identifiers are bound to the parts of the AST: +/// InitVarName: 'it' (as a VarDecl) +/// ConditionVarName: 'f' (as a VarDecl) +/// LoopName: The entire for loop (as a ForStmt) +/// In the first example only: +/// EndVarName: 'e' (as a VarDecl) +/// ConditionEndVarName: 'g' (as a VarDecl) +/// In the second example only: +/// EndCallName: 'container.end()' (as a CXXMemberCallExpr) +/// +/// Client code will need to make sure that: +/// - The iterator variables 'it', 'f', and 'h' are the same +/// - The two containers on which 'begin' and 'end' are called are the same +/// - If the end iterator variable 'g' is defined, it is the same as 'f' +StatementMatcher makeIteratorLoopMatcher() { + StatementMatcher BeginCallMatcher = + memberCallExpr( + argumentCountIs(0), + callee( + methodDecl(hasName("begin")) + ) + ).bind(BeginCallName); + + DeclarationMatcher InitDeclMatcher = + varDecl( + hasInitializer( + anyOf( + ignoringParenImpCasts(BeginCallMatcher), + materializeTemporaryExpr(ignoringParenImpCasts(BeginCallMatcher)), + hasDescendant(BeginCallMatcher) + ) + ) + ).bind(InitVarName); + + DeclarationMatcher EndDeclMatcher = + varDecl(hasInitializer(anything())).bind(EndVarName); + + StatementMatcher EndCallMatcher = + memberCallExpr(argumentCountIs(0), callee(methodDecl(hasName("end")))); + + StatementMatcher IteratorBoundMatcher = + expr(anyOf(ignoringParenImpCasts(declRefExpr(to( + varDecl().bind(ConditionEndVarName)))), + ignoringParenImpCasts( + expr(EndCallMatcher).bind(EndCallName)), + materializeTemporaryExpr(ignoringParenImpCasts( + expr(EndCallMatcher).bind(EndCallName))))); + + StatementMatcher IteratorComparisonMatcher = + expr(ignoringParenImpCasts(declRefExpr(to( + varDecl().bind(ConditionVarName))))); + + StatementMatcher OverloadedNEQMatcher = operatorCallExpr( + hasOverloadedOperatorName("!="), + argumentCountIs(2), + hasArgument(0, IteratorComparisonMatcher), + hasArgument(1, IteratorBoundMatcher)); + + // This matcher tests that a declaration is a CXXRecordDecl that has an + // overloaded operator*(). If the operator*() returns by value instead of by + // reference then the return type is tagged with DerefByValueResultName. + internal::Matcher<VarDecl> TestDerefReturnsByValue = + hasType( + recordDecl( + hasMethod( + allOf( + hasOverloadedOperatorName("*"), + anyOf( + // Tag the return type if it's by value. + returns( + qualType( + unless(hasCanonicalType(referenceType())) + ).bind(DerefByValueResultName) + ), + returns( + // Skip loops where the iterator's operator* returns an + // rvalue reference. This is just weird. + qualType( + unless( + hasCanonicalType(rValueReferenceType()) + ) + ).bind(DerefByRefResultName) + ) + ) + ) + ) + ) + ); + + + return + forStmt( + hasLoopInit(anyOf( + declStmt( + declCountIs(2), + containsDeclaration(0, InitDeclMatcher), + containsDeclaration(1, EndDeclMatcher) + ), + declStmt(hasSingleDecl(InitDeclMatcher)) + )), + hasCondition(anyOf( + binaryOperator( + hasOperatorName("!="), + hasLHS(IteratorComparisonMatcher), + hasRHS(IteratorBoundMatcher) + ), + binaryOperator( + hasOperatorName("!="), + hasLHS(IteratorBoundMatcher), + hasRHS(IteratorComparisonMatcher) + ), + OverloadedNEQMatcher + )), + hasIncrement(anyOf( + unaryOperator( + hasOperatorName("++"), + hasUnaryOperand( + declRefExpr(to( + varDecl(hasType(pointsTo(AnyType))).bind(IncrementVarName) + )) + ) + ), + operatorCallExpr( + hasOverloadedOperatorName("++"), + hasArgument(0, + declRefExpr(to( + varDecl(TestDerefReturnsByValue).bind(IncrementVarName) + )) + ) + ) + )) + ).bind(LoopName); +} + +/// \brief The matcher used for array-like containers (pseudoarrays). +/// +/// This matcher is more flexible than array-based loops. It will match +/// loops of the following textual forms (regardless of whether the +/// iterator type is actually a pointer type or a class type): +/// +/// Assuming f, g, and h are of type containerType::iterator, +/// \code +/// for (int i = 0, j = container.size(); f < g; ++h) { ... } +/// for (int i = 0; f < container.size(); ++h) { ... } +/// \endcode +/// The following string identifiers are bound to the parts of the AST: +/// InitVarName: 'i' (as a VarDecl) +/// ConditionVarName: 'f' (as a VarDecl) +/// LoopName: The entire for loop (as a ForStmt) +/// In the first example only: +/// EndVarName: 'j' (as a VarDecl) +/// ConditionEndVarName: 'g' (as a VarDecl) +/// In the second example only: +/// EndCallName: 'container.size()' (as a CXXMemberCallExpr) +/// +/// Client code will need to make sure that: +/// - The index variables 'i', 'f', and 'h' are the same +/// - The containers on which 'size()' is called is the container indexed +/// - The index variable is only used in overloaded operator[] or +/// container.at() +/// - If the end iterator variable 'g' is defined, it is the same as 'j' +/// - The container's iterators would not be invalidated during the loop +StatementMatcher makePseudoArrayLoopMatcher() { + // Test that the incoming type has a record declaration that has methods + // called 'begin' and 'end'. If the incoming type is const, then make sure + // these methods are also marked const. + // + // FIXME: To be completely thorough this matcher should also ensure the + // return type of begin/end is an iterator that dereferences to the same as + // what operator[] or at() returns. Such a test isn't likely to fail except + // for pathological cases. + // + // FIXME: Also, a record doesn't necessarily need begin() and end(). Free + // functions called begin() and end() taking the container as an argument + // are also allowed. + TypeMatcher RecordWithBeginEnd = + qualType(anyOf( + qualType( + isConstQualified(), + hasDeclaration( + recordDecl( + hasMethod( + methodDecl( + hasName("begin"), + isConst() + ) + ), + hasMethod( + methodDecl( + hasName("end"), + isConst() + ) + ) + ) + ) // hasDeclaration + ), // qualType + qualType( + unless(isConstQualified()), + hasDeclaration( + recordDecl( + hasMethod(hasName("begin")), + hasMethod(hasName("end")) + ) + ) + ) // qualType + ) + ); + + StatementMatcher SizeCallMatcher = + memberCallExpr(argumentCountIs(0), + callee(methodDecl(anyOf(hasName("size"), + hasName("length")))), + on(anyOf(hasType(pointsTo(RecordWithBeginEnd)), + hasType(RecordWithBeginEnd)))); + + StatementMatcher EndInitMatcher = + expr(anyOf( + ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)), + explicitCastExpr(hasSourceExpression(ignoringParenImpCasts( + expr(SizeCallMatcher).bind(EndCallName)))))); + + DeclarationMatcher EndDeclMatcher = + varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName); + + StatementMatcher IndexBoundMatcher = + expr(anyOf( + ignoringParenImpCasts(declRefExpr(to( + varDecl(hasType(isInteger())).bind(ConditionEndVarName)))), + EndInitMatcher)); + + return forStmt( + hasLoopInit(anyOf( + declStmt(declCountIs(2), + containsDeclaration(0, InitToZeroMatcher), + containsDeclaration(1, EndDeclMatcher)), + declStmt(hasSingleDecl(InitToZeroMatcher)))), + hasCondition(anyOf( + binaryOperator(hasOperatorName("<"), + hasLHS(IntegerComparisonMatcher), + hasRHS(IndexBoundMatcher)), + binaryOperator(hasOperatorName(">"), + hasLHS(IndexBoundMatcher), + hasRHS(IntegerComparisonMatcher)))), + hasIncrement(unaryOperator( + hasOperatorName("++"), + hasUnaryOperand(IncrementVarMatcher)))) + .bind(LoopName); +} |