C++: Fix auto variable resolving for template class constructor call

Code snippet:
template<class T> struct MyStruct { int value; };
int main() {
    auto s = MyStruct<int>();
    s.value;  // "value" is not found
}

This fixes find usages for unique_ptr declared as auto like this:
auto ptr = std::unique_ptr<MyStruct>(new MyStruct());
ptr->value;

Also fixes in-place constructors:
std::unique_ptr<MyStruct>(new MyStruct())->value;

Fixes: QTCREATORBUG-15364
Change-Id: I8d452a77fe85e63665ec8d4c4afbcf8aad063121
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Volodymyr Zibarov
2020-06-18 15:22:13 +03:00
parent a927c320f7
commit 40173ad4ab
3 changed files with 124 additions and 6 deletions

View File

@@ -818,6 +818,10 @@ void CreateBindings::lookupInScope(const Name *name, Scope *scope,
if (Symbol *inst = instantiateTemplateFunction(name, s->asTemplate())) if (Symbol *inst = instantiateTemplateFunction(name, s->asTemplate()))
item.setType(inst->type()); item.setType(inst->type());
if (Template *templ = s->asTemplate())
if (templ->declaration() && templ->declaration()->asClass())
item.setType(control()->namedType(name));
result->append(item); result->append(item);
} }
} }

View File

@@ -880,12 +880,18 @@ bool ResolveExpression::visit(CallAST *ast)
if (NamedType *namedTy = ty->asNamedType()) { if (NamedType *namedTy = ty->asNamedType()) {
if (ClassOrNamespace *b = _context.lookupType(namedTy->name(), scope)) { if (ClassOrNamespace *b = _context.lookupType(namedTy->name(), scope)) {
foreach (const LookupItem &r, b->find(functionCallOp)) { if (b->templateId() && result.declaration() && result.declaration()->asTemplate()) {
Symbol *overload = r.declaration(); // Template class constructor
if (Function *funTy = overload->type()->asFunctionType()) { addResult(ty.simplified(), scope);
if (maybeValidPrototype(funTy, actualArgumentCount)) { } else {
if (Function *proto = instantiate(namedTy->name(), funTy)->asFunctionType()) // operator()
addResult(proto->returnType().simplified(), scope); foreach (const LookupItem &r, b->find(functionCallOp)) {
Symbol *overload = r.declaration();
if (Function *funTy = overload->type()->asFunctionType()) {
if (maybeValidPrototype(funTy, actualArgumentCount)) {
if (Function *proto = instantiate(namedTy->name(), funTy)->asFunctionType())
addResult(proto->returnType().simplified(), scope);
}
} }
} }
} }

View File

@@ -86,6 +86,8 @@ private Q_SLOTS:
void functionNameFoundInArguments(); void functionNameFoundInArguments();
void memberFunctionFalsePositives_QTCREATORBUG2176(); void memberFunctionFalsePositives_QTCREATORBUG2176();
void resolveTemplateConstructor();
void templateConstructorVsCallOperator();
// Qt keywords // Qt keywords
void qproperty_1(); void qproperty_1();
@@ -465,6 +467,112 @@ struct Struct{
QCOMPARE(find.usages()[1].col, 22); QCOMPARE(find.usages()[1].col, 22);
} }
void tst_FindUsages::resolveTemplateConstructor()
{
const QByteArray src =
R"(
struct MyStruct { int value; };
template <class T> struct Tmp {
T str;
};
template <class T> struct Tmp2 {
Tmp2(){}
T str;
};
template <class T> struct Tmp3 {
Tmp3(int i){}
T str;
};
int main() {
auto tmp = Tmp<MyStruct>();
auto tmp2 = Tmp2<MyStruct>();
auto tmp3 = Tmp3<MyStruct>(1);
tmp.str.value;
tmp2.str.value;
tmp3.str.value;
Tmp<MyStruct>().str.value;
Tmp2<MyStruct>().str.value;
Tmp3<MyStruct>(1).str.value;
}
)";
Document::Ptr doc = Document::create("resolveTemplateConstructor");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount() == 5);
Class *s = doc->globalSymbolAt(0)->asClass();
QVERIFY(s);
QCOMPARE(s->name()->identifier()->chars(), "MyStruct");
QCOMPARE(s->memberCount(), 1);
Declaration *sv = s->memberAt(0)->asDeclaration();
QVERIFY(sv);
QCOMPARE(sv->name()->identifier()->chars(), "value");
Snapshot snapshot;
snapshot.insert(doc);
FindUsages find(src, doc, snapshot);
find(sv);
QCOMPARE(find.usages().size(), 7);
}
void tst_FindUsages::templateConstructorVsCallOperator()
{
const QByteArray src =
R"(
struct MyStruct { int value; };
template<class T> struct Tmp {
T str;
MyStruct operator()() { return MyStruct(); }
};
struct Simple {
MyStruct str;
MyStruct operator()() { return MyStruct(); }
};
int main()
{
Tmp<MyStruct>().str.value;
Tmp<MyStruct>()().value;
Tmp<MyStruct> t;
t().value;
Simple().str.value;
Simple()().value;
Simple s;
s().value;
}
)";
Document::Ptr doc = Document::create("resolveTemplateConstructor");
doc->setUtf8Source(src);
doc->parse();
doc->check();
QVERIFY(doc->diagnosticMessages().isEmpty());
QVERIFY(doc->globalSymbolCount() == 4);
Class *s = doc->globalSymbolAt(0)->asClass();
QVERIFY(s);
QCOMPARE(s->name()->identifier()->chars(), "MyStruct");
QCOMPARE(s->memberCount(), 1);
Declaration *sv = s->memberAt(0)->asDeclaration();
QVERIFY(sv);
QCOMPARE(sv->name()->identifier()->chars(), "value");
Snapshot snapshot;
snapshot.insert(doc);
FindUsages find(src, doc, snapshot);
find(sv);
QCOMPARE(find.usages().size(), 7);
}
#if 0 #if 0
@interface Clazz {} +(void)method:(int)arg; @end @interface Clazz {} +(void)method:(int)arg; @end
@implementation Clazz +(void)method:(int)arg { @implementation Clazz +(void)method:(int)arg {