diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -3007,7 +3007,7 @@
-
Matcher<Decl> | isExpansionInFileMatching | std::string RegExp |
+Matcher<Decl> | isExpansionInFileMatching | StringRef RegExp, Regex::RegexFlags Flags = NoFlags |
Matches AST nodes that were expanded within files whose name is
partially matching a given regex.
@@ -3019,6 +3019,10 @@
class Y {};
Usable as: Matcher<Decl>, Matcher<Stmt>, Matcher<TypeLoc>
+
+If the matcher is used in clang-query, RegexFlags parameter
+should be passed as a quoted string. e.g: "NoFlags".
+Flags can be combined with '|' example "IgnoreCase | BasicRegex"
|
@@ -3725,7 +3729,7 @@
-Matcher<NamedDecl> | matchesName | std::string RegExp |
+Matcher<NamedDecl> | matchesName | StringRef RegExp, Regex::RegexFlags Flags = NoFlags |
Matches NamedDecl nodes whose fully qualified names contain
a substring matched by the given RegExp.
@@ -3738,6 +3742,10 @@
Example matches X (regexp is one of "::X", "^foo::.*X", among others)
namespace foo { namespace bar { class X; } }
+
+If the matcher is used in clang-query, RegexFlags parameter
+should be passed as a quoted string. e.g: "NoFlags".
+Flags can be combined with '|' example "IgnoreCase | BasicRegex"
|
@@ -3932,12 +3940,16 @@
-Matcher<ObjCMessageExpr> | matchesSelector | std::string RegExp |
+Matcher<ObjCMessageExpr> | matchesSelector | StringRef RegExp, Regex::RegexFlags Flags = NoFlags |
Matches ObjC selectors whose name contains
a substring matched by the given RegExp.
matcher = objCMessageExpr(matchesSelector("loadHTMLStringmatches the outer message expr in the code below, but NOT the message
invocation for self.bodyView.
[self.bodyView loadHTMLString:html baseURL:NULL];
+
+If the matcher is used in clang-query, RegexFlags parameter
+should be passed as a quoted string. e.g: "NoFlags".
+Flags can be combined with '|' example "IgnoreCase | BasicRegex"
|
@@ -4228,7 +4240,7 @@
-Matcher<Stmt> | isExpansionInFileMatching | std::string RegExp |
+Matcher<Stmt> | isExpansionInFileMatching | StringRef RegExp, Regex::RegexFlags Flags = NoFlags |
Matches AST nodes that were expanded within files whose name is
partially matching a given regex.
@@ -4240,6 +4252,10 @@
class Y {};
Usable as: Matcher<Decl>, Matcher<Stmt>, Matcher<TypeLoc>
+
+If the matcher is used in clang-query, RegexFlags parameter
+should be passed as a quoted string. e.g: "NoFlags".
+Flags can be combined with '|' example "IgnoreCase | BasicRegex"
|
@@ -4410,7 +4426,7 @@
-Matcher<TypeLoc> | isExpansionInFileMatching | std::string RegExp |
+Matcher<TypeLoc> | isExpansionInFileMatching | StringRef RegExp, Regex::RegexFlags Flags = NoFlags |
Matches AST nodes that were expanded within files whose name is
partially matching a given regex.
@@ -4422,6 +4438,10 @@
class Y {};
Usable as: Matcher<Decl>, Matcher<Stmt>, Matcher<TypeLoc>
+
+If the matcher is used in clang-query, RegexFlags parameter
+should be passed as a quoted string. e.g: "NoFlags".
+Flags can be combined with '|' example "IgnoreCase | BasicRegex"
|
diff --git a/clang/docs/tools/dump_ast_matchers.py b/clang/docs/tools/dump_ast_matchers.py
--- a/clang/docs/tools/dump_ast_matchers.py
+++ b/clang/docs/tools/dump_ast_matchers.py
@@ -230,6 +230,28 @@
add_matcher(result_type, name, args, comment)
return
+ m = re.match(r"""^\s*AST_POLYMORPHIC_MATCHER_REGEX(?:_OVERLOAD)?\(
+ \s*([^\s,]+)\s*,
+ \s*AST_POLYMORPHIC_SUPPORTED_TYPES\(([^)]*)\),
+ \s*([^\s,]+)\s*
+ (?:,\s*\d+\s*)?
+ \)\s*{\s*$""", declaration, flags=re.X)
+
+ if m:
+ name, results, arg_name = m.groups()[0:3]
+ result_types = [r.strip() for r in results.split(',')]
+ if allowed_types and allowed_types != result_types:
+ raise Exception('Inconsistent documentation for: %s' % name)
+ arg = "StringRef %s, Regex::RegexFlags Flags = NoFlags" % arg_name
+ comment += """
+If the matcher is used in clang-query, RegexFlags parameter
+should be passed as a quoted string. e.g: "NoFlags".
+Flags can be combined with '|' example \"IgnoreCase | BasicRegex\"
+"""
+ for result_type in result_types:
+ add_matcher(result_type, name, arg, comment)
+ return
+
m = re.match(r"""^\s*AST_MATCHER_FUNCTION(_P)?(.?)(?:_OVERLOAD)?\(
(?:\s*([^\s,]+)\s*,)?
\s*([^\s,]+)\s*
@@ -275,6 +297,31 @@
add_matcher(result_type, name, args, comment)
return
+ m = re.match(r"""^\s*AST_MATCHER_REGEX(?:_OVERLOAD)?\(
+ \s*([^\s,]+)\s*,
+ \s*([^\s,]+)\s*,
+ \s*([^\s,]+)\s*
+ (?:,\s*\d+\s*)?
+ \)\s*{""", declaration, flags=re.X)
+ if m:
+ result, name, arg_name = m.groups()[0:3]
+ if not result:
+ if not allowed_types:
+ raise Exception('Did not find allowed result types for: %s' % name)
+ result_types = allowed_types
+ else:
+ result_types = [result]
+ arg = "StringRef %s, Regex::RegexFlags Flags = NoFlags" % arg_name
+ comment += """
+If the matcher is used in clang-query, RegexFlags parameter
+should be passed as a quoted string. e.g: "NoFlags".
+Flags can be combined with '|' example \"IgnoreCase | BasicRegex\"
+"""
+
+ for result_type in result_types:
+ add_matcher(result_type, name, arg, comment)
+ return
+
# Parse ArgumentAdapting matchers.
m = re.match(
r"""^.*ArgumentAdaptingMatcherFunc<.*>\s*
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -283,9 +283,10 @@
/// \endcode
///
/// Usable as: Matcher, Matcher, Matcher
-AST_POLYMORPHIC_MATCHER_P(isExpansionInFileMatching,
- AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc),
- std::string, RegExp) {
+AST_POLYMORPHIC_MATCHER_REGEX(isExpansionInFileMatching,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt,
+ TypeLoc),
+ RegExp) {
auto &SourceManager = Finder->getASTContext().getSourceManager();
auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getBeginLoc());
if (ExpansionLoc.isInvalid()) {
@@ -298,8 +299,7 @@
}
auto Filename = FileEntry->getName();
- llvm::Regex RE(RegExp);
- return RE.match(Filename);
+ return RegExp->match(Filename);
}
/// Matches statements that are (transitively) expanded from the named macro.
@@ -2748,11 +2748,9 @@
/// \code
/// namespace foo { namespace bar { class X; } }
/// \endcode
-AST_MATCHER_P(NamedDecl, matchesName, std::string, RegExp) {
- assert(!RegExp.empty());
+AST_MATCHER_REGEX(NamedDecl, matchesName, RegExp) {
std::string FullNameString = "::" + Node.getQualifiedNameAsString();
- llvm::Regex RE(RegExp);
- return RE.match(FullNameString);
+ return RegExp->match(FullNameString);
}
/// Matches overloaded operator names.
@@ -3373,11 +3371,9 @@
/// \code
/// [self.bodyView loadHTMLString:html baseURL:NULL];
/// \endcode
-AST_MATCHER_P(ObjCMessageExpr, matchesSelector, std::string, RegExp) {
- assert(!RegExp.empty());
+AST_MATCHER_REGEX(ObjCMessageExpr, matchesSelector, RegExp) {
std::string SelectorString = Node.getSelector().getAsString();
- llvm::Regex RE(RegExp);
- return RE.match(SelectorString);
+ return RegExp->match(SelectorString);
}
/// Matches when the selector is the empty selector
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersMacros.h b/clang/include/clang/ASTMatchers/ASTMatchersMacros.h
--- a/clang/include/clang/ASTMatchers/ASTMatchersMacros.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersMacros.h
@@ -438,4 +438,133 @@
ReturnTypesF>::Func MatcherName##Loc; \
AST_TYPE_TRAVERSE_MATCHER(MatcherName, FunctionName##Type, ReturnTypesF)
+/// AST_MATCHER_REGEX(Type, DefineMatcher, Param) { ... }
+/// defines a function named DefineMatcher() that takes a regular expression
+/// string paramater and an optional RegexFlags parameter and returns a
+/// Matcher object.
+///
+/// The code between the curly braces has access to the following variables:
+///
+/// Node: the AST node being matched; its type is Type.
+/// Param: a pointer to an \ref llvm::Regex object
+/// Finder: an ASTMatchFinder*.
+/// Builder: a BoundNodesTreeBuilder*.
+///
+/// The code should return true if 'Node' matches.
+#define AST_MATCHER_REGEX(Type, DefineMatcher, Param) \
+ AST_MATCHER_REGEX_OVERLOAD(Type, DefineMatcher, Param, 0)
+
+#define AST_MATCHER_REGEX_OVERLOAD(Type, DefineMatcher, Param, OverloadId) \
+ namespace internal { \
+ class matcher_##DefineMatcher##OverloadId##Matcher \
+ : public ::clang::ast_matchers::internal::MatcherInterface { \
+ public: \
+ explicit matcher_##DefineMatcher##OverloadId##Matcher( \
+ std::shared_ptr RE) \
+ : Param(std::move(RE)) {} \
+ bool matches(const Type &Node, \
+ ::clang::ast_matchers::internal::ASTMatchFinder *Finder, \
+ ::clang::ast_matchers::internal::BoundNodesTreeBuilder \
+ *Builder) const override; \
+ \
+ private: \
+ std::shared_ptr const Param; \
+ }; \
+ } \
+ inline ::clang::ast_matchers::internal::Matcher DefineMatcher( \
+ llvm::StringRef Param, llvm::Regex::RegexFlags RegexFlags) { \
+ assert(!Param.empty() && "Empty regex string"); \
+ auto SharedRegex = std::make_shared(Param, RegexFlags); \
+ std::string Error; \
+ if (!SharedRegex->isValid(Error)) { \
+ llvm::errs() << "error: building matcher '" #DefineMatcher "': " \
+ << Error << "\nnote: input was '" << Param << "'\n"; \
+ } \
+ return ::clang::ast_matchers::internal::makeMatcher( \
+ new internal::matcher_##DefineMatcher##OverloadId##Matcher( \
+ std::move(SharedRegex))); \
+ } \
+ inline ::clang::ast_matchers::internal::Matcher DefineMatcher( \
+ llvm::StringRef Param) { \
+ return DefineMatcher(Param, llvm::Regex::NoFlags); \
+ } \
+ \
+ typedef ::clang::ast_matchers::internal::Matcher ( \
+ &DefineMatcher##_Type##OverloadId##Flags)(llvm::StringRef, \
+ llvm::Regex::RegexFlags); \
+ typedef ::clang::ast_matchers::internal::Matcher ( \
+ &DefineMatcher##_Type##OverloadId)(llvm::StringRef); \
+ inline bool internal::matcher_##DefineMatcher##OverloadId##Matcher::matches( \
+ const Type &Node, \
+ ::clang::ast_matchers::internal::ASTMatchFinder *Finder, \
+ ::clang::ast_matchers::internal::BoundNodesTreeBuilder *Builder) const
+
+/// AST_POLYMORPHIC_MATCHER_REGEX(DefineMatcher, ReturnTypesF, Param) { ... }
+/// defines a function named DefineMatcher() that takes a regular expression
+/// string paramater and an optional RegexFlags parameter that is polymorphic in
+/// the return type.
+///
+/// The variables are the same as for
+/// AST_MATCHER_REGEX, with the addition of NodeType, which specifies the node
+/// type of the matcher Matcher returned by the function matcher().
+#define AST_POLYMORPHIC_MATCHER_REGEX(DefineMatcher, ReturnTypesF, Param) \
+ AST_POLYMORPHIC_MATCHER_REGEX_OVERLOAD(DefineMatcher, ReturnTypesF, Param, 0)
+
+#define AST_POLYMORPHIC_MATCHER_REGEX_OVERLOAD(DefineMatcher, ReturnTypesF, \
+ Param, OverloadId) \
+ namespace internal { \
+ template \
+ class matcher_##DefineMatcher##OverloadId##Matcher \
+ : public ::clang::ast_matchers::internal::MatcherInterface { \
+ public: \
+ explicit matcher_##DefineMatcher##OverloadId##Matcher( \
+ std::shared_ptr RE) \
+ : Param(std::move(RE)) {} \
+ bool matches(const NodeType &Node, \
+ ::clang::ast_matchers::internal::ASTMatchFinder *Finder, \
+ ::clang::ast_matchers::internal::BoundNodesTreeBuilder \
+ *Builder) const override; \
+ \
+ private: \
+ std::shared_ptr const Param; \
+ }; \
+ } \
+ inline ::clang::ast_matchers::internal::PolymorphicMatcherWithParam1< \
+ internal::matcher_##DefineMatcher##OverloadId##Matcher, \
+ std::shared_ptr, ReturnTypesF> \
+ DefineMatcher(llvm::StringRef Param, llvm::Regex::RegexFlags RegexFlags) { \
+ assert(!Param.empty() && "Empty regex string"); \
+ auto SharedRegex = std::make_shared(Param, RegexFlags); \
+ std::string Error; \
+ if (!SharedRegex->isValid(Error)) { \
+ llvm::errs() << "error: building matcher '" #DefineMatcher "': " \
+ << Error << "\nnote: input was '" << Param << "'\n"; \
+ } \
+ return ::clang::ast_matchers::internal::PolymorphicMatcherWithParam1< \
+ internal::matcher_##DefineMatcher##OverloadId##Matcher, \
+ std::shared_ptr, ReturnTypesF>(std::move(SharedRegex)); \
+ } \
+ inline ::clang::ast_matchers::internal::PolymorphicMatcherWithParam1< \
+ internal::matcher_##DefineMatcher##OverloadId##Matcher, \
+ std::shared_ptr, ReturnTypesF> \
+ DefineMatcher(llvm::StringRef Param) { \
+ return DefineMatcher(Param, llvm::Regex::NoFlags); \
+ } \
+ typedef ::clang::ast_matchers::internal::PolymorphicMatcherWithParam1< \
+ internal::matcher_##DefineMatcher##OverloadId##Matcher, \
+ std::shared_ptr, ReturnTypesF> ( \
+ &DefineMatcher##_Type##OverloadId##Flags)( \
+ llvm::StringRef Param, llvm::Regex::RegexFlags RegexFlags); \
+ typedef ::clang::ast_matchers::internal::PolymorphicMatcherWithParam1< \
+ internal::matcher_##DefineMatcher##OverloadId##Matcher, \
+ std::shared_ptr, ReturnTypesF> ( \
+ &DefineMatcher##_Type##OverloadId)(llvm::StringRef Param); \
+ template \
+ bool internal:: \
+ matcher_##DefineMatcher##OverloadId##Matcher::matches( \
+ const NodeType &Node, \
+ ::clang::ast_matchers::internal::ASTMatchFinder *Finder, \
+ ::clang::ast_matchers::internal::BoundNodesTreeBuilder *Builder) \
+ const
+
#endif // LLVM_CLANG_ASTMATCHERS_ASTMATCHERSMACROS_H
diff --git a/clang/lib/ASTMatchers/Dynamic/Marshallers.h b/clang/lib/ASTMatchers/Dynamic/Marshallers.h
--- a/clang/lib/ASTMatchers/Dynamic/Marshallers.h
+++ b/clang/lib/ASTMatchers/Dynamic/Marshallers.h
@@ -35,6 +35,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Regex.h"
#include
#include
#include
@@ -192,6 +193,26 @@
static llvm::Optional getBestGuess(const VariantValue &Value);
};
+template <> struct ArgTypeTraits {
+private:
+ static Optional getFlags(llvm::StringRef Flags);
+
+public:
+ static bool is(const VariantValue &Value) {
+ return Value.isString() && getFlags(Value.getString());
+ }
+
+ static llvm::Regex::RegexFlags get(const VariantValue &Value) {
+ return *getFlags(Value.getString());
+ }
+
+ static ArgKind getKind() { return ArgKind(ArgKind::AK_String); }
+
+ static llvm::Optional getBestGuess(const VariantValue &Value) {
+ return llvm::None;
+ }
+};
+
template <> struct ArgTypeTraits {
private:
static Optional getClauseKind(llvm::StringRef ClauseKind) {
@@ -711,6 +732,63 @@
std::vector> Overloads;
};
+template
+class RegexMatcherDescriptor : public MatcherDescriptor {
+public:
+ RegexMatcherDescriptor(ReturnType (*WithFlags)(StringRef,
+ llvm::Regex::RegexFlags),
+ ReturnType (*NoFlags)(StringRef),
+ ArrayRef RetKinds)
+ : WithFlags(WithFlags), NoFlags(NoFlags),
+ RetKinds(RetKinds.begin(), RetKinds.end()) {}
+ bool isVariadic() const override { return true; }
+ unsigned getNumArgs() const override { return 0; }
+
+ void getArgKinds(ASTNodeKind ThisKind, unsigned ArgNo,
+ std::vector &Kinds) const override {
+ assert(ArgNo < 2);
+ Kinds.push_back(ArgKind::AK_String);
+ }
+
+ bool isConvertibleTo(ASTNodeKind Kind, unsigned *Specificity,
+ ASTNodeKind *LeastDerivedKind) const override {
+ return isRetKindConvertibleTo(RetKinds, Kind, Specificity,
+ LeastDerivedKind);
+ }
+
+ VariantMatcher create(SourceRange NameRange, ArrayRef Args,
+ Diagnostics *Error) const override {
+ if (Args.size() < 1 || Args.size() > 2) {
+ Error->addError(NameRange, Diagnostics::ET_RegistryWrongArgCount)
+ << "1 or 2" << Args.size();
+ return VariantMatcher();
+ }
+ if (!ArgTypeTraits::is(Args[0].Value)) {
+ Error->addError(Args[0].Range, Error->ET_RegistryWrongArgType)
+ << 1 << ArgTypeTraits::getKind().asString()
+ << Args[0].Value.getTypeAsString();
+ }
+ if (Args.size() == 1) {
+ return outvalueToVariantMatcher(
+ NoFlags(ArgTypeTraits::get(Args[0].Value)));
+ }
+ if (!ArgTypeTraits::is(Args[1].Value)) {
+ Error->addError(Args[1].Range, Error->ET_RegistryWrongArgType)
+ << 2 << ArgTypeTraits::getKind().asString()
+ << Args[1].Value.getTypeAsString();
+ return VariantMatcher();
+ }
+ return outvalueToVariantMatcher(
+ WithFlags(ArgTypeTraits::get(Args[0].Value),
+ ArgTypeTraits::get(Args[1].Value)));
+ }
+
+private:
+ ReturnType (*const WithFlags)(StringRef, llvm::Regex::RegexFlags);
+ ReturnType (*const NoFlags)(StringRef);
+ const std::vector RetKinds;
+};
+
/// Variadic operator marshaller function.
class VariadicOperatorMatcherDescriptor : public MatcherDescriptor {
public:
@@ -814,6 +892,16 @@
reinterpret_cast(Func), MatcherName, RetTypes, AKs);
}
+template
+std::unique_ptr makeMatcherRegexMarshall(
+ ReturnType (*FuncFlags)(llvm::StringRef, llvm::Regex::RegexFlags),
+ ReturnType (*Func)(llvm::StringRef)) {
+ std::vector RetTypes;
+ BuildReturnTypeVector::build(RetTypes);
+ return std::make_unique>(FuncFlags, Func,
+ RetTypes);
+}
+
/// Variadic overload.
template )>
diff --git a/clang/lib/ASTMatchers/Dynamic/Marshallers.cpp b/clang/lib/ASTMatchers/Dynamic/Marshallers.cpp
--- a/clang/lib/ASTMatchers/Dynamic/Marshallers.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Marshallers.cpp
@@ -110,3 +110,27 @@
"UETT_");
return llvm::None;
}
+
+llvm::Optional
+clang::ast_matchers::dynamic::internal::ArgTypeTraits<
+ llvm::Regex::RegexFlags>::getFlags(llvm::StringRef Flags) {
+ llvm::Regex::RegexFlags Flag = llvm::Regex::NoFlags;
+ SmallVector Split;
+ Flags.split(Split, '|', -1, false);
+ for (StringRef OrFlag : Split) {
+ OrFlag = OrFlag.trim();
+ if (OrFlag.empty())
+ continue;
+ if (OrFlag == "IgnoreCase")
+ Flag |= llvm::Regex::IgnoreCase;
+ else if (OrFlag == "NewLine")
+ Flag |= llvm::Regex::Newline;
+ else if (OrFlag == "BasicRegex")
+ Flag |= llvm::Regex::BasicRegex;
+ else if (OrFlag == "NoFlags")
+ continue;
+ else
+ return None;
+ }
+ return Flag;
+}
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -90,6 +90,9 @@
REGISTER_MATCHER_OVERLOAD(name); \
} while (false)
+#define REGISTER_REGEX_MATCHER(name) \
+ registerMatcher(#name, internal::makeMatcherRegexMarshall(name, name))
+
/// Generate a registry map with all the known matchers.
/// Please keep sorted alphabetically!
RegistryMaps::RegistryMaps() {
@@ -121,6 +124,10 @@
};
REGISTER_MATCHER_OVERLOAD(equals);
+ REGISTER_REGEX_MATCHER(isExpansionInFileMatching);
+ REGISTER_REGEX_MATCHER(matchesName);
+ REGISTER_REGEX_MATCHER(matchesSelector);
+
REGISTER_MATCHER(accessSpecDecl);
REGISTER_MATCHER(addrLabelExpr);
REGISTER_MATCHER(alignOfExpr);
@@ -374,7 +381,6 @@
REGISTER_MATCHER(isEnum);
REGISTER_MATCHER(isExceptionVariable);
REGISTER_MATCHER(isExpandedFromMacro);
- REGISTER_MATCHER(isExpansionInFileMatching);
REGISTER_MATCHER(isExpansionInMainFile);
REGISTER_MATCHER(isExpansionInSystemHeader);
REGISTER_MATCHER(isExplicit);
@@ -429,8 +435,6 @@
REGISTER_MATCHER(labelStmt);
REGISTER_MATCHER(lambdaExpr);
REGISTER_MATCHER(linkageSpecDecl);
- REGISTER_MATCHER(matchesName);
- REGISTER_MATCHER(matchesSelector);
REGISTER_MATCHER(materializeTemporaryExpr);
REGISTER_MATCHER(member);
REGISTER_MATCHER(memberExpr);
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
--- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -77,6 +77,12 @@
EXPECT_TRUE(matches("namespace x { int kTest; }", StartsWithK));
EXPECT_TRUE(matches("class C { int k; };", StartsWithK));
EXPECT_TRUE(notMatches("class C { int ckc; };", StartsWithK));
+ EXPECT_TRUE(notMatches("int K;", StartsWithK));
+
+ DeclarationMatcher StartsWithKIgnoreCase =
+ namedDecl(matchesName(":k[^:]*$", llvm::Regex::IgnoreCase));
+ EXPECT_TRUE(matches("int k;", StartsWithKIgnoreCase));
+ EXPECT_TRUE(matches("int K;", StartsWithKIgnoreCase));
}
TEST(DeclarationMatcher, MatchClass) {
diff --git a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
--- a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
+++ b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
@@ -259,6 +259,15 @@
EXPECT_TRUE(matches("unsigned X = sizeof(int);", MStmt));
EXPECT_FALSE(matches("unsigned X = alignof(int);", MStmt));
+ Code =
+ R"query(namedDecl(matchesName("^::[ABC]*$", "IgnoreCase | BasicRegex")))query";
+ llvm::Optional MatchesName(
+ Parser::parseMatcherExpression(Code, nullptr, nullptr, &Error));
+ EXPECT_EQ("", Error.toStringFull());
+ M = MatchesName->unconditionalConvertTo();
+ EXPECT_TRUE(matches("unsigned AAACCBB;", M));
+ EXPECT_TRUE(matches("unsigned aaaccbb;", M));
+
Code = "hasInitializer(\n binaryOperator(hasLHS(\"A\")))";
EXPECT_TRUE(!Parser::parseMatcherExpression(Code, &Error).hasValue());
EXPECT_EQ("1:1: Error parsing argument 1 for matcher hasInitializer.\n"
@@ -348,6 +357,26 @@
"1:14: Incorrect type for arg 1. (Expected = string) != (Actual = "
"String)",
ParseMatcherWithError(R"query(decl(hasAttr("unrelated")))query"));
+ EXPECT_EQ("1:1: Error parsing argument 1 for matcher namedDecl.\n"
+ "1:11: Error building matcher matchesName.\n"
+ "1:33: Incorrect type for arg 2. (Expected = string) != (Actual = "
+ "String)",
+ ParseMatcherWithError(
+ R"query(namedDecl(matchesName("[ABC]*", "Ignorecase")))query"));
+ EXPECT_EQ(
+ "1:1: Error parsing argument 1 for matcher namedDecl.\n"
+ "1:11: Error building matcher matchesName.\n"
+ "1:33: Incorrect type for arg 2. (Expected = string) != (Actual = "
+ "String)",
+ ParseMatcherWithError(
+ R"query(namedDecl(matchesName("[ABC]*", "IgnoreCase & BasicRegex")))query"));
+ EXPECT_EQ(
+ "1:1: Error parsing argument 1 for matcher namedDecl.\n"
+ "1:11: Error building matcher matchesName.\n"
+ "1:33: Incorrect type for arg 2. (Expected = string) != (Actual = "
+ "String)",
+ ParseMatcherWithError(
+ R"query(namedDecl(matchesName("[ABC]*", "IgnoreCase | Basicregex")))query"));
}
TEST(ParserTest, OverloadErrors) {
diff --git a/llvm/include/llvm/Support/Regex.h b/llvm/include/llvm/Support/Regex.h
--- a/llvm/include/llvm/Support/Regex.h
+++ b/llvm/include/llvm/Support/Regex.h
@@ -16,6 +16,7 @@
#ifndef LLVM_SUPPORT_REGEX_H
#define LLVM_SUPPORT_REGEX_H
+#include "llvm/ADT/BitmaskEnum.h"
#include
struct llvm_regex;
@@ -26,20 +27,22 @@
class Regex {
public:
- enum {
- NoFlags=0,
+ enum RegexFlags : unsigned {
+ NoFlags = 0,
/// Compile for matching that ignores upper/lower case distinctions.
- IgnoreCase=1,
+ IgnoreCase = 1,
/// Compile for newline-sensitive matching. With this flag '[^' bracket
/// expressions and '.' never match newline. A ^ anchor matches the
/// null string after any newline in the string in addition to its normal
/// function, and the $ anchor matches the null string before any
/// newline in the string in addition to its normal function.
- Newline=2,
+ Newline = 2,
/// By default, the POSIX extended regular expression (ERE) syntax is
/// assumed. Pass this flag to turn on basic regular expressions (BRE)
/// instead.
- BasicRegex=4
+ BasicRegex = 4,
+
+ LLVM_MARK_AS_BITMASK_ENUM(BasicRegex)
};
Regex();
@@ -47,7 +50,8 @@
///
/// \param Regex - referenced string is no longer needed after this
/// constructor does finish. Only its compiled form is kept stored.
- Regex(StringRef Regex, unsigned Flags = NoFlags);
+ Regex(StringRef Regex, RegexFlags Flags = NoFlags);
+ Regex(StringRef Regex, unsigned Flags);
Regex(const Regex &) = delete;
Regex &operator=(Regex regex) {
std::swap(preg, regex.preg);
diff --git a/llvm/lib/Support/Regex.cpp b/llvm/lib/Support/Regex.cpp
--- a/llvm/lib/Support/Regex.cpp
+++ b/llvm/lib/Support/Regex.cpp
@@ -26,7 +26,7 @@
Regex::Regex() : preg(nullptr), error(REG_BADPAT) {}
-Regex::Regex(StringRef regex, unsigned Flags) {
+Regex::Regex(StringRef regex, RegexFlags Flags) {
unsigned flags = 0;
preg = new llvm_regex();
preg->re_endp = regex.end();
@@ -39,6 +39,9 @@
error = llvm_regcomp(preg, regex.data(), flags|REG_PEND);
}
+Regex::Regex(StringRef regex, unsigned Flags)
+ : Regex(regex, static_cast(Flags)) {}
+
Regex::Regex(Regex &®ex) {
preg = regex.preg;
error = regex.error;