Search code examples
c++clangabstract-syntax-treeclang-tidy

How to use clang AST matcher usingDirectiveDecl to find using namespace directive with specific name?


I'm trying to prototype a clang-tidy matcher with clang-query to find using namespace directive with a certain name like using namespace ns1::ns2;.

With clang-query I tried these variants but none is matching anything:

clang-query> match usingDirectiveDecl(hasName("ns1")).bind("changeNamespaceName")
0 matches.

clang-query> match usingDirectiveDecl(hasName("ns2")).bind("changeNamespaceName")
0 matches.

clang-query> match usingDirectiveDecl(hasName("ns1::ns2")).bind("changeNamespaceName")
0 matches.

Is usingDirectiveDecl the right matcher for this task?

What is the right way to find a using namespace directive with a specific name?


Solution

  • Unfortunately, it appears to be impossible to do this with a clang-query AST matcher alone. usingDirectiveDecl matches using namespace declarations, but further restriction based on the nominated namespace is incomplete.

    While UsingDirectiveDecl does have a name, it is simply the placeholder string ::<using-directive>:

    clang-query> m usingDirectiveDecl(matchesName("^::<using-directive>$"))
    
    Match #1:
    
    /home/scott/wrk/learn/clang/using-namespace/un1.cc:22:3: note: "root" binds here
      using namespace ns1;
      ^~~~~~~~~~~~~~~~~~~
    
    Match #2:
    
    /home/scott/wrk/learn/clang/using-namespace/un1.cc:28:3: note: "root" binds here
      using namespace ns1::ns2;
      ^~~~~~~~~~~~~~~~~~~~~~~~
    2 matches.
    

    It turns out we can use hasDescendant to filter based on a namespace qualifier if one is present:

    clang-query> m usingDirectiveDecl(hasDescendant(nestedNameSpecifier(specifiesNamespace(matchesName("ns1")))))
    
    Match #1:
    
    /home/scott/wrk/learn/clang/using-namespace/un1.cc:28:3: note: "root" binds here
      using namespace ns1::ns2;
      ^~~~~~~~~~~~~~~~~~~~~~~~
    1 match.
    

    This is filtering using the QualifierLoc member of UsingDirectiveDecl. But there's no way to filter on what comes after the qualifier, nor if there is no qualifier at all. The reason appears to be simply the absence of an AST matcher that would restrict UsingDirectiveDecl based on its NominatedNamespace field.

    Consequently, within a clang-tidy check, the best you can do is use a matcher to find all using namespace declarations, then (in subsequent C++ code) inspect the result of calling getNominatedNamespace().