Search code examples
clangclang++llvm-clang

Determine if a CXXMethodDecl is instantiated or not


I'm writing a tool that traverses a clang AST and prints it out in a particular format, but I don't want to print function templates, just their (full) specializations. I'm wondering how to determine if a const CXXMethodDecl* represents a fully instantiated method (either it is not templated or it is a full instantiation of a templated method) or a not completely instantiated method. Here is an example of what I am seeing with my tool:

template <typename Z>
class P
{
    int lookup ();
};

template <typename Z>
int P<Z>::lookup ()
{
    return Z::f();
}

struct X {
    static int f();
};

template class P<X>;

constructs the following AST. I don't want to see the first CXXRecordDecl because that is not fully instantiated, but I do want to see the second one (which is fully instantiated).

|-ClassTemplateDecl 0x3eff5b8 <minimal.cpp:1:1, line:5:1> line:2:7 Pte
| |-TemplateTypeParmDecl 0x3eff480 <line:1:11, col:20> col:20 typename depth 0 index 0 PTT
| |-CXXRecordDecl 0x3eff530 <line:2:1, line:5:1> line:2:7 class Pte definition
| | |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveConstructor exists simple trivial needs_implicit
| | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveAssignment exists simple trivial needs_implicit
| | | `-Destructor simple irrelevant trivial needs_implicit
| | |-CXXRecordDecl 0x3eff800 <col:1, col:7> col:7 implicit class Pte
| | `-CXXMethodDecl 0x3eff910 <line:4:5, col:17> col:9 lookup 'int ()'
| `-ClassTemplateSpecialization 0x3f00010 'Pte'
|-CXXMethodDecl 0x3effbb0 parent 0x3eff530 prev 0x3eff910 <line:7:1, line:11:1> line:8:15 lookup 'int ()'
  ^^^^^^^^^^^^^^^^^^^^^^^^^^ I DO NOT WANT TO SEE THIS
| `-CompoundStmt 0x3effd50 <line:9:1, line:11:1>
|   `-ReturnStmt 0x3effd40 <line:10:5, col:19>
|     `-CallExpr 0x3effd20 <col:12, col:19> '<dependent type>'
|       `-CXXDependentScopeMemberExpr 0x3effcd8 <col:12, col:17> '<dependent type>' lvalue ->f
|-CXXRecordDecl 0x3effd68 <line:13:1, line:15:1> line:13:8 referenced struct X definition
| |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveConstructor exists simple trivial needs_implicit
| | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveAssignment exists simple trivial needs_implicit
| | `-Destructor simple irrelevant trivial needs_implicit
| |-CXXRecordDecl 0x3effe78 <col:1, col:8> col:8 implicit struct X
| `-CXXMethodDecl 0x3efff50 <line:14:5, col:18> col:16 used f 'int ()' static
`-ClassTemplateSpecializationDecl 0x3f00010 <line:17:1, col:21> col:16 class Pte definition
  |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
  | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
  | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
  | |-MoveConstructor exists simple trivial needs_implicit
  | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
  | |-MoveAssignment exists simple trivial needs_implicit
  | `-Destructor simple irrelevant trivial needs_implicit
  |-TemplateArgument type 'X'
  |-CXXRecordDecl 0x3f001f8 prev 0x3f00010 <line:2:1, col:7> col:7 implicit class Pte
  `-CXXMethodDecl 0x3f00280 <line:8:1, line:11:1> line:4:9 lookup 'int ()'
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ I want to see this
    `-CompoundStmt 0x3f2e1b0 <line:9:1, line:11:1>
      `-ReturnStmt 0x3f2e1a0 <line:10:5, col:19>
        `-CallExpr 0x3f2e180 <col:12, col:19> 'int'
          `-ImplicitCastExpr 0x3f2e168 <col:12, col:17> 'int (*)()' <FunctionToPointerDecay>
            `-DeclRefExpr 0x3f2e110 <col:12, col:17> 'int ()' lvalue CXXMethod 0x3efff50 'f' 'int ()'

Solution

  • Whenever type/function/variable is declared in a template or are a template, clang calls it a dependent context. You probably have seen the word dependent all around AST nodes within templates.

    I've modified your original test case to include a templated method in a non-template type:

    template <typename Z>
    class P
    {
        int lookup ();
    };
    
    template <typename Z>
    int P<Z>::lookup ()
    {
        return Z::f();
    }
    
    struct X {
        static int f() { return 42; }
    };
    
    struct Y {
        template <typename Z>
        int foo() { return Z::f(); }
    };
    
    template class P<X>;
    
    

    Here is a very simple recursive AST visitor printing out only functions that you are interested in:

    class NonDependentMethodVisitor
        : public clang::ASTConsumer,
          public clang::RecursiveASTVisitor<NonDependentMethodVisitor> {
    public:
      void HandleTranslationUnit(clang::ASTContext &Context) {
        this->TraverseTranslationUnitDecl(Context.getTranslationUnitDecl());
      }
    
      bool shouldVisitTemplateInstantiations() const { return true; }
    
      bool VisitCXXMethodDecl(clang::CXXMethodDecl *MD) {
        if (!MD->isDependentContext()) {
          MD->dump();
        }
    
        return true;
      }
    };
    
    

    which produces the following output for the test snippet:

    CXXMethodDecl 0x384eda0 <$TEST_DIR/test.cpp:14:5, col:33> col:16 used f 'int ()' static
    `-CompoundStmt 0x384ee80 <col:20, col:33>
      `-ReturnStmt 0x384ee70 <col:22, col:29>
        `-IntegerLiteral 0x384ee50 <col:29> 'int' 42
    CXXMethodDecl 0x387e170 <$TEST_DIR/test.cpp:8:1, line:11:1> line:4:9 lookup 'int ()'
    `-CompoundStmt 0x387e450 <line:9:1, line:11:1>
      `-ReturnStmt 0x387e440 <line:10:5, col:17>
        `-CallExpr 0x387e420 <col:12, col:17> 'int'
          `-ImplicitCastExpr 0x387e408 <col:12, col:15> 'int (*)()' <FunctionToPointerDecay>
            `-DeclRefExpr 0x387e3b0 <col:12, col:15> 'int ()' lvalue CXXMethod 0x384eda0 'f' 'int ()'
    
    

    I hope this answers your question!