forked from qt-creator/qt-creator
C++: fix code completion for decltyped type
example: struct Foo { int bar; }; Foo foo() { return Foo; } typedef decltype(foo()) TypedefedFooWithDecltype; void fun() { decltype(foo()) decltypeFoo; decltypeFoo.;// code completion should work here TypedefedFooWithDecltype typedefedFooWithDecltype; typedefedFooWithDecltype.;// code completion should work here } Started-by: Przemyslaw Gorszkowski <pgorszkowski@gmail.com> Task-number: QTCREATORBUG-14483 Change-Id: I296ceed9d896c68cf0651265afb08a1fc42f9a68 Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
This commit is contained in:
committed by
Orgad Shaneh
parent
57e3714db4
commit
de68ac5407
17
src/libs/3rdparty/cplusplus/Bind.cpp
vendored
17
src/libs/3rdparty/cplusplus/Bind.cpp
vendored
@@ -1921,9 +1921,22 @@ bool Bind::visit(SimpleDeclarationAST *ast)
|
|||||||
methodKey = methodKeyForInvokableToken(tokenKind(ast->qt_invokable_token));
|
methodKey = methodKeyForInvokableToken(tokenKind(ast->qt_invokable_token));
|
||||||
|
|
||||||
// unsigned qt_invokable_token = ast->qt_invokable_token;
|
// unsigned qt_invokable_token = ast->qt_invokable_token;
|
||||||
|
unsigned declTypeStartOfExpression = 0;
|
||||||
|
unsigned declTypeEndOfExpression = 0;
|
||||||
|
bool isTypedef = false;
|
||||||
FullySpecifiedType type;
|
FullySpecifiedType type;
|
||||||
for (SpecifierListAST *it = ast->decl_specifier_list; it; it = it->next) {
|
for (SpecifierListAST *it = ast->decl_specifier_list; it; it = it->next) {
|
||||||
type = this->specifier(it->value, type);
|
type = this->specifier(it->value, type);
|
||||||
|
if (type.isTypedef())
|
||||||
|
isTypedef = true;
|
||||||
|
|
||||||
|
type.setTypedef(isTypedef);
|
||||||
|
if (type.isDecltype()) {
|
||||||
|
if (DecltypeSpecifierAST *decltypeSpec = it->value->asDecltypeSpecifier()) {
|
||||||
|
declTypeStartOfExpression = decltypeSpec->expression->firstToken();
|
||||||
|
declTypeEndOfExpression = decltypeSpec->expression->lastToken();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Symbol *> **symbolTail = &ast->symbols;
|
List<Symbol *> **symbolTail = &ast->symbols;
|
||||||
@@ -1982,6 +1995,9 @@ bool Bind::visit(SimpleDeclarationAST *ast)
|
|||||||
unsigned endOfExpression = initializer->lastToken();
|
unsigned endOfExpression = initializer->lastToken();
|
||||||
decl->setInitializer(asStringLiteral(startOfExpression, endOfExpression));
|
decl->setInitializer(asStringLiteral(startOfExpression, endOfExpression));
|
||||||
}
|
}
|
||||||
|
} else if (declTy.isDecltype()) {
|
||||||
|
decl->setInitializer(asStringLiteral(declTypeStartOfExpression,
|
||||||
|
declTypeEndOfExpression));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_scope->isClass()) {
|
if (_scope->isClass()) {
|
||||||
@@ -3028,6 +3044,7 @@ bool Bind::visit(TypeofSpecifierAST *ast)
|
|||||||
bool Bind::visit(DecltypeSpecifierAST *ast)
|
bool Bind::visit(DecltypeSpecifierAST *ast)
|
||||||
{
|
{
|
||||||
_type = this->expression(ast->expression);
|
_type = this->expression(ast->expression);
|
||||||
|
_type.setDecltype(true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -100,6 +100,12 @@ bool FullySpecifiedType::isAuto() const
|
|||||||
void FullySpecifiedType::setAuto(bool isAuto)
|
void FullySpecifiedType::setAuto(bool isAuto)
|
||||||
{ f._isAuto = isAuto; }
|
{ f._isAuto = isAuto; }
|
||||||
|
|
||||||
|
bool FullySpecifiedType::isDecltype() const
|
||||||
|
{ return f._isDecltype; }
|
||||||
|
|
||||||
|
void FullySpecifiedType::setDecltype(bool isDecltype)
|
||||||
|
{ f._isDecltype = isDecltype; }
|
||||||
|
|
||||||
bool FullySpecifiedType::isRegister() const
|
bool FullySpecifiedType::isRegister() const
|
||||||
{ return f._isRegister; }
|
{ return f._isRegister; }
|
||||||
|
|
||||||
|
@@ -58,6 +58,9 @@ public:
|
|||||||
bool isAuto() const;
|
bool isAuto() const;
|
||||||
void setAuto(bool isAuto);
|
void setAuto(bool isAuto);
|
||||||
|
|
||||||
|
bool isDecltype() const;
|
||||||
|
void setDecltype(bool isDecltype);
|
||||||
|
|
||||||
bool isRegister() const;
|
bool isRegister() const;
|
||||||
void setRegister(bool isRegister);
|
void setRegister(bool isRegister);
|
||||||
|
|
||||||
@@ -125,6 +128,7 @@ private:
|
|||||||
// storage class specifiers
|
// storage class specifiers
|
||||||
unsigned _isFriend: 1;
|
unsigned _isFriend: 1;
|
||||||
unsigned _isAuto: 1;
|
unsigned _isAuto: 1;
|
||||||
|
unsigned _isDecltype: 1;
|
||||||
unsigned _isRegister: 1;
|
unsigned _isRegister: 1;
|
||||||
unsigned _isStatic: 1;
|
unsigned _isStatic: 1;
|
||||||
unsigned _isExtern: 1;
|
unsigned _isExtern: 1;
|
||||||
|
@@ -41,6 +41,7 @@
|
|||||||
#include <cplusplus/Control.h>
|
#include <cplusplus/Control.h>
|
||||||
#include <cplusplus/Name.h>
|
#include <cplusplus/Name.h>
|
||||||
|
|
||||||
|
#include <QEnableSharedFromThis>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
|
||||||
@@ -99,7 +100,9 @@ private:
|
|||||||
friend class CreateBindings;
|
friend class CreateBindings;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CPLUSPLUS_EXPORT CreateBindings: protected SymbolVisitor
|
class CPLUSPLUS_EXPORT CreateBindings
|
||||||
|
: protected SymbolVisitor
|
||||||
|
, public QEnableSharedFromThis<CreateBindings>
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY(CreateBindings)
|
Q_DISABLE_COPY(CreateBindings)
|
||||||
|
|
||||||
@@ -119,6 +122,9 @@ public:
|
|||||||
QSharedPointer<Control> control() const
|
QSharedPointer<Control> control() const
|
||||||
{ return _control; }
|
{ return _control; }
|
||||||
|
|
||||||
|
Snapshot &snapshot()
|
||||||
|
{ return _snapshot; }
|
||||||
|
|
||||||
/// Adds an expression document in order to keep their symbols and names alive
|
/// Adds an expression document in order to keep their symbols and names alive
|
||||||
void addExpressionDocument(Document::Ptr document)
|
void addExpressionDocument(Document::Ptr document)
|
||||||
{ _expressionDocuments.append(document); }
|
{ _expressionDocuments.append(document); }
|
||||||
|
@@ -526,48 +526,6 @@ bool ResolveExpression::visit(QualifiedNameAST *ast)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class DeduceAutoCheck : public ASTVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DeduceAutoCheck(const Identifier *id, TranslationUnit *tu)
|
|
||||||
: ASTVisitor(tu), _id(id), _block(false)
|
|
||||||
{
|
|
||||||
accept(tu->ast());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool preVisit(AST *)
|
|
||||||
{
|
|
||||||
if (_block)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool visit(SimpleNameAST *ast)
|
|
||||||
{
|
|
||||||
if (ast->name
|
|
||||||
&& ast->name->identifier()
|
|
||||||
&& strcmp(ast->name->identifier()->chars(), _id->chars()) == 0) {
|
|
||||||
_block = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool visit(MemberAccessAST *ast)
|
|
||||||
{
|
|
||||||
accept(ast->base_expression);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Identifier *_id;
|
|
||||||
bool _block;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace anonymous
|
|
||||||
|
|
||||||
bool ResolveExpression::visit(SimpleNameAST *ast)
|
bool ResolveExpression::visit(SimpleNameAST *ast)
|
||||||
{
|
{
|
||||||
QList<LookupItem> candidates = _context.lookup(ast->name, _scope);
|
QList<LookupItem> candidates = _context.lookup(ast->name, _scope);
|
||||||
@@ -581,7 +539,7 @@ bool ResolveExpression::visit(SimpleNameAST *ast)
|
|||||||
if (item.declaration() == 0)
|
if (item.declaration() == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (item.type().isAuto()) {
|
if (item.type().isAuto() || item.type().isDecltype()) {
|
||||||
const Declaration *decl = item.declaration()->asDeclaration();
|
const Declaration *decl = item.declaration()->asDeclaration();
|
||||||
if (!decl)
|
if (!decl)
|
||||||
continue;
|
continue;
|
||||||
@@ -590,35 +548,10 @@ bool ResolveExpression::visit(SimpleNameAST *ast)
|
|||||||
if (_autoDeclarationsBeingResolved.contains(decl))
|
if (_autoDeclarationsBeingResolved.contains(decl))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const StringLiteral *initializationString = decl->getInitializer();
|
newCandidates +=
|
||||||
if (initializationString == 0)
|
TypeResolver::resolveDeclInitializer(*_context.bindings(), decl,
|
||||||
continue;
|
_autoDeclarationsBeingResolved << decl,
|
||||||
|
ast->name->identifier());
|
||||||
const QByteArray &initializer =
|
|
||||||
QByteArray::fromRawData(initializationString->chars(),
|
|
||||||
initializationString->size()).trimmed();
|
|
||||||
|
|
||||||
// Skip lambda-function initializers
|
|
||||||
if (initializer.length() > 0 && initializer[0] == '[')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
TypeOfExpression exprTyper;
|
|
||||||
exprTyper.setExpandTemplates(true);
|
|
||||||
Document::Ptr doc = _context.document(QString::fromLocal8Bit(decl->fileName()));
|
|
||||||
exprTyper.init(doc, _context.snapshot(), _context.bindings(),
|
|
||||||
QSet<const Declaration* >(_autoDeclarationsBeingResolved) << decl);
|
|
||||||
|
|
||||||
Document::Ptr exprDoc =
|
|
||||||
documentForExpression(exprTyper.preprocessedExpression(initializer));
|
|
||||||
exprDoc->check();
|
|
||||||
_context.bindings()->addExpressionDocument(exprDoc);
|
|
||||||
|
|
||||||
DeduceAutoCheck deduceAuto(ast->name->identifier(), exprDoc->translationUnit());
|
|
||||||
if (deduceAuto._block)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
newCandidates += exprTyper(extractExpressionAST(exprDoc), exprDoc,
|
|
||||||
decl->enclosingScope());
|
|
||||||
} else {
|
} else {
|
||||||
item.setType(item.declaration()->type());
|
item.setType(item.declaration()->type());
|
||||||
item.setScope(item.declaration()->enclosingScope());
|
item.setScope(item.declaration()->enclosingScope());
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#include "TypeResolver.h"
|
#include "TypeResolver.h"
|
||||||
#include "Overview.h"
|
#include "Overview.h"
|
||||||
|
#include "TypeOfExpression.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
@@ -37,6 +38,48 @@ static const bool debug = ! qgetenv("QTC_LOOKUPCONTEXT_DEBUG").isEmpty();
|
|||||||
|
|
||||||
namespace CPlusPlus {
|
namespace CPlusPlus {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class DeduceAutoCheck : public ASTVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeduceAutoCheck(const Identifier *id, TranslationUnit *tu)
|
||||||
|
: ASTVisitor(tu), _id(id), _block(false)
|
||||||
|
{
|
||||||
|
accept(tu->ast());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool preVisit(AST *)
|
||||||
|
{
|
||||||
|
if (_block)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool visit(SimpleNameAST *ast)
|
||||||
|
{
|
||||||
|
if (ast->name
|
||||||
|
&& ast->name->identifier()
|
||||||
|
&& strcmp(ast->name->identifier()->chars(), _id->chars()) == 0) {
|
||||||
|
_block = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool visit(MemberAccessAST *ast)
|
||||||
|
{
|
||||||
|
accept(ast->base_expression);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Identifier *_id;
|
||||||
|
bool _block;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace anonymous
|
||||||
|
|
||||||
void TypeResolver::resolve(FullySpecifiedType *type, Scope **scope, LookupScope *binding)
|
void TypeResolver::resolve(FullySpecifiedType *type, Scope **scope, LookupScope *binding)
|
||||||
{
|
{
|
||||||
QSet<Symbol *> visited;
|
QSet<Symbol *> visited;
|
||||||
@@ -115,6 +158,43 @@ QList<LookupItem> TypeResolver::typedefsFromScopeUpToFunctionScope(const Name *n
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolves auto and decltype initializer string
|
||||||
|
QList<LookupItem> TypeResolver::resolveDeclInitializer(
|
||||||
|
CreateBindings &factory, const Declaration *decl,
|
||||||
|
const QSet<const Declaration* > &declarationsBeingResolved,
|
||||||
|
const Identifier *id)
|
||||||
|
{
|
||||||
|
const StringLiteral *initializationString = decl->getInitializer();
|
||||||
|
if (initializationString == 0)
|
||||||
|
return QList<LookupItem>();
|
||||||
|
|
||||||
|
const QByteArray &initializer =
|
||||||
|
QByteArray::fromRawData(initializationString->chars(),
|
||||||
|
initializationString->size()).trimmed();
|
||||||
|
|
||||||
|
// Skip lambda-function initializers
|
||||||
|
if (initializer.length() > 0 && initializer[0] == '[')
|
||||||
|
return QList<LookupItem>();
|
||||||
|
|
||||||
|
TypeOfExpression exprTyper;
|
||||||
|
exprTyper.setExpandTemplates(true);
|
||||||
|
Document::Ptr doc = factory.snapshot().document(QString::fromLocal8Bit(decl->fileName()));
|
||||||
|
exprTyper.init(doc, factory.snapshot(), factory.sharedFromThis(), declarationsBeingResolved);
|
||||||
|
|
||||||
|
Document::Ptr exprDoc =
|
||||||
|
documentForExpression(exprTyper.preprocessedExpression(initializer));
|
||||||
|
factory.addExpressionDocument(exprDoc);
|
||||||
|
exprDoc->check();
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
DeduceAutoCheck deduceAuto(id, exprDoc->translationUnit());
|
||||||
|
if (deduceAuto._block)
|
||||||
|
return QList<LookupItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return exprTyper(extractExpressionAST(exprDoc), exprDoc, decl->enclosingScope());
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeResolver::isTypedefWithName(const Declaration *declaration, const Name *name)
|
bool TypeResolver::isTypedefWithName(const Declaration *declaration, const Name *name)
|
||||||
{
|
{
|
||||||
if (declaration->isTypedef()) {
|
if (declaration->isTypedef()) {
|
||||||
@@ -130,7 +210,7 @@ bool TypeResolver::findTypedef(const QList<LookupItem> &namedTypeItems, FullySpe
|
|||||||
{
|
{
|
||||||
foreach (const LookupItem &it, namedTypeItems) {
|
foreach (const LookupItem &it, namedTypeItems) {
|
||||||
Symbol *declaration = it.declaration();
|
Symbol *declaration = it.declaration();
|
||||||
if (!declaration || !declaration->isTypedef())
|
if (!declaration || (!declaration->isTypedef() && !declaration->type().isDecltype()))
|
||||||
continue;
|
continue;
|
||||||
if (visited.contains(declaration))
|
if (visited.contains(declaration))
|
||||||
break;
|
break;
|
||||||
@@ -145,6 +225,17 @@ bool TypeResolver::findTypedef(const QList<LookupItem> &namedTypeItems, FullySpe
|
|||||||
_factory.control()->referenceType(
|
_factory.control()->referenceType(
|
||||||
declaration->type(),
|
declaration->type(),
|
||||||
declaration->type()->asReferenceType()->isRvalueReference()));
|
declaration->type()->asReferenceType()->isRvalueReference()));
|
||||||
|
} else if (declaration->type().isDecltype()) {
|
||||||
|
Declaration *decl = declaration->asDeclaration();
|
||||||
|
const QList<LookupItem> resolved =
|
||||||
|
resolveDeclInitializer(_factory, decl, QSet<const Declaration* >() << decl);
|
||||||
|
if (!resolved.isEmpty()) {
|
||||||
|
LookupItem item = resolved.first();
|
||||||
|
*type = item.type();
|
||||||
|
*scope = item.scope();
|
||||||
|
_binding = item.binding();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
*type = declaration->type();
|
*type = declaration->type();
|
||||||
}
|
}
|
||||||
|
@@ -40,6 +40,10 @@ class TypeResolver
|
|||||||
public:
|
public:
|
||||||
TypeResolver(CreateBindings &factory) : _factory(factory) {}
|
TypeResolver(CreateBindings &factory) : _factory(factory) {}
|
||||||
void resolve(FullySpecifiedType *type, Scope **scope, LookupScope *binding);
|
void resolve(FullySpecifiedType *type, Scope **scope, LookupScope *binding);
|
||||||
|
static QList<LookupItem> resolveDeclInitializer(
|
||||||
|
CreateBindings &factory, const Declaration *decl,
|
||||||
|
const QSet<const Declaration *> &declarationsBeingResolved,
|
||||||
|
const Identifier *id = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NamedType *getNamedType(FullySpecifiedType& type) const;
|
NamedType *getNamedType(FullySpecifiedType& type) const;
|
||||||
|
@@ -2940,6 +2940,29 @@ void CppToolsPlugin::test_completion_data()
|
|||||||
) << _("p.") << (QStringList()
|
) << _("p.") << (QStringList()
|
||||||
<< QLatin1String("Nested2")
|
<< QLatin1String("Nested2")
|
||||||
<< QLatin1String("bar"));
|
<< QLatin1String("bar"));
|
||||||
|
|
||||||
|
QTest::newRow("simple_decltype_declaration") << _(
|
||||||
|
"struct Foo { int bar; };\n"
|
||||||
|
"Foo foo;\n"
|
||||||
|
"void fun() {\n"
|
||||||
|
" decltype(foo) s;\n"
|
||||||
|
" @\n"
|
||||||
|
"}\n"
|
||||||
|
) << _("s.") << (QStringList()
|
||||||
|
<< QLatin1String("Foo")
|
||||||
|
<< QLatin1String("bar"));
|
||||||
|
|
||||||
|
QTest::newRow("typedefed_decltype_declaration") << _(
|
||||||
|
"struct Foo { int bar; };\n"
|
||||||
|
"Foo foo;\n"
|
||||||
|
"typedef decltype(foo) TypedefedFooWithDecltype;\n"
|
||||||
|
"void fun() {\n"
|
||||||
|
" TypedefedFooWithDecltype s;\n"
|
||||||
|
" @\n"
|
||||||
|
"}\n"
|
||||||
|
) << _("s.") << (QStringList()
|
||||||
|
<< QLatin1String("Foo")
|
||||||
|
<< QLatin1String("bar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CppToolsPlugin::test_completion_member_access_operator()
|
void CppToolsPlugin::test_completion_member_access_operator()
|
||||||
|
Reference in New Issue
Block a user