From e9e1e1983fbe536aaf638c3a8022410c5834e6d0 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 1 Nov 2022 13:49:08 +0300 Subject: [PATCH] NODE-2526 Fix IDE highlighting type error (#3777) --- lang/js/src/main/scala/JsApiUtils.scala | 7 +++ .../lang/v1/compiler/ExpressionCompiler.scala | 48 ++++++++--------- lang/tests-js/src/test/scala/JsAPITest.scala | 53 +++++++++++++++++++ .../ExpressionCompilerWithParserV2Test.scala | 8 +-- 4 files changed, 86 insertions(+), 30 deletions(-) diff --git a/lang/js/src/main/scala/JsApiUtils.scala b/lang/js/src/main/scala/JsApiUtils.scala index 516b9026a23..fdf15621f4d 100644 --- a/lang/js/src/main/scala/JsApiUtils.scala +++ b/lang/js/src/main/scala/JsApiUtils.scala @@ -144,6 +144,13 @@ object JsApiUtils { mergeJSObjects(commonDataObj, additionalDataObj) } + case Expressions.FOLD(_, limit, value, acc, func, _, _) => + val additionalDataObj = jObj.applyDynamic("apply")( + "name" -> s"FOLD<$limit>", + "args" -> js.Array(serExpr(value), serExpr(acc), func.key.toString: js.Any) + ) + mergeJSObjects(commonDataObj, additionalDataObj) + case Expressions.MATCH(_, expr, cases, _, ctxOpt) => { val additionalDataObj = jObj.applyDynamic("apply")( "expr" -> serExpr(expr), diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ExpressionCompiler.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ExpressionCompiler.scala index e142d2651df..fae94c28753 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ExpressionCompiler.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ExpressionCompiler.scala @@ -117,16 +117,16 @@ object ExpressionCompiler { .recover { case err => CompilationStepResultExpr(ctx, FAILED_EXPR(), NOTHING, expr, List(err)) } expr match { - case x: Expressions.CONST_LONG => CompilationStepResultExpr(ctx, CONST_LONG(x.value): EXPR, LONG: FINAL, x: Expressions.EXPR).pure[CompileM] + case x: Expressions.CONST_LONG => CompilationStepResultExpr(ctx, CONST_LONG(x.value), LONG, x).pure[CompileM] case x: Expressions.CONST_BYTESTR => handlePart(x.value).flatMap(b => liftEither(adjustByteStr(x, b))) case x: Expressions.CONST_STRING => handlePart(x.value).flatMap(s => liftEither(adjustStr(x, s))) - case x: Expressions.TRUE => CompilationStepResultExpr(ctx, TRUE: EXPR, BOOLEAN: FINAL, x: Expressions.EXPR).pure[CompileM] - case x: Expressions.FALSE => CompilationStepResultExpr(ctx, FALSE: EXPR, BOOLEAN: FINAL, x: Expressions.EXPR).pure[CompileM] + case x: Expressions.TRUE => CompilationStepResultExpr(ctx, TRUE, BOOLEAN, x).pure[CompileM] + case x: Expressions.FALSE => CompilationStepResultExpr(ctx, FALSE, BOOLEAN, x).pure[CompileM] case x: Expressions.INVALID => CompilationStepResultExpr( ctx, - FAILED_EXPR(): EXPR, + FAILED_EXPR(), NOTHING, x: Expressions.EXPR, List(Generic(x.position.start, x.position.end, x.message)) @@ -138,7 +138,7 @@ object ExpressionCompiler { case Expressions.REF(p, key, _, _) => compileRef(p, key, saveExprContext) case Expressions.FUNCTION_CALL(p, name, args, _, _) => compileFunctionCall(p, name, args, saveExprContext, allowIllFormedStrings) case Expressions.MATCH(p, ex, cases, _, _) => compileMatch(p, ex, cases.toList, saveExprContext, allowIllFormedStrings) - case Expressions.FOLD(p, limit, list, acc, f, _, _) => compileFold(p, limit, list, acc, f.key) + case f: Expressions.FOLD => compileFold(f) case Expressions.GENERIC_FUNCTION_CALL(p, e, name, t, _, _) => compileGenericFunctionCall(p, e, name, t, saveExprContext, allowIllFormedStrings) case Expressions.BINARY_OP(p, a, op, b, _, _) => @@ -176,7 +176,8 @@ object ExpressionCompiler { condWithErr._1.parseNodeExpr, ifTrue.parseNodeExpr, ifFalse.parseNodeExpr, - ctxOpt = saveExprContext.toOption(ctx.getSimpleContext()) + Some(t), + saveExprContext.toOption(ctx.getSimpleContext()) ) errorList = condWithErr._1.errors ++ ifTrue.errors ++ ifFalse.errors @@ -185,7 +186,7 @@ object ExpressionCompiler { ctx, IF(condWithErr._1.expr, ifTrue.expr, ifFalse.expr), t, - parseNodeExpr.copy(resultType = Some(t)), + parseNodeExpr, errorList ) } else { @@ -507,7 +508,7 @@ object ExpressionCompiler { compLetResult.parseNodeExpr, compiledBody.parseNodeExpr, compiledBody.parseNodeExpr.resultType, - ctxOpt = saveExprContext.toOption(compiledBody.ctx.getSimpleContext()) + saveExprContext.toOption(compiledBody.ctx.getSimpleContext()) ) result = if (!compLetResult.dec.isItFailed) { LET_BLOCK(compLetResult.dec.asInstanceOf[LET], compiledBody.expr) @@ -604,7 +605,8 @@ object ExpressionCompiler { p, namePart, compiledArgs.map(_.parseNodeExpr), - ctxOpt = saveExprContext.toOption(ctx.getSimpleContext()) + funcCallWithErr._1.map(_._2), + saveExprContext.toOption(ctx.getSimpleContext()) ) result = if (errorList.isEmpty) { @@ -647,7 +649,7 @@ object ExpressionCompiler { ctx, REF(keyWithErr._1.get), typeWithErr._1.get, - Expressions.REF(p, keyPart, None, ctxOpt = saveExprContext.toOption(ctx.getSimpleContext())) + Expressions.REF(p, keyPart, typeWithErr._1, saveExprContext.toOption(ctx.getSimpleContext())) ) } else { CompilationStepResultExpr( @@ -660,23 +662,17 @@ object ExpressionCompiler { } } yield result - private def compileFold( - p: Pos, - limit: Int, - list: Expressions.EXPR, - acc: Expressions.EXPR, - func: PART[String] - ): CompileM[CompilationStepResultExpr] = + private def compileFold(fold: Expressions.FOLD): CompileM[CompilationStepResultExpr] = for { - (compiledList, listType, _, compileListErrors) <- compileExpr(list) - name = s"FOLD<$limit>" + (compiledList, listType, _, compileListErrors) <- compileExpr(fold.list) + name = s"FOLD<${fold.limit}>" listInnerType <- (listType match { case list: LIST => Right(list.innerType) - case other => Left(Generic(p.start, p.end, s"First $name argument should be List[A], but $other found")) + case other => Left(Generic(fold.position.start, fold.position.end, s"First $name argument should be List[A], but $other found")) }).toCompileM - (compiledAcc, accType, accRaw, compileAccErrors) <- compileExpr(acc) - funcName <- handlePart(func) - ctx <- get[Id, CompilerContext, CompilationError] + (compiledAcc, accType, _, compileAccErrors) <- compileExpr(fold.acc) + funcName <- handlePart(fold.func.key) + ctx <- get[Id, CompilerContext, CompilationError] compiledFunc <- ctx .functionTypeSignaturesByName(funcName, args = 2) .collectFirst { @@ -686,14 +682,14 @@ object ExpressionCompiler { .getOrElse { val accTypeStr = if (accType == NOTHING) ANY else accType val listInnerTypeStr = if (listInnerType == NOTHING) ANY else listInnerType - Left(Generic(p.start, p.end, s"Can't find suitable function $funcName(a: $accTypeStr, b: $listInnerTypeStr) for $name")) + Left(Generic(fold.position.start, fold.position.end, s"Can't find suitable function $funcName(a: $accTypeStr, b: $listInnerTypeStr) for $name")) } .toCompileM _ <- set[Id, CompilerContext, CompilationError](ctx.copy(foldIdx = ctx.foldIdx + 1)) resultType = compiledFunc.args.head._2.asInstanceOf[FINAL] compiledFold <- { - val unwrapped = CompilerMacro.unwrapFold(ctx.foldIdx, limit, compiledList, compiledAcc, compiledFunc.header) - CompilationStepResultExpr(ctx, unwrapped, resultType, accRaw, compileListErrors ++ compileAccErrors) + val unwrapped = CompilerMacro.unwrapFold(ctx.foldIdx, fold.limit, compiledList, compiledAcc, compiledFunc.header) + CompilationStepResultExpr(ctx, unwrapped, resultType, fold.copy(resultType = Some(resultType)), compileListErrors ++ compileAccErrors) .asRight[CompilationError] .toCompileM } diff --git a/lang/tests-js/src/test/scala/JsAPITest.scala b/lang/tests-js/src/test/scala/JsAPITest.scala index 80bc6fe89bf..88f74e9100c 100644 --- a/lang/tests-js/src/test/scala/JsAPITest.scala +++ b/lang/tests-js/src/test/scala/JsAPITest.scala @@ -1,6 +1,8 @@ import com.wavesplatform.lang.directives.values.{V5, V6} import utest.* +import scala.scalajs.js.{Array, Dynamic, JSON} + object JsAPITest extends JsTestBase { private def simpleDApp(result: String): String = dApp( @@ -49,5 +51,56 @@ object JsAPITest extends JsTestBase { r.userFunctionComplexities ==> Map("f" -> 2) r.globalVariableComplexities ==> Map("x" -> 1) } + + test("AST result type for declarations") { + val compiled = JsAPI.parseAndCompile( + dApp( + """ + | func sum(acc: List[Int], elem: Int) = acc :+ elem + | let arr = [1, 2, 3, 4, 5] + | let letFold = FOLD<5>(arr, [], sum) + | + | @Callable(i) + | func default() = { + | let letCall = i.caller.toString() + | let letIf = if (true) then 1 else "" + | let letMatch = match letIf { + | case _: Int => true + | case _: String => Address(base58'') + | } + | func funcRef() = letCall + | [] + | } + """.stripMargin, + V6 + ), + 3 + ) + val callables = compiled.dAppAst.annFuncList.asInstanceOf[Array[Dynamic]] + + val invocation = callables(0).func.expr.dec.expr.args.asInstanceOf[Array[Dynamic]].apply(0).ref + invocation.name ==> "i" + invocation.resultType.`type` ==> "Invocation" + + val letCall = callables(0).func.expr.dec + letCall.name.value ==> "letCall" + letCall.expr.resultType.`type` ==> "String" + + val letIf = callables(0).func.expr.body.dec + letIf.name.value ==> "letIf" + JSON.stringify(letIf.expr.resultType.unionTypes) ==> """[{"type":"Int"},{"type":"String"}]""" + + val letMatch = callables(0).func.expr.body.body.dec + letMatch.name.value ==> "letMatch" + JSON.stringify(letMatch.expr.resultType.unionTypes) ==> """[{"type":"Boolean"},{"type":"Address"}]""" + + val funcRef = callables(0).func.expr.body.body.body.dec + funcRef.name.value ==> "funcRef" + funcRef.expr.resultType.`type` ==> "String" + + val letFold = compiled.dAppAst.decList.asInstanceOf[Array[Dynamic]].apply(2) + letFold.name.value ==> "letFold" + JSON.stringify(letFold.expr.resultType) ==> """{"listOf":{"type":"Int"}}""" + } } } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerWithParserV2Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerWithParserV2Test.scala index 48c11cf5bdc..8735be1687f 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerWithParserV2Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerWithParserV2Test.scala @@ -39,7 +39,7 @@ class ExpressionCompilerWithParserV2Test extends PropSpec { |else | false | - |""".stripMargin + """.stripMargin val result = compile(script) @@ -60,13 +60,13 @@ class ExpressionCompilerWithParserV2Test extends PropSpec { FUNCTION_CALL( AnyPos, PART.VALID(AnyPos, "+"), - List(REF(AnyPos, PART.VALID(AnyPos, "foo"), None, None), REF(AnyPos, PART.VALID(AnyPos, "bar"), None, None)), - None, + List(REF(AnyPos, PART.VALID(AnyPos, "foo"), Some(LONG), None), REF(AnyPos, PART.VALID(AnyPos, "bar"), Some(LONG), None)), + Some(LONG), None ), CONST_LONG(AnyPos, 123456, None) ), - None, + Some(BOOLEAN), None ), TRUE(AnyPos, None),