mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-12 17:55:33 +00:00
feat(compiler): Enhance message of type error [LNG-313] (#1033)
This commit is contained in:
parent
ae32f80277
commit
d5cd77bb86
@ -17,6 +17,7 @@ import cats.data.{NonEmptyList, OptionT}
|
||||
import cats.instances.list.*
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.bifunctor.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.foldable.*
|
||||
import cats.syntax.functor.*
|
||||
@ -136,27 +137,23 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](using
|
||||
reportNamedArgsDuplicates(fields)
|
||||
)
|
||||
fieldsGiven <- fields
|
||||
.traverse(arg => OptionT(valueToRaw(arg.argValue)).map(arg.argName.value -> _))
|
||||
.traverse(arg =>
|
||||
OptionT(valueToRaw(arg.argValue)).map(valueRaw =>
|
||||
arg.argName.value -> (arg, valueRaw)
|
||||
)
|
||||
)
|
||||
.map(_.toNem) // Take only last value for a field
|
||||
fieldsGivenTypes = fieldsGiven.map(_.`type`)
|
||||
generated <- OptionT.fromOption(
|
||||
resolvedType match {
|
||||
case struct: StructType =>
|
||||
(
|
||||
struct.copy(fields = fieldsGivenTypes),
|
||||
MakeStructRaw(fieldsGiven, struct)
|
||||
).some
|
||||
case ability: AbilityType =>
|
||||
(
|
||||
ability.copy(fields = fieldsGivenTypes),
|
||||
AbilityRaw(fieldsGiven, ability)
|
||||
).some
|
||||
}
|
||||
)
|
||||
(genType, genData) = generated
|
||||
fieldsGivenRaws = fieldsGiven.map { case (_, raw) => raw }
|
||||
fieldsGivenTypes = fieldsGiven.map(_.map(_.`type`))
|
||||
generated = resolvedType match {
|
||||
case struct: StructType =>
|
||||
MakeStructRaw(fieldsGivenRaws, struct)
|
||||
case ability: AbilityType =>
|
||||
AbilityRaw(fieldsGivenRaws, ability)
|
||||
}
|
||||
data <- OptionT.whenM(
|
||||
T.ensureTypeMatches(dvt, resolvedType, genType)
|
||||
)(genData.pure)
|
||||
T.ensureTypeConstructibleFrom(dvt, resolvedType, fieldsGivenTypes)
|
||||
)(generated.pure)
|
||||
} yield data).value
|
||||
|
||||
case ct @ CollectionToken(_, values) =>
|
||||
|
@ -60,6 +60,22 @@ trait TypesAlgebra[S[_], Alg[_]] {
|
||||
|
||||
def ensureTypeMatches(token: Token[S], expected: Type, givenType: Type): Alg[Boolean]
|
||||
|
||||
/**
|
||||
* Check if given type (ability or struct)
|
||||
* can be constructed from given arguments
|
||||
*
|
||||
* @param token token of construction expression (for error reporting)
|
||||
* @param expected type to construct
|
||||
* @param arguments arguments to construct with (name -> (named arg, type))
|
||||
* @return true if type can be constructed from given arguments
|
||||
* reports error and warnings if necessary
|
||||
*/
|
||||
def ensureTypeConstructibleFrom(
|
||||
token: Token[S],
|
||||
expected: AbilityType | StructType,
|
||||
arguments: NonEmptyMap[String, (NamedArg[S], Type)]
|
||||
): Alg[Boolean]
|
||||
|
||||
def typeToCollectible(token: Token[S], givenType: Type): OptionT[Alg, CollectibleType]
|
||||
|
||||
def typeToStream(token: Token[S], givenType: Type): OptionT[Alg, StreamType]
|
||||
|
@ -2,6 +2,7 @@ package aqua.semantics.rules.types
|
||||
|
||||
import aqua.parser.lexer.*
|
||||
import aqua.raw.value.*
|
||||
import aqua.semantics.Levenshtein
|
||||
import aqua.semantics.rules.StackInterpreter
|
||||
import aqua.semantics.rules.locations.{DefinitionInfo, LocationsAlgebra}
|
||||
import aqua.semantics.rules.report.ReportAlgebra
|
||||
@ -415,6 +416,47 @@ class TypesInterpreter[S[_], X](using
|
||||
}
|
||||
}
|
||||
|
||||
override def ensureTypeConstructibleFrom(
|
||||
token: Token[S],
|
||||
expected: AbilityType | StructType,
|
||||
arguments: NonEmptyMap[String, (NamedArg[S], Type)]
|
||||
): State[X, Boolean] = for {
|
||||
/* Check that required fields are present
|
||||
among arguments and have correct types */
|
||||
enough <- expected.fields.toNel.traverse { case (name, typ) =>
|
||||
arguments.lookup(name) match {
|
||||
case Some(arg -> givenType) =>
|
||||
ensureTypeMatches(arg.argValue, typ, givenType)
|
||||
case None =>
|
||||
report
|
||||
.error(
|
||||
token,
|
||||
s"Missing argument '$name' of type '$typ'"
|
||||
)
|
||||
.as(false)
|
||||
}
|
||||
}.map(_.forall(identity))
|
||||
expectedKeys = expected.fields.keys.toNonEmptyList
|
||||
/* Report unexpected arguments */
|
||||
_ <- arguments.toNel.traverse_ { case (name, arg -> typ) =>
|
||||
expected.fields.lookup(name) match {
|
||||
case Some(_) => State.pure(())
|
||||
case None =>
|
||||
lazy val similar = Levenshtein
|
||||
.mostSimilar(name, expectedKeys, 3)
|
||||
.map(s => s"'$s'")
|
||||
.mkString(", ")
|
||||
val message =
|
||||
if (enough)
|
||||
s"Unexpected argument '$name'"
|
||||
else
|
||||
s"Unexpected argument '$name', did you mean $similar?"
|
||||
|
||||
report.warning(arg.argName, message)
|
||||
}
|
||||
}
|
||||
} yield enough
|
||||
|
||||
private def typeTo[T <: Type](
|
||||
token: Token[S],
|
||||
givenType: Type,
|
||||
|
Loading…
Reference in New Issue
Block a user