Commit de68ac54 authored by Orgad Shaneh's avatar Orgad Shaneh Committed by Orgad Shaneh
Browse files

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: default avatarPrzemyslaw Gorszkowski <pgorszkowski@gmail.com>
Task-number: QTCREATORBUG-14483
Change-Id: I296ceed9d896c68cf0651265afb08a1fc42f9a68
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@theqtcompany.com>
parent 57e3714d
......@@ -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()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment