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));
|
||||
|
||||
// unsigned qt_invokable_token = ast->qt_invokable_token;
|
||||
unsigned declTypeStartOfExpression = 0;
|
||||
unsigned declTypeEndOfExpression = 0;
|
||||
bool isTypedef = false;
|
||||
FullySpecifiedType type;
|
||||
for (SpecifierListAST *it = ast->decl_specifier_list; it; it = it->next) {
|
||||
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;
|
||||
@@ -1982,6 +1995,9 @@ bool Bind::visit(SimpleDeclarationAST *ast)
|
||||
unsigned endOfExpression = initializer->lastToken();
|
||||
decl->setInitializer(asStringLiteral(startOfExpression, endOfExpression));
|
||||
}
|
||||
} else if (declTy.isDecltype()) {
|
||||
decl->setInitializer(asStringLiteral(declTypeStartOfExpression,
|
||||
declTypeEndOfExpression));
|
||||
}
|
||||
|
||||
if (_scope->isClass()) {
|
||||
@@ -3028,6 +3044,7 @@ bool Bind::visit(TypeofSpecifierAST *ast)
|
||||
bool Bind::visit(DecltypeSpecifierAST *ast)
|
||||
{
|
||||
_type = this->expression(ast->expression);
|
||||
_type.setDecltype(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -100,6 +100,12 @@ bool FullySpecifiedType::isAuto() const
|
||||
void FullySpecifiedType::setAuto(bool isAuto)
|
||||
{ f._isAuto = isAuto; }
|
||||
|
||||
bool FullySpecifiedType::isDecltype() const
|
||||
{ return f._isDecltype; }
|
||||
|
||||
void FullySpecifiedType::setDecltype(bool isDecltype)
|
||||
{ f._isDecltype = isDecltype; }
|
||||
|
||||
bool FullySpecifiedType::isRegister() const
|
||||
{ return f._isRegister; }
|
||||
|
||||
|
@@ -58,6 +58,9 @@ public:
|
||||
bool isAuto() const;
|
||||
void setAuto(bool isAuto);
|
||||
|
||||
bool isDecltype() const;
|
||||
void setDecltype(bool isDecltype);
|
||||
|
||||
bool isRegister() const;
|
||||
void setRegister(bool isRegister);
|
||||
|
||||
@@ -125,6 +128,7 @@ private:
|
||||
// storage class specifiers
|
||||
unsigned _isFriend: 1;
|
||||
unsigned _isAuto: 1;
|
||||
unsigned _isDecltype: 1;
|
||||
unsigned _isRegister: 1;
|
||||
unsigned _isStatic: 1;
|
||||
unsigned _isExtern: 1;
|
||||
|
@@ -41,6 +41,7 @@
|
||||
#include <cplusplus/Control.h>
|
||||
#include <cplusplus/Name.h>
|
||||
|
||||
#include <QEnableSharedFromThis>
|
||||
#include <QSet>
|
||||
#include <QMap>
|
||||
|
||||
@@ -99,7 +100,9 @@ private:
|
||||
friend class CreateBindings;
|
||||
};
|
||||
|
||||
class CPLUSPLUS_EXPORT CreateBindings: protected SymbolVisitor
|
||||
class CPLUSPLUS_EXPORT CreateBindings
|
||||
: protected SymbolVisitor
|
||||
, public QEnableSharedFromThis<CreateBindings>
|
||||
{
|
||||
Q_DISABLE_COPY(CreateBindings)
|
||||
|
||||
@@ -119,6 +122,9 @@ public:
|
||||
QSharedPointer<Control> control() const
|
||||
{ return _control; }
|
||||
|
||||
Snapshot &snapshot()
|
||||
{ return _snapshot; }
|
||||
|
||||
/// Adds an expression document in order to keep their symbols and names alive
|
||||
void addExpressionDocument(Document::Ptr document)
|
||||
{ _expressionDocuments.append(document); }
|
||||
|
@@ -526,48 +526,6 @@ bool ResolveExpression::visit(QualifiedNameAST *ast)
|
||||
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)
|
||||
{
|
||||
QList<LookupItem> candidates = _context.lookup(ast->name, _scope);
|
||||
@@ -581,7 +539,7 @@ bool ResolveExpression::visit(SimpleNameAST *ast)
|
||||
if (item.declaration() == 0)
|
||||
continue;
|
||||
|
||||
if (item.type().isAuto()) {
|
||||
if (item.type().isAuto() || item.type().isDecltype()) {
|
||||
const Declaration *decl = item.declaration()->asDeclaration();
|
||||
if (!decl)
|
||||
continue;
|
||||
@@ -590,35 +548,10 @@ bool ResolveExpression::visit(SimpleNameAST *ast)
|
||||
if (_autoDeclarationsBeingResolved.contains(decl))
|
||||
continue;
|
||||
|
||||
const StringLiteral *initializationString = decl->getInitializer();
|
||||
if (initializationString == 0)
|
||||
continue;
|
||||
|
||||
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());
|
||||
newCandidates +=
|
||||
TypeResolver::resolveDeclInitializer(*_context.bindings(), decl,
|
||||
_autoDeclarationsBeingResolved << decl,
|
||||
ast->name->identifier());
|
||||
} else {
|
||||
item.setType(item.declaration()->type());
|
||||
item.setScope(item.declaration()->enclosingScope());
|
||||
|
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "TypeResolver.h"
|
||||
#include "Overview.h"
|
||||
#include "TypeOfExpression.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
@@ -37,6 +38,48 @@ static const bool debug = ! qgetenv("QTC_LOOKUPCONTEXT_DEBUG").isEmpty();
|
||||
|
||||
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)
|
||||
{
|
||||
QSet<Symbol *> visited;
|
||||
@@ -115,6 +158,43 @@ QList<LookupItem> TypeResolver::typedefsFromScopeUpToFunctionScope(const Name *n
|
||||
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)
|
||||
{
|
||||
if (declaration->isTypedef()) {
|
||||
@@ -130,7 +210,7 @@ bool TypeResolver::findTypedef(const QList<LookupItem> &namedTypeItems, FullySpe
|
||||
{
|
||||
foreach (const LookupItem &it, namedTypeItems) {
|
||||
Symbol *declaration = it.declaration();
|
||||
if (!declaration || !declaration->isTypedef())
|
||||
if (!declaration || (!declaration->isTypedef() && !declaration->type().isDecltype()))
|
||||
continue;
|
||||
if (visited.contains(declaration))
|
||||
break;
|
||||
@@ -145,6 +225,17 @@ bool TypeResolver::findTypedef(const QList<LookupItem> &namedTypeItems, FullySpe
|
||||
_factory.control()->referenceType(
|
||||
declaration->type(),
|
||||
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 {
|
||||
*type = declaration->type();
|
||||
}
|
||||
|
@@ -40,6 +40,10 @@ class TypeResolver
|
||||
public:
|
||||
TypeResolver(CreateBindings &factory) : _factory(factory) {}
|
||||
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:
|
||||
NamedType *getNamedType(FullySpecifiedType& type) const;
|
||||
|
@@ -2940,6 +2940,29 @@ void CppToolsPlugin::test_completion_data()
|
||||
) << _("p.") << (QStringList()
|
||||
<< QLatin1String("Nested2")
|
||||
<< 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()
|
||||
|
Reference in New Issue
Block a user