mirror of
https://github.com/apple/pkl.git
synced 2026-05-25 16:19:20 +02:00
Implement power assertions (#1384)
This adds power assertions to Pkl! This implements the SPICE described in https://github.com/apple/pkl-evolution/pull/29 This follows the power assertions style of reporting also found in Groovy, Kotlin, and others. * Literal values are not emitted in the diagram * Stdlib constructors of literals like `List(1, 2)` are also considered literals Power assertions are added to: * Failing type constraints * Failing test facts Power assertions are implemented as a truffle instrument to observe execution. When an assertion fails, the instrument is created and the assertion is run again to observe facts. This incurs runtime overhead to collect facts, but has no impact on code in the non-error case. --------- Co-authored-by: Islon Scherer <islonscherer@gmail.com>
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
foo: String(this == "foo") = "bar"
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
local list = List(1, 2, 3)
|
||||
|
||||
foo: Dynamic(this == new Dynamic { ...list })
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
local list = List(1)
|
||||
|
||||
foo: Dynamic(this == new Dynamic { for (elem in list) { elem + 1 } })
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
foo: String(true, this.length > 5) = "bob"
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
name: String = "Bub"
|
||||
|
||||
foo: String(this == "Hello, \(name)") = "Hello, Patty"
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
open class Foo {
|
||||
name: String = "Foo"
|
||||
}
|
||||
|
||||
class Bar extends Foo {
|
||||
bub: String(startsWith(super.name)) = "Bubby"
|
||||
}
|
||||
|
||||
bar: Bar
|
||||
+1
@@ -0,0 +1 @@
|
||||
foo: String(this == "foo") | String(this == "qux") = "bar"
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
class Bird {
|
||||
name: String?
|
||||
}
|
||||
|
||||
// this will create `NullPropagatingOperationNode` and `InvokeVirtualMethodNode` for
|
||||
// `name?.contains("Bob")`.
|
||||
// this tests that we don't have two duplicate values in the diagram.
|
||||
p: Bird(name?.contains("Bob") ?? false) = new {
|
||||
name = "Pigeon"
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
local name = "Bob"
|
||||
local function greet(name: String) = "Hello, \(name)"
|
||||
|
||||
foo: Any(greet(name) == "Hello, \(name)!") = null
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
foo: String(startsWith(bub.name)) = "Huzzah"
|
||||
|
||||
bub: Person = new { name = "Bub" }
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
foo: Int(this
|
||||
+ two
|
||||
/ three
|
||||
== four) = 4
|
||||
|
||||
local two = 2
|
||||
|
||||
local three = 3
|
||||
|
||||
local four = 4
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
foo: List(fold(0, (a, b) -> a + b) == 5) = myList
|
||||
|
||||
myList = List(1, 2, 3)
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
foo: String(isCapitalized) = "hello"
|
||||
|
||||
local isCapitalized = (it: String) -> it == it.capitalize()
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
foo: String(isCapitalized) = "hello"
|
||||
|
||||
local isCapitalized = (it: String) ->
|
||||
let (first = it.take(1).toUpperCase())
|
||||
it == "\(first)\(it.drop(1))"
|
||||
+1
@@ -0,0 +1 @@
|
||||
foo: UInt8 = -5
|
||||
+1
@@ -0,0 +1 @@
|
||||
foo: NonNull = null
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// stdlib constructors are treated as literals
|
||||
foo: Int(
|
||||
IntSeq(1, 5).toList().contains(this)
|
||||
|| Map(1, 2, 3, 4).values.contains(this)
|
||||
|| List(1, 2, 3, 4).contains(this)
|
||||
|| Set(1, 2, 3, 4).contains(this)
|
||||
|| Pair(1, 2).second == this
|
||||
|| Bytes(1, 2, 3, 4).toList().contains(this)
|
||||
|| Regex("1234").toString().length == this
|
||||
) = 10
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
local num = 1
|
||||
|
||||
// stdlib constructors that have a non-literal are not treated as literals
|
||||
foo: Int(
|
||||
IntSeq(num, 5).toList().contains(this)
|
||||
|| Map(num, 2, 3, 4).values.contains(this)
|
||||
|| List(num, 2, 3, 4).contains(this)
|
||||
|| Set(num, 2, 3, 4).contains(this)
|
||||
|| Pair(num, 2).second == this
|
||||
|| Bytes(num, 2, 3, 4).toList().contains(this)
|
||||
|| Regex("\(num)234").toString().length == this
|
||||
) = 10
|
||||
+6
-1
@@ -1,5 +1,10 @@
|
||||
amends "pkl:Project"
|
||||
|
||||
dependencies {
|
||||
["badLocalProject"] = import("../badLocalProject/PklProject")
|
||||
["badLocalProject"] = import("PklProject")
|
||||
}
|
||||
|
||||
// hack: override the project file URI so that our tests render the same error message
|
||||
// across different machines (it will truncate at 100 chars, and otherwise would be influenced by
|
||||
// where the pkl project is placed within the file tree).
|
||||
projectFileUri = "file:///$snippetsDir/projects/badProjectDeps4/PklProject"
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
Type constraint `this >= min` violated.
|
||||
Value: 3
|
||||
|
||||
this >= min
|
||||
│ │ │
|
||||
3 │ 4
|
||||
false
|
||||
|
||||
x | max: Int(this >= min)
|
||||
^^^^^^^^^^^
|
||||
at constraints5#Gauge.max (file:///$snippetsDir/input/classes/constraints5.pkl)
|
||||
|
||||
+4
@@ -10,6 +10,10 @@ at unionTypesErrorDifferent2#X.a (file:///$snippetsDir/input/classes/unionTypesE
|
||||
Type constraint `length > 3` violated.
|
||||
Value: List(1, 2, 3)
|
||||
|
||||
length > 3
|
||||
│ │
|
||||
3 false
|
||||
|
||||
x | a = List(1, 2, 3)
|
||||
^^^^^^^^^^^^^
|
||||
at unionTypesErrorDifferent2#res1.a (file:///$snippetsDir/input/classes/unionTypesErrorDifferent2.pkl)
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
Type constraint `!isEmpty` violated.
|
||||
Value: List()
|
||||
|
||||
!isEmpty
|
||||
││
|
||||
│true
|
||||
false
|
||||
|
||||
x | names: List<String>(!isEmpty)
|
||||
^^^^^^^^
|
||||
at wrongType6#Person.names (file:///$snippetsDir/input/classes/wrongType6.pkl)
|
||||
|
||||
+7
@@ -2,6 +2,13 @@
|
||||
Type constraint `firstOneIsSandy` violated.
|
||||
Value: new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
(it: Listing<Bird>) -> it[0].name == "Sandy"
|
||||
│ │ │ │
|
||||
│ │ │ false
|
||||
│ │ "Bob"
|
||||
│ new Bird { name = "Bob" }
|
||||
new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
x | birds: Listing(firstOneIsSandy) = new {
|
||||
^^^^^^^^^^^^^^^
|
||||
at constraintDetails1#birds (file:///$snippetsDir/input/errors/constraintDetails1.pkl)
|
||||
|
||||
+11
@@ -3,6 +3,17 @@ Type constraint `let (myself: Listing<Bird> = this)
|
||||
myself[0].name == "Sandy"` violated.
|
||||
Value: new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
let (myself: Listing<Bird> = this)
|
||||
│
|
||||
new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
myself[0].name == "Sandy"
|
||||
│ │ │ │
|
||||
│ │ │ false
|
||||
│ │ "Bob"
|
||||
│ new Bird { name = "Bob" }
|
||||
new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
|
||||
|
||||
x | let (myself: Listing<Bird> = this)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at constraintDetails2#birds (file:///$snippetsDir/input/errors/constraintDetails2.pkl)
|
||||
|
||||
+5
@@ -2,6 +2,11 @@
|
||||
Type constraint `toList().every((it: Listing<Bird>) -> it[0].name == "Bob")` violated.
|
||||
Value: new Listing { new Listing { new Bird { name = "Eagle" }; new Bird { name = ? ...
|
||||
|
||||
toList().every((it: Listing<Bird>) -> it[0].name == "Bob")
|
||||
│ │
|
||||
│ false
|
||||
List(new Listing { new Bird { name = "Eagle" }; new Bird { name = ? } })
|
||||
|
||||
x | foo: Listing(toList().every((it: Listing<Bird>) -> it[0].name == "Bob")) = new {
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at constraintDetails3#foo (file:///$snippetsDir/input/errors/constraintDetails3.pkl)
|
||||
|
||||
+4
@@ -2,6 +2,10 @@
|
||||
Type constraint `isBetween(0, 65535)` violated.
|
||||
Value: -1
|
||||
|
||||
isBetween(0, 65535)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt16 = Int(isBetween(0, 65535))
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
at intrinsifiedTypeAlias2#res1 (pkl:base)
|
||||
|
||||
+4
@@ -2,6 +2,10 @@
|
||||
Type constraint `!(this is Null)` violated.
|
||||
Value: null
|
||||
|
||||
!(this is Null)
|
||||
│
|
||||
false
|
||||
|
||||
xx | typealias NonNull = Any(!(this is Null))
|
||||
^^^^^^^^^^^^^^^
|
||||
at intrinsifiedTypeAlias3#res1 (pkl:base)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `isBetween(0, 255)` violated.
|
||||
Value: 65535
|
||||
|
||||
isBetween(0, 255)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt8 = Int(isBetween(0, 255))
|
||||
^^^^^^^^^^^^^^^^^
|
||||
at invalidBytes1#foo (pkl:base)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `isBetween(0, 255)` violated.
|
||||
Value: 4095
|
||||
|
||||
isBetween(0, 255)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt8 = Int(isBetween(0, 255))
|
||||
^^^^^^^^^^^^^^^^^
|
||||
at invalidBytes2#res (pkl:base)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `isBetween(0, 255)` violated.
|
||||
Value: 65535
|
||||
|
||||
isBetween(0, 255)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt8 = Int(isBetween(0, 255))
|
||||
^^^^^^^^^^^^^^^^^
|
||||
at invalidBytes3#bytes (pkl:base)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
Type constraint `isBetween(0, 255)` violated.
|
||||
Value: 65535
|
||||
|
||||
isBetween(0, 255)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt8 = Int(isBetween(0, 255))
|
||||
^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#List.toBytes (pkl:base)
|
||||
|
||||
+5
@@ -2,6 +2,11 @@
|
||||
Type constraint `!isEmpty` violated.
|
||||
Value: ""
|
||||
|
||||
!isEmpty
|
||||
││
|
||||
│true
|
||||
false
|
||||
|
||||
x | res: Listing<String(!isEmpty)> = new Listing<String> {
|
||||
^^^^^^^^
|
||||
at listingTypeCheckError3#res (file:///$snippetsDir/input/errors/listingTypeCheckError3.pkl)
|
||||
|
||||
+4
@@ -2,6 +2,10 @@
|
||||
Type constraint `endsWith("ga")` violated.
|
||||
Value: "hola"
|
||||
|
||||
endsWith("ga")
|
||||
│
|
||||
false
|
||||
|
||||
x | res: Listing<String(!isEmpty)> = new Listing<String(endsWith("ga"))> {
|
||||
^^^^^^^^^^^^^^
|
||||
at listingTypeCheckError4#res (file:///$snippetsDir/input/errors/listingTypeCheckError4.pkl)
|
||||
|
||||
+4
@@ -2,6 +2,10 @@
|
||||
Type constraint `length == 5` violated.
|
||||
Value: "hi"
|
||||
|
||||
length == 5
|
||||
│ │
|
||||
2 false
|
||||
|
||||
xx | res: Mapping<Listing<String(length == 5)>, String> = bar
|
||||
^^^^^^^^^^^
|
||||
at mappingTypeCheckError6#res (file:///$snippetsDir/input/errors/mappingTypeCheckError6.pkl)
|
||||
|
||||
+4
@@ -2,6 +2,10 @@
|
||||
Type constraint `length == 5` violated.
|
||||
Value: "hi"
|
||||
|
||||
length == 5
|
||||
│ │
|
||||
2 false
|
||||
|
||||
x | res = new Mapping<Listing<String(length == 5)>, String> {
|
||||
^^^^^^^^^^^
|
||||
at mappingTypeCheckError7#res (file:///$snippetsDir/input/errors/mappingTypeCheckError7.pkl)
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this == "foo"` violated.
|
||||
Value: "bar"
|
||||
|
||||
this == "foo"
|
||||
│ │
|
||||
│ false
|
||||
"bar"
|
||||
|
||||
x | foo: String(this == "foo") = "bar"
|
||||
^^^^^^^^^^^^^
|
||||
at typeConstraints1#foo (file:///$snippetsDir/input/errors/power/typeConstraints1.pkl)
|
||||
|
||||
x | foo: String(this == "foo") = "bar"
|
||||
^^^^^
|
||||
at typeConstraints1#foo (file:///$snippetsDir/input/errors/power/typeConstraints1.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this == new Dynamic { ...list }` violated.
|
||||
Value: new Dynamic {}
|
||||
|
||||
this == new Dynamic { ...list }
|
||||
│ │ │ │
|
||||
│ │ │ List(1, 2, 3)
|
||||
│ │ new Dynamic { 1; 2; 3 }
|
||||
│ false
|
||||
new Dynamic {}
|
||||
|
||||
x | foo: Dynamic(this == new Dynamic { ...list })
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints10#foo (file:///$snippetsDir/input/errors/power/typeConstraints10.pkl)
|
||||
|
||||
x | foo: Dynamic(this == new Dynamic { ...list })
|
||||
^^^
|
||||
at typeConstraints10#foo (file:///$snippetsDir/input/errors/power/typeConstraints10.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this == new Dynamic { for (elem in list) { elem + 1 } }` violated.
|
||||
Value: new Dynamic {}
|
||||
|
||||
this == new Dynamic { for (elem in list) { elem + 1 } }
|
||||
│ │ │ │
|
||||
│ │ new Dynamic { 2 } List(1)
|
||||
│ false
|
||||
new Dynamic {}
|
||||
|
||||
x | foo: Dynamic(this == new Dynamic { for (elem in list) { elem + 1 } })
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints11#foo (file:///$snippetsDir/input/errors/power/typeConstraints11.pkl)
|
||||
|
||||
x | foo: Dynamic(this == new Dynamic { for (elem in list) { elem + 1 } })
|
||||
^^^
|
||||
at typeConstraints11#foo (file:///$snippetsDir/input/errors/power/typeConstraints11.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this.length > 5` violated.
|
||||
Value: "bob"
|
||||
|
||||
this.length > 5
|
||||
│ │ │
|
||||
│ 3 false
|
||||
"bob"
|
||||
|
||||
x | foo: String(true, this.length > 5) = "bob"
|
||||
^^^^^^^^^^^^^^^
|
||||
at typeConstraints12#foo (file:///$snippetsDir/input/errors/power/typeConstraints12.pkl)
|
||||
|
||||
x | foo: String(true, this.length > 5) = "bob"
|
||||
^^^^^
|
||||
at typeConstraints12#foo (file:///$snippetsDir/input/errors/power/typeConstraints12.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this == "Hello, \(name)"` violated.
|
||||
Value: "Hello, Patty"
|
||||
|
||||
this == "Hello, \(name)"
|
||||
│ │ │ │
|
||||
│ │ │ "Bub"
|
||||
│ │ "Hello, Bub"
|
||||
│ false
|
||||
"Hello, Patty"
|
||||
|
||||
x | foo: String(this == "Hello, \(name)") = "Hello, Patty"
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints13#foo (file:///$snippetsDir/input/errors/power/typeConstraints13.pkl)
|
||||
|
||||
x | foo: String(this == "Hello, \(name)") = "Hello, Patty"
|
||||
^^^^^^^^^^^^^^
|
||||
at typeConstraints13#foo (file:///$snippetsDir/input/errors/power/typeConstraints13.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `startsWith(super.name)` violated.
|
||||
Value: "Bubby"
|
||||
|
||||
startsWith(super.name)
|
||||
│ │
|
||||
false "Foo"
|
||||
|
||||
x | bub: String(startsWith(super.name)) = "Bubby"
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints14#Bar.bub (file:///$snippetsDir/input/errors/power/typeConstraints14.pkl)
|
||||
|
||||
x | bub: String(startsWith(super.name)) = "Bubby"
|
||||
^^^^^^^
|
||||
at typeConstraints14#Bar.bub (file:///$snippetsDir/input/errors/power/typeConstraints14.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String(this == "foo") | String(this == "qux")`, but got a different `String`.
|
||||
Value: "bar"
|
||||
|
||||
x | foo: String(this == "foo") | String(this == "qux") = "bar"
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints15#foo (file:///$snippetsDir/input/errors/power/typeConstraints15.pkl)
|
||||
|
||||
* Value is not of type `String(this == "foo")` because:
|
||||
Type constraint `this == "foo"` violated.
|
||||
Value: "bar"
|
||||
|
||||
this == "foo"
|
||||
│ │
|
||||
│ false
|
||||
"bar"
|
||||
|
||||
* Value is not of type `String(this == "qux")` because:
|
||||
Type constraint `this == "qux"` violated.
|
||||
Value: "bar"
|
||||
|
||||
this == "qux"
|
||||
│ │
|
||||
│ false
|
||||
"bar"
|
||||
|
||||
x | foo: String(this == "foo") | String(this == "qux") = "bar"
|
||||
^^^^^
|
||||
at typeConstraints15#foo (file:///$snippetsDir/input/errors/power/typeConstraints15.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `name?.contains("Bob") ?? false` violated.
|
||||
Value: new Bird { name = "Pigeon" }
|
||||
|
||||
name?.contains("Bob") ?? false
|
||||
│ │ │
|
||||
│ false false
|
||||
"Pigeon"
|
||||
|
||||
x | p: Bird(name?.contains("Bob") ?? false) = new {
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints16#p (file:///$snippetsDir/input/errors/power/typeConstraints16.pkl)
|
||||
|
||||
x | p: Bird(name?.contains("Bob") ?? false) = new {
|
||||
^^^^^
|
||||
at typeConstraints16#p (file:///$snippetsDir/input/errors/power/typeConstraints16.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `greet(name) == "Hello, \(name)!"` violated.
|
||||
Value: null
|
||||
|
||||
greet(name) == "Hello, \(name)!"
|
||||
│ │ │ │ │
|
||||
│ │ │ │ "Bob"
|
||||
│ │ │ "Hello, Bob!"
|
||||
│ │ false
|
||||
│ "Bob"
|
||||
"Hello, Bob"
|
||||
|
||||
x | foo: Any(greet(name) == "Hello, \(name)!") = null
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints17#foo (file:///$snippetsDir/input/errors/power/typeConstraints17.pkl)
|
||||
|
||||
x | foo: Any(greet(name) == "Hello, \(name)!") = null
|
||||
^^^^
|
||||
at typeConstraints17#foo (file:///$snippetsDir/input/errors/power/typeConstraints17.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `startsWith(bub.name)` violated.
|
||||
Value: "Huzzah"
|
||||
|
||||
startsWith(bub.name)
|
||||
│ │ │
|
||||
false │ "Bub"
|
||||
new Person { name = "Bub" }
|
||||
|
||||
x | foo: String(startsWith(bub.name)) = "Huzzah"
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints2#foo (file:///$snippetsDir/input/errors/power/typeConstraints2.pkl)
|
||||
|
||||
x | foo: String(startsWith(bub.name)) = "Huzzah"
|
||||
^^^^^^^^
|
||||
at typeConstraints2#foo (file:///$snippetsDir/input/errors/power/typeConstraints2.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `this
|
||||
+ two
|
||||
/ three
|
||||
== four` violated.
|
||||
Value: 4
|
||||
|
||||
this
|
||||
│
|
||||
4
|
||||
|
||||
+ two
|
||||
│ │
|
||||
│ 2
|
||||
4.666666666666667
|
||||
|
||||
/ three
|
||||
│ │
|
||||
│ 3
|
||||
0.6666666666666666
|
||||
|
||||
== four
|
||||
│ │
|
||||
│ 4
|
||||
false
|
||||
|
||||
x | foo: Int(this
|
||||
^^^^
|
||||
at typeConstraints3#foo (file:///$snippetsDir/input/errors/power/typeConstraints3.pkl)
|
||||
|
||||
x | == four) = 4
|
||||
^
|
||||
at typeConstraints3#foo (file:///$snippetsDir/input/errors/power/typeConstraints3.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `fold(0, (a, b) -> a + b) == 5` violated.
|
||||
Value: List(1, 2, 3)
|
||||
|
||||
fold(0, (a, b) -> a + b) == 5
|
||||
│ │
|
||||
6 false
|
||||
|
||||
x | foo: List(fold(0, (a, b) -> a + b) == 5) = myList
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints4#foo (file:///$snippetsDir/input/errors/power/typeConstraints4.pkl)
|
||||
|
||||
x | foo: List(fold(0, (a, b) -> a + b) == 5) = myList
|
||||
^^^^^^
|
||||
at typeConstraints4#foo (file:///$snippetsDir/input/errors/power/typeConstraints4.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `isCapitalized` violated.
|
||||
Value: "hello"
|
||||
|
||||
(it: String) -> it == it.capitalize()
|
||||
│ │ │ │
|
||||
│ │ │ "Hello"
|
||||
│ │ "hello"
|
||||
│ false
|
||||
"hello"
|
||||
|
||||
x | foo: String(isCapitalized) = "hello"
|
||||
^^^^^^^^^^^^^
|
||||
at typeConstraints5#foo (file:///$snippetsDir/input/errors/power/typeConstraints5.pkl)
|
||||
|
||||
x | foo: String(isCapitalized) = "hello"
|
||||
^^^^^^^
|
||||
at typeConstraints5#foo (file:///$snippetsDir/input/errors/power/typeConstraints5.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `isCapitalized` violated.
|
||||
Value: "hello"
|
||||
|
||||
(it: String) ->
|
||||
let (first = it.take(1).toUpperCase())
|
||||
│ │ │
|
||||
│ "h" "H"
|
||||
"hello"
|
||||
|
||||
it == "\(first)\(it.drop(1))"
|
||||
│ │ │ │ │ │
|
||||
│ │ │ "H" │ "ello"
|
||||
│ │ "Hello" "hello"
|
||||
│ false
|
||||
"hello"
|
||||
|
||||
x | foo: String(isCapitalized) = "hello"
|
||||
^^^^^^^^^^^^^
|
||||
at typeConstraints6#foo (file:///$snippetsDir/input/errors/power/typeConstraints6.pkl)
|
||||
|
||||
x | foo: String(isCapitalized) = "hello"
|
||||
^^^^^^^
|
||||
at typeConstraints6#foo (file:///$snippetsDir/input/errors/power/typeConstraints6.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `isBetween(0, 255)` violated.
|
||||
Value: -5
|
||||
|
||||
isBetween(0, 255)
|
||||
│
|
||||
false
|
||||
|
||||
xxxx | typealias UInt8 = Int(isBetween(0, 255))
|
||||
^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints7#foo (pkl:base)
|
||||
|
||||
x | foo: UInt8 = -5
|
||||
^^
|
||||
at typeConstraints7#foo (file:///$snippetsDir/input/errors/power/typeConstraints7.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `!(this is Null)` violated.
|
||||
Value: null
|
||||
|
||||
!(this is Null)
|
||||
│
|
||||
false
|
||||
|
||||
xx | typealias NonNull = Any(!(this is Null))
|
||||
^^^^^^^^^^^^^^^
|
||||
at typeConstraints8#foo (pkl:base)
|
||||
|
||||
x | foo: NonNull = null
|
||||
^^^^
|
||||
at typeConstraints8#foo (file:///$snippetsDir/input/errors/power/typeConstraints8.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `IntSeq(1, 5).toList().contains(this)
|
||||
|| Map(1, 2, 3, 4).values.contains(this)
|
||||
|| List(1, 2, 3, 4).contains(this)
|
||||
|| Set(1, 2, 3, 4).contains(this)
|
||||
|| Pair(1, 2).second == this
|
||||
|| Bytes(1, 2, 3, 4).toList().contains(this)
|
||||
|| Regex("1234").toString().length == this` violated.
|
||||
Value: 10
|
||||
|
||||
IntSeq(1, 5).toList().contains(this)
|
||||
│ │ │
|
||||
│ false 10
|
||||
List(1, 2, 3, 4, 5)
|
||||
|
||||
|| Map(1, 2, 3, 4).values.contains(this)
|
||||
│ │ │ │
|
||||
false │ false 10
|
||||
List(2, 4)
|
||||
|
||||
|| List(1, 2, 3, 4).contains(this)
|
||||
│ │ │
|
||||
false false 10
|
||||
|
||||
|| Set(1, 2, 3, 4).contains(this)
|
||||
│ │ │
|
||||
false false 10
|
||||
|
||||
|| Pair(1, 2).second == this
|
||||
│ │ │ │
|
||||
false 2 │ 10
|
||||
false
|
||||
|
||||
|| Bytes(1, 2, 3, 4).toList().contains(this)
|
||||
│ │ │ │
|
||||
false │ false 10
|
||||
List(1, 2, 3, 4)
|
||||
|
||||
|| Regex("1234").toString().length == this
|
||||
│ │ │ │ │
|
||||
false │ 13 │ 10
|
||||
│ false
|
||||
"Regex(\"1234\")"
|
||||
|
||||
x | IntSeq(1, 5).toList().contains(this)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints9#foo (file:///$snippetsDir/input/errors/power/typeConstraints9.pkl)
|
||||
|
||||
xx | ) = 10
|
||||
^^
|
||||
at typeConstraints9#foo (file:///$snippetsDir/input/errors/power/typeConstraints9.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `IntSeq(num, 5).toList().contains(this)
|
||||
|| Map(num, 2, 3, 4).values.contains(this)
|
||||
|| List(num, 2, 3, 4).contains(this)
|
||||
|| Set(num, 2, 3, 4).contains(this)
|
||||
|| Pair(num, 2).second == this
|
||||
|| Bytes(num, 2, 3, 4).toList().contains(this)
|
||||
|| Regex("\(num)234").toString().length == this` violated.
|
||||
Value: 10
|
||||
|
||||
IntSeq(num, 5).toList().contains(this)
|
||||
│ │ │ │ │
|
||||
│ 1 │ false 10
|
||||
IntSeq(1, 5) List(1, 2, 3, 4, 5)
|
||||
|
||||
|| Map(num, 2, 3, 4).values.contains(this)
|
||||
│ │ │ │ │ │
|
||||
│ │ 1 │ false 10
|
||||
│ Map(1, 2, 3, 4) List(2, 4)
|
||||
false
|
||||
|
||||
|| List(num, 2, 3, 4).contains(this)
|
||||
│ │ │ │ │
|
||||
│ │ 1 false 10
|
||||
│ List(1, 2, 3, 4)
|
||||
false
|
||||
|
||||
|| Set(num, 2, 3, 4).contains(this)
|
||||
│ │ │ │ │
|
||||
│ │ 1 false 10
|
||||
│ Set(1, 2, 3, 4)
|
||||
false
|
||||
|
||||
|| Pair(num, 2).second == this
|
||||
│ │ │ │ │ │
|
||||
│ │ 1 2 │ 10
|
||||
│ Pair(1, 2) false
|
||||
false
|
||||
|
||||
|| Bytes(num, 2, 3, 4).toList().contains(this)
|
||||
│ │ │ │ │ │
|
||||
│ │ 1 │ false 10
|
||||
│ Bytes(1, 2, 3, 4) List(1, 2, 3, 4)
|
||||
false
|
||||
|
||||
|| Regex("\(num)234").toString().length == this
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ │ 1 │ 13 │ 10
|
||||
│ │ "1234" │ false
|
||||
│ Regex("1234") "Regex(\"1234\")"
|
||||
false
|
||||
|
||||
x | IntSeq(num, 5).toList().contains(this)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at typeConstraints9a#foo (file:///$snippetsDir/input/errors/power/typeConstraints9a.pkl)
|
||||
|
||||
xx | ) = 10
|
||||
^^
|
||||
at typeConstraints9a#foo (file:///$snippetsDir/input/errors/power/typeConstraints9a.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
|
||||
^^^^
|
||||
at pkl.base#Module.output.bytes (pkl:base)
|
||||
+5
@@ -2,6 +2,11 @@
|
||||
Type constraint `!isEmpty` violated.
|
||||
Value: ""
|
||||
|
||||
!isEmpty
|
||||
││
|
||||
│true
|
||||
false
|
||||
|
||||
x | res1: String(!isEmpty) = ""
|
||||
^^^^^^^^
|
||||
at typedModuleProperties3#res1 (file:///$snippetsDir/input/modules/typedModuleProperties3.pkl)
|
||||
|
||||
+28
-4
@@ -1,6 +1,6 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `*RemoteDependency | Project(isValidLoadDependency)`, but got a different `pkl.Project`.
|
||||
Value: new ModuleClass { package = null; tests {}; dependencies {}; evaluatorSetting...
|
||||
Value: new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] ...
|
||||
|
||||
xxx | dependencies: Mapping<String(!contains("/")), *RemoteDependency | Project(isValidLoadDependency)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -8,8 +8,32 @@ at pkl.Project#dependencies (pkl:Project)
|
||||
|
||||
* Value is not of type `Project(isValidLoadDependency)` because:
|
||||
Type constraint `isValidLoadDependency` violated.
|
||||
Value: new ModuleClass { package = null; tests {}; dependencies {}; evaluatorSetti...
|
||||
Value: new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"...
|
||||
|
||||
x | ["badLocalProject"] = import("../badLocalProject/PklProject")
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
(it: Project) ->
|
||||
isUriLocal(projectFileUri, it.projectFileUri)
|
||||
│ │ │ │
|
||||
true │ │ "file:///$snippetsDir/projects/badProjectDeps4/PklProject"
|
||||
│ new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] = ? }; evaluatorSett...
|
||||
"file:///$snippetsDir/projects/badProjectDeps4/PklProject"
|
||||
|
||||
&& it.projectFileUri.endsWith("/PklProject")
|
||||
│ │ │ │
|
||||
│ │ │ true
|
||||
│ │ "file:///$snippetsDir/projects/badProjectDeps4/PklProject"
|
||||
│ new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] = ? }; evaluatorSett...
|
||||
true
|
||||
|
||||
&& it != module
|
||||
│ │ │
|
||||
│ │ false
|
||||
│ new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] = ? }; evaluatorSett...
|
||||
false
|
||||
|
||||
&& it.package != null
|
||||
│
|
||||
false
|
||||
|
||||
x | ["badLocalProject"] = import("PklProject")
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
at PklProject#dependencies["badLocalProject"] (file:///$snippetsDir/input/projects/badProjectDeps4/PklProject)
|
||||
|
||||
+4
@@ -2,6 +2,10 @@
|
||||
Type constraint `endsWith(lastName)` violated.
|
||||
Value: "Jimmy Bird"
|
||||
|
||||
endsWith(lastName)
|
||||
│ │
|
||||
false "Birdo"
|
||||
|
||||
x | typealias Birds = Listing<String(endsWith(lastName))>
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
at typeAliasContext#res (file:///$snippetsDir/input/types/helpers/originalTypealias.pkl)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -88,10 +88,26 @@ class EvaluateTestsTest {
|
||||
assertThat(res.failures.size).isEqualTo(2)
|
||||
|
||||
val fail1 = res.failures[0]
|
||||
assertThat(fail1.message).isEqualTo("1 == 2 (repl:text)")
|
||||
assertThat(fail1.message)
|
||||
.isEqualTo(
|
||||
"""
|
||||
1 == 2 (repl:text)
|
||||
│
|
||||
false
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
|
||||
val fail2 = res.failures[1]
|
||||
assertThat(fail2.message).isEqualTo(""""foo" == "bar" (repl:text)""")
|
||||
assertThat(fail2.message)
|
||||
.isEqualTo(
|
||||
"""
|
||||
"foo" == "bar" (repl:text)
|
||||
│
|
||||
false
|
||||
"""
|
||||
.trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -192,7 +192,7 @@ class StackTraceRendererTest {
|
||||
val loop = StackTraceRenderer.StackFrameLoop(loopFrames, 1)
|
||||
val frames = listOf(createFrame("bar", 1), createFrame("baz", 2), loop)
|
||||
val formatter = AnsiStringBuilder(false)
|
||||
renderer.doRender(frames, null, formatter, "", true)
|
||||
renderer.doRender(frames, null, null, formatter, "", true)
|
||||
val renderedFrames = formatter.toString()
|
||||
assertThat(renderedFrames)
|
||||
.isEqualTo(
|
||||
|
||||
Reference in New Issue
Block a user