someItemsOf(
return result;
}
+ /**
+ * Check whether {@code simple} is in {@code simples}.
+ *
+ * The targets compare the instances of the classes by their references.
+ */
+ public static Boolean isInSimples(
+ ISimple simple,
+ List simples) {
+ return simples.contains(simple);
+ }
+
+ /**
+ * Check whether {@code simple} is in a local copy of {@code simples}.
+ *
+ * The local copy is built with {@code append}, and holds the same instances as
+ * {@code simples}.
+ */
+ public static Boolean isInLocalCopyOfSimples(
+ ISimple simple,
+ List simples) {
+ List copied = new java.util.ArrayList();
+ for (var other : simples) {
+ copied.add(other);
+ }
+ return copied.contains(simple);
+ }
+
/**
* Hash allowed enum values for efficient validation of enums.
*/
@@ -143,6 +170,24 @@ public Stream transformSomething(
"At most one of some items must be a some item.")));
}
+ if (!(
+ !isInSimples(that.getItem(), that.getSomeSimples()))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Item must not be one of some simples.")));
+ }
+
+ if (!(
+ that.getSomeSimples().stream().allMatch(
+ simple -> isInLocalCopyOfSimples(simple, that.getSomeSimples())))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Each of some simples must be in a local copy of some " +
+ "simples.")));
+ }
+
errorStream = Stream.concat(errorStream,
Stream.of(that.getItem())
.flatMap(Verification::verifyToErrorStream)
diff --git a/dev/test_data/main/java/expected/list_of_constrained_primitives/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/list_of_constrained_primitives/expected_output/src/main/java/dummy/verification/Verification.java
index 6e68127a7..9915d3734 100644
--- a/dev/test_data/main/java/expected/list_of_constrained_primitives/expected_output/src/main/java/dummy/verification/Verification.java
+++ b/dev/test_data/main/java/expected/list_of_constrained_primitives/expected_output/src/main/java/dummy/verification/Verification.java
@@ -56,6 +56,39 @@ public static List initialsOf(
return initials;
}
+ /**
+ * Check that {@code names} start with unique characters.
+ */
+ public static Boolean initialsAreUnique(
+ List names) {
+ List initials = new java.util.ArrayList();
+ for (var name : names) {
+ var initial = StringHelpers.slice(name, 0, 1);
+ if (initials.contains(initial)) {
+ return false;
+ }
+ initials.add(initial);
+ }
+ return true;
+ }
+
+ /**
+ * Check whether {@code text} is in {@code names}.
+ */
+ public static Boolean isInNames(
+ String text,
+ List names) {
+ return names.contains(text);
+ }
+
+ /**
+ * Check whether {@code name} contains a space.
+ */
+ public static Boolean containsSpace(
+ String name) {
+ return name.contains(" ");
+ }
+
/**
* Hash allowed enum values for efficient validation of enums.
*/
@@ -90,6 +123,36 @@ public Stream transformSomething(
"Some names must not start with x.")));
}
+ if (!(!that.getSomeNames().contains("Eve"))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Some names must not contain Eve.")));
+ }
+
+ if (!(!isInNames("Mallory", that.getSomeNames()))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Some names must not contain Mallory.")));
+ }
+
+ if (!(
+ that.getSomeNames().stream().allMatch(
+ name -> !containsSpace(name)))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Some names must not contain spaces.")));
+ }
+
+ if (!initialsAreUnique(that.getSomeNames())) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Initials of some names must be unique.")));
+ }
+
errorStream = Stream.concat(errorStream,
Stream.of(that.getSomeNames())
.flatMap(Verification::verifyListOf_Name)
diff --git a/dev/test_data/main/java/expected/list_of_enums/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/list_of_enums/expected_output/src/main/java/dummy/verification/Verification.java
index 176dc14f0..f3e3fcadd 100644
--- a/dev/test_data/main/java/expected/list_of_enums/expected_output/src/main/java/dummy/verification/Verification.java
+++ b/dev/test_data/main/java/expected/list_of_enums/expected_output/src/main/java/dummy/verification/Verification.java
@@ -57,6 +57,15 @@ public static List failuresOf(
return failures;
}
+ /**
+ * Check whether {@code result} is in {@code results}.
+ */
+ public static Boolean isInResults(
+ Result result,
+ List results) {
+ return results.contains(result);
+ }
+
/**
* Hash allowed enum values for efficient validation of enums.
*/
@@ -100,6 +109,24 @@ public Stream transformSomething(
"Some results must contain at most one failure.")));
}
+ if (!(
+ !isInResults(Result.FAIL, that.getSomeResults())
+ || that.getSomeResults().contains(Result.OK))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Some results must contain an ok if they contain a failure.")));
+ }
+
+ if (!(
+ that.getSomeResults().stream().allMatch(
+ result -> (new java.util.ArrayList(java.util.List.of(Result.OK, Result.FAIL))).contains(result)))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Some results must be either ok or fail.")));
+ }
+
errorStream = Stream.concat(errorStream,
Stream.of(that.getSomeResults())
.flatMap(Verification::verifyListOf_Result)
diff --git a/dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/verification/Verification.java b/dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/verification/Verification.java
index b5a7a3718..bd19f880a 100644
--- a/dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/verification/Verification.java
+++ b/dev/test_data/main/java/expected/list_of_primitives/expected_output/src/main/java/dummy/verification/Verification.java
@@ -28,6 +28,7 @@
import dummy.types.model.*;
import dummy.visitation.AbstractTransformer;
import java.util.List;
+import java.util.Optional;
public class Verification {
/**
@@ -70,6 +71,59 @@ public static List positivesOf(
return result;
}
+ /**
+ * Check that {@code numbers} are unique with a local list of the seen.
+ */
+ public static Boolean intsAreUnique(
+ List numbers) {
+ List seen = new java.util.ArrayList();
+ for (var number : numbers) {
+ if (seen.contains(number)) {
+ return false;
+ }
+ seen.add(number);
+ }
+ return true;
+ }
+
+ /**
+ * Check whether {@code text} is one of the separators.
+ */
+ public static Boolean isSeparator(
+ String text) {
+ return (
+ (new java.util.ArrayList(java.util.List.of(".", ",", ";"))).contains(text));
+ }
+
+ /**
+ * Check whether {@code number} is in {@code numbers}.
+ */
+ public static Boolean isInInts(
+ Long number,
+ List numbers) {
+ return numbers.contains(number);
+ }
+
+ /**
+ * Check whether {@code text} is in {@code texts}, if specified.
+ */
+ public static Boolean isInOptionalStrings(
+ String text,
+ Optional> texts) {
+ if (!texts.isPresent()) {
+ return false;
+ }
+ return texts.get().contains(text);
+ }
+
+ /**
+ * Check whether {@code text} contains a dot.
+ */
+ public static Boolean containsDot(
+ String text) {
+ return text.contains(".");
+ }
+
/**
* Hash allowed enum values for efficient validation of enums.
*/
@@ -111,6 +165,63 @@ public Stream transformSomething(
"At most one of some ints must be positive.")));
}
+ if (!(!that.getSomeInts().contains(0L))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Ints must not contain zero.")));
+ }
+
+ if (!(
+ !isInInts(
+ (long) that.getSomeStrings().size(),
+ that.getSomeInts()))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Ints must not contain the number of strings.")));
+ }
+
+ if (!(!that.getSomeFloats().contains(-1.0))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Floats must not contain minus one.")));
+ }
+
+ if (!(
+ !isInOptionalStrings("", Optional.of(that.getSomeStrings())))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Strings must not contain an empty string.")));
+ }
+
+ if (!(
+ that.getSomeStrings().stream().allMatch(
+ text -> !containsDot(text)))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Strings must not contain dots.")));
+ }
+
+ if (!intsAreUnique(that.getSomeInts())) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Some ints must be unique.")));
+ }
+
+ if (!(
+ that.getSomeStrings().stream().allMatch(
+ text -> !isSeparator(text)))) {
+ errorStream = Stream.concat(errorStream,
+ Stream.of(new Reporting.Error(
+ "Invariant violated:\n" +
+ "Some strings must not be separators.")));
+ }
+
return errorStream;
}
}
diff --git a/dev/test_data/main/python/expected/list_of_classes/expected_output/dummy/verification.py b/dev/test_data/main/python/expected/list_of_classes/expected_output/dummy/verification.py
index 860c02869..5a8b3de48 100644
--- a/dev/test_data/main/python/expected/list_of_classes/expected_output/dummy/verification.py
+++ b/dev/test_data/main/python/expected/list_of_classes/expected_output/dummy/verification.py
@@ -102,6 +102,36 @@ def some_items_of(
return result
+def is_in_simples(
+ simple: our_types.Simple,
+ simples: Sequence[our_types.Simple]
+) -> bool:
+ """
+ Check whether :paramref:`simple` is in :paramref:`simples`.
+
+ The targets compare the instances of the classes by their references.
+ """
+ # pylint: disable=all
+ return simple in simples
+
+
+def is_in_local_copy_of_simples(
+ simple: our_types.Simple,
+ simples: Sequence[our_types.Simple]
+) -> bool:
+ """
+ Check whether :paramref:`simple` is in a local copy of :paramref:`simples`.
+
+ The local copy is built with ``append``, and holds the same instances as
+ :paramref:`simples`.
+ """
+ # pylint: disable=all
+ copied: List[our_types.Simple] = []
+ for other in simples:
+ copied.append(other)
+ return simple in copied
+
+
class _Transformer(
our_types.AbstractTransformer[
Iterator[Error]
@@ -171,6 +201,25 @@ def transform_something(
'At most one of some items must be a some item.'
)
+ if not (not is_in_simples(that.item, that.some_simples)):
+ yield Error(
+ 'Item must not be one of some simples.'
+ )
+
+ if not (
+ all(
+ is_in_local_copy_of_simples(
+ simple,
+ that.some_simples
+ )
+ for simple in that.some_simples
+ )
+ ):
+ yield Error(
+ 'Each of some simples must be in a local copy of some ' +
+ 'simples.'
+ )
+
for error in verify(that.item):
error.path._prepend(
PropertySegment(
diff --git a/dev/test_data/main/python/expected/list_of_constrained_primitives/expected_output/dummy/types.py b/dev/test_data/main/python/expected/list_of_constrained_primitives/expected_output/dummy/types.py
index d8dded918..4814b0a9b 100644
--- a/dev/test_data/main/python/expected/list_of_constrained_primitives/expected_output/dummy/types.py
+++ b/dev/test_data/main/python/expected/list_of_constrained_primitives/expected_output/dummy/types.py
@@ -3,6 +3,10 @@
The list literal holds constrained primitives, while the appended items are
plain strings sliced from them.
+
+We also check the membership ``in`` them. We look up a plain string in the list
+of constrained primitives, a sliced string in a local list built with
+``append``, and check for a sub-string in a constrained primitive.
"""
diff --git a/dev/test_data/main/python/expected/list_of_constrained_primitives/expected_output/dummy/verification.py b/dev/test_data/main/python/expected/list_of_constrained_primitives/expected_output/dummy/verification.py
index cbde4e67b..5f607e35a 100644
--- a/dev/test_data/main/python/expected/list_of_constrained_primitives/expected_output/dummy/verification.py
+++ b/dev/test_data/main/python/expected/list_of_constrained_primitives/expected_output/dummy/verification.py
@@ -87,6 +87,37 @@ def initials_of(
return initials
+def initials_are_unique(
+ names: Sequence[str]
+) -> bool:
+ """Check that :paramref:`names` start with unique characters."""
+ # pylint: disable=all
+ initials: List[str] = []
+ for name in names:
+ initial = name[:1]
+ if initial in initials:
+ return False
+ initials.append(initial)
+ return True
+
+
+def is_in_names(
+ text: str,
+ names: Sequence[str]
+) -> bool:
+ """Check whether :paramref:`text` is in :paramref:`names`."""
+ # pylint: disable=all
+ return text in names
+
+
+def contains_space(
+ name: str
+) -> bool:
+ """Check whether :paramref:`name` contains a space."""
+ # pylint: disable=all
+ return ' ' in name
+
+
class _Transformer(
our_types.AbstractTransformer[
Iterator[Error]
@@ -118,6 +149,31 @@ def transform_something(
'Some names must not start with x.'
)
+ if not (not ('Eve' in that.some_names)):
+ yield Error(
+ 'Some names must not contain Eve.'
+ )
+
+ if not (not is_in_names('Mallory', that.some_names)):
+ yield Error(
+ 'Some names must not contain Mallory.'
+ )
+
+ if not (
+ all(
+ not contains_space(name)
+ for name in that.some_names
+ )
+ ):
+ yield Error(
+ 'Some names must not contain spaces.'
+ )
+
+ if not initials_are_unique(that.some_names):
+ yield Error(
+ 'Initials of some names must be unique.'
+ )
+
for error in _verify_list_of__name(that.some_names):
error.path._prepend(
PropertySegment(
diff --git a/dev/test_data/main/python/expected/list_of_enums/expected_output/dummy/types.py b/dev/test_data/main/python/expected/list_of_enums/expected_output/dummy/types.py
index 4ae7204e0..dbc637bc6 100644
--- a/dev/test_data/main/python/expected/list_of_enums/expected_output/dummy/types.py
+++ b/dev/test_data/main/python/expected/list_of_enums/expected_output/dummy/types.py
@@ -1,5 +1,8 @@
"""
Check the lists of enumeration literals, their list literals and ``append``.
+
+We also check the membership ``in`` a list of enumeration literals, passed as
+an argument and given as a list literal.
"""
diff --git a/dev/test_data/main/python/expected/list_of_enums/expected_output/dummy/verification.py b/dev/test_data/main/python/expected/list_of_enums/expected_output/dummy/verification.py
index 01e85a4f3..c14cd65a5 100644
--- a/dev/test_data/main/python/expected/list_of_enums/expected_output/dummy/verification.py
+++ b/dev/test_data/main/python/expected/list_of_enums/expected_output/dummy/verification.py
@@ -85,6 +85,15 @@ def failures_of(
return failures
+def is_in_results(
+ result: our_types.Result,
+ results: Sequence[our_types.Result]
+) -> bool:
+ """Check whether :paramref:`result` is in :paramref:`results`."""
+ # pylint: disable=all
+ return result in results
+
+
class _Transformer(
our_types.AbstractTransformer[
Iterator[Error]
@@ -110,6 +119,27 @@ def transform_something(
'Some results must contain at most one failure.'
)
+ if not (
+ not is_in_results(
+ our_types.Result.FAIL,
+ that.some_results
+ )
+ or (our_types.Result.OK in that.some_results)
+ ):
+ yield Error(
+ 'Some results must contain an ok if they contain a failure.'
+ )
+
+ if not (
+ all(
+ result in [our_types.Result.OK, our_types.Result.FAIL]
+ for result in that.some_results
+ )
+ ):
+ yield Error(
+ 'Some results must be either ok or fail.'
+ )
+
_TRANSFORMER = _Transformer()
diff --git a/dev/test_data/main/python/expected/list_of_primitives/expected_output/dummy/types.py b/dev/test_data/main/python/expected/list_of_primitives/expected_output/dummy/types.py
index 1969ed391..2d3893348 100644
--- a/dev/test_data/main/python/expected/list_of_primitives/expected_output/dummy/types.py
+++ b/dev/test_data/main/python/expected/list_of_primitives/expected_output/dummy/types.py
@@ -1,9 +1,15 @@
"""
Check the lists of primitives.
-We also check the list literals and the local lists built with ``append``.
+We check the list literals and the local lists built with ``append``.
The lengths are appended to and put in a list literal on purpose, since
the targets represent them differently from our integers.
+
+We also check the membership ``in`` a list of primitives, passed as a read-only
+and as an optional argument, built locally with ``append`` or given as a list
+literal, and the membership ``in`` a string, *i.e.*, the check for a sub-string.
+The integer literals and the lengths are looked up in the list of integers on
+purpose, since the targets represent them differently from the items.
"""
diff --git a/dev/test_data/main/python/expected/list_of_primitives/expected_output/dummy/verification.py b/dev/test_data/main/python/expected/list_of_primitives/expected_output/dummy/verification.py
index 63ae09f0a..a010f8e6e 100644
--- a/dev/test_data/main/python/expected/list_of_primitives/expected_output/dummy/verification.py
+++ b/dev/test_data/main/python/expected/list_of_primitives/expected_output/dummy/verification.py
@@ -96,6 +96,59 @@ def positives_of(
return result
+def ints_are_unique(
+ numbers: Sequence[int]
+) -> bool:
+ """
+ Check that :paramref:`numbers` are unique with a local list of the seen.
+ """
+ # pylint: disable=all
+ seen: List[int] = []
+ for number in numbers:
+ if number in seen:
+ return False
+ seen.append(number)
+ return True
+
+
+def is_separator(
+ text: str
+) -> bool:
+ """Check whether :paramref:`text` is one of the separators."""
+ # pylint: disable=all
+ return text in ['.', ',', ';']
+
+
+def is_in_ints(
+ number: int,
+ numbers: Sequence[int]
+) -> bool:
+ """Check whether :paramref:`number` is in :paramref:`numbers`."""
+ # pylint: disable=all
+ return number in numbers
+
+
+def is_in_optional_strings(
+ text: str,
+ texts: Optional[Sequence[str]]
+) -> bool:
+ """
+ Check whether :paramref:`text` is in :paramref:`texts`, if specified.
+ """
+ # pylint: disable=all
+ if texts is None:
+ return False
+ return text in texts
+
+
+def contains_dot(
+ text: str
+) -> bool:
+ """Check whether :paramref:`text` contains a dot."""
+ # pylint: disable=all
+ return '.' in text
+
+
class _Transformer(
our_types.AbstractTransformer[
Iterator[Error]
@@ -132,6 +185,53 @@ def transform_something(
'At most one of some ints must be positive.'
)
+ if not (not (0 in that.some_ints)):
+ yield Error(
+ 'Ints must not contain zero.'
+ )
+
+ if not (
+ not is_in_ints(len(that.some_strings), that.some_ints)
+ ):
+ yield Error(
+ 'Ints must not contain the number of strings.'
+ )
+
+ if not (not (-1.0 in that.some_floats)):
+ yield Error(
+ 'Floats must not contain minus one.'
+ )
+
+ if not (not is_in_optional_strings('', that.some_strings)):
+ yield Error(
+ 'Strings must not contain an empty string.'
+ )
+
+ if not (
+ all(
+ not contains_dot(text)
+ for text in that.some_strings
+ )
+ ):
+ yield Error(
+ 'Strings must not contain dots.'
+ )
+
+ if not ints_are_unique(that.some_ints):
+ yield Error(
+ 'Some ints must be unique.'
+ )
+
+ if not (
+ all(
+ not is_separator(text)
+ for text in that.some_strings
+ )
+ ):
+ yield Error(
+ 'Some strings must not be separators.'
+ )
+
_TRANSFORMER = _Transformer()
diff --git a/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/verification.ts b/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/verification.ts
index 9db3e4da5..02f1feeb1 100644
--- a/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/verification.ts
+++ b/dev/test_data/main/typescript/expected/list_of_classes/expected_output/src/verification.ts
@@ -236,6 +236,39 @@ export function someItemsOf(
return result;
}
+/**
+ * Check whether `simple` is in `simples`.
+ *
+ * @remarks
+ *
+ * The targets compare the instances of the classes by their references.
+ */
+export function isInSimples(
+ simple: OurTypes.Simple,
+ simples: Array
+): boolean {
+ return simples.includes(simple);
+}
+
+/**
+ * Check whether `simple` is in a local copy of `simples`.
+ *
+ * @remarks
+ *
+ * The local copy is built with `append`, and holds the same instances as
+ * `simples`.
+ */
+export function isInLocalCopyOfSimples(
+ simple: OurTypes.Simple,
+ simples: Array
+): boolean {
+ const copied: Array = [];
+ for (const other of simples) {
+ copied.push(other);
+ }
+ return copied.includes(simple);
+}
+
/**
* Verify the items of `that` recursively.
*/
@@ -322,6 +355,27 @@ class Verifier
)
}
+ if (!(!isInSimples(that.item, that.someSimples))) {
+ yield new VerificationError(
+ "Item must not be one of some simples."
+ )
+ }
+
+ if (!(
+ OurCommon.every(
+ OurCommon.map(
+ that.someSimples,
+ simple =>
+ isInLocalCopyOfSimples(simple, that.someSimples)
+ )
+ )
+ )) {
+ yield new VerificationError(
+ "Each of some simples must be in a local copy of some " +
+ "simples."
+ )
+ }
+
if (context === true) {
for (const error of verify(that.item)) {
error.path.prepend(
diff --git a/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/types.ts b/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/types.ts
index 9c8bc05c8..0e5daa12d 100644
--- a/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/types.ts
+++ b/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/types.ts
@@ -4,6 +4,10 @@
* @remarks
* The list literal holds constrained primitives, while the appended items are
* plain strings sliced from them.
+ *
+ * We also check the membership `in` them. We look up a plain string in the list
+ * of constrained primitives, a sliced string in a local list built with
+ * `append`, and check for a sub-string in a constrained primitive.
*/
// This code has been automatically generated by aas-core-codegen.
diff --git a/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/verification.ts b/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/verification.ts
index c57a519ae..912a35af0 100644
--- a/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/verification.ts
+++ b/dev/test_data/main/typescript/expected/list_of_constrained_primitives/expected_output/src/verification.ts
@@ -216,6 +216,42 @@ export function initialsOf(
return initials;
}
+/**
+ * Check that `names` start with unique characters.
+ */
+export function initialsAreUnique(
+ names: Array
+): boolean {
+ const initials: Array = [];
+ for (const name of names) {
+ const initial = OurCommon.sliceStr(name, 0, 1);
+ if (initials.includes(initial)) {
+ return false;
+ }
+ initials.push(initial);
+ }
+ return true;
+}
+
+/**
+ * Check whether `text` is in `names`.
+ */
+export function isInNames(
+ text: string,
+ names: Array
+): boolean {
+ return names.includes(text);
+}
+
+/**
+ * Check whether `name` contains a space.
+ */
+export function containsSpace(
+ name: string
+): boolean {
+ return name.includes(" ");
+}
+
/**
* Verify the items of `that` recursively.
*/
@@ -275,6 +311,38 @@ class Verifier
)
}
+ if (!(!(that.someNames.includes("Eve")))) {
+ yield new VerificationError(
+ "Some names must not contain Eve."
+ )
+ }
+
+ if (!(!isInNames("Mallory", that.someNames))) {
+ yield new VerificationError(
+ "Some names must not contain Mallory."
+ )
+ }
+
+ if (!(
+ OurCommon.every(
+ OurCommon.map(
+ that.someNames,
+ name =>
+ !containsSpace(name)
+ )
+ )
+ )) {
+ yield new VerificationError(
+ "Some names must not contain spaces."
+ )
+ }
+
+ if (!initialsAreUnique(that.someNames)) {
+ yield new VerificationError(
+ "Initials of some names must be unique."
+ )
+ }
+
if (context === true) {
for (const error of verify_ListOf_Name(that.someNames)) {
error.path.prepend(
diff --git a/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/types.ts b/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/types.ts
index cf3c751c2..797792721 100644
--- a/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/types.ts
+++ b/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/types.ts
@@ -1,5 +1,9 @@
/**
* Check the lists of enumeration literals, their list literals and `append`.
+ *
+ * @remarks
+ * We also check the membership `in` a list of enumeration literals, passed as
+ * an argument and given as a list literal.
*/
// This code has been automatically generated by aas-core-codegen.
diff --git a/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/verification.ts b/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/verification.ts
index 2e98038cb..d0e569464 100644
--- a/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/verification.ts
+++ b/dev/test_data/main/typescript/expected/list_of_enums/expected_output/src/verification.ts
@@ -218,6 +218,16 @@ export function failuresOf(
return failures;
}
+/**
+ * Check whether `result` is in `results`.
+ */
+export function isInResults(
+ result: OurTypes.Result,
+ results: Array
+): boolean {
+ return results.includes(result);
+}
+
/**
* Verify an instance of the model recursively or non-recursively (depending on the context).
*/
@@ -249,6 +259,29 @@ class Verifier
"Some results must contain at most one failure."
)
}
+
+ if (!(
+ !isInResults(OurTypes.Result.Fail, that.someResults)
+ || (that.someResults.includes(OurTypes.Result.Ok))
+ )) {
+ yield new VerificationError(
+ "Some results must contain an ok if they contain a failure."
+ )
+ }
+
+ if (!(
+ OurCommon.every(
+ OurCommon.map(
+ that.someResults,
+ result =>
+ [OurTypes.Result.Ok, OurTypes.Result.Fail].includes(result)
+ )
+ )
+ )) {
+ yield new VerificationError(
+ "Some results must be either ok or fail."
+ )
+ }
}
}
diff --git a/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/types.ts b/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/types.ts
index 646c92dd1..d34dddaed 100644
--- a/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/types.ts
+++ b/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/types.ts
@@ -2,9 +2,15 @@
* Check the lists of primitives.
*
* @remarks
- * We also check the list literals and the local lists built with `append`.
+ * We check the list literals and the local lists built with `append`.
* The lengths are appended to and put in a list literal on purpose, since
* the targets represent them differently from our integers.
+ *
+ * We also check the membership `in` a list of primitives, passed as a read-only
+ * and as an optional argument, built locally with `append` or given as a list
+ * literal, and the membership `in` a string, *i.e.*, the check for a sub-string.
+ * The integer literals and the lengths are looked up in the list of integers on
+ * purpose, since the targets represent them differently from the items.
*/
// This code has been automatically generated by aas-core-codegen.
diff --git a/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/verification.ts b/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/verification.ts
index 647d773f2..8ba11fd66 100644
--- a/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/verification.ts
+++ b/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/src/verification.ts
@@ -231,6 +231,63 @@ export function positivesOf(
return result;
}
+/**
+ * Check that `numbers` are unique with a local list of the seen.
+ */
+export function intsAreUnique(
+ numbers: Array
+): boolean {
+ const seen: Array = [];
+ for (const number of numbers) {
+ if (seen.includes(number)) {
+ return false;
+ }
+ seen.push(number);
+ }
+ return true;
+}
+
+/**
+ * Check whether `text` is one of the separators.
+ */
+export function isSeparator(
+ text: string
+): boolean {
+ return [".", ",", ";"].includes(text);
+}
+
+/**
+ * Check whether `number` is in `numbers`.
+ */
+export function isInInts(
+ number: number,
+ numbers: Array
+): boolean {
+ return numbers.includes(number);
+}
+
+/**
+ * Check whether `text` is in `texts`, if specified.
+ */
+export function isInOptionalStrings(
+ text: string,
+ texts: Array | null
+): boolean {
+ if (texts === null) {
+ return false;
+ }
+ return texts.includes(text);
+}
+
+/**
+ * Check whether `text` contains a dot.
+ */
+export function containsDot(
+ text: string
+): boolean {
+ return text.includes(".");
+}
+
/**
* Verify an instance of the model recursively or non-recursively (depending on the context).
*/
@@ -277,6 +334,64 @@ class Verifier
"At most one of some ints must be positive."
)
}
+
+ if (!(!(that.someInts.includes(0)))) {
+ yield new VerificationError(
+ "Ints must not contain zero."
+ )
+ }
+
+ if (!(!isInInts(that.someStrings.length, that.someInts))) {
+ yield new VerificationError(
+ "Ints must not contain the number of strings."
+ )
+ }
+
+ if (!(!(that.someFloats.includes(-1.0)))) {
+ yield new VerificationError(
+ "Floats must not contain minus one."
+ )
+ }
+
+ if (!(!isInOptionalStrings("", that.someStrings))) {
+ yield new VerificationError(
+ "Strings must not contain an empty string."
+ )
+ }
+
+ if (!(
+ OurCommon.every(
+ OurCommon.map(
+ that.someStrings,
+ text =>
+ !containsDot(text)
+ )
+ )
+ )) {
+ yield new VerificationError(
+ "Strings must not contain dots."
+ )
+ }
+
+ if (!intsAreUnique(that.someInts)) {
+ yield new VerificationError(
+ "Some ints must be unique."
+ )
+ }
+
+ if (!(
+ OurCommon.every(
+ OurCommon.map(
+ that.someStrings,
+ text =>
+ !isSeparator(text)
+ )
+ )
+ )) {
+ yield new VerificationError(
+ "Some strings must not be separators."
+ )
+ }
}
}
diff --git a/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/test/verification.spec.ts b/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/test/verification.spec.ts
index 5ee1ece36..512e61da4 100644
--- a/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/test/verification.spec.ts
+++ b/dev/test_data/main/typescript/expected/list_of_primitives/expected_output/test/verification.spec.ts
@@ -45,5 +45,21 @@ test("verification error stores provided path", () => {
expect(error.path.toString()).toStrictEqual(".idShort");
});
+test("isSeparator returns boolean", () => {
+ const resultOnEmpty = OurVerification.isSeparator("");
+ expect(typeof resultOnEmpty).toStrictEqual("boolean");
+
+ const resultOnSample = OurVerification.isSeparator("sample-value");
+ expect(typeof resultOnSample).toStrictEqual("boolean");
+});
+
+test("containsDot returns boolean", () => {
+ const resultOnEmpty = OurVerification.containsDot("");
+ expect(typeof resultOnEmpty).toStrictEqual("boolean");
+
+ const resultOnSample = OurVerification.containsDot("sample-value");
+ expect(typeof resultOnSample).toStrictEqual("boolean");
+});
+
// This code has been automatically generated by aas-core-codegen.
// Do NOT edit or append.
diff --git a/dev/test_data/parse/common_meta_models/list_of_classes/expected_symbol_table.txt b/dev/test_data/parse/common_meta_models/list_of_classes/expected_symbol_table.txt
index f2436d336..a5f956cf2 100644
--- a/dev/test_data/parse/common_meta_models/list_of_classes/expected_symbol_table.txt
+++ b/dev/test_data/parse/common_meta_models/list_of_classes/expected_symbol_table.txt
@@ -344,6 +344,58 @@ UnverifiedSymbolTable(
value=1,
original_node=...),
original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Item must not be one of some simples.',
+ body=textwrap.dedent("""\
+ Not(
+ operand=FunctionCall(
+ name='is_in_simples',
+ args=[
+ Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='item',
+ original_node=...),
+ Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_simples',
+ original_node=...)],
+ original_node=...),
+ original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Each of some simples must be in a local copy of some simples.',
+ body=textwrap.dedent("""\
+ All(
+ generator=ForEach(
+ variable=Name(
+ identifier='simple',
+ original_node=...),
+ iteration=Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_simples',
+ original_node=...),
+ original_node=...),
+ condition=FunctionCall(
+ name='is_in_local_copy_of_simples',
+ args=[
+ Name(
+ identifier='simple',
+ original_node=...),
+ Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_simples',
+ original_node=...)],
+ original_node=...),
+ original_node=...)"""),
node=...)],
serialization=None,
description=None,
@@ -623,6 +675,145 @@ UnverifiedSymbolTable(
original_node=...),
original_node=...)""")],
node=...,
+ arguments_by_name=...),
+ UnderstoodMethod(
+ name='is_in_simples',
+ verification=True,
+ visibility='PUBLIC',
+ arguments=[
+ Argument(
+ name='simple',
+ type_annotation=AtomicTypeAnnotation(
+ identifier='Simple',
+ node=...),
+ default=None,
+ node=...),
+ Argument(
+ name='simples',
+ type_annotation=SubscriptedTypeAnnotation(
+ identifier='Sequence',
+ subscripts=[
+ AtomicTypeAnnotation(
+ identifier='Simple',
+ node=...)],
+ node=...),
+ default=None,
+ node=...)],
+ returns=AtomicTypeAnnotation(
+ identifier='bool',
+ node=...),
+ description=Description(
+ document=...,
+ node=...),
+ contracts=Contracts(
+ preconditions=[],
+ snapshots=[],
+ postconditions=[]),
+ non_mutating=False,
+ body=[
+ textwrap.dedent("""\
+ Return(
+ value=IsIn(
+ member=Name(
+ identifier='simple',
+ original_node=...),
+ container=Name(
+ identifier='simples',
+ original_node=...),
+ original_node=...),
+ original_node=...)""")],
+ node=...,
+ arguments_by_name=...),
+ UnderstoodMethod(
+ name='is_in_local_copy_of_simples',
+ verification=True,
+ visibility='PUBLIC',
+ arguments=[
+ Argument(
+ name='simple',
+ type_annotation=AtomicTypeAnnotation(
+ identifier='Simple',
+ node=...),
+ default=None,
+ node=...),
+ Argument(
+ name='simples',
+ type_annotation=SubscriptedTypeAnnotation(
+ identifier='Sequence',
+ subscripts=[
+ AtomicTypeAnnotation(
+ identifier='Simple',
+ node=...)],
+ node=...),
+ default=None,
+ node=...)],
+ returns=AtomicTypeAnnotation(
+ identifier='bool',
+ node=...),
+ description=Description(
+ document=...,
+ node=...),
+ contracts=Contracts(
+ preconditions=[],
+ snapshots=[],
+ postconditions=[]),
+ non_mutating=False,
+ body=[
+ textwrap.dedent("""\
+ Assignment(
+ target=Name(
+ identifier='copied',
+ original_node=...),
+ value=ListLiteral(
+ items=[],
+ original_node=...),
+ annotation=Index(
+ collection=Name(
+ identifier='List',
+ original_node=...),
+ index=Name(
+ identifier='Simple',
+ original_node=...),
+ original_node=...),
+ original_node=...)"""),
+ textwrap.dedent("""\
+ For(
+ generator=ForEach(
+ variable=Name(
+ identifier='other',
+ original_node=...),
+ iteration=Name(
+ identifier='simples',
+ original_node=...),
+ original_node=...),
+ body=[
+ ExpressionStatement(
+ expression=MethodCall(
+ member=Member(
+ instance=Name(
+ identifier='copied',
+ original_node=...),
+ name='append',
+ original_node=...),
+ args=[
+ Name(
+ identifier='other',
+ original_node=...)],
+ original_node=...),
+ original_node=...)],
+ original_node=...)"""),
+ textwrap.dedent("""\
+ Return(
+ value=IsIn(
+ member=Name(
+ identifier='simple',
+ original_node=...),
+ container=Name(
+ identifier='copied',
+ original_node=...),
+ original_node=...),
+ original_node=...)""")],
+ node=...,
arguments_by_name=...)],
meta_model=MetaModel(
description=None,
diff --git a/dev/test_data/parse/common_meta_models/list_of_constrained_primitives/expected_symbol_table.txt b/dev/test_data/parse/common_meta_models/list_of_constrained_primitives/expected_symbol_table.txt
index 9449c81c8..bfe20394b 100644
--- a/dev/test_data/parse/common_meta_models/list_of_constrained_primitives/expected_symbol_table.txt
+++ b/dev/test_data/parse/common_meta_models/list_of_constrained_primitives/expected_symbol_table.txt
@@ -160,6 +160,82 @@ UnverifiedSymbolTable(
original_node=...),
original_node=...),
original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Some names must not contain Eve.',
+ body=textwrap.dedent("""\
+ Not(
+ operand=IsIn(
+ member=Constant(
+ value='Eve',
+ original_node=...),
+ container=Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_names',
+ original_node=...),
+ original_node=...),
+ original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Some names must not contain Mallory.',
+ body=textwrap.dedent("""\
+ Not(
+ operand=FunctionCall(
+ name='is_in_names',
+ args=[
+ Constant(
+ value='Mallory',
+ original_node=...),
+ Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_names',
+ original_node=...)],
+ original_node=...),
+ original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Some names must not contain spaces.',
+ body=textwrap.dedent("""\
+ All(
+ generator=ForEach(
+ variable=Name(
+ identifier='name',
+ original_node=...),
+ iteration=Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_names',
+ original_node=...),
+ original_node=...),
+ condition=Not(
+ operand=FunctionCall(
+ name='contains_space',
+ args=[
+ Name(
+ identifier='name',
+ original_node=...)],
+ original_node=...),
+ original_node=...),
+ original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Initials of some names must be unique.',
+ body=textwrap.dedent("""\
+ FunctionCall(
+ name='initials_are_unique',
+ args=[
+ Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_names',
+ original_node=...)],
+ original_node=...)"""),
node=...)],
serialization=None,
description=None,
@@ -343,6 +419,205 @@ UnverifiedSymbolTable(
original_node=...),
original_node=...)""")],
node=...,
+ arguments_by_name=...),
+ UnderstoodMethod(
+ name='initials_are_unique',
+ verification=True,
+ visibility='PUBLIC',
+ arguments=[
+ Argument(
+ name='names',
+ type_annotation=SubscriptedTypeAnnotation(
+ identifier='Sequence',
+ subscripts=[
+ AtomicTypeAnnotation(
+ identifier='Name',
+ node=...)],
+ node=...),
+ default=None,
+ node=...)],
+ returns=AtomicTypeAnnotation(
+ identifier='bool',
+ node=...),
+ description=Description(
+ document=...,
+ node=...),
+ contracts=Contracts(
+ preconditions=[],
+ snapshots=[],
+ postconditions=[]),
+ non_mutating=False,
+ body=[
+ textwrap.dedent("""\
+ Assignment(
+ target=Name(
+ identifier='initials',
+ original_node=...),
+ value=ListLiteral(
+ items=[],
+ original_node=...),
+ annotation=Index(
+ collection=Name(
+ identifier='List',
+ original_node=...),
+ index=Name(
+ identifier='str',
+ original_node=...),
+ original_node=...),
+ original_node=...)"""),
+ textwrap.dedent("""\
+ For(
+ generator=ForEach(
+ variable=Name(
+ identifier='name',
+ original_node=...),
+ iteration=Name(
+ identifier='names',
+ original_node=...),
+ original_node=...),
+ body=[
+ Assignment(
+ target=Name(
+ identifier='initial',
+ original_node=...),
+ value=Slice(
+ collection=Name(
+ identifier='name',
+ original_node=...),
+ start=None,
+ end=Constant(
+ value=1,
+ original_node=...),
+ original_node=...),
+ annotation=None,
+ original_node=...),
+ If(
+ branches=[
+ IfBranch(
+ condition=IsIn(
+ member=Name(
+ identifier='initial',
+ original_node=...),
+ container=Name(
+ identifier='initials',
+ original_node=...),
+ original_node=...),
+ body=[
+ Return(
+ value=Constant(
+ value=False,
+ original_node=...),
+ original_node=...)],
+ original_node=...)],
+ default=None,
+ original_node=...),
+ ExpressionStatement(
+ expression=MethodCall(
+ member=Member(
+ instance=Name(
+ identifier='initials',
+ original_node=...),
+ name='append',
+ original_node=...),
+ args=[
+ Name(
+ identifier='initial',
+ original_node=...)],
+ original_node=...),
+ original_node=...)],
+ original_node=...)"""),
+ textwrap.dedent("""\
+ Return(
+ value=Constant(
+ value=True,
+ original_node=...),
+ original_node=...)""")],
+ node=...,
+ arguments_by_name=...),
+ UnderstoodMethod(
+ name='is_in_names',
+ verification=True,
+ visibility='PUBLIC',
+ arguments=[
+ Argument(
+ name='text',
+ type_annotation=AtomicTypeAnnotation(
+ identifier='str',
+ node=...),
+ default=None,
+ node=...),
+ Argument(
+ name='names',
+ type_annotation=SubscriptedTypeAnnotation(
+ identifier='Sequence',
+ subscripts=[
+ AtomicTypeAnnotation(
+ identifier='Name',
+ node=...)],
+ node=...),
+ default=None,
+ node=...)],
+ returns=AtomicTypeAnnotation(
+ identifier='bool',
+ node=...),
+ description=Description(
+ document=...,
+ node=...),
+ contracts=Contracts(
+ preconditions=[],
+ snapshots=[],
+ postconditions=[]),
+ non_mutating=False,
+ body=[
+ textwrap.dedent("""\
+ Return(
+ value=IsIn(
+ member=Name(
+ identifier='text',
+ original_node=...),
+ container=Name(
+ identifier='names',
+ original_node=...),
+ original_node=...),
+ original_node=...)""")],
+ node=...,
+ arguments_by_name=...),
+ UnderstoodMethod(
+ name='contains_space',
+ verification=True,
+ visibility='PUBLIC',
+ arguments=[
+ Argument(
+ name='name',
+ type_annotation=AtomicTypeAnnotation(
+ identifier='Name',
+ node=...),
+ default=None,
+ node=...)],
+ returns=AtomicTypeAnnotation(
+ identifier='bool',
+ node=...),
+ description=Description(
+ document=...,
+ node=...),
+ contracts=Contracts(
+ preconditions=[],
+ snapshots=[],
+ postconditions=[]),
+ non_mutating=False,
+ body=[
+ textwrap.dedent("""\
+ Return(
+ value=IsIn(
+ member=Constant(
+ value=' ',
+ original_node=...),
+ container=Name(
+ identifier='name',
+ original_node=...),
+ original_node=...),
+ original_node=...)""")],
+ node=...,
arguments_by_name=...)],
meta_model=MetaModel(
description=Description(
diff --git a/dev/test_data/parse/common_meta_models/list_of_enums/expected_symbol_table.txt b/dev/test_data/parse/common_meta_models/list_of_enums/expected_symbol_table.txt
index 64b7b9e82..c15443f69 100644
--- a/dev/test_data/parse/common_meta_models/list_of_enums/expected_symbol_table.txt
+++ b/dev/test_data/parse/common_meta_models/list_of_enums/expected_symbol_table.txt
@@ -113,6 +113,79 @@ UnverifiedSymbolTable(
value=1,
original_node=...),
original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Some results must contain an ok if they contain a failure.',
+ body=textwrap.dedent("""\
+ Implication(
+ antecedent=FunctionCall(
+ name='is_in_results',
+ args=[
+ Member(
+ instance=Name(
+ identifier='Result',
+ original_node=...),
+ name='Fail',
+ original_node=...),
+ Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_results',
+ original_node=...)],
+ original_node=...),
+ consequent=IsIn(
+ member=Member(
+ instance=Name(
+ identifier='Result',
+ original_node=...),
+ name='Ok',
+ original_node=...),
+ container=Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_results',
+ original_node=...),
+ original_node=...),
+ original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Some results must be either ok or fail.',
+ body=textwrap.dedent("""\
+ All(
+ generator=ForEach(
+ variable=Name(
+ identifier='result',
+ original_node=...),
+ iteration=Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_results',
+ original_node=...),
+ original_node=...),
+ condition=IsIn(
+ member=Name(
+ identifier='result',
+ original_node=...),
+ container=ListLiteral(
+ items=[
+ Member(
+ instance=Name(
+ identifier='Result',
+ original_node=...),
+ name='Ok',
+ original_node=...),
+ Member(
+ instance=Name(
+ identifier='Result',
+ original_node=...),
+ name='Fail',
+ original_node=...)],
+ original_node=...),
+ original_node=...),
+ original_node=...)"""),
node=...)],
serialization=None,
description=None,
@@ -300,6 +373,54 @@ UnverifiedSymbolTable(
original_node=...),
original_node=...)""")],
node=...,
+ arguments_by_name=...),
+ UnderstoodMethod(
+ name='is_in_results',
+ verification=True,
+ visibility='PUBLIC',
+ arguments=[
+ Argument(
+ name='result',
+ type_annotation=AtomicTypeAnnotation(
+ identifier='Result',
+ node=...),
+ default=None,
+ node=...),
+ Argument(
+ name='results',
+ type_annotation=SubscriptedTypeAnnotation(
+ identifier='Sequence',
+ subscripts=[
+ AtomicTypeAnnotation(
+ identifier='Result',
+ node=...)],
+ node=...),
+ default=None,
+ node=...)],
+ returns=AtomicTypeAnnotation(
+ identifier='bool',
+ node=...),
+ description=Description(
+ document=...,
+ node=...),
+ contracts=Contracts(
+ preconditions=[],
+ snapshots=[],
+ postconditions=[]),
+ non_mutating=False,
+ body=[
+ textwrap.dedent("""\
+ Return(
+ value=IsIn(
+ member=Name(
+ identifier='result',
+ original_node=...),
+ container=Name(
+ identifier='results',
+ original_node=...),
+ original_node=...),
+ original_node=...)""")],
+ node=...,
arguments_by_name=...)],
meta_model=MetaModel(
description=Description(
diff --git a/dev/test_data/parse/common_meta_models/list_of_primitives/expected_symbol_table.txt b/dev/test_data/parse/common_meta_models/list_of_primitives/expected_symbol_table.txt
index 7ae95f31a..fe41c321f 100644
--- a/dev/test_data/parse/common_meta_models/list_of_primitives/expected_symbol_table.txt
+++ b/dev/test_data/parse/common_meta_models/list_of_primitives/expected_symbol_table.txt
@@ -225,6 +225,151 @@ UnverifiedSymbolTable(
value=1,
original_node=...),
original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Ints must not contain zero.',
+ body=textwrap.dedent("""\
+ Not(
+ operand=IsIn(
+ member=Constant(
+ value=0,
+ original_node=...),
+ container=Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_ints',
+ original_node=...),
+ original_node=...),
+ original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Ints must not contain the number of strings.',
+ body=textwrap.dedent("""\
+ Not(
+ operand=FunctionCall(
+ name='is_in_ints',
+ args=[
+ FunctionCall(
+ name='len',
+ args=[
+ Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_strings',
+ original_node=...)],
+ original_node=...),
+ Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_ints',
+ original_node=...)],
+ original_node=...),
+ original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Floats must not contain minus one.',
+ body=textwrap.dedent("""\
+ Not(
+ operand=IsIn(
+ member=Constant(
+ value=-1.0,
+ original_node=...),
+ container=Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_floats',
+ original_node=...),
+ original_node=...),
+ original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Strings must not contain an empty string.',
+ body=textwrap.dedent("""\
+ Not(
+ operand=FunctionCall(
+ name='is_in_optional_strings',
+ args=[
+ Constant(
+ value='',
+ original_node=...),
+ Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_strings',
+ original_node=...)],
+ original_node=...),
+ original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Strings must not contain dots.',
+ body=textwrap.dedent("""\
+ All(
+ generator=ForEach(
+ variable=Name(
+ identifier='text',
+ original_node=...),
+ iteration=Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_strings',
+ original_node=...),
+ original_node=...),
+ condition=Not(
+ operand=FunctionCall(
+ name='contains_dot',
+ args=[
+ Name(
+ identifier='text',
+ original_node=...)],
+ original_node=...),
+ original_node=...),
+ original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Some ints must be unique.',
+ body=textwrap.dedent("""\
+ FunctionCall(
+ name='ints_are_unique',
+ args=[
+ Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_ints',
+ original_node=...)],
+ original_node=...)"""),
+ node=...),
+ Invariant(
+ description='Some strings must not be separators.',
+ body=textwrap.dedent("""\
+ All(
+ generator=ForEach(
+ variable=Name(
+ identifier='text',
+ original_node=...),
+ iteration=Member(
+ instance=Name(
+ identifier='self',
+ original_node=...),
+ name='some_strings',
+ original_node=...),
+ original_node=...),
+ condition=Not(
+ operand=FunctionCall(
+ name='is_separator',
+ args=[
+ Name(
+ identifier='text',
+ original_node=...)],
+ original_node=...),
+ original_node=...),
+ original_node=...)"""),
node=...)],
serialization=None,
description=None,
@@ -496,6 +641,306 @@ UnverifiedSymbolTable(
original_node=...),
original_node=...)""")],
node=...,
+ arguments_by_name=...),
+ UnderstoodMethod(
+ name='ints_are_unique',
+ verification=True,
+ visibility='PUBLIC',
+ arguments=[
+ Argument(
+ name='numbers',
+ type_annotation=SubscriptedTypeAnnotation(
+ identifier='Sequence',
+ subscripts=[
+ AtomicTypeAnnotation(
+ identifier='int',
+ node=...)],
+ node=...),
+ default=None,
+ node=...)],
+ returns=AtomicTypeAnnotation(
+ identifier='bool',
+ node=...),
+ description=Description(
+ document=...,
+ node=...),
+ contracts=Contracts(
+ preconditions=[],
+ snapshots=[],
+ postconditions=[]),
+ non_mutating=False,
+ body=[
+ textwrap.dedent("""\
+ Assignment(
+ target=Name(
+ identifier='seen',
+ original_node=...),
+ value=ListLiteral(
+ items=[],
+ original_node=...),
+ annotation=Index(
+ collection=Name(
+ identifier='List',
+ original_node=...),
+ index=Name(
+ identifier='int',
+ original_node=...),
+ original_node=...),
+ original_node=...)"""),
+ textwrap.dedent("""\
+ For(
+ generator=ForEach(
+ variable=Name(
+ identifier='number',
+ original_node=...),
+ iteration=Name(
+ identifier='numbers',
+ original_node=...),
+ original_node=...),
+ body=[
+ If(
+ branches=[
+ IfBranch(
+ condition=IsIn(
+ member=Name(
+ identifier='number',
+ original_node=...),
+ container=Name(
+ identifier='seen',
+ original_node=...),
+ original_node=...),
+ body=[
+ Return(
+ value=Constant(
+ value=False,
+ original_node=...),
+ original_node=...)],
+ original_node=...)],
+ default=None,
+ original_node=...),
+ ExpressionStatement(
+ expression=MethodCall(
+ member=Member(
+ instance=Name(
+ identifier='seen',
+ original_node=...),
+ name='append',
+ original_node=...),
+ args=[
+ Name(
+ identifier='number',
+ original_node=...)],
+ original_node=...),
+ original_node=...)],
+ original_node=...)"""),
+ textwrap.dedent("""\
+ Return(
+ value=Constant(
+ value=True,
+ original_node=...),
+ original_node=...)""")],
+ node=...,
+ arguments_by_name=...),
+ UnderstoodMethod(
+ name='is_separator',
+ verification=True,
+ visibility='PUBLIC',
+ arguments=[
+ Argument(
+ name='text',
+ type_annotation=AtomicTypeAnnotation(
+ identifier='str',
+ node=...),
+ default=None,
+ node=...)],
+ returns=AtomicTypeAnnotation(
+ identifier='bool',
+ node=...),
+ description=Description(
+ document=...,
+ node=...),
+ contracts=Contracts(
+ preconditions=[],
+ snapshots=[],
+ postconditions=[]),
+ non_mutating=False,
+ body=[
+ textwrap.dedent("""\
+ Return(
+ value=IsIn(
+ member=Name(
+ identifier='text',
+ original_node=...),
+ container=ListLiteral(
+ items=[
+ Constant(
+ value='.',
+ original_node=...),
+ Constant(
+ value=',',
+ original_node=...),
+ Constant(
+ value=';',
+ original_node=...)],
+ original_node=...),
+ original_node=...),
+ original_node=...)""")],
+ node=...,
+ arguments_by_name=...),
+ UnderstoodMethod(
+ name='is_in_ints',
+ verification=True,
+ visibility='PUBLIC',
+ arguments=[
+ Argument(
+ name='number',
+ type_annotation=AtomicTypeAnnotation(
+ identifier='int',
+ node=...),
+ default=None,
+ node=...),
+ Argument(
+ name='numbers',
+ type_annotation=SubscriptedTypeAnnotation(
+ identifier='Sequence',
+ subscripts=[
+ AtomicTypeAnnotation(
+ identifier='int',
+ node=...)],
+ node=...),
+ default=None,
+ node=...)],
+ returns=AtomicTypeAnnotation(
+ identifier='bool',
+ node=...),
+ description=Description(
+ document=...,
+ node=...),
+ contracts=Contracts(
+ preconditions=[],
+ snapshots=[],
+ postconditions=[]),
+ non_mutating=False,
+ body=[
+ textwrap.dedent("""\
+ Return(
+ value=IsIn(
+ member=Name(
+ identifier='number',
+ original_node=...),
+ container=Name(
+ identifier='numbers',
+ original_node=...),
+ original_node=...),
+ original_node=...)""")],
+ node=...,
+ arguments_by_name=...),
+ UnderstoodMethod(
+ name='is_in_optional_strings',
+ verification=True,
+ visibility='PUBLIC',
+ arguments=[
+ Argument(
+ name='text',
+ type_annotation=AtomicTypeAnnotation(
+ identifier='str',
+ node=...),
+ default=None,
+ node=...),
+ Argument(
+ name='texts',
+ type_annotation=SubscriptedTypeAnnotation(
+ identifier='Optional',
+ subscripts=[
+ SubscriptedTypeAnnotation(
+ identifier='Sequence',
+ subscripts=[
+ AtomicTypeAnnotation(
+ identifier='str',
+ node=...)],
+ node=...)],
+ node=...),
+ default=None,
+ node=...)],
+ returns=AtomicTypeAnnotation(
+ identifier='bool',
+ node=...),
+ description=Description(
+ document=...,
+ node=...),
+ contracts=Contracts(
+ preconditions=[],
+ snapshots=[],
+ postconditions=[]),
+ non_mutating=False,
+ body=[
+ textwrap.dedent("""\
+ If(
+ branches=[
+ IfBranch(
+ condition=IsNone(
+ value=Name(
+ identifier='texts',
+ original_node=...),
+ original_node=...),
+ body=[
+ Return(
+ value=Constant(
+ value=False,
+ original_node=...),
+ original_node=...)],
+ original_node=...)],
+ default=None,
+ original_node=...)"""),
+ textwrap.dedent("""\
+ Return(
+ value=IsIn(
+ member=Name(
+ identifier='text',
+ original_node=...),
+ container=Name(
+ identifier='texts',
+ original_node=...),
+ original_node=...),
+ original_node=...)""")],
+ node=...,
+ arguments_by_name=...),
+ UnderstoodMethod(
+ name='contains_dot',
+ verification=True,
+ visibility='PUBLIC',
+ arguments=[
+ Argument(
+ name='text',
+ type_annotation=AtomicTypeAnnotation(
+ identifier='str',
+ node=...),
+ default=None,
+ node=...)],
+ returns=AtomicTypeAnnotation(
+ identifier='bool',
+ node=...),
+ description=Description(
+ document=...,
+ node=...),
+ contracts=Contracts(
+ preconditions=[],
+ snapshots=[],
+ postconditions=[]),
+ non_mutating=False,
+ body=[
+ textwrap.dedent("""\
+ Return(
+ value=IsIn(
+ member=Constant(
+ value='.',
+ original_node=...),
+ container=Name(
+ identifier='text',
+ original_node=...),
+ original_node=...),
+ original_node=...)""")],
+ node=...,
arguments_by_name=...)],
meta_model=MetaModel(
description=Description(
diff --git a/dev/tests/intermediate/test_type_inference.py b/dev/tests/intermediate/test_type_inference.py
index c71c14bc8..cfe0d18c3 100644
--- a/dev/tests/intermediate/test_type_inference.py
+++ b/dev/tests/intermediate/test_type_inference.py
@@ -6023,6 +6023,63 @@ def some_func(lists: Sequence[Sequence[str]], texts: Sequence[str]) -> bool:
),
)
+ def test_is_in_list_of_wrong_type_fails(self) -> None:
+ self.expect_type_inference_to_fail(
+ body="""\
+@verification
+def some_func(number: int, texts: Sequence[str]) -> bool:
+ return number in texts""",
+ expected_joined_message=(
+ "Expected the member to be of the type of the items of the list "
+ "List[str], but got: int"
+ ),
+ )
+
+ def test_is_in_str_of_non_str_fails(self) -> None:
+ self.expect_type_inference_to_fail(
+ body="""\
+@verification
+def some_func(number: int, text: str) -> bool:
+ return number in text""",
+ expected_joined_message=(
+ "Expected the member to be a string as we check for a sub-string "
+ "in the container str, but got: int"
+ ),
+ )
+
+ def test_bytearray_as_member_of_in_fails(self) -> None:
+ self.expect_type_inference_to_fail(
+ body="""\
+@verification
+def some_func(blob: bytearray, blobs: Sequence[bytearray]) -> bool:
+ return blob in blobs""",
+ expected_joined_message=(
+ "We do not support looking up a byte array with ``in``, but "
+ "the member is inferred to be bytearray. The targets disagree on "
+ "the comparison: Python compares the byte arrays byte by byte, "
+ "C# and Java compare the references of their ``byte[]``'s, while "
+ "Go can not compare ``[]byte`` with ``==`` at all. Please compare "
+ "the byte arrays explicitly instead, *e.g.*, in a for-loop."
+ ),
+ )
+
+ def test_json_value_as_member_of_in_list_fails(self) -> None:
+ self.expect_type_inference_to_fail(
+ body="""\
+@verification
+def some_func(value: JSONValue, values: Sequence[JSONValue]) -> bool:
+ return value in values""",
+ expected_joined_message=(
+ "We do not support searching for a JSON-able value in a list "
+ "with ``in``, but the member is inferred to be JSONValue. "
+ "The targets disagree on the comparison: Python compares "
+ "the JSON-able values by value, while C#, Java and TypeScript "
+ "compare the references of their arrays and objects, and Go "
+ "panics when comparing them. Please contact the developers if "
+ "you need this feature."
+ ),
+ )
+
def test_copy_of_list_of_sets_fails(self) -> None:
self.expect_type_inference_to_fail(
body="""\