Commit 136bf571 authored by Milian Wolff's avatar Milian Wolff
Browse files

c++2011 support: proper type deduction in auto range-based-for

for c-arrays we take the element type
for all other types we look via ADL for begin(listType)
and then dereference the return type of that function

See 6.5.4 in the C++2011 spec

BUG: 279728
parent caf7c40d
......@@ -838,10 +838,19 @@ void ContextBuilder::visitForStatement(ForStatementAST *node)
DUContext* secondParentContext = openContext(first, second, DUContext::Other);
visit(node->init_statement);
visit(node->condition);
visit(node->range_declaration);
visit(node->expression);
if (node->range_declaration) {
// in range-based for we first visit the expression
// since it might define the type for cases like
// for(auto i : foo)
visit(node->expression);
visit(node->range_declaration);
Q_ASSERT(!node->init_statement);
Q_ASSERT(!node->condition);
} else {
visit(node->init_statement);
visit(node->condition);
visit(node->expression);
}
closeContext();
......
......@@ -59,6 +59,8 @@
#include "name_visitor.h"
#include "usebuilder.h"
#include "overloadresolutionhelper.h"
using namespace KTextEditor;
using namespace KDevelop;
using namespace Cpp;
......@@ -83,7 +85,7 @@ bool DeclarationBuilder::changeWasSignificant() const
}
DeclarationBuilder::DeclarationBuilder (ParseSession* session)
: DeclarationBuilderBase(session), m_changeWasSignificant(false), m_ignoreDeclarators(false), m_functionFlag(NoFunctionFlag), m_collectQtFunctionSignature(false)
: DeclarationBuilderBase(session), m_changeWasSignificant(false), m_ignoreDeclarators(false), m_functionFlag(NoFunctionFlag), m_collectQtFunctionSignature(false), m_lastDeclaration(0)
{
}
......@@ -267,6 +269,51 @@ void DeclarationBuilder::visitQPropertyDeclaration(QPropertyDeclarationAST* node
m_pendingPropertyDeclarations.insert(currentContext(), qMakePair(decl, node));
}
void DeclarationBuilder::visitForRangeDeclaration(ForRangeDeclarationAst* node)
{
AbstractType::Ptr listType = lastType();
DefaultVisitor::visitForRangeDeclaration(node);
if (lastTypeWasAuto() && listType && m_lastDeclaration) {
// auto support for range-based for
AbstractType::Ptr realListType = TypeUtils::realType(listType);
// step 1: find type of elements in list
AbstractType::Ptr elementType;
if (ArrayType::Ptr array = realListType.cast<ArrayType>()) {
// case a: c-array, i.e. foo bar[5]; -> type is foo
elementType = array->elementType();
} else {
// case b: look for begin(listType) function using ADL
DUChainReadLocker lock;
OverloadResolutionHelper helper = OverloadResolutionHelper( DUContextPointer(currentContext()), TopDUContextPointer(topContext()) );
static const QualifiedIdentifier begin("begin");
helper.setFunctionNameForADL(begin);
helper.setKnownParameters(OverloadResolver::ParameterList(listType, false));
helper.setFunctions( currentContext()->findDeclarations(begin, CursorInRevision::invalid(), AbstractType::Ptr(), 0, DUContext::OnlyFunctions) );
ViableFunction func = helper.resolve();
if (func.isValid()) {
AbstractType::Ptr type = func.declaration()->type<FunctionType>()->returnType();
// see spec: for-range-declaration = *__begin;
elementType = TypeUtils::decreasePointerDepth(type, topContext(), true);
}
}
// step 2: set last type, but keep const&
if (elementType) {
DUChainWriteLocker lock;
AbstractType::Ptr type = m_lastDeclaration->abstractType();
elementType->setModifiers(type->modifiers());
if (ReferenceType::Ptr ref = type.cast<ReferenceType>()) {
ref->setBaseType(elementType);
} else {
type = elementType;
}
m_lastDeclaration->setAbstractType(type);
}
}
}
KDevelop::IndexedDeclaration DeclarationBuilder::resolveMethodName(NameAST *node)
{
QualifiedIdentifier id;
......@@ -816,7 +863,7 @@ void DeclarationBuilder::closeDeclaration(bool forceInstance)
ifDebugCurrentFile( DUChainReadLocker lock(DUChain::lock()); kDebug() << "closing declaration" << currentDeclaration()->toString() << "type" << (currentDeclaration()->abstractType() ? currentDeclaration()->abstractType()->toString() : QString("notype")) << "last:" << (lastType() ? lastType()->toString() : QString("(notype)")); )
m_declarationStack.pop();
m_lastDeclaration = m_declarationStack.pop();
}
void DeclarationBuilder::visitTypedef(TypedefAST *def)
......
......@@ -97,6 +97,7 @@ protected:
virtual void visitTypeId(TypeIdAST *);
virtual void visitInitDeclarator(InitDeclaratorAST *node);
virtual void visitQPropertyDeclaration(QPropertyDeclarationAST *);
virtual void visitForRangeDeclaration(ForRangeDeclarationAst *node);
virtual void classTypeOpened(KDevelop::AbstractType::Ptr);
virtual void classContextOpened(ClassSpecifierAST *node, DUContext* context);
......@@ -202,6 +203,7 @@ private:
private:
QStack<Declaration*> m_declarationStack;
Declaration* m_lastDeclaration;
QByteArray m_lastComment;
};
......
......@@ -192,6 +192,7 @@ private slots:
//BEGIN C++2011
void testRangeBasedFor();
void testRangeBasedForClass();
void testRValueReference();
void testDefaultDelete();
void testDelete_Bug278781();
......
......@@ -78,6 +78,60 @@ void TestDUChain::testRangeBasedFor() {
QVERIFY(decls.at(0)->uses().isEmpty());
}
void TestDUChain::testRangeBasedForClass() {
// 1 2 3
// 123456789012345678901234567890
LockedTopDUContext top = parse( "struct MyType{};"
"\nstruct Iterator{"
"\n MyType operator*(){return 0;}"
"\n bool operator!=(const Iterator&){return false;}"
"\n Iterator& operator++(){return *this;}"
"\n};"
// begin + end as normal functions
"\nstruct List1{ }; Iterator begin(List1); Iterator end(List1);"
// begin with const& + end as normal functions
"\nstruct List2{ }; Iterator begin(const List2&); Iterator end(const List2&);"
"\nint main() {"
"\n List1 l1;"
"\n for(auto i : l1) { }"
"\n List2 l2;"
"\n for(auto i : l2) { }"
"\n}"
"\n", DumpAll
);
QVERIFY(top);
DUChainReadLocker lock;
QVERIFY(top->problems().isEmpty());
QCOMPARE(top->childContexts().size(), 10);
QCOMPARE(top->localDeclarations().size(), 9);
DUContext* ctx = top->childContexts().last();
QVector< Declaration* > decls = ctx->localDeclarations();
QCOMPARE(decls.size(), 2);
QCOMPARE(decls.at(0)->identifier().toString(), QString("l1"));
QCOMPARE(decls.at(0)->abstractType()->toString(), QString("List1"));
QCOMPARE(decls.at(0)->uses().constBegin()->size(), 1);
QCOMPARE(decls.at(0)->uses().constBegin()->first().start.line, 10);
QCOMPARE(decls.at(1)->identifier().toString(), QString("l2"));
QCOMPARE(decls.at(1)->abstractType()->toString(), QString("List2"));
QCOMPARE(decls.at(1)->uses().constBegin()->size(), 1);
QCOMPARE(decls.at(1)->uses().constBegin()->first().start.line, 12);
QCOMPARE(ctx->childContexts().size(), 4);
decls = ctx->childContexts().at(0)->localDeclarations();
QCOMPARE(decls.size(), 1);
QCOMPARE(decls.at(0)->identifier().toString(), QString("i"));
QCOMPARE(decls.at(0)->abstractType()->toString(), QString("MyType"));
QCOMPARE(decls.at(0)->range().start.line, 10);
decls = ctx->childContexts().at(2)->localDeclarations();
QCOMPARE(decls.size(), 1);
QCOMPARE(decls.at(0)->identifier().toString(), QString("i"));
QCOMPARE(decls.at(0)->abstractType()->toString(), QString("MyType"));
QCOMPARE(decls.at(0)->range().start.line, 12);
}
void TestDUChain::testRValueReference() {
// 1 2 3
// 123456789012345678901234567890
......
......@@ -210,6 +210,11 @@ bool TypeBuilder::lastTypeWasInstance() const
return m_lastTypeWasInstance;
}
bool TypeBuilder::lastTypeWasAuto() const
{
return m_lastTypeWasAuto;
}
void TypeBuilder::visitElaboratedTypeSpecifier(ElaboratedTypeSpecifierAST *node)
{
if(m_onlyComputeSimplified) {
......
......@@ -144,6 +144,9 @@ protected:
/// (in contrast to a type-declaration like a class-declaration or a typedef)
bool lastTypeWasInstance() const;
/// Returns true when last type was an auto-type
bool lastTypeWasAuto() const;
// Use PushValue<bool> to manipulate this.
bool m_inTypedef;
......
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