Unexpected TypeLoc match for captured variable in lambda

Hi,

In the following example, the captured variable fn is matched as a TypeLoc of Foo; fn outside of the lambda is not matched.

// test.cc

class Foo {};

template <typename T> struct Fn { T t; };

void f(Fn<Foo> &fn) {
[&](const Foo &foo) {
fn; // <--------- *** This is matched as a TypeLoc of Foo ***
};
}

clang-query> m typeLoc(loc(qualType(hasDeclaration(namedDecl(hasName(“Foo”))))))

Match #2:
test.cc:7:5: note: “root” binds here
fn;
^~

Is this a bug? If not, is there a way to filter this out?

Thanks,
Eric

I think there was some work recently that changed how we represented lambdas, but not sure whether that affected this behavior.
Here, the FieldDecl of the CXXRecordDecl has a const Foo & type and it has the ‘fn’ as the TypeLoc - I assume the location is a bug.

I got there through clang-query by adding hasParent() and guessing the types, iterating between output dump, print and diag.

Note that the output of the class in the second query is a red herring - the first one shows that we end up there via a FieldDecl (Binding for “3”).

clang-query> m typeLoc(loc(qualType(hasDeclaration(namedDecl(hasName(“Foo”))))), hasParent(typeLoc(hasParent(typeLoc(hasParent(decl(hasParent(decl().bind(“4”))).bind(“3”))).bind(“2”))).bind(“1”)))

Match #1:

Binding for “1”:
Unable to dump values of type TypeLoc

Binding for “2”:
Unable to dump values of type TypeLoc

Binding for “3”:
FieldDecl 0x7f51ffd91ee0 </tmp/t.cc:7:5> col:5 implicit ‘Fn &’

Binding for “4”:
CXXRecordDecl 0x7f51ffd91c88 </tmp/t.cc:6:3> col:3 implicit class definition

-DefinitionData lambda pass_in_registers trivially_copyable can_const_default_init

-DefaultConstructor
-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
-Destructor simple irrelevant trivial -FieldDecl 0x7f51ffd91ee0 <line:7:5> col:5 implicit 'Fn<Foo> &' -CXXMethodDecl 0x7f51ffd91dc0 <line:6:21, line:8:3> line:6:3 operator() 'void (const Foo &) const' inline -ParmVarDecl 0x7f51ffd91ba8 <col:7, col:18> col:18 foo 'const Foo &' -CompoundStmt 0x7f51ffd91f30 <col:23, line:8:3>
-DeclRefExpr 0x7f51ffd91e78 <line:7:5> 'Fn<Foo>':'Fn<Foo>' lvalue ParmVar 0x7f51ffd91980 'fn' 'Fn<Foo> &' -CXXDestructorDecl 0x7f51ffdc3080 line:6:3 col:3 implicit referenced ~ ‘void () noexcept’ inline default trivial

Binding for “root”:
Unable to dump values of type TypeLoc

clang-query> set output print

clang-query> m typeLoc(loc(qualType(hasDeclaration(namedDecl(hasName(“Foo”))))), hasParent(typeLoc(hasParent(typeLoc(hasParent(decl(hasParent(decl().bind(“4”))).bind(“3”))).bind(“2”))).bind(“1”)))

Match #1:

Binding for “1”:
Fn
Binding for “2”:
Fn &
Binding for “3”:
Fn &
Binding for “4”:
class {
inline void operator()(const Foo &foo) const {
fn;
}
}
Binding for “root”:
Foo
1 match.

I think there was some work recently that changed how we represented lambdas, but not sure whether that affected this behavior.
Here, the FieldDecl of the CXXRecordDecl has a const Foo & type and it has the 'fn' as the TypeLoc - I assume the location is a bug.

I don't know if it is a bug, but from the FieldDecl the matched typeloc seems to be:

getTypeSourceInfo()->getTypeLoc().getNextTypeLoc().getAs<clang::TemplateSpecializationTypeLoc>().getArgLoc(0).getTypeSourceInfo()->getTypeLoc().getAs<clang::TypeSpecTypeLoc>().getNameLoc()

  http://ce.steveire.com/z/Iw8YiQ

I got there through clang-query by adding hasParent() and guessing the types, iterating between output dump, print and diag.

You might already know this, but you don't have to iterate between them anymore. You can just enable all three:

  enable output detailed-ast
  enable output print
  enable output diag # On by default, so this line is not needed

Thanks,

Stephen.