How to match a struct member access using clang AST matcher?

298 Views Asked by At

I am trying to write a clang-tidy check to rename struct members. For this, I need to match MemberExpr nodes that access a certain member, from certain structs.

In the following code, it should only match all uses of member on types S1 and S2 (8 matches in total).

typedef struct S1 { int member; int v; } S1; typedef struct MS1 {S1 s1; } MS1;
typedef struct S2 { int member; int v; } S2; typedef struct MS2 {S2 s2; } MS2;
typedef struct S3 { int member; int v; } S3; typedef struct MS3 {S2 s3; } MS3;

void f() {
  S1 *s1a, s1b; MS1 *ms1a, ms1b;
  S2 *s2a, s2b; MS2 *ms2a, ms2b;
  S3 *s3a, s3b; MS3 *ms3a, ms3b;

  (void)s1a->member; (void)s1b.member; (void)ms1a->s1.member; (void)ms1b.s1.member;
  (void)s2a->member; (void)s2b.member; (void)ms2a->s2.member; (void)ms2b.s2.member;
  (void)s3a->member; (void)s3b.member; (void)ms3a->s3.member; (void)ms3b.s3.member;

  (void)s1a->v; (void)s1b.v; (void)ms1a->s1.v; (void)ms1b.s1.v;
  (void)s2a->v; (void)s2b.v; (void)ms2a->s2.v; (void)ms2b.s2.v;
  (void)s3a->v; (void)s3b.v; (void)ms3a->s3.v; (void)ms3b.s3.v;
}

The matcher memberExpr(member(hasName("member"))) is too broad and also includes type S3.

How can I limit the matcher to only return those member accesses of S1 and S2? Thanks.

1

There are 1 best solutions below

1
Scott McPeak On BEST ANSWER

Perhaps surprisingly, the hasName selector can be used to restrict the member's containing class or namespace as well as its simple identifier name. Quoting the linked documentation:

Matches NamedDecl nodes that have the specified name.

Supports specifying enclosing namespaces or classes by prefixing the name
with '<enclosing>::'.
Does not match typedefs of an underlying type with the given name.

Example matches X (Name == "X")
  class X;

Example matches X (Name is one of "::a::b::X", "a::b::X", "b::X", "X")
  namespace a { namespace b { class X; } }

This means we can match S1::member and S2::member like this:

memberExpr(member(anyOf(hasName("S1::member"),hasName("S2::member"))))

We can also use matchesName to express the conditionality using a regular expression:

memberExpr(member(matchesName("S[12]::member")))

With either of the above, I get eight matches on your testcase, but only after fixing the apparent typo in this line:

typedef struct S3 { int member; int v; } S3; typedef struct MS3 {S2 s3; } MS3;

which I think should be:

typedef struct S3 { int member; int v; } S3; typedef struct MS3 {S3 s3; } MS3;
                                                                  ^