Skip to content

Commit

Permalink
fix(compiler): Return error if SemanticError occures [LNG-356] (#1126)
Browse files Browse the repository at this point in the history
  • Loading branch information
DieMyst authored Apr 17, 2024
1 parent f29e44e commit e6c5d00
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 104 deletions.
33 changes: 26 additions & 7 deletions aqua-src/antithesis.aqua
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
aqua Main
aqua Job declares *

export main
export aaa, bbb

func reward(amount: ?u32) -> u32:
result = ?[amount! / 10, 0]
<- result!
data Peer:
id: string

func main(a: u32) -> u32:
<- reward(?[a])
func aaa() -> Peer:
peer1 = Peer(id = "123")
peer2 = Peer(id = peer1.id)
<- peer2

data BrokenStruct:
fff: UnknownType

alias BrokenAlias: str3ng

ability BrokenAbility:
fff: str4ng

const BROKEN_CONST = UNKNOWN_CONST

func bbb() -> string:
<- 323

func ccc() -> Peer:
peer1 = Peer(id = "123")
peer2 = Peer(id = peer1.id)
<- peer2
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ object LspContext {
def blank[S[_]]: LspContext[S] = LspContext[S](raw = RawContext())

given [S[_]]: Monoid[LspContext[S]] with {
override def empty = blank[S]
override def empty: LspContext[S] = blank[S]

override def combine(x: LspContext[S], y: LspContext[S]) =
override def combine(x: LspContext[S], y: LspContext[S]): LspContext[S] =
LspContext[S](
raw = x.raw |+| y.raw,
abDefinitions = x.abDefinitions ++ y.abDefinitions,
Expand All @@ -52,7 +52,7 @@ object LspContext {
given [S[_]]: Picker[LspContext[S]] with {
import aqua.semantics.header.Picker.*

override def blank: LspContext[S] = LspContext[S](Picker[RawContext].blank, Map.empty)
override def blank: LspContext[S] = LspContext.blank[S]
override def exports(ctx: LspContext[S]): Map[String, Option[String]] = ctx.raw.exports

override def isAbility(ctx: LspContext[S], name: String): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,15 @@ package aqua.lsp

import aqua.parser.Ast
import aqua.parser.head.{ImportExpr, ImportFromExpr, UseExpr, UseFromExpr}
import aqua.parser.lexer.{LiteralToken, Token}
import aqua.parser.lexer.LiteralToken
import aqua.raw.ConstantRaw
import aqua.semantics.*
import aqua.semantics.header.Picker.*
import aqua.semantics.rules.locations.LocationsState
import aqua.semantics.{CompilerState, RawSemantics, SemanticError, SemanticWarning, Semantics}

import cats.data.Validated.{Invalid, Valid}
import cats.data.{NonEmptyChain, ValidatedNec}
import cats.syntax.applicative.*
import cats.syntax.apply.*
import cats.syntax.either.*
import cats.syntax.flatMap.*
import cats.syntax.foldable.*
import cats.data.{EitherT, NonEmptyChain, Writer}
import cats.syntax.functor.*
import cats.syntax.reducible.*
import cats.syntax.applicative.*
import monocle.Lens
import monocle.macros.GenLens

Expand Down Expand Up @@ -74,7 +68,6 @@ class LspSemantics[S[_]](
warnings = state.warnings.toList
).pure[Result]
}
// TODO: return as Eval
.value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package aqua.lsp
import aqua.compiler.FileIdString.given_FileId_String
import aqua.compiler.{AquaCompilerConf, AquaError, AquaSources}
import aqua.parser.Parser
import aqua.parser.lexer.Token
import aqua.parser.lift.Span
import aqua.parser.lift.Span.S
import aqua.raw.ConstantRaw
import aqua.semantics.rules.locations.{DefinitionInfo, TokenLocation, VariableInfo}
import aqua.semantics.{RulesViolated, SemanticError}
import aqua.types.*

import cats.Id
Expand Down Expand Up @@ -131,6 +133,16 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
ee
}

def insideError(err: SemanticError[S], str: String, pos: Int, code: String) = {
inside(err) { case RulesViolated(token, _) =>
val span = token.unit._1
val locatedOp = getByPosition(code, str, pos)
locatedOp shouldBe defined
val located = locatedOp.get
(span.startIndex, span.endIndex) shouldBe (located._1, located._2)
}
}

it should "return right tokens" in {
val main =
"""aqua Import declares foo_wrapper, Ab, Str, useAbAndStruct, SOME_CONST
Expand All @@ -153,6 +165,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
| num(someVar)
| OneMore fooResult
| OneMore.more_call()
| <- "123"
|
|ability Ab:
| someField: u32
Expand Down Expand Up @@ -362,26 +375,26 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
| a: SomeAlias
|
|data SomeStruct:
| al: SomeAlias
| al11: SomeAlias
| nested: NestedStruct
|
|ability SomeAbility:
| someStr: SomeStruct
| nested: NestedStruct
| al: SomeAlias
| someFunc(ss: SomeStruct, nest: NestedStruct, al: SomeAlias) -> NestedStruct, SomeStruct, SomeAlias
| al11: SomeAlias
| someFunc(ss: SomeStruct, nest: NestedStruct, al11: SomeAlias) -> NestedStruct, SomeStruct, SomeAlias
|
|service Srv("a"):
| check(ss: SomeStruct, nest: NestedStruct, al: SomeAlias) -> NestedStruct
| check(ss: SomeStruct, nest: NestedStruct, al11: SomeAlias) -> NestedStruct
| check2() -> SomeStruct
| check3() -> SomeAlias
|
|func withAb{SomeAbility}() -> SomeStruct:
| Srv.check()
| Srv.check(SomeAbility.someStr, SomeAbility.nested, SomeAbility.al11)
| Srv.check2()
| <- SomeAbility.someStr
|
|func main(ss: SomeStruct, nest: NestedStruct, al: SomeAlias) -> string:
|func main(ss: SomeStruct, nest: NestedStruct, al11: SomeAlias) -> string:
| Srv.check3()
| <- ""
|""".stripMargin
Expand All @@ -394,11 +407,11 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {

val nestedType = StructType("NestedStruct", NonEmptyMap.of(("a", ScalarType.string)))
val someStr =
StructType("SomeStruct", NonEmptyMap.of(("nested", nestedType), ("al", ScalarType.string)))
StructType("SomeStruct", NonEmptyMap.of(("nested", nestedType), ("al11", ScalarType.string)))

val abFuncType = ArrowType(
ProductType.labelled(
("ss", someStr) :: ("nest", nestedType) :: ("al", ScalarType.string) :: Nil
("ss", someStr) :: ("nest", nestedType) :: ("al11", ScalarType.string) :: Nil
),
ProductType(nestedType :: someStr :: ScalarType.string :: Nil)
)
Expand All @@ -407,7 +420,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
NonEmptyMap.of(
("someStr", someStr),
("nested", nestedType),
("al", ScalarType.string),
("al11", ScalarType.string),
("someFunc", abFuncType)
)
)
Expand All @@ -419,7 +432,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
"check",
ArrowType(
ProductType.labelled(
("ss", someStr) :: ("nest", nestedType) :: ("al", ScalarType.string) :: Nil
("ss", someStr) :: ("nest", nestedType) :: ("al11", ScalarType.string) :: Nil
),
ProductType(nestedType :: Nil)
)
Expand All @@ -445,10 +458,14 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
}

res.checkTokenLoc(main, "SomeAbility", 0, someAb) shouldBe true
// from {SomeAbility} to 'ability SomeAbility'
res.checkLocations("SomeAbility", 0, 1, main) shouldBe true
// from 'SomeAbility.someStr' to {SomeAbility}
res.checkLocations("SomeAbility", 0, 2, main) shouldBe true
Range.inclusive(1, 5).foreach { n =>
res.checkLocations("SomeAbility", 0, n, main) shouldBe true
}

res.checkLocations("someStr", 0, 1, main) shouldBe true
res.checkLocations("someStr", 0, 2, main) shouldBe true
res.checkLocations("nested", 1, 2, main) shouldBe true
res.checkLocations("al11", 1, 4, main) shouldBe true

res.checkTokenLoc(main, "Srv", 0, srvType) shouldBe true
Range.inclusive(1, 3).foreach { n =>
Expand Down Expand Up @@ -538,4 +555,99 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
res.checkLocations("Abilyy", 0, 2, main) shouldBe true
res.checkLocations("Abilyy", 0, 3, main) shouldBe true
}

it should "return correct locations of errors on exported functions (LNG-356)" in {
val main =
"""aqua Job declares *
|
|export aaa
|
|data Peer:
| id: string
|
|func aaa() -> string:
| peer = Pe2er(id = "123")
| <- peer.id""".stripMargin
val src = Map(
"index.aqua" -> main
)

val imports = Map.empty[String, String]

val res = compile(src, imports).toEither.toOption.get.values.head

val errors = res.errors

insideError(errors.head, "Pe2er", 0, main)
insideError(errors(1), "peer", 1, main)
insideError(errors(2), "string", 1, main)
}

it should "return correct locations in functions even if there is errors in other parts of a code" in {
val main =
"""aqua Job declares *
|
|export aaa, bbb
|
|data Peer:
| id: string
|
|func aaa() -> Peer:
| peer1 = Peer(id = "123")
| peer2 = Peer(id = peer1.id)
| <- peer2
|
|data BrokenStruct:
| fff: UnknownType1
|
|alias BrokenAlias: UnknownType2
|
|ability BrokenAbility:
| fff: UnknownType3
|
|const BROKEN_CONST = UNKNOWN_CONST
|
|func bbb() -> string:
| <- 323
|
|func ccc() -> Peer:
| peer1 = Peer(id = "123")
| peer2 = Peer(id = peer1.id)
| <- peer2
|
|""".stripMargin
val src = Map(
"index.aqua" -> main
)

val imports = Map.empty[String, String]

val res = compile(src, imports).toOption.get.values.head
val errors = res.errors

// 'aaa' function
res.checkLocations("Peer", 0, 1, main) shouldBe true
res.checkLocations("Peer", 0, 2, main) shouldBe true
res.checkLocations("Peer", 0, 3, main) shouldBe true
res.checkLocations("peer1", 0, 1, main) shouldBe true
res.checkLocations("peer1", 0, 1, main) shouldBe true

// 'ccc' function
res.checkLocations("Peer", 0, 4, main) shouldBe true
res.checkLocations("Peer", 0, 5, main) shouldBe true
res.checkLocations("Peer", 0, 6, main) shouldBe true
res.checkLocations("peer1", 2, 3, main) shouldBe true
res.checkLocations("peer1", 2, 3, main) shouldBe true

// errors
insideError(errors.head, "UnknownType1", 0, main)
insideError(errors(1), "BrokenStruct", 0, main)
insideError(errors(2), "UnknownType2", 0, main)
insideError(errors(3), "UnknownType3", 0, main)
insideError(errors(4), "BrokenAbility", 0, main)
insideError(errors(5), "UNKNOWN_CONST", 0, main)
insideError(errors(6), "323", 0, main)
insideError(errors(7), "string", 1, main)
}

}
10 changes: 10 additions & 0 deletions model/raw/src/main/scala/aqua/raw/ErroredPart.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package aqua.raw

import aqua.types.{BottomType, Type}

// Part for structures that have errors inside but must be registered in context
case class ErroredPart(name: String) extends RawPart {
override def rawPartType: Type = BottomType

override def rename(s: String): RawPart = copy(name = s)
}
4 changes: 2 additions & 2 deletions model/raw/src/main/scala/aqua/raw/RawContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ case class RawContext(
parts.map { case (_, p) => p.name }.toList.toSet

lazy val declaredNames: Set[String] =
allNames.filter(declares.contains)
allNames.intersect(declares)

override def toString: String =
s"""|module: ${module.getOrElse("unnamed")}
Expand All @@ -109,7 +109,7 @@ object RawContext {

override def empty: RawContext = blank

override def combine(x: RawContext, y: RawContext) =
override def combine(x: RawContext, y: RawContext): RawContext =
RawContext(
x.module orElse y.module,
x.declares ++ y.declares,
Expand Down
1 change: 0 additions & 1 deletion semantics/src/main/scala/aqua/semantics/RawSemantics.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ class RawSemantics[S[_]](
)
)
}
// TODO: return as Eval
.value
}
}
Expand Down
15 changes: 3 additions & 12 deletions semantics/src/main/scala/aqua/semantics/expr/AbilitySem.scala
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
package aqua.semantics.expr

import aqua.parser.expr.AbilityExpr
import aqua.raw.{Raw, TypeRaw}
import aqua.parser.lexer.{Name, NamedTypeToken}
import aqua.raw.{ErroredPart, Raw, TypeRaw}
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.abilities.AbilitiesAlgebra
import aqua.semantics.rules.definitions.DefinitionsAlgebra
import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra
import aqua.types.{AbilityType, ArrowType, Type}

import cats.Monad
import cats.syntax.apply.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.applicative.*
import cats.syntax.semigroupal.*
import cats.syntax.traverse.*
import cats.Monad
import cats.data.{NonEmptyList, NonEmptyMap}

class AbilitySem[S[_]](val expr: AbilityExpr[S]) extends AnyVal {

Expand All @@ -32,7 +23,7 @@ class AbilitySem[S[_]](val expr: AbilityExpr[S]) extends AnyVal {
fields = defs.view.mapValues(d => d.name -> d.`type`).toMap
abilityType <- T.defineAbilityType(expr.name, fields)
result = abilityType.map(st => TypeRaw(expr.name.value, st))
} yield result.getOrElse(Raw.error("Ability types unresolved"))
} yield result.getOrElse(ErroredPart(expr.name.value))
)
}
}
Loading

0 comments on commit e6c5d00

Please sign in to comment.