7,951 changes: 5,679 additions & 2,272 deletions clang/docs/LibASTMatchersReference.html

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,9 @@ AST Matchers
- Fixed an ordering issue with the `hasOperands` matcher occuring when setting a
binding in the first matcher and using it in the second matcher.

- The examples in the AST matcher reference are now tested and additional
examples and descriptions were added.

clang-format
------------

Expand Down
9 changes: 8 additions & 1 deletion clang/docs/doxygen.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,14 @@ TAB_SIZE = 2
# "Side Effects:". You can put \n's in the value part of an alias to insert
# newlines.

ALIASES =
ALIASES += compile_args{1}="Compiled with <tt>\1</tt>.\n"
ALIASES += matcher{1}="<tt>\1</tt>"
ALIASES += matcher{2$}="<tt>\2</tt>"
ALIASES += match{1}="<tt>\1</tt>"
ALIASES += match{2$}="<tt>\2</tt>"
ALIASES += nomatch{1}="<tt>\1</tt>"
ALIASES += header{1}="\code"
ALIASES += endheader="\endcode"

# This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding "class=itcl::class"
Expand Down
68 changes: 63 additions & 5 deletions clang/docs/tools/dump_ast_matchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,72 @@ def extract_result_types(comment):
comment = m.group(1)


def find_next_closing_rbrace(
data: str, start_pos: int, braces_to_be_matched: int
) -> int:
"""Finds the location of the closing rbrace '}' inside of data."""
"""'start_pos' should be one past the opening lbrace and braces_to_be_matched is initialized with 0"""
next_lbrace = data.find("{", start_pos)
next_rbrace = data.find("}", start_pos)
if next_lbrace != -1:
if next_lbrace < next_rbrace:
return find_next_closing_rbrace(
data, next_lbrace + 1, braces_to_be_matched + 1
)
if braces_to_be_matched == 0:
return next_rbrace
return find_next_closing_rbrace(data, next_rbrace + 1, braces_to_be_matched - 1)

if braces_to_be_matched > 0:
return find_next_closing_rbrace(data, next_rbrace + 1, braces_to_be_matched - 1)

return next_rbrace


def strip_doxygen(comment):
"""Returns the given comment without \-escaped words."""
# If there is only a doxygen keyword in the line, delete the whole line.
comment = re.sub(r"^\\[^\s]+\n", r"", comment, flags=re.M)

# If there is a doxygen \see command, change the \see prefix into "See also:".
# FIXME: it would be better to turn this into a link to the target instead.
comment = re.sub(r"\\see", r"See also:", comment)

commands: list[str] = [
"\\compile_args{",
"\\matcher{",
"\\match{",
"\\nomatch{",
]

for command in commands:
delete_command = command == "\\compile_args{"
command_begin_loc = comment.find(command)
while command_begin_loc != -1:
command_end_loc = command_begin_loc + len(command)
end_brace_loc = find_next_closing_rbrace(comment, command_end_loc + 1, 0)
if end_brace_loc == -1:
print("found unmatched {")
command_begin_loc = comment.find(command, command_end_loc)
continue

if delete_command:
comment = comment[0:command_begin_loc] + comment[end_brace_loc + 1 :]
command_begin_loc = comment.find(command, command_begin_loc)
continue

tag_seperator_loc = comment.find("$", command_end_loc)
if tag_seperator_loc != -1 and tag_seperator_loc < end_brace_loc:
command_end_loc = tag_seperator_loc + 1

comment = (
comment[0:command_begin_loc]
+ comment[command_end_loc:end_brace_loc]
+ comment[end_brace_loc + 1 :]
)

command_begin_loc = comment.find(command, command_begin_loc)

# If there is only a doxygen keyword in the line, delete the whole line.
comment = re.sub(r"^\\[^\s]+\n", r"", comment, flags=re.M)

# Delete the doxygen command and the following whitespace.
comment = re.sub(r"\\[^\s]+\s+", r"", comment)
return comment
Expand Down Expand Up @@ -191,8 +248,9 @@ def act_on_decl(declaration, comment, allowed_types):
definition.
"""
if declaration.strip():

if re.match(r"^\s?(#|namespace|using|template <typename NodeType> using|})", declaration):
if re.match(
r"^\s?(#|namespace|using|template <typename NodeType> using|})", declaration
):
return

# Node matchers are defined by writing:
Expand Down
5,783 changes: 4,120 additions & 1,663 deletions clang/include/clang/ASTMatchers/ASTMatchers.h

Large diffs are not rendered by default.

45 changes: 19 additions & 26 deletions clang/include/clang/Testing/TestClangConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ struct TestClangConfig {
;
}

bool isC(int Version) const {
return false
#define TESTLANGUAGE_C(lang, version, std_flag, version_index) \
|| (Version == version && Language == Lang_##lang##version)
#include "clang/Testing/TestLanguage.def"
;
}

bool isCOrLater(int MinimumStdVersion) const {
const auto MinimumStdVersionIndex = 0
#define TESTLANGUAGE_C(lang, version, std_flag, version_index) \
Expand All @@ -54,19 +62,7 @@ struct TestClangConfig {
bool isC99OrLater() const { return isCOrLater(99); }

bool isCOrEarlier(int MaximumStdVersion) const {
const auto MaximumStdVersionIndex = 0
#define TESTLANGUAGE_C(lang, version, std_flag, version_index) \
+(MaximumStdVersion == version ? version_index : 0)
#include "clang/Testing/TestLanguage.def"
;
switch (Language) {
#define TESTLANGUAGE_C(lang, version, std_flag, version_index) \
case Lang_##lang##version: \
return MaximumStdVersionIndex >= version_index;
#include "clang/Testing/TestLanguage.def"
default:
return false;
}
return isC() && (isC(MaximumStdVersion) || !isCOrLater(MaximumStdVersion));
}

bool isCXX() const {
Expand All @@ -77,6 +73,14 @@ struct TestClangConfig {
;
}

bool isCXX(int Version) const {
return false
#define TESTLANGUAGE_CXX(lang, version, std_flag, version_index) \
|| (Version == version && Language == Lang_##lang##version)
#include "clang/Testing/TestLanguage.def"
;
}

bool isCXXOrLater(int MinimumStdVersion) const {
const auto MinimumStdVersionIndex = 0
#define TESTLANGUAGE_CXX(lang, version, std_flag, version_index) \
Expand Down Expand Up @@ -104,19 +108,8 @@ struct TestClangConfig {
bool isCXX23OrLater() const { return isCXXOrLater(23); }

bool isCXXOrEarlier(int MaximumStdVersion) const {
const auto MaximumStdVersionIndex = 0
#define TESTLANGUAGE_CXX(lang, version, std_flag, version_index) \
+(MaximumStdVersion == version ? version_index : 0)
#include "clang/Testing/TestLanguage.def"
;
switch (Language) {
#define TESTLANGUAGE_CXX(lang, version, std_flag, version_index) \
case Lang_##lang##version: \
return MaximumStdVersionIndex >= version_index;
#include "clang/Testing/TestLanguage.def"
default:
return false;
}
return isCXX() &&
(isCXX(MaximumStdVersion) || !isCXXOrLater(MaximumStdVersion));
}

bool supportsCXXDynamicExceptionSpecification() const {
Expand Down
2 changes: 0 additions & 2 deletions clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2030,8 +2030,6 @@ TEST_P(ASTMatchersTest,
template <typename T>
class VerifyAncestorHasChildIsEqual : public BoundNodesCallback {
public:
bool run(const BoundNodes *Nodes) override { return false; }

bool run(const BoundNodes *Nodes, ASTContext *Context) override {
const T *Node = Nodes->getNodeAs<T>("");
return verify(*Nodes, *Context, Node);
Expand Down
435 changes: 426 additions & 9 deletions clang/unittests/ASTMatchers/ASTMatchersTest.h

Large diffs are not rendered by default.

15 changes: 4 additions & 11 deletions clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5653,7 +5653,6 @@ TEST(HasParent, MatchesAllParents) {
TEST(HasParent, NoDuplicateParents) {
class HasDuplicateParents : public BoundNodesCallback {
public:
bool run(const BoundNodes *Nodes) override { return false; }
bool run(const BoundNodes *Nodes, ASTContext *Context) override {
const Stmt *Node = Nodes->getNodeAs<Stmt>("node");
std::set<const void *> Parents;
Expand Down Expand Up @@ -5862,16 +5861,14 @@ template <typename T> class VerifyMatchOnNode : public BoundNodesCallback {
public:
VerifyMatchOnNode(StringRef Id, const internal::Matcher<T> &InnerMatcher,
StringRef InnerId)
: Id(Id), InnerMatcher(InnerMatcher), InnerId(InnerId) {
}

bool run(const BoundNodes *Nodes) override { return false; }
: Id(Id), InnerMatcher(InnerMatcher), InnerId(InnerId) {}

bool run(const BoundNodes *Nodes, ASTContext *Context) override {
const T *Node = Nodes->getNodeAs<T>(Id);
return selectFirst<T>(InnerId, match(InnerMatcher, *Node, *Context)) !=
nullptr;
nullptr;
}

private:
std::string Id;
internal::Matcher<T> InnerMatcher;
Expand Down Expand Up @@ -6065,7 +6062,7 @@ namespace {
class ForCallablePreservesBindingWithMultipleParentsTestCallback
: public BoundNodesCallback {
public:
bool run(const BoundNodes *BoundNodes) override {
bool run(const BoundNodes *BoundNodes, ASTContext *Context) override {
FunctionDecl const *FunDecl =
BoundNodes->getNodeAs<FunctionDecl>("funDecl");
// Validate test assumptions. This would be expressed as ASSERT_* in
Expand Down Expand Up @@ -6102,10 +6099,6 @@ class ForCallablePreservesBindingWithMultipleParentsTestCallback
return true;
}

bool run(const BoundNodes *BoundNodes, ASTContext *Context) override {
return run(BoundNodes);
}

private:
void ExpectCorrectResult(StringRef LogInfo,
ArrayRef<BoundNodes> Results) const {
Expand Down
15 changes: 15 additions & 0 deletions clang/unittests/ASTMatchers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,26 @@ set(LLVM_LINK_COMPONENTS
TargetParser
)

add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ASTMatchersDocTests.cpp
COMMAND
${CLANG_SOURCE_DIR}/utils/generate_ast_matcher_doc_tests.py ARGS
--input-file
${CLANG_SOURCE_DIR}/include/clang/ASTMatchers/ASTMatchers.h
--output-file
${CMAKE_CURRENT_BINARY_DIR}/ASTMatchersDocTests.cpp
MAIN_DEPENDENCY
${CLANG_SOURCE_DIR}/include/clang/ASTMatchers/ASTMatchers.h
DEPENDS
${CLANG_SOURCE_DIR}/utils/generate_ast_matcher_doc_tests.py ARGS
)

add_clang_unittest(ASTMatchersTests
ASTMatchersInternalTest.cpp
ASTMatchersNodeTest.cpp
ASTMatchersNarrowingTest.cpp
ASTMatchersTraversalTest.cpp
${CMAKE_CURRENT_BINARY_DIR}/ASTMatchersDocTests.cpp
GtestMatchersTest.cpp
)

Expand Down
1,165 changes: 1,165 additions & 0 deletions clang/utils/generate_ast_matcher_doc_tests.py

Large diffs are not rendered by default.