Search code examples
c++templatesclanglibtoolinglocal-class

Use Clang LibTooling to scan C++ source that has call to local class in a templated parent class


Source code to scan:

template <typename T>
class HB {
    T m;
public:
    void HBfunc1();
};

template <typename T>
void HB<T>::HBfunc1() {  
    class HC {      
    public:
        void HCfunc2() { };
    };    
    HC().HCfunc2();
}

void TestTemplate() { HB<int>().HBfunc1(); };

The above code works fine in VS2019, when use Clang LibTooling to scan the AST:

virtual bool VisitCallExpr(CallExpr *call) {

    std::cout << "VisitCallExpr: ";
    if (call->getDirectCallee()) {
        std::cout << " "<<call->getDirectCallee()->getQualifiedNameAsString();
    }
    else {
        Decl *callee = call->getCalleeDecl();
        if (!callee) {
            std::cout << "\nNow dump call:\n";
            call->dump();
        }
    }
    std::cout<<"\n";
    return true;
}

When visiting the CallExpr for this line in the scaned source:

HC().HCfunc2();

The callee is null and the dump of the CallExpr are:

VisitCallExpr:
Now dump call:
CallExpr 0x1c2ef83b3a0 '<dependent type>'
`-CXXDependentScopeMemberExpr 0x1c2ef83b348 '<dependent type>' lvalue .HCfunc2
  `-CXXUnresolvedConstructExpr 0x1c2ef83b320 'class HC' 'class HC'

Clang doesn't report error in the scanning process (Since the code works fine).

In LLVM source code there is:

// This represents the type of an expression whose type is
// totally unknown, e.g. 'T::foo'.  It is permitted for this to
// appear in situations where the structure of the type is
// theoretically deducible.
BUILTIN_TYPE(Dependent, DependentTy)

Why the type HC is considered unknown? Will any type be unknown while scanning the AST without issueing any warning/erroe? How to visit invocation like this and extract information about its callee? Does the scaned code have problems?


Solution

  • Clang is being conservative in this case by marking the function call as unresolved (aka dependent). Dependent things appear only in templates. It means that some syntactic structure might depend on template parameters.

    HC class is not a usual class, you will have as many separate HC classes, as the number of instantiations of HB. So, if HC does not use T, there is little to no sense to place it inside of HBfunc1. Otherwise, it is dependent indeed. Clang prefers to be safe and avoids making assumptions that smth is not dependent when it is defined in a dependent context.

    Because of these reasons, it is absolutely normal to have AST nodes like this. It is not a parser error, everything worked as intended. As per exact information about the callee, short answer is - there is no way while analyzing the template. However, clang generates AST for every instantiation. Those have usual AST structure, and can be easily visited.

    I hope this information is helpful!