-
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"))))