diff --git a/duchain/declarationbuilder.cpp b/duchain/declarationbuilder.cpp --- a/duchain/declarationbuilder.cpp +++ b/duchain/declarationbuilder.cpp @@ -852,15 +852,27 @@ void DeclarationBuilder::visitLambda(LambdaAst* node) { - Python::AstDefaultVisitor::visitLambda(node); + const CorrectionHelper::Recursion r(m_correctionHelper->enterFunction(node->lambda_id->value)); + FunctionType::Ptr type(new FunctionType()); + DUChainWriteLocker lock; - // A context must be opened, because the lamdba's arguments are local to the lambda: - // d = lambda x: x*2; print x # <- gives an error - openContext(node, editorFindRange(node, node->body), DUContext::Other); - foreach ( ArgAst* argument, node->arguments->arguments ) { - visitVariableDeclaration(argument->argumentName); - } + FunctionDeclaration* dec = eventuallyReopenDeclaration( + node->lambda_id, node->lambda_id, FunctionDeclarationType); + + Q_ASSERT(dec->isFunctionDeclaration()); + dec->setInSymbolTable(false); + openType(type); + lock.unlock(); + openContext(node, editorFindRange(node->arguments, node->body), DUContext::Function); + visitArguments(node->arguments); + ExpressionVisitor v(currentContext()); + v.visitNode(node->body); + lock.lock(); + type->setReturnType(v.lastType()); + dec->setType(type); + dec->setInternalContext(currentContext()); closeContext(); + dec->setInSymbolTable(true); } void DeclarationBuilder::applyDocstringHints(CallAst* node, FunctionDeclaration::Ptr function) diff --git a/duchain/expressionvisitor.h b/duchain/expressionvisitor.h --- a/duchain/expressionvisitor.h +++ b/duchain/expressionvisitor.h @@ -73,6 +73,7 @@ virtual void visitCall(CallAst* node); virtual void visitAttribute(AttributeAst* node); virtual void visitTuple(TupleAst* node); + virtual void visitLambda(LambdaAst* node); virtual void visitListComprehension(ListComprehensionAst* node); virtual void visitDictionaryComprehension(DictionaryComprehensionAst* node); virtual void visitSetComprehension(SetComprehensionAst* node); diff --git a/duchain/expressionvisitor.cpp b/duchain/expressionvisitor.cpp --- a/duchain/expressionvisitor.cpp +++ b/duchain/expressionvisitor.cpp @@ -374,6 +374,22 @@ encounter(result); } +void ExpressionVisitor::visitLambda(LambdaAst* node) +{ + DUChainReadLocker lock; + auto d = Helper::declarationForName(QualifiedIdentifier(node->lambda_id->value), + RangeInRevision::invalid(), // ? + DUChainPointer(context())); + if (d && d->isFunctionDeclaration()) { + if ( auto functionType = d->type() ) { + encounter(functionType, DeclarationPointer(d), true); + } + } + else { + encounterUnknown(); + } +} + void ExpressionVisitor::visitList(ListAst* node) { DUChainReadLocker lock; diff --git a/duchain/tests/pyduchaintest.cpp b/duchain/tests/pyduchaintest.cpp --- a/duchain/tests/pyduchaintest.cpp +++ b/duchain/tests/pyduchaintest.cpp @@ -793,7 +793,6 @@ visitor->ctx = TopDUContextPointer(ctx.data()); visitor->searchingForType = expectedType; visitor->visitCode(m_ast.data()); - QEXPECT_FAIL("lambda", "not implemented: aliasing lambdas", Continue); QEXPECT_FAIL("return_builtin_iterator", "fake builtin iter()", Continue); QEXPECT_FAIL("staticmethod_args_type", "bugged somewhere", Continue); QEXPECT_FAIL("init_class_no_decl", "aliasing info lost", Continue); @@ -917,7 +916,12 @@ QTest::newRow("yield_return") << "def myfun():\n return 3\n yield 'foo'\ncheckme = myfun()" << "unsure (int, list of str)"; QTest::newRow("lambda") << "x = lambda t: 3\ncheckme = x()" << "int"; - QTest::newRow("lambda_failure") << "x = lambda t: 3\ncheckme = t" << "mixed"; + QTest::newRow("lambda_context") << "x = lambda t: 3\ncheckme = t" << "mixed"; + QTest::newRow("lambda_default_arg") << "x = lambda t=3: t\ncheckme = x()" << "int"; + QTest::newRow("lambda_hints") << "x = lambda t: t\ncheckme = x(3.5)" << "float"; + QTest::newRow("lambda_default_arg_hints") << "x = lambda t=3: t\ncheckme = x(3.5)" << "unsure (int, float)"; + QTest::newRow("lambda_vararg_hints") << "x = lambda arg, *args: args[1]\ncheckme = x(3.5, 2, str())" << "str"; + QTest::newRow("lambda_kwarg_hints") << "x = lambda arg, **kwargs: kwargs['a']\ncheckme = x(3.5, a=2)" << "int"; QTest::newRow("function_arg_tuple") << "def func(*arg):\n foo, bar = arg\n return bar\ncheckme = func(3, 5)" << "int"; QTest::newRow("function_arg_tuple2") << "def func(*arg):\n return arg[-1]\ncheckme = func(3, \"Foo\")" << "str"; diff --git a/parser/ast.h b/parser/ast.h --- a/parser/ast.h +++ b/parser/ast.h @@ -510,6 +510,7 @@ class KDEVPYTHONPARSER_EXPORT LambdaAst : public ExpressionAst { public: LambdaAst(Ast* parent); + Identifier* lambda_id; ArgumentsAst* arguments; ExpressionAst* body; }; diff --git a/parser/ast.cpp b/parser/ast.cpp --- a/parser/ast.cpp +++ b/parser/ast.cpp @@ -218,7 +218,7 @@ } -LambdaAst::LambdaAst(Ast* parent): ExpressionAst(parent, Ast::LambdaAstType), arguments(0) +LambdaAst::LambdaAst(Ast* parent): ExpressionAst(parent, Ast::LambdaAstType), lambda_id(0), arguments(0) { } diff --git a/parser/astbuilder.cpp b/parser/astbuilder.cpp --- a/parser/astbuilder.cpp +++ b/parser/astbuilder.cpp @@ -398,6 +398,23 @@ }; }; +#undef arg // Python defines one, but I want to use QString::arg here + +class LambdaIdVisitor : public AstDefaultVisitor { +public: + using AstDefaultVisitor::AstDefaultVisitor; + void visitLambda(Python::LambdaAst* node) override { + AstDefaultVisitor::visitLambda(node); + // The tooltip breaks with colons, hence '|'. Not worth fixing; shouldn't really be shown at all. + auto id = QStringLiteral("").arg(node->startLine).arg(node->startCol); + node->lambda_id = new Identifier(id); + node->lambda_id->parent = node; + node->lambda_id->startLine = node->lambda_id->endLine = node->startLine; + node->lambda_id->startCol = node->startCol; + node->lambda_id->endCol = node->startCol + 5; // len("lambda") - 1 + }; +}; + #include "generated.h" QMutex AstBuilder::pyInitLock; @@ -706,6 +723,9 @@ cythonSyntaxRemover.fixAstRanges(t.ast); + LambdaIdVisitor lambdaVisitor; + lambdaVisitor.visitNode(t.ast); + return CodeAst::Ptr(t.ast); }