Clang AST matching method call on class, derived class or typedef to either - clang

I have a matcher that works perfectly for matching operator() calls on instances of a class or classes derived from that class. For example, it matches the final line of:
class MyBase { void operator()(...) {} };
MyBase b;
b(parameters);
using a matcher like:
const auto MyBaseExpr =
expr(hasType(cxxRecordDecl(isSameOrDerivedFrom("::MyBase"))));
Finder->addMatcher(traverse(
TK_AsIs, cxxOperatorCallExpr(
hasOverloadedOperatorName("()"),
hasArgument(0, anyOf(MyBaseExpr, MyOtherBaseExpr)),
hasAnyArgument(...),
this);
But I'd also like to be able to match such calls on instances of typedefs for the base or derived types like in the last line below:
typedef MyBase MyTypedef;
MyTypedef t;
t(parameters);
and I can't seem to fathom the correct way to specify this match. Attempting to use hasUnqualifiedDesugaredType rather than hasType doesn't work since it works on a type rather than a Decl and if I try to do more matching with the type then I can't use isSameOrDerived which returns a Matcher<CXXRecordDecl>. A similar problem occurs when trying to use hasCanonicalType:
.../RedundantStringCStrCheck.cpp:193:40: error: invalid initialization of reference of type ‘const clang::ast_matchers:
:internal::Matcher<clang::QualType>&’ from expression of type ‘clang::ast_matchers::internal::BindableMatcher<clang::Decl>’
193 | expr(hasCanonicalType(cxxRecordDecl(isSameOrDerivedFrom("::MyBase"))));

MyTypedef is defined from MyBase so its Canonical Type should be MyBase. More information about canonical type: https://clang.llvm.org/docs/InternalsManual.html#canonical-types
This is the example from LibASTMatchersReference , it uses hasType().

Thien Tran provided the pointer which led me to the right answer. Here's my original expression
const auto MyBaseExpr =
expr(hasType(cxxRecordDecl(isSameOrDerivedFrom("::MyBase"))));
I was trying to use:
const auto MyBaseExpr =
expr(hasCanonicalType(cxxRecordDecl(isSameOrDerivedFrom("::MyBase"))));
but the description of hasCanonicalType in LibASTMatchersReference shows that it takes and returns Matcher<QualType> yet cxxRecordDecl has type Matcher<Decl>, so this did not compile.
The mismatch of types can be corrected by inserting a call to hasDeclaration. It's then also necessary to keep the call to hasType in order to turn the Matcher<QualType> result of hasCanonicalType back into something that can be passed to expr.
After all that I ended up with:
const auto MyBaseExpr =
expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(isSameOrDerivedFrom("::MyBase"))))));
which seems to work perfectly.

Related

Find function/method body explicit dependency types using Dart analyzer package

I would like to understand how can I analyze methods / functions body to find types that are explicitly referenced from it. I have success analyzing method declaration (return type, parameter types, etc..), however I have no idea how to do that for body.
Assuming following function:
String someFunction(int param) {
final list = <String>['a', 'b', 'c']; // -> DartTypes: String, List<String>
final myClass = MyClass<Arg>(); // -> DartTypes: Arg, MyClass<Arg>
final functionCall = anotherFunction<FunctionArg<Arg>>(); // -> DartTypes: Arg, FunctionArg<Arg>
return 'result';
}
// At is point I would like to know that my function depends on
// String, List<String>, Arg, MyClass<Arg>, FunctionArg<Arg>
// in term of DartType instances with proper typeArguments.
I tried getting AstNode for method element described here: https://stackoverflow.com/a/57043177/2033394
However I could not get elements from nodes to figure out their types. Their declaredElement values are always null. So I can not get back to Element API from AST API.
If you've used the exact snippet from the answer you've referenced, the problem is likely in getParsedLibraryByElement(). This method only parses the referenced library - meaning that you'll get an AST that doesn't necessarily have semantic references (like the declaredElement of AST nodes) set.
Instead, you'll want to use getResolvedLibraryByElement. The AST returned by that method will have its types and references fully resolved.
With the resolved AST, you could visit the body of the method with a custom visitor to find type references. Your definition of "referenced types" isn't really exact - but perhaps you can collect types in visitNamedType for type references and visitVariableDeclaration to collect the types of variables.

Does the using declaration allow for incomplete types in all cases?

I'm a bit confused about the implications of the using declaration. The keyword implies that a new type is merely declared. This would allow for incomplete types. However, in some cases it is also a definition, no? Compare the following code:
#include <variant>
#include <iostream>
struct box;
using val = std::variant<std::monostate, box, int, char>;
struct box
{
int a;
long b;
double c;
box(std::initializer_list<val>) {
}
};
int main()
{
std::cout << sizeof(val) << std::endl;
}
In this case I'm defining val to be some instantiation of variant. Is this undefined behaviour? If the using-declaration is in fact a declaration and not a definition, incomplete types such as box would be allowed to instantiate the variant type. However, if it is also a definition, it would be UB no?
For the record, both gcc and clang both create "32" as output.
Since you've not included language-lawyer, I'm attempting a non-lawyer answer.
Why should that be UB?
With a using delcaration, you're just providing a synonym for std::variant<whatever>. That doesn't require an instantiation of the object, nor of the class std::variant, pretty much like a function declaration with a parameter of that class doesn't require it:
void f(val); // just fine
The problem would occur as soon as you give to that function a definition (if val is still incomplete because box is still incomplete):
void f(val) {}
But it's enough just to change val to val& for allowing a definition,
void f(val&) {}
because the compiler doesn't need to know anything else of val than its name.
Furthermore, and here I'm really inventing, "incomplete type" means that some definition is lacking at the point it's needed, so I expect you should discover such an issue at compile/link time, and not by being hit by UB. As in, how can the compiler and linker even finish their job succesfully if a definition to do something wasn't found?

Type safety in Dart using Container

I found something strange in dart. If there is a list that contains instances of a base class (in this example Super), the list can be set with a list of inherited instances. It seems that this changes the list type at runtime.
Is this intended behavior or is this a bug in Dart?
abstract class Super {}
class A extends Super {}
class B extends Super {}
class Container {
List<Super> mylist = [];
Container(this.mylist);
}
void main() {
// 1. dont't works
final container = Container(<A>[A(), A()]);
// 2. works
final container = Container([A(), A()]);
print(container.mylist.runtimeType);
container.mylist.add(B());
print(container.mylist);
}
If case 1 is used in the code above I get the following error:
JSArray<A>
Uncaught Error: TypeError: Instance of 'B': type 'B' is not a subtype of type 'A'
The error is at the line where I try to add an instance of B:
container.mylist.add(B());
Dart has a system called type promotion, where it can promote the type of a variable, similar to type inference.
It works as a cast. On the first example you've explicit promoted the type of your list to be of type A, so there's nothing strange about this.
Take a look at the first article that explains this mechanism.
When you do:
final container = Container(<A>[A(), A()]);
you explicitly create a List<A> object. Although Container's constructor expects a List<Super>, it accepts a List<A> argument because Dart considers Generic<Derived> to be a subtype of Generic<Base> if Derived is a subtype of Base. Your later attempt to do container.mylist.add(B()); will fail because container.mylist is actually a List<A> and therefore cannot legally store any B elements.
When you instead do:
final container = Container([A(), A()]);
then, because the List literal is not given an explicit type, its type is inferred to be List<Super> from Container's expected construction parameter. container.mylist.add(B()); will succeed since container.mylist is actually a List<Super> and therefore can legally store B elements.

How to getting in Dart the type mirror with the specified type arguments through reflection?

Sample code that explains problem.
import "dart:mirrors";
void main() {
var type = getTypeFromDeclaration();
var typeArguments = getAnotherTypeArguments();
var myType = reflectType(type, typeArguments);
}
How to getting in Dart the type mirror with the specified type arguments through reflection?
P.S.
I think that I don't need to explain "Why this need?" because we all know that this functionality required for the data hydration.
Also this is very useful in data codecs that used reflection for the better data consistency.
As you noticed, I will not explain why.
First, I agree that it would be best if the mirror system allowed creating a type mirror of Map from the type mirrors of Foo and Bar and the class mirror of Map. That is currently not the case.
Without that, I don't think you can solve the problem as written.
There is no way to create a parameterized type with a type argument that is not known as a type at compile-time.
For completeness, I'll include a way to reflect a parameterized type if you know the type at compile time. If the type arguments can be represented as something else than a Type object or a TypeMirror object, you can build your own representation that allows operations like this.
If you can't use reflectType(Map<Foo,Baz>) because Map<Foo,Baz> is not a valid type literal, there is a small workaround to get a Type for any type: Have a class with a type parameter and a way to get the Type value of that parameter.
import "dart:mirrors";
class Typer<T> { Type get type => T; }
main() {
var mapStringInt = reflectType(new Typer<Map<String,int>>().type);
print(mapStringInt); // ClassMirror on 'Map'
print(mapStringInt.typeArguments); // [ClassMirror on 'String', ClassMirror on 'int']
// That is: it's a TypeMirror on Map<String,int>.
}

Getting the parameters of a method call from a clang match callback

I'm adapting the Clang tool-template (as described here) to search for a particular method call in my code. In order to later rewrite that call, I would like to get the type of the parameters the method was called with, as well as the type of the object the method was called on.
I managed to find a matcher that calls back the following:
class AddListenerPrinter : public MatchFinder::MatchCallback
{
public :
virtual void run(const MatchFinder::MatchResult &Result) {
if (const auto *FS = Result.Nodes.getNodeAs<clang::MemberExpr>("ListeningBound"))
{
FS->dump();
}
}
};
which prints out:
MemberExpr 0x7fb05b07b948 '<bound member function type>' .addListener 0x7fb05b077670
`-MemberExpr 0x7fb05b07b918 'class MyCore' lvalue ->mCore 0x7fb05b078e30
`-CXXThisExpr 0x7fb05b07b900 'class MyComponent *' this
Now I can't find any way to retrieve the type of the object the method was called on (here class MyCore) or the type of the method argument (here class MyComponent).
How can I do this?
I found the answer by browsing the code of the existing matchers.
Using matcher = memberCallExpr( callee(methodDecl(hasName("addListener"))) )
I was able to retrieve a CXXMemberCallExpr node. Then getting the type of the object the method was called on:
// FS is the CXXMemberCallExpr
// Prints out the type of x in x.method()
llvm::outs() << FS->getRecordDecl()->getName();
and the method parameters are accessible through FS->getArg(n).
Bottom line is: Find the CXX object that contains what you're looking for first (e.g. which class has methods to access function arguments?), then find the matcher that will return the same type of object in ASTMatchers.h.
Hoping this can help anybody else with the same problem.

Resources