clangllvmlibtooling

clang DeclContext::getParent() not returning parent RecordDecl for nested structs


I'm using the following code get parent struct of a nested struct using clang libtooling.

bool VisitRecordDecl(clang::RecordDecl *RD) {
    llvm::outs() <<"\n\tTrying to get parents of "<<RD->getNameAsString()<<"\n\n";
    clang::RecordDecl *Parent = dyn_cast<clang::RecordDecl>(RD->getParent()); 
    if(!Parent) {
      llvm::outs()<<"NULL RecordDecl with dyn_cast\n";
      clang::DeclContext *DParent = RD->getParent(); // trying to find what did getParent return
      llvm::outs()<<DParent->getDeclKindName()<<"\n"; // prints TransaltionUnit
      DParent->dumpDeclContext(); // prints whole source file
    }
    else{
      Parent->dump();
    }
    return true;
}

This is the source file in which I run the code

struct outer{
        struct inner{
                int ia;
                int ib;
                struct deep{
                        int da;
                }dobj;
        }iobj;
        int oa;
}oboj;
int main(){
        return 0;
}

For each invocation of VisitRecordDecl, getParent returns a TranslationUnit which cannot be casted into RecordDecl to get parent struct of inner or deep struct. getLexicalParent() returns RecordDecl correctly but why is getParent() not working?


Solution

  • The DeclContext getParent method returns the "semantic" parent. Although the concept of semantic parent has some quirks (see the getLookupParent method implementation), to a first approximation, the semantic parent of B is A if B can be found by qualified lookup as A::B.

    But the code in question is being interpreted as C, not C++ (per a clarification in a comment), and C does not have qualified lookup at all! C does allow structure definitions to appear inside other structure definitions, for example:

    struct Outer {
      struct Inner {
        int i;
      };
      int o;
    };
    

    but the effect is semantically the same as if they had not been nested:

    struct Outer {
      int o;
    };
    struct Inner {
      int i;
    };
    

    In particular, to name the inner type in C, one writes just struct Inner, not struct Outer::Inner. See C99 6.7.2.1p7 and compare to C++11 9.2p1. See also the question Nested structures in C and C++.

    Consequently, getParent returns the TranslationUnitDecl for both RecordDecls.

    As you've noted, you can instead use the getLexicalParent method to get the syntactic context in which the declaration appeared.

    If you compile the code as C++, either by changing the file extension to one that clang recognizes as being C++ (such as .cc or .cpp), or pass the -xc++ command line option to the command line parser, then the semantic parent of Inner will be reported as Outer.