-
Notifications
You must be signed in to change notification settings - Fork 1
Matcher Reference
Clang for Unreal Engine supports all of the AST matchers that are built into the original Clang, plus additional matchers to support Unreal Engine code and a few other common scenarios.
For a list of matchers that are supported because they're in the original Clang, refer to the Upstream AST Matcher Reference document. Most of the matchers you are interested in will be found on that page.
Narrowing matchers match certain attributes on the current node, thus narrowing down the set of nodes of the current type to match on.
Matcher<NamedDecl> | isUClass |
Matches if the declaration is a UCLASS()
.
For example, using this matcher would match UA
but not B
:
UCLASS()
class UA : public UObject
{
GENERATED_BODY();
};
class B
{
};
Matcher<NamedDecl> | isUStruct |
Matches if the declaration is a USTRUCT()
.
For example, using this matcher would match FA
but not B
:
USTRUCT()
struct FA
{
GENERATED_BODY();
};
struct B
{
};
Matcher<NamedDecl> | isUInterface |
Matches if the declaration is a UINTERFACE()
.
For example, using this matcher would match UI
but not B
(it also doesn't match II
):
UINTERFACE()
class UI : public UInterface
{
GENERATED_BODY();
};
class II
{
GENERATED_BODY();
}
class B
{
};
Matcher<NamedDecl> | isIInterface |
Matches if the declaration is the associated interface of a UINTERFACE()
.
For example, using this matcher would match II
but not UI
:
UINTERFACE()
class UI : public UInterface
{
GENERATED_BODY();
};
class II
{
GENERATED_BODY();
}
class B
{
};
Matcher<NamedDecl> | isUFunction |
Matches if the declaration is a UFUNCTION()
.
For example, using this matcher would match A
but not B
:
UCLASS()
class UObj : public UObject
{
GENERATED_BODY();
public:
UFUNCTION()
void A();
void B();
};
Matcher<NamedDecl> | isUProperty |
Matches if the declaration is a UPROPERTY()
.
For example, using this matcher would match A
but not B
:
UCLASS()
class UObj : public UObject
{
GENERATED_BODY();
public:
UPROPERTY()
int A;
int B;
};
Matcher<NamedDecl> | hasUSpecifier | std::string Name |
Matches if the declaration has Name
as a specifier. Name
is a case insensitive match. For the following code:
UPROPERTY(Replicated)
int A;
the A
field declaration would be matched by hasUSpecifier("Replicated")
.
Matcher<NamedDecl> | hasUSpecifierValue | std::string Name, std::string Value |
Matches if the declaration has Name
as a specifier with the value Value
. Name
is a case insensitive match. Value
is matched exactly. For the following code:
UPROPERTY(BlueprintGetter=Hello)
int A;
the A
field declaration would be matched by hasUSpecifierValue("blueprintgetter", "Hello")
.
Matcher<NamedDecl> | hasUMetadata | std::string Name |
Matches if the declaration has Name
as a specifier. Name
is a case insensitive match. For the following code:
UPROPERTY(meta = (Category = "Hello"))
int A;
the A
field declaration would be matched by hasUMetadata("Category")
.
Matcher<NamedDecl> | hasUMetadataValue | std::string Name, std::string Value |
Matches if the declaration has Name
as a specifier with the value Value
. Name
is a case insensitive match. Value
is matched exactly. For the following code:
UPROPERTY(meta = (Category = "Hello"))
int A;
the A
field declaration would be matched by hasUMetadataValue("category", "Hello")
.
Matcher<QualType> | isPODType |
Matches if the type is a Plain Old Data (POD) type.
For example, using this matcher would match the type of A
but not the type of B
:
class FStruct {
int A;
FString B;
}
Matcher<CXXRecordDecl>, Matcher<FunctionDecl>, Matcher<VarDecl> | isMissingDllImportOrExport |
Matches if the C++ class, struct, function or variable declaration could be marked __declspec(dllexport)
, but isn't. This can be used to find declarations in Public
folders that should have the ..._API
specifier on them, but don't.
This matcher only makes sense to use when targeting Windows, so make sure any rule that uses it is flagged with WindowsOnly: true
in the .clang-rules
file.
For example, using this matcher would match the variable A
but not the variable B
:
extern int A;
__declspec(dllexport) extern int B;
Matcher<ElaboratedTypeLoc> | hasRedundantNamespacing |
Matches if the type specifier has redundant namespace components.
For example, using this matcher would match the variable B
and C
but not A
:
namespace Z::Y::X {
class F {};
}
namespace Z::Y::W {
X::F A;
Y::X::F B;
Z::Y::X::F C;
}
Traversal matchers specify the relationship to other nodes that are reachable from the current node.
Matcher<*> | forNone | Matcher<*> |
The exclusion version of forEach
, this is intended to be used when you have at least one other forEach
matcher in the expression, and you want exclude a set of nodes that meet that other condition.
It's best explained with an example. Let us say you have a class whose method refers to the field declarations through member access, and you want to find the fields that are not accessed in Method
:
class Test {
int A;
int B;
int C;
int D;
void Method() {
this->A;
this->D;
}
}
If you were to use the matcher expression:
cxxMethodDecl(
hasName("Method"),
ofClass(
cxxRecordDecl(
forEach(
fieldDecl().bind("declared_field")
)
)
),
hasBody(
compoundStmt(
forEach(
memberExpr(
member(
fieldDecl().bind("referenced_field")
)
)
)
)
)
)
It would give you the following set of matches:
Match | declared_field | referenced_field |
---|---|---|
#1 | A | A |
#2 | B | A |
#3 | C | A |
#4 | D | A |
#5 | A | D |
#6 | B | D |
#7 | C | D |
#8 | D | D |
That is, it gives you every combination as a unique match result.
If you were to constrain the referenced_field
so that it had to match the declared_field
using equalsBoundNode
within forEach(memberExpr(member(fieldDecl(equalsBoundNode("declared_field")).bind("referenced_field"))))
, it would give you the subset where both are equal:
Match | declared_field | referenced_field |
---|---|---|
#1 | A | A |
#2 | D | D |
forNone
would give you the subset where there is no matching referenced_field
for declared_field
with forNone(memberExpr(member(fieldDecl(equalsBoundNode("declared_field")).bind("referenced_field"))))
:
Match | declared_field |
---|---|
#1 | B |
#2 | C |
Note that .bind()
within a forNone
has no effect, because any match that would generate bindings within a forNone
would preclude the result from being included anyway.
Matcher<*> | forNoDescendant | Matcher<*> |
forNoDescendant
is the forEachDescendant
equivalent of forNone
. Rather than just checking if there are no immediate children that match the inner matcher, it checks all descendants.
Matcher<TemplateArgument> | refersToPack | Matcher<TemplateArgument> InnerMatcher |
Where a given template argument matches a pack parameter (...
), this iterates over all of template arguments contained within a pack, and matches if any of them match InnerMatcher
.
Matcher<CXXRecordDecl> | withUInterface | Matcher<CXXRecordDecl> InnerMatcher |
Where a given matcher is an interface class of a UINTERFACE()
(that is, it is the ITheInterface
to an interface declared as UTheInterface
), this allows you to match on the the associated UInterface
declaration.
For example, in the following code:
UINTERFACE()
class UTheInterface : public UInterface
{
GENERATED_BODY()
};
class ITheInterface
{
GENERATED_BODY()
};
Then the following matcher expression would match:
cxxRecordDecl(hasName("ITheInterface"), withUInterface(cxxRecordDecl(hasName("UTheInterface"))))
Matcher<CXXRecordDecl> | withIInterface | Matcher<CXXRecordDecl> InnerMatcher |
Where a given matcher is a UINTERFACE()
(that is, it is the UTheInterface
which has an interface class ITheInterface
declared), this allows you to match on the the associated interface class declaration.
For example, in the following code:
UINTERFACE()
class UTheInterface : public UInterface
{
GENERATED_BODY()
};
class ITheInterface
{
GENERATED_BODY()
};
Then the following matcher expression would match:
cxxRecordDecl(hasName("UTheInterface"), withIInterface(cxxRecordDecl(hasName("ITheInterface"))))