Yes those lines represent nodes. You do not necessarily need ASTContext to look at a node’s descendants: you can just use the node’s methods.
Were these sugar (= syntax-only) type nodes, you could iterate over the child of each successive node via desugar(), and successively desugar while searching for a Type subclass T via getAs<T>().
But pointee types/array element types are of course not sugar: they are distinct semantic entities, so desugar/getAs won’t will not pass through them. Hence your problem.
Fortunately there seems to be a getPointeeOrArrayElementType() to help peel off the semantic layers you want to look through, analogous to desugar() — but unfortunately there is not a getAs analog for this case, so you have to write the search manually.
Something like this should work (not tested):
PointerType *getInnermostPointerType(const Type *T) {
const PointerType *res = nullptr;
while (true) {
const Type *pointeeOrElement = T->getPointeeOrArrayElementType();
if (pointeeOrElement == T)
// T is neither a pointer nor array type: we’re done.
break;
// T is a pointer or array type.
if (isa<PointerType>(T))
res = cast<PointerType>(T);
// iterate T, keep looking
T = pointeeOrElement;
}
return res;
}