diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dd315eb..7e2e50ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ generated in the following situations: ### 3.0.0-dev +* [TRLC] * Add support for nested (dotted) packages, e.g. `package com.bigcorp.safety`. + * [TRLC_RST] Render soft-wrapped description lines as a single RST paragraph instead of splitting each line into its own paragraph. Insert a blank line to start a new paragraph. diff --git a/documentation/TUTORIAL-ADVANCED.md b/documentation/TUTORIAL-ADVANCED.md index 1e0ef821..ce433cdb 100644 --- a/documentation/TUTORIAL-ADVANCED.md +++ b/documentation/TUTORIAL-ADVANCED.md @@ -144,6 +144,10 @@ Finally note that a reference is always a reference. You cannot specify an entirely new record "inline". We plan to support something like this in a future release, but it will be distinct from records. +For grouping packages into a hierarchy (`com.bigcorp.safety`) and +importing whole subtrees at once, see +[Subpackages](TUTORIAL-PACKAGE.md#subpackages). + ## In-line references It is also possible to refer to other TRLC records directly in diff --git a/documentation/TUTORIAL-PACKAGE.md b/documentation/TUTORIAL-PACKAGE.md index c01b4e60..2f2b0562 100644 --- a/documentation/TUTORIAL-PACKAGE.md +++ b/documentation/TUTORIAL-PACKAGE.md @@ -64,6 +64,84 @@ the same package. For example instead of having one massive file `car.trlc`, you could have many: `engine_control.trlc`, `cruise_control.trlc`, `radar.trlc`, etc. +## Subpackages + +In very large projects a single flat list of package names becomes hard +to manage. Package names may instead be *dotted*, forming a hierarchy of +subpackages: + +``` +package com.bigcorp.safety +``` + +Before you can declare a nested package, every prefix must already be a +declared package. So to declare `com.bigcorp.safety` you must also have +declared `com` and `com.bigcorp` somewhere (each in its own `.rsl` +file). + +For example, you would need three separate `.rsl` files: + +``` +// com.rsl +package com +``` + +``` +// com.bigcorp.rsl +package com.bigcorp +``` + +``` +// com.bigcorp.safety.rsl +package com.bigcorp.safety + +enum ASIL_Level { QM A B C D } +``` + +To reference a type or object from a nested package, use its full dotted +prefix: + +``` +package com.bigcorp.brakes +import com.bigcorp.safety + +type Brake_Requirement { + level com.bigcorp.safety.ASIL_Level +} +``` + +### Importing subpackages + +Importing is *not* transitive: `import com.bigcorp.safety` gives you +access to the types in `com.bigcorp.safety` only — not to its parent +`com.bigcorp`, and not to any of its children. + +If you want a whole subtree at once, use a *wildcard import*: + +``` +package com.bigcorp.brakes +import com.bigcorp.safety.* + +type Brake_Requirement { + level com.bigcorp.safety.ASIL_Level // from com.bigcorp.safety + unit com.bigcorp.safety.units.Pressure // from a child package +} +``` + +`import com.bigcorp.safety.*` makes `com.bigcorp.safety` **and every +package below it** visible. The linter will warn you about a wildcard +import that ends up unused, and about an explicit import that is already +covered by a wildcard. + +A small note on the `.` separator: it means different things depending +on what is to its left. After a *package* it descends the namespace +(`com.bigcorp.safety.Type`); after a *value* it accesses a tuple or +record field (`my_record.field`). TRLC resolves the package prefix +first, against the imported packages, before any field access — so there +is no ambiguity. To keep it that way, a subpackage's leaf name must be +distinct from any type or object in its parent package (TRLC rejects the +clash). + ## Sections Even if you split the requirements of your project over many files, diff --git a/language-reference-manual/lrm.trlc b/language-reference-manual/lrm.trlc index fb59dae1..a6fde5f0 100644 --- a/language-reference-manual/lrm.trlc +++ b/language-reference-manual/lrm.trlc @@ -281,9 +281,14 @@ section "Preamble" { file_preamble ::= package_indication { import_clause } -package_indication ::= 'package' IDENTIFIER_name +package_name ::= IDENTIFIER { '.' IDENTIFIER } -import_clause ::= 'import' IDENTIFIER_name +package_indication ::= 'package' package_name + +import_clause ::= 'import' package_name + | 'import' wildcard_import + +wildcard_import ::= package_name '.' '*' ''' } @@ -300,6 +305,68 @@ import_clause ::= 'import' IDENTIFIER_name text = '''A package may not import itself.''' } + Static_Semantics Nested_Package_Names { + text = '''A package name may consist of multiple dot-separated + IDENTIFIER segments, forming a nested package hierarchy. + Each segment must be a valid IDENTIFIER. The full + dotted name is subject to the Sufficiently_Distinct rule.''' + } + + Note Nested_Package_Name_Similarity { + text = '''When applying the Sufficiently_Distinct rule to nested package + names, dots are preserved in the simplified form. For example + `foo.bar` and `Foo.Bar` both simplify to `foo.bar` and + therefore clash. However `foobar` and `foo.bar` have different + simplified forms (`foobar` vs `foo.bar`) and do not clash.''' + } + + Static_Semantics Parent_Package_Required { + text = '''A package `foo.bar` may only be declared if a package `foo` + has already been declared. More generally every prefix of a + nested package name must itself be a declared package.''' + } + + Static_Semantics Nested_Visibility { + text = '''Importing a nested package `foo.bar` grants access to the + types declared in `foo.bar`. Importing the parent package + `foo` does not grant access to types in `foo.bar`.''' + } + + Static_Semantics Wildcard_Import { + text = '''An import clause of the form `foo.*` is a wildcard import. It + grants access to the package `foo` and to every package that + is a descendant of `foo` in the nested package hierarchy. It + is equivalent to importing each of those packages + individually. A wildcard import `foo.*` is only legal if a + package `foo` has been declared; if `foo` has no descendants + the wildcard import is equivalent to importing `foo`.''' + } + + Recommendation Wildcard_Trivial { + text = '''If a wildcard import `foo.*` is used but `foo` has no + sub-packages, the wildcard is equivalent to a plain + `import foo`. Prefer the explicit form to make the + import intent clear.''' + } + + Static_Semantics Wildcard_Self_Cover { + text = '''A wildcard import whose root covers the current package (i.e. + the current package is the root or a descendant of it) is + permitted. The current package remains implicitly visible and + is not considered imported by the wildcard. An explicit + (non-wildcard) import of the current package remains illegal + (see Self_Imports).''' + } + + Static_Semantics Subpackage_Member_Distinct { + text = '''Within a package, the leaf name of a direct sub-package must be + sufficiently distinct (see Sufficiently_Distinct) from the + name of any type or object declared in that package. This + avoids ambiguity in qualified-name resolution, which consults + sub-packages before members. The rule applies to all type + kinds: record types, union types, and enumeration types.''' + } + } /***********************************************************************/ @@ -431,13 +498,15 @@ section "Qualified names" { Grammar Qualified_Name { bnf = ''' - qualified_name ::= [ IDENTIFIER_package_name '.' ] IDENTIFIER_name + qualified_name ::= [ package_name '.' ] IDENTIFIER_name ''' } Static_Semantics Valid_Qualifier { text = '''The package name of a qualified name must be the current - package or an imported package.''' + package or an imported package. For nested packages, the + full dotted package name must be imported; importing a + parent package does not give access to sub-packages.''' } Static_Semantics Valid_Name { diff --git a/tests-system/cyclic-packages/output b/tests-system/cyclic-packages/output index 8ac2b814..d6fe40a8 100644 --- a/tests-system/cyclic-packages/output +++ b/tests-system/cyclic-packages/output @@ -1,4 +1,5 @@ -cyclic-packages/bar.rsl: error: circular inheritence between Baz | Bork +cyclic-packages/bar.rsl: error: circular inheritance between Bar | Baz | Bork +| Bar is declared in bar.rsl | Baz is declared in baz.rsl | Bork is declared in bork.rsl Processed 4 models and 0 requirement files and found 1 error diff --git a/tests-system/cyclic-packages/output.brief b/tests-system/cyclic-packages/output.brief index 62c8b8b3..02bb6296 100644 --- a/tests-system/cyclic-packages/output.brief +++ b/tests-system/cyclic-packages/output.brief @@ -1,2 +1,2 @@ -cyclic-packages/bar.rsl: trlc error: circular inheritence between Baz | Bork +cyclic-packages/bar.rsl: trlc error: circular inheritance between Bar | Baz | Bork Processed 4 models and 0 requirement files and found 1 error diff --git a/tests-system/cyclic-packages/output.json b/tests-system/cyclic-packages/output.json index 8ac2b814..d6fe40a8 100644 --- a/tests-system/cyclic-packages/output.json +++ b/tests-system/cyclic-packages/output.json @@ -1,4 +1,5 @@ -cyclic-packages/bar.rsl: error: circular inheritence between Baz | Bork +cyclic-packages/bar.rsl: error: circular inheritance between Bar | Baz | Bork +| Bar is declared in bar.rsl | Baz is declared in baz.rsl | Bork is declared in bork.rsl Processed 4 models and 0 requirement files and found 1 error diff --git a/tests-system/cyclic-packages/output.smtlib b/tests-system/cyclic-packages/output.smtlib index 8ac2b814..d6fe40a8 100644 --- a/tests-system/cyclic-packages/output.smtlib +++ b/tests-system/cyclic-packages/output.smtlib @@ -1,4 +1,5 @@ -cyclic-packages/bar.rsl: error: circular inheritence between Baz | Bork +cyclic-packages/bar.rsl: error: circular inheritance between Bar | Baz | Bork +| Bar is declared in bar.rsl | Baz is declared in baz.rsl | Bork is declared in bork.rsl Processed 4 models and 0 requirement files and found 1 error diff --git a/tests-system/duplicate-imports/output b/tests-system/duplicate-imports/output index 689110cd..319d6634 100644 --- a/tests-system/duplicate-imports/output +++ b/tests-system/duplicate-imports/output @@ -1,3 +1,6 @@ import Foo ^^^ duplicate-imports/bar.trlc:4: warning: duplicate import of package Foo -Processed 1 model and 1 requirement file and found 1 warning +import Foo + ^^^ duplicate-imports/bar.trlc:3: issue: unused import Foo [unused_imports] + | Consider deleting this import statement if not needed. +Processed 1 model and 1 requirement file and found 2 warnings diff --git a/tests-system/duplicate-imports/output.smtlib b/tests-system/duplicate-imports/output.smtlib index 689110cd..319d6634 100644 --- a/tests-system/duplicate-imports/output.smtlib +++ b/tests-system/duplicate-imports/output.smtlib @@ -1,3 +1,6 @@ import Foo ^^^ duplicate-imports/bar.trlc:4: warning: duplicate import of package Foo -Processed 1 model and 1 requirement file and found 1 warning +import Foo + ^^^ duplicate-imports/bar.trlc:3: issue: unused import Foo [unused_imports] + | Consider deleting this import statement if not needed. +Processed 1 model and 1 requirement file and found 2 warnings diff --git a/tests-system/lint-nested-unused/app.rsl b/tests-system/lint-nested-unused/app.rsl new file mode 100644 index 00000000..50033f4a --- /dev/null +++ b/tests-system/lint-nested-unused/app.rsl @@ -0,0 +1,6 @@ +package app +import ns.pkg + +type R { + v Integer +} diff --git a/tests-system/lint-nested-unused/ns.rsl b/tests-system/lint-nested-unused/ns.rsl new file mode 100644 index 00000000..216d9697 --- /dev/null +++ b/tests-system/lint-nested-unused/ns.rsl @@ -0,0 +1 @@ +package ns diff --git a/tests-system/lint-nested-unused/nspkg.rsl b/tests-system/lint-nested-unused/nspkg.rsl new file mode 100644 index 00000000..231eefa1 --- /dev/null +++ b/tests-system/lint-nested-unused/nspkg.rsl @@ -0,0 +1,5 @@ +package ns.pkg + +type T { + v Integer +} diff --git a/tests-system/lint-nested-unused/options b/tests-system/lint-nested-unused/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/lint-nested-unused/output b/tests-system/lint-nested-unused/output new file mode 100644 index 00000000..6bfeffe0 --- /dev/null +++ b/tests-system/lint-nested-unused/output @@ -0,0 +1,4 @@ +import ns.pkg + ^^ lint-nested-unused/app.rsl:2: issue: unused import ns.pkg [unused_imports] + | Consider deleting this import statement if not needed. +Processed 3 models and 0 requirement files and found 1 warning diff --git a/tests-system/lint-nested-unused/output.brief b/tests-system/lint-nested-unused/output.brief new file mode 100644 index 00000000..660b91a7 --- /dev/null +++ b/tests-system/lint-nested-unused/output.brief @@ -0,0 +1 @@ +Processed 3 models and 0 requirement files and found no issues diff --git a/tests-system/lint-nested-unused/output.json b/tests-system/lint-nested-unused/output.json new file mode 100644 index 00000000..41e3f1e4 --- /dev/null +++ b/tests-system/lint-nested-unused/output.json @@ -0,0 +1,2 @@ +{} +Processed 3 models and 0 requirement files and found no issues diff --git a/tests-system/lint-nested-unused/output.smtlib b/tests-system/lint-nested-unused/output.smtlib new file mode 100644 index 00000000..6bfeffe0 --- /dev/null +++ b/tests-system/lint-nested-unused/output.smtlib @@ -0,0 +1,4 @@ +import ns.pkg + ^^ lint-nested-unused/app.rsl:2: issue: unused import ns.pkg [unused_imports] + | Consider deleting this import statement if not needed. +Processed 3 models and 0 requirement files and found 1 warning diff --git a/tests-system/lint-nested-unused/tracing b/tests-system/lint-nested-unused/tracing new file mode 100644 index 00000000..6dfe5ea2 --- /dev/null +++ b/tests-system/lint-nested-unused/tracing @@ -0,0 +1,2 @@ +LRM.Nested_Visibility +LRM.Import_Visibility diff --git a/tests-system/lint-wildcard-redundant/foo.rsl b/tests-system/lint-wildcard-redundant/foo.rsl new file mode 100644 index 00000000..f52652b1 --- /dev/null +++ b/tests-system/lint-wildcard-redundant/foo.rsl @@ -0,0 +1 @@ +package foo diff --git a/tests-system/lint-wildcard-redundant/foobar.rsl b/tests-system/lint-wildcard-redundant/foobar.rsl new file mode 100644 index 00000000..8c35d461 --- /dev/null +++ b/tests-system/lint-wildcard-redundant/foobar.rsl @@ -0,0 +1,7 @@ +package foo.bar +import sub.* +import sub.deep + +type R { + x sub.deep.U +} diff --git a/tests-system/lint-wildcard-redundant/options b/tests-system/lint-wildcard-redundant/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/lint-wildcard-redundant/output b/tests-system/lint-wildcard-redundant/output new file mode 100644 index 00000000..1650ff30 --- /dev/null +++ b/tests-system/lint-wildcard-redundant/output @@ -0,0 +1,4 @@ +import sub.deep + ^^^ lint-wildcard-redundant/foobar.rsl:3: issue: redundant import sub.deep, already covered by wildcard import sub.* [unused_imports] + | Consider deleting this import statement. +Processed 4 models and 0 requirement files and found 1 warning diff --git a/tests-system/lint-wildcard-redundant/output.brief b/tests-system/lint-wildcard-redundant/output.brief new file mode 100644 index 00000000..8f096195 --- /dev/null +++ b/tests-system/lint-wildcard-redundant/output.brief @@ -0,0 +1 @@ +Processed 4 models and 0 requirement files and found no issues diff --git a/tests-system/lint-wildcard-redundant/output.json b/tests-system/lint-wildcard-redundant/output.json new file mode 100644 index 00000000..0b79e52a --- /dev/null +++ b/tests-system/lint-wildcard-redundant/output.json @@ -0,0 +1,2 @@ +{} +Processed 4 models and 0 requirement files and found no issues diff --git a/tests-system/lint-wildcard-redundant/output.smtlib b/tests-system/lint-wildcard-redundant/output.smtlib new file mode 100644 index 00000000..1650ff30 --- /dev/null +++ b/tests-system/lint-wildcard-redundant/output.smtlib @@ -0,0 +1,4 @@ +import sub.deep + ^^^ lint-wildcard-redundant/foobar.rsl:3: issue: redundant import sub.deep, already covered by wildcard import sub.* [unused_imports] + | Consider deleting this import statement. +Processed 4 models and 0 requirement files and found 1 warning diff --git a/tests-system/lint-wildcard-redundant/sub.rsl b/tests-system/lint-wildcard-redundant/sub.rsl new file mode 100644 index 00000000..1dc3d0d9 --- /dev/null +++ b/tests-system/lint-wildcard-redundant/sub.rsl @@ -0,0 +1 @@ +package sub diff --git a/tests-system/lint-wildcard-redundant/subdeep.rsl b/tests-system/lint-wildcard-redundant/subdeep.rsl new file mode 100644 index 00000000..ad031dba --- /dev/null +++ b/tests-system/lint-wildcard-redundant/subdeep.rsl @@ -0,0 +1,5 @@ +package sub.deep + +type U { + v Integer +} diff --git a/tests-system/lint-wildcard-redundant/tracing b/tests-system/lint-wildcard-redundant/tracing new file mode 100644 index 00000000..c507a1d6 --- /dev/null +++ b/tests-system/lint-wildcard-redundant/tracing @@ -0,0 +1 @@ +LRM.Wildcard_Import diff --git a/tests-system/lint-wildcard-unused/foo.rsl b/tests-system/lint-wildcard-unused/foo.rsl new file mode 100644 index 00000000..f52652b1 --- /dev/null +++ b/tests-system/lint-wildcard-unused/foo.rsl @@ -0,0 +1 @@ +package foo diff --git a/tests-system/lint-wildcard-unused/foobar.rsl b/tests-system/lint-wildcard-unused/foobar.rsl new file mode 100644 index 00000000..3c0bcc74 --- /dev/null +++ b/tests-system/lint-wildcard-unused/foobar.rsl @@ -0,0 +1,6 @@ +package foo.bar +import sub.* + +type R { + v Integer +} diff --git a/tests-system/lint-wildcard-unused/options b/tests-system/lint-wildcard-unused/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/lint-wildcard-unused/output b/tests-system/lint-wildcard-unused/output new file mode 100644 index 00000000..5ca0c3a9 --- /dev/null +++ b/tests-system/lint-wildcard-unused/output @@ -0,0 +1,4 @@ +import sub.* + ^^^ lint-wildcard-unused/foobar.rsl:2: issue: unused wildcard import sub.* [unused_imports] + | Consider deleting this import statement if not needed. +Processed 3 models and 0 requirement files and found 1 warning diff --git a/tests-system/lint-wildcard-unused/output.brief b/tests-system/lint-wildcard-unused/output.brief new file mode 100644 index 00000000..660b91a7 --- /dev/null +++ b/tests-system/lint-wildcard-unused/output.brief @@ -0,0 +1 @@ +Processed 3 models and 0 requirement files and found no issues diff --git a/tests-system/lint-wildcard-unused/output.json b/tests-system/lint-wildcard-unused/output.json new file mode 100644 index 00000000..41e3f1e4 --- /dev/null +++ b/tests-system/lint-wildcard-unused/output.json @@ -0,0 +1,2 @@ +{} +Processed 3 models and 0 requirement files and found no issues diff --git a/tests-system/lint-wildcard-unused/output.smtlib b/tests-system/lint-wildcard-unused/output.smtlib new file mode 100644 index 00000000..5ca0c3a9 --- /dev/null +++ b/tests-system/lint-wildcard-unused/output.smtlib @@ -0,0 +1,4 @@ +import sub.* + ^^^ lint-wildcard-unused/foobar.rsl:2: issue: unused wildcard import sub.* [unused_imports] + | Consider deleting this import statement if not needed. +Processed 3 models and 0 requirement files and found 1 warning diff --git a/tests-system/lint-wildcard-unused/sub.rsl b/tests-system/lint-wildcard-unused/sub.rsl new file mode 100644 index 00000000..a226f2e1 --- /dev/null +++ b/tests-system/lint-wildcard-unused/sub.rsl @@ -0,0 +1,5 @@ +package sub + +type T { + v Integer +} diff --git a/tests-system/lint-wildcard-unused/tracing b/tests-system/lint-wildcard-unused/tracing new file mode 100644 index 00000000..c507a1d6 --- /dev/null +++ b/tests-system/lint-wildcard-unused/tracing @@ -0,0 +1 @@ +LRM.Wildcard_Import diff --git a/tests-system/nested-packages-basic/foo.rsl b/tests-system/nested-packages-basic/foo.rsl new file mode 100644 index 00000000..e97abb35 --- /dev/null +++ b/tests-system/nested-packages-basic/foo.rsl @@ -0,0 +1,3 @@ +package foo + +type Holder {} diff --git a/tests-system/nested-packages-basic/foo_bar.rsl b/tests-system/nested-packages-basic/foo_bar.rsl new file mode 100644 index 00000000..44e7e437 --- /dev/null +++ b/tests-system/nested-packages-basic/foo_bar.rsl @@ -0,0 +1,5 @@ +package foo.bar + +type Req { + title String +} diff --git a/tests-system/nested-packages-basic/mydata.trlc b/tests-system/nested-packages-basic/mydata.trlc new file mode 100644 index 00000000..19394d34 --- /dev/null +++ b/tests-system/nested-packages-basic/mydata.trlc @@ -0,0 +1,6 @@ +package mydata +import foo.bar + +foo.bar.Req REQ_001 { + title = "first requirement" +} diff --git a/tests-system/nested-packages-basic/output b/tests-system/nested-packages-basic/output new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-basic/output @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-basic/output.brief b/tests-system/nested-packages-basic/output.brief new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-basic/output.brief @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-basic/output.json b/tests-system/nested-packages-basic/output.json new file mode 100644 index 00000000..4cf7fdf1 --- /dev/null +++ b/tests-system/nested-packages-basic/output.json @@ -0,0 +1,6 @@ +{ + "REQ_001": { + "title": "first requirement" + } +} +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-basic/output.smtlib b/tests-system/nested-packages-basic/output.smtlib new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-basic/output.smtlib @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-basic/tracing b/tests-system/nested-packages-basic/tracing new file mode 100644 index 00000000..50380f9a --- /dev/null +++ b/tests-system/nested-packages-basic/tracing @@ -0,0 +1,3 @@ +LRM.Nested_Package_Names +LRM.Parent_Package_Required +LRM.Qualified_Name diff --git a/tests-system/nested-packages-collision/FooBar.rsl b/tests-system/nested-packages-collision/FooBar.rsl new file mode 100644 index 00000000..36f4b18d --- /dev/null +++ b/tests-system/nested-packages-collision/FooBar.rsl @@ -0,0 +1,3 @@ +package Foo.Bar + +type Widget {} diff --git a/tests-system/nested-packages-collision/foo.rsl b/tests-system/nested-packages-collision/foo.rsl new file mode 100644 index 00000000..1a8ddb76 --- /dev/null +++ b/tests-system/nested-packages-collision/foo.rsl @@ -0,0 +1,3 @@ +package foo + +type Dummy {} diff --git a/tests-system/nested-packages-collision/foo_bar.rsl b/tests-system/nested-packages-collision/foo_bar.rsl new file mode 100644 index 00000000..a77118a2 --- /dev/null +++ b/tests-system/nested-packages-collision/foo_bar.rsl @@ -0,0 +1,3 @@ +package foo.bar + +type Item {} diff --git a/tests-system/nested-packages-collision/output b/tests-system/nested-packages-collision/output new file mode 100644 index 00000000..2b8ec924 --- /dev/null +++ b/tests-system/nested-packages-collision/output @@ -0,0 +1,3 @@ +package foo.bar + ^^^ nested-packages-collision/foo_bar.rsl:1: error: foo.bar is too similar to Foo.Bar, declared at FooBar.rsl:1 +Processed 3 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-collision/output.brief b/tests-system/nested-packages-collision/output.brief new file mode 100644 index 00000000..26945560 --- /dev/null +++ b/tests-system/nested-packages-collision/output.brief @@ -0,0 +1,2 @@ +nested-packages-collision/foo_bar.rsl:1:9: trlc error: foo.bar is too similar to Foo.Bar, declared at FooBar.rsl:1 +Processed 3 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-collision/output.json b/tests-system/nested-packages-collision/output.json new file mode 100644 index 00000000..2b8ec924 --- /dev/null +++ b/tests-system/nested-packages-collision/output.json @@ -0,0 +1,3 @@ +package foo.bar + ^^^ nested-packages-collision/foo_bar.rsl:1: error: foo.bar is too similar to Foo.Bar, declared at FooBar.rsl:1 +Processed 3 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-collision/output.smtlib b/tests-system/nested-packages-collision/output.smtlib new file mode 100644 index 00000000..2b8ec924 --- /dev/null +++ b/tests-system/nested-packages-collision/output.smtlib @@ -0,0 +1,3 @@ +package foo.bar + ^^^ nested-packages-collision/foo_bar.rsl:1: error: foo.bar is too similar to Foo.Bar, declared at FooBar.rsl:1 +Processed 3 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-collision/tracing b/tests-system/nested-packages-collision/tracing new file mode 100644 index 00000000..da5a77ee --- /dev/null +++ b/tests-system/nested-packages-collision/tracing @@ -0,0 +1,2 @@ +LRM.Sufficiently_Distinct +LRM.Nested_Package_Names diff --git a/tests-system/nested-packages-cyclic/a.rsl b/tests-system/nested-packages-cyclic/a.rsl new file mode 100644 index 00000000..60db65a9 --- /dev/null +++ b/tests-system/nested-packages-cyclic/a.rsl @@ -0,0 +1,5 @@ +package a + +import a.b + +type Root extends a.b.Leaf {} diff --git a/tests-system/nested-packages-cyclic/a_b.rsl b/tests-system/nested-packages-cyclic/a_b.rsl new file mode 100644 index 00000000..b98334dc --- /dev/null +++ b/tests-system/nested-packages-cyclic/a_b.rsl @@ -0,0 +1,5 @@ +package a.b + +import a + +type Leaf extends a.Root {} diff --git a/tests-system/nested-packages-cyclic/output b/tests-system/nested-packages-cyclic/output new file mode 100644 index 00000000..2a6b12e2 --- /dev/null +++ b/tests-system/nested-packages-cyclic/output @@ -0,0 +1,4 @@ +nested-packages-cyclic/a.rsl: error: circular inheritance between a | a.b +| a is declared in a.rsl +| a.b is declared in a_b.rsl +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-cyclic/output.brief b/tests-system/nested-packages-cyclic/output.brief new file mode 100644 index 00000000..b69b3a25 --- /dev/null +++ b/tests-system/nested-packages-cyclic/output.brief @@ -0,0 +1,2 @@ +nested-packages-cyclic/a.rsl: trlc error: circular inheritance between a | a.b +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-cyclic/output.json b/tests-system/nested-packages-cyclic/output.json new file mode 100644 index 00000000..2a6b12e2 --- /dev/null +++ b/tests-system/nested-packages-cyclic/output.json @@ -0,0 +1,4 @@ +nested-packages-cyclic/a.rsl: error: circular inheritance between a | a.b +| a is declared in a.rsl +| a.b is declared in a_b.rsl +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-cyclic/output.smtlib b/tests-system/nested-packages-cyclic/output.smtlib new file mode 100644 index 00000000..2a6b12e2 --- /dev/null +++ b/tests-system/nested-packages-cyclic/output.smtlib @@ -0,0 +1,4 @@ +nested-packages-cyclic/a.rsl: error: circular inheritance between a | a.b +| a is declared in a.rsl +| a.b is declared in a_b.rsl +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-cyclic/tracing b/tests-system/nested-packages-cyclic/tracing new file mode 100644 index 00000000..4dbd3e69 --- /dev/null +++ b/tests-system/nested-packages-cyclic/tracing @@ -0,0 +1 @@ +LRM.Circular_Dependencies diff --git a/tests-system/nested-packages-deep/a.rsl b/tests-system/nested-packages-deep/a.rsl new file mode 100644 index 00000000..ef85b901 --- /dev/null +++ b/tests-system/nested-packages-deep/a.rsl @@ -0,0 +1,3 @@ +package a + +type AType {} diff --git a/tests-system/nested-packages-deep/a_b.rsl b/tests-system/nested-packages-deep/a_b.rsl new file mode 100644 index 00000000..f6444958 --- /dev/null +++ b/tests-system/nested-packages-deep/a_b.rsl @@ -0,0 +1,3 @@ +package a.b + +type BType {} diff --git a/tests-system/nested-packages-deep/a_b_c.rsl b/tests-system/nested-packages-deep/a_b_c.rsl new file mode 100644 index 00000000..990058c2 --- /dev/null +++ b/tests-system/nested-packages-deep/a_b_c.rsl @@ -0,0 +1,5 @@ +package a.b.c + +type Req { + title String +} diff --git a/tests-system/nested-packages-deep/data.trlc b/tests-system/nested-packages-deep/data.trlc new file mode 100644 index 00000000..c2710b6a --- /dev/null +++ b/tests-system/nested-packages-deep/data.trlc @@ -0,0 +1,6 @@ +package data +import a.b.c + +a.b.c.Req R_001 { + title = "three levels deep" +} diff --git a/tests-system/nested-packages-deep/output b/tests-system/nested-packages-deep/output new file mode 100644 index 00000000..fc3f4e64 --- /dev/null +++ b/tests-system/nested-packages-deep/output @@ -0,0 +1 @@ +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-deep/output.brief b/tests-system/nested-packages-deep/output.brief new file mode 100644 index 00000000..fc3f4e64 --- /dev/null +++ b/tests-system/nested-packages-deep/output.brief @@ -0,0 +1 @@ +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-deep/output.json b/tests-system/nested-packages-deep/output.json new file mode 100644 index 00000000..01649ac8 --- /dev/null +++ b/tests-system/nested-packages-deep/output.json @@ -0,0 +1,6 @@ +{ + "R_001": { + "title": "three levels deep" + } +} +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-deep/output.smtlib b/tests-system/nested-packages-deep/output.smtlib new file mode 100644 index 00000000..fc3f4e64 --- /dev/null +++ b/tests-system/nested-packages-deep/output.smtlib @@ -0,0 +1 @@ +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-deep/tracing b/tests-system/nested-packages-deep/tracing new file mode 100644 index 00000000..644d4a94 --- /dev/null +++ b/tests-system/nested-packages-deep/tracing @@ -0,0 +1,2 @@ +LRM.Nested_Package_Names +LRM.Parent_Package_Required diff --git a/tests-system/nested-packages-field-vs-pkg/a.rsl b/tests-system/nested-packages-field-vs-pkg/a.rsl new file mode 100644 index 00000000..2a93cdef --- /dev/null +++ b/tests-system/nested-packages-field-vs-pkg/a.rsl @@ -0,0 +1 @@ +package a diff --git a/tests-system/nested-packages-field-vs-pkg/a_b.rsl b/tests-system/nested-packages-field-vs-pkg/a_b.rsl new file mode 100644 index 00000000..f99345d1 --- /dev/null +++ b/tests-system/nested-packages-field-vs-pkg/a_b.rsl @@ -0,0 +1,14 @@ +package a.b + +tuple Coord { + x Integer + y Integer +} + +type R { + c Coord +} + +checks R { + c.x >= 0, "x must be non-negative" +} diff --git a/tests-system/nested-packages-field-vs-pkg/data.trlc b/tests-system/nested-packages-field-vs-pkg/data.trlc new file mode 100644 index 00000000..4b17253e --- /dev/null +++ b/tests-system/nested-packages-field-vs-pkg/data.trlc @@ -0,0 +1,6 @@ +package app +import a.b + +a.b.R R_001 { + c = (3, 4) +} diff --git a/tests-system/nested-packages-field-vs-pkg/options b/tests-system/nested-packages-field-vs-pkg/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/nested-packages-field-vs-pkg/output b/tests-system/nested-packages-field-vs-pkg/output new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-field-vs-pkg/output @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-field-vs-pkg/output.brief b/tests-system/nested-packages-field-vs-pkg/output.brief new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-field-vs-pkg/output.brief @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-field-vs-pkg/output.json b/tests-system/nested-packages-field-vs-pkg/output.json new file mode 100644 index 00000000..2f09a9bf --- /dev/null +++ b/tests-system/nested-packages-field-vs-pkg/output.json @@ -0,0 +1,9 @@ +{ + "R_001": { + "c": { + "x": 3, + "y": 4 + } + } +} +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-field-vs-pkg/output.smtlib b/tests-system/nested-packages-field-vs-pkg/output.smtlib new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-field-vs-pkg/output.smtlib @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-field-vs-pkg/tracing b/tests-system/nested-packages-field-vs-pkg/tracing new file mode 100644 index 00000000..58523413 --- /dev/null +++ b/tests-system/nested-packages-field-vs-pkg/tracing @@ -0,0 +1,2 @@ +LRM.Qualified_Name +LRM.Nested_Visibility diff --git a/tests-system/nested-packages-import/data.trlc b/tests-system/nested-packages-import/data.trlc new file mode 100644 index 00000000..b4578867 --- /dev/null +++ b/tests-system/nested-packages-import/data.trlc @@ -0,0 +1,10 @@ +package data +import ns.pkg + +ns.pkg.Requirement R_001 { + description = "first requirement" +} + +ns.pkg.Requirement R_002 { + description = "second requirement" +} diff --git a/tests-system/nested-packages-import/ns.rsl b/tests-system/nested-packages-import/ns.rsl new file mode 100644 index 00000000..8dd7c56b --- /dev/null +++ b/tests-system/nested-packages-import/ns.rsl @@ -0,0 +1,3 @@ +package ns + +type Base {} diff --git a/tests-system/nested-packages-import/ns_pkg.rsl b/tests-system/nested-packages-import/ns_pkg.rsl new file mode 100644 index 00000000..4e100a6d --- /dev/null +++ b/tests-system/nested-packages-import/ns_pkg.rsl @@ -0,0 +1,5 @@ +package ns.pkg + +type Requirement { + description String +} diff --git a/tests-system/nested-packages-import/output b/tests-system/nested-packages-import/output new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-import/output @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-import/output.brief b/tests-system/nested-packages-import/output.brief new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-import/output.brief @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-import/output.json b/tests-system/nested-packages-import/output.json new file mode 100644 index 00000000..0c93d622 --- /dev/null +++ b/tests-system/nested-packages-import/output.json @@ -0,0 +1,9 @@ +{ + "R_001": { + "description": "first requirement" + }, + "R_002": { + "description": "second requirement" + } +} +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-import/output.smtlib b/tests-system/nested-packages-import/output.smtlib new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-import/output.smtlib @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-import/tracing b/tests-system/nested-packages-import/tracing new file mode 100644 index 00000000..399e207f --- /dev/null +++ b/tests-system/nested-packages-import/tracing @@ -0,0 +1,2 @@ +LRM.Nested_Visibility +LRM.Valid_Qualifier diff --git a/tests-system/nested-packages-late/outer.rsl b/tests-system/nested-packages-late/outer.rsl new file mode 100644 index 00000000..e7e9998d --- /dev/null +++ b/tests-system/nested-packages-late/outer.rsl @@ -0,0 +1,3 @@ +package outer + +type Base {} diff --git a/tests-system/nested-packages-late/outer_inner.rsl b/tests-system/nested-packages-late/outer_inner.rsl new file mode 100644 index 00000000..491841f6 --- /dev/null +++ b/tests-system/nested-packages-late/outer_inner.rsl @@ -0,0 +1,5 @@ +package outer.inner + +type Item { + value Integer +} diff --git a/tests-system/nested-packages-late/output b/tests-system/nested-packages-late/output new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-late/output @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-late/output.brief b/tests-system/nested-packages-late/output.brief new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-late/output.brief @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-late/output.json b/tests-system/nested-packages-late/output.json new file mode 100644 index 00000000..4d6f7e0e --- /dev/null +++ b/tests-system/nested-packages-late/output.json @@ -0,0 +1,6 @@ +{ + "IT_001": { + "value": 42 + } +} +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-late/output.smtlib b/tests-system/nested-packages-late/output.smtlib new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-late/output.smtlib @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-late/tracing b/tests-system/nested-packages-late/tracing new file mode 100644 index 00000000..55d14a9d --- /dev/null +++ b/tests-system/nested-packages-late/tracing @@ -0,0 +1,2 @@ +LRM.Late_Package_Declarations +LRM.Nested_Package_Names diff --git a/tests-system/nested-packages-late/user.trlc b/tests-system/nested-packages-late/user.trlc new file mode 100644 index 00000000..5c7f86e3 --- /dev/null +++ b/tests-system/nested-packages-late/user.trlc @@ -0,0 +1,6 @@ +package user +import outer.inner + +outer.inner.Item IT_001 { + value = 42 +} diff --git a/tests-system/nested-packages-member-collision-enum-deep/foo.rsl b/tests-system/nested-packages-member-collision-enum-deep/foo.rsl new file mode 100644 index 00000000..323170e1 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum-deep/foo.rsl @@ -0,0 +1,3 @@ +package foo + +enum Level { A B C } diff --git a/tests-system/nested-packages-member-collision-enum-deep/foo_level.rsl b/tests-system/nested-packages-member-collision-enum-deep/foo_level.rsl new file mode 100644 index 00000000..ccaaa631 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum-deep/foo_level.rsl @@ -0,0 +1,5 @@ +package foo.Level + +type Data { + x Integer +} diff --git a/tests-system/nested-packages-member-collision-enum-deep/foo_level_a.rsl b/tests-system/nested-packages-member-collision-enum-deep/foo_level_a.rsl new file mode 100644 index 00000000..a6ee2fea --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum-deep/foo_level_a.rsl @@ -0,0 +1,5 @@ +package foo.Level.A + +type Sub { + y Integer +} diff --git a/tests-system/nested-packages-member-collision-enum-deep/options b/tests-system/nested-packages-member-collision-enum-deep/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/nested-packages-member-collision-enum-deep/output b/tests-system/nested-packages-member-collision-enum-deep/output new file mode 100644 index 00000000..736936d7 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum-deep/output @@ -0,0 +1,3 @@ +package foo.Level + ^^^ nested-packages-member-collision-enum-deep/foo_level.rsl:1: error: sub-package Level clashes with a type or object of the same name in package foo +Processed 3 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-enum-deep/output.brief b/tests-system/nested-packages-member-collision-enum-deep/output.brief new file mode 100644 index 00000000..3d34884d --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum-deep/output.brief @@ -0,0 +1,2 @@ +nested-packages-member-collision-enum-deep/foo_level.rsl:1:9: trlc error: sub-package Level clashes with a type or object of the same name in package foo +Processed 3 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-enum-deep/output.json b/tests-system/nested-packages-member-collision-enum-deep/output.json new file mode 100644 index 00000000..736936d7 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum-deep/output.json @@ -0,0 +1,3 @@ +package foo.Level + ^^^ nested-packages-member-collision-enum-deep/foo_level.rsl:1: error: sub-package Level clashes with a type or object of the same name in package foo +Processed 3 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-enum-deep/output.smtlib b/tests-system/nested-packages-member-collision-enum-deep/output.smtlib new file mode 100644 index 00000000..736936d7 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum-deep/output.smtlib @@ -0,0 +1,3 @@ +package foo.Level + ^^^ nested-packages-member-collision-enum-deep/foo_level.rsl:1: error: sub-package Level clashes with a type or object of the same name in package foo +Processed 3 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-enum-deep/tracing b/tests-system/nested-packages-member-collision-enum-deep/tracing new file mode 100644 index 00000000..cc968985 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum-deep/tracing @@ -0,0 +1 @@ +LRM.Subpackage_Member_Distinct diff --git a/tests-system/nested-packages-member-collision-enum/foo.rsl b/tests-system/nested-packages-member-collision-enum/foo.rsl new file mode 100644 index 00000000..323170e1 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum/foo.rsl @@ -0,0 +1,3 @@ +package foo + +enum Level { A B C } diff --git a/tests-system/nested-packages-member-collision-enum/foo_level.rsl b/tests-system/nested-packages-member-collision-enum/foo_level.rsl new file mode 100644 index 00000000..ccaaa631 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum/foo_level.rsl @@ -0,0 +1,5 @@ +package foo.Level + +type Data { + x Integer +} diff --git a/tests-system/nested-packages-member-collision-enum/options b/tests-system/nested-packages-member-collision-enum/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/nested-packages-member-collision-enum/output b/tests-system/nested-packages-member-collision-enum/output new file mode 100644 index 00000000..100be799 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum/output @@ -0,0 +1,3 @@ +package foo.Level + ^^^ nested-packages-member-collision-enum/foo_level.rsl:1: error: sub-package Level clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-enum/output.brief b/tests-system/nested-packages-member-collision-enum/output.brief new file mode 100644 index 00000000..b419d0f7 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum/output.brief @@ -0,0 +1,2 @@ +nested-packages-member-collision-enum/foo_level.rsl:1:9: trlc error: sub-package Level clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-enum/output.json b/tests-system/nested-packages-member-collision-enum/output.json new file mode 100644 index 00000000..100be799 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum/output.json @@ -0,0 +1,3 @@ +package foo.Level + ^^^ nested-packages-member-collision-enum/foo_level.rsl:1: error: sub-package Level clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-enum/output.smtlib b/tests-system/nested-packages-member-collision-enum/output.smtlib new file mode 100644 index 00000000..100be799 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum/output.smtlib @@ -0,0 +1,3 @@ +package foo.Level + ^^^ nested-packages-member-collision-enum/foo_level.rsl:1: error: sub-package Level clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-enum/tracing b/tests-system/nested-packages-member-collision-enum/tracing new file mode 100644 index 00000000..cc968985 --- /dev/null +++ b/tests-system/nested-packages-member-collision-enum/tracing @@ -0,0 +1 @@ +LRM.Subpackage_Member_Distinct diff --git a/tests-system/nested-packages-member-collision-union/foo.rsl b/tests-system/nested-packages-member-collision-union/foo.rsl new file mode 100644 index 00000000..2f3c6a67 --- /dev/null +++ b/tests-system/nested-packages-member-collision-union/foo.rsl @@ -0,0 +1,13 @@ +package foo + +type Alpha { + v Integer +} + +type Beta { + v Integer +} + +type Holder { + val [Alpha, Beta] +} diff --git a/tests-system/nested-packages-member-collision-union/foo_alpha.rsl b/tests-system/nested-packages-member-collision-union/foo_alpha.rsl new file mode 100644 index 00000000..a081f861 --- /dev/null +++ b/tests-system/nested-packages-member-collision-union/foo_alpha.rsl @@ -0,0 +1,5 @@ +package foo.Alpha + +type Sub { + x Integer +} diff --git a/tests-system/nested-packages-member-collision-union/options b/tests-system/nested-packages-member-collision-union/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/nested-packages-member-collision-union/output b/tests-system/nested-packages-member-collision-union/output new file mode 100644 index 00000000..645d764c --- /dev/null +++ b/tests-system/nested-packages-member-collision-union/output @@ -0,0 +1,3 @@ +package foo.Alpha + ^^^ nested-packages-member-collision-union/foo_alpha.rsl:1: error: sub-package Alpha clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-union/output.brief b/tests-system/nested-packages-member-collision-union/output.brief new file mode 100644 index 00000000..072eca52 --- /dev/null +++ b/tests-system/nested-packages-member-collision-union/output.brief @@ -0,0 +1,2 @@ +nested-packages-member-collision-union/foo_alpha.rsl:1:9: trlc error: sub-package Alpha clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-union/output.json b/tests-system/nested-packages-member-collision-union/output.json new file mode 100644 index 00000000..645d764c --- /dev/null +++ b/tests-system/nested-packages-member-collision-union/output.json @@ -0,0 +1,3 @@ +package foo.Alpha + ^^^ nested-packages-member-collision-union/foo_alpha.rsl:1: error: sub-package Alpha clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-union/output.smtlib b/tests-system/nested-packages-member-collision-union/output.smtlib new file mode 100644 index 00000000..645d764c --- /dev/null +++ b/tests-system/nested-packages-member-collision-union/output.smtlib @@ -0,0 +1,3 @@ +package foo.Alpha + ^^^ nested-packages-member-collision-union/foo_alpha.rsl:1: error: sub-package Alpha clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision-union/tracing b/tests-system/nested-packages-member-collision-union/tracing new file mode 100644 index 00000000..cc968985 --- /dev/null +++ b/tests-system/nested-packages-member-collision-union/tracing @@ -0,0 +1 @@ +LRM.Subpackage_Member_Distinct diff --git a/tests-system/nested-packages-member-collision/foo.rsl b/tests-system/nested-packages-member-collision/foo.rsl new file mode 100644 index 00000000..f7917fa3 --- /dev/null +++ b/tests-system/nested-packages-member-collision/foo.rsl @@ -0,0 +1,5 @@ +package foo + +type Bar { + v Integer +} diff --git a/tests-system/nested-packages-member-collision/foo_bar.rsl b/tests-system/nested-packages-member-collision/foo_bar.rsl new file mode 100644 index 00000000..87c80dbb --- /dev/null +++ b/tests-system/nested-packages-member-collision/foo_bar.rsl @@ -0,0 +1,3 @@ +package foo.Bar + +type Widget {} diff --git a/tests-system/nested-packages-member-collision/options b/tests-system/nested-packages-member-collision/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/nested-packages-member-collision/output b/tests-system/nested-packages-member-collision/output new file mode 100644 index 00000000..93c6a662 --- /dev/null +++ b/tests-system/nested-packages-member-collision/output @@ -0,0 +1,3 @@ +package foo.Bar + ^^^ nested-packages-member-collision/foo_bar.rsl:1: error: sub-package Bar clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision/output.brief b/tests-system/nested-packages-member-collision/output.brief new file mode 100644 index 00000000..89e28264 --- /dev/null +++ b/tests-system/nested-packages-member-collision/output.brief @@ -0,0 +1,2 @@ +nested-packages-member-collision/foo_bar.rsl:1:9: trlc error: sub-package Bar clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision/output.json b/tests-system/nested-packages-member-collision/output.json new file mode 100644 index 00000000..93c6a662 --- /dev/null +++ b/tests-system/nested-packages-member-collision/output.json @@ -0,0 +1,3 @@ +package foo.Bar + ^^^ nested-packages-member-collision/foo_bar.rsl:1: error: sub-package Bar clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision/output.smtlib b/tests-system/nested-packages-member-collision/output.smtlib new file mode 100644 index 00000000..93c6a662 --- /dev/null +++ b/tests-system/nested-packages-member-collision/output.smtlib @@ -0,0 +1,3 @@ +package foo.Bar + ^^^ nested-packages-member-collision/foo_bar.rsl:1: error: sub-package Bar clashes with a type or object of the same name in package foo +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-member-collision/tracing b/tests-system/nested-packages-member-collision/tracing new file mode 100644 index 00000000..cc968985 --- /dev/null +++ b/tests-system/nested-packages-member-collision/tracing @@ -0,0 +1 @@ +LRM.Subpackage_Member_Distinct diff --git a/tests-system/nested-packages-no-parent/foo_bar.rsl b/tests-system/nested-packages-no-parent/foo_bar.rsl new file mode 100644 index 00000000..4f142020 --- /dev/null +++ b/tests-system/nested-packages-no-parent/foo_bar.rsl @@ -0,0 +1,5 @@ +package foo.bar + +type T { + x String +} diff --git a/tests-system/nested-packages-no-parent/output b/tests-system/nested-packages-no-parent/output new file mode 100644 index 00000000..0f84a810 --- /dev/null +++ b/tests-system/nested-packages-no-parent/output @@ -0,0 +1,3 @@ +package foo.bar + ^^^ nested-packages-no-parent/foo_bar.rsl:1: error: parent package foo of nested package foo.bar has not been declared +Processed 1 model and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-no-parent/output.brief b/tests-system/nested-packages-no-parent/output.brief new file mode 100644 index 00000000..26d4c078 --- /dev/null +++ b/tests-system/nested-packages-no-parent/output.brief @@ -0,0 +1,2 @@ +nested-packages-no-parent/foo_bar.rsl:1:9: trlc error: parent package foo of nested package foo.bar has not been declared +Processed 1 model and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-no-parent/output.json b/tests-system/nested-packages-no-parent/output.json new file mode 100644 index 00000000..0f84a810 --- /dev/null +++ b/tests-system/nested-packages-no-parent/output.json @@ -0,0 +1,3 @@ +package foo.bar + ^^^ nested-packages-no-parent/foo_bar.rsl:1: error: parent package foo of nested package foo.bar has not been declared +Processed 1 model and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-no-parent/output.smtlib b/tests-system/nested-packages-no-parent/output.smtlib new file mode 100644 index 00000000..0f84a810 --- /dev/null +++ b/tests-system/nested-packages-no-parent/output.smtlib @@ -0,0 +1,3 @@ +package foo.bar + ^^^ nested-packages-no-parent/foo_bar.rsl:1: error: parent package foo of nested package foo.bar has not been declared +Processed 1 model and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-no-parent/tracing b/tests-system/nested-packages-no-parent/tracing new file mode 100644 index 00000000..55b6b64c --- /dev/null +++ b/tests-system/nested-packages-no-parent/tracing @@ -0,0 +1 @@ +LRM.Parent_Package_Required diff --git a/tests-system/nested-packages-self-import/foo.rsl b/tests-system/nested-packages-self-import/foo.rsl new file mode 100644 index 00000000..1a8ddb76 --- /dev/null +++ b/tests-system/nested-packages-self-import/foo.rsl @@ -0,0 +1,3 @@ +package foo + +type Dummy {} diff --git a/tests-system/nested-packages-self-import/foo_bar.rsl b/tests-system/nested-packages-self-import/foo_bar.rsl new file mode 100644 index 00000000..ca03a1f8 --- /dev/null +++ b/tests-system/nested-packages-self-import/foo_bar.rsl @@ -0,0 +1,6 @@ +package foo.bar +import foo.bar + +type T { + x String +} diff --git a/tests-system/nested-packages-self-import/output b/tests-system/nested-packages-self-import/output new file mode 100644 index 00000000..a1e2d036 --- /dev/null +++ b/tests-system/nested-packages-self-import/output @@ -0,0 +1,3 @@ +import foo.bar + ^^^ nested-packages-self-import/foo_bar.rsl:2: error: package foo.bar cannot import itself +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-self-import/output.brief b/tests-system/nested-packages-self-import/output.brief new file mode 100644 index 00000000..b0a53a07 --- /dev/null +++ b/tests-system/nested-packages-self-import/output.brief @@ -0,0 +1,2 @@ +nested-packages-self-import/foo_bar.rsl:2:8: trlc error: package foo.bar cannot import itself +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-self-import/output.json b/tests-system/nested-packages-self-import/output.json new file mode 100644 index 00000000..a1e2d036 --- /dev/null +++ b/tests-system/nested-packages-self-import/output.json @@ -0,0 +1,3 @@ +import foo.bar + ^^^ nested-packages-self-import/foo_bar.rsl:2: error: package foo.bar cannot import itself +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-self-import/output.smtlib b/tests-system/nested-packages-self-import/output.smtlib new file mode 100644 index 00000000..a1e2d036 --- /dev/null +++ b/tests-system/nested-packages-self-import/output.smtlib @@ -0,0 +1,3 @@ +import foo.bar + ^^^ nested-packages-self-import/foo_bar.rsl:2: error: package foo.bar cannot import itself +Processed 2 models and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-self-import/tracing b/tests-system/nested-packages-self-import/tracing new file mode 100644 index 00000000..c9893f63 --- /dev/null +++ b/tests-system/nested-packages-self-import/tracing @@ -0,0 +1 @@ +LRM.Self_Imports diff --git a/tests-system/nested-packages-shadow-resolve/data.trlc b/tests-system/nested-packages-shadow-resolve/data.trlc new file mode 100644 index 00000000..631661c5 --- /dev/null +++ b/tests-system/nested-packages-shadow-resolve/data.trlc @@ -0,0 +1,11 @@ +package app +import foo +import foo.bar + +foo.T X_001 { + a = 1 +} + +foo.bar.T Y_001 { + b = 2 +} diff --git a/tests-system/nested-packages-shadow-resolve/foo.rsl b/tests-system/nested-packages-shadow-resolve/foo.rsl new file mode 100644 index 00000000..1890d9d2 --- /dev/null +++ b/tests-system/nested-packages-shadow-resolve/foo.rsl @@ -0,0 +1,5 @@ +package foo + +type T { + a Integer +} diff --git a/tests-system/nested-packages-shadow-resolve/foo_bar.rsl b/tests-system/nested-packages-shadow-resolve/foo_bar.rsl new file mode 100644 index 00000000..db49a25c --- /dev/null +++ b/tests-system/nested-packages-shadow-resolve/foo_bar.rsl @@ -0,0 +1,5 @@ +package foo.bar + +type T { + b Integer +} diff --git a/tests-system/nested-packages-shadow-resolve/options b/tests-system/nested-packages-shadow-resolve/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/nested-packages-shadow-resolve/output b/tests-system/nested-packages-shadow-resolve/output new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-shadow-resolve/output @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-shadow-resolve/output.brief b/tests-system/nested-packages-shadow-resolve/output.brief new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-shadow-resolve/output.brief @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-shadow-resolve/output.json b/tests-system/nested-packages-shadow-resolve/output.json new file mode 100644 index 00000000..05364a15 --- /dev/null +++ b/tests-system/nested-packages-shadow-resolve/output.json @@ -0,0 +1,9 @@ +{ + "X_001": { + "a": 1 + }, + "Y_001": { + "b": 2 + } +} +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-shadow-resolve/output.smtlib b/tests-system/nested-packages-shadow-resolve/output.smtlib new file mode 100644 index 00000000..cc167599 --- /dev/null +++ b/tests-system/nested-packages-shadow-resolve/output.smtlib @@ -0,0 +1 @@ +Processed 2 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-shadow-resolve/tracing b/tests-system/nested-packages-shadow-resolve/tracing new file mode 100644 index 00000000..69d884e0 --- /dev/null +++ b/tests-system/nested-packages-shadow-resolve/tracing @@ -0,0 +1,2 @@ +LRM.Subpackage_Member_Distinct +LRM.Qualified_Name diff --git a/tests-system/nested-packages-unimported/foo.rsl b/tests-system/nested-packages-unimported/foo.rsl new file mode 100644 index 00000000..1a8ddb76 --- /dev/null +++ b/tests-system/nested-packages-unimported/foo.rsl @@ -0,0 +1,3 @@ +package foo + +type Dummy {} diff --git a/tests-system/nested-packages-unimported/foo_bar.rsl b/tests-system/nested-packages-unimported/foo_bar.rsl new file mode 100644 index 00000000..4f142020 --- /dev/null +++ b/tests-system/nested-packages-unimported/foo_bar.rsl @@ -0,0 +1,5 @@ +package foo.bar + +type T { + x String +} diff --git a/tests-system/nested-packages-unimported/mydata.trlc b/tests-system/nested-packages-unimported/mydata.trlc new file mode 100644 index 00000000..c8d8ab99 --- /dev/null +++ b/tests-system/nested-packages-unimported/mydata.trlc @@ -0,0 +1,5 @@ +package mydata + +foo.bar.T OBJ { + x = "hello" +} diff --git a/tests-system/nested-packages-unimported/output b/tests-system/nested-packages-unimported/output new file mode 100644 index 00000000..491763ba --- /dev/null +++ b/tests-system/nested-packages-unimported/output @@ -0,0 +1,3 @@ +foo.bar.T OBJ { + ^^^ nested-packages-unimported/mydata.trlc:3: error: package must be imported before use +Processed 2 models and 1 requirement file and found 1 error diff --git a/tests-system/nested-packages-unimported/output.brief b/tests-system/nested-packages-unimported/output.brief new file mode 100644 index 00000000..e1eb93e2 --- /dev/null +++ b/tests-system/nested-packages-unimported/output.brief @@ -0,0 +1,2 @@ +nested-packages-unimported/mydata.trlc:3:5: trlc error: package must be imported before use +Processed 2 models and 1 requirement file and found 1 error diff --git a/tests-system/nested-packages-unimported/output.json b/tests-system/nested-packages-unimported/output.json new file mode 100644 index 00000000..491763ba --- /dev/null +++ b/tests-system/nested-packages-unimported/output.json @@ -0,0 +1,3 @@ +foo.bar.T OBJ { + ^^^ nested-packages-unimported/mydata.trlc:3: error: package must be imported before use +Processed 2 models and 1 requirement file and found 1 error diff --git a/tests-system/nested-packages-unimported/output.smtlib b/tests-system/nested-packages-unimported/output.smtlib new file mode 100644 index 00000000..491763ba --- /dev/null +++ b/tests-system/nested-packages-unimported/output.smtlib @@ -0,0 +1,3 @@ +foo.bar.T OBJ { + ^^^ nested-packages-unimported/mydata.trlc:3: error: package must be imported before use +Processed 2 models and 1 requirement file and found 1 error diff --git a/tests-system/nested-packages-unimported/tracing b/tests-system/nested-packages-unimported/tracing new file mode 100644 index 00000000..6dfe5ea2 --- /dev/null +++ b/tests-system/nested-packages-unimported/tracing @@ -0,0 +1,2 @@ +LRM.Nested_Visibility +LRM.Import_Visibility diff --git a/tests-system/nested-packages-wildcard-deps/core.rsl b/tests-system/nested-packages-wildcard-deps/core.rsl new file mode 100644 index 00000000..33fc1883 --- /dev/null +++ b/tests-system/nested-packages-wildcard-deps/core.rsl @@ -0,0 +1,5 @@ +package lib.core + +type Base { + v Integer +} diff --git a/tests-system/nested-packages-wildcard-deps/data.trlc b/tests-system/nested-packages-wildcard-deps/data.trlc new file mode 100644 index 00000000..5d3a6df5 --- /dev/null +++ b/tests-system/nested-packages-wildcard-deps/data.trlc @@ -0,0 +1,10 @@ +package app +import lib.* + +lib.core.Base B_001 { + v = 1 +} + +lib.core.ext.Ext E_001 { + w = 2 +} diff --git a/tests-system/nested-packages-wildcard-deps/ext.rsl b/tests-system/nested-packages-wildcard-deps/ext.rsl new file mode 100644 index 00000000..2c2039e2 --- /dev/null +++ b/tests-system/nested-packages-wildcard-deps/ext.rsl @@ -0,0 +1,5 @@ +package lib.core.ext + +type Ext { + w Integer +} diff --git a/tests-system/nested-packages-wildcard-deps/lib.rsl b/tests-system/nested-packages-wildcard-deps/lib.rsl new file mode 100644 index 00000000..55c21f80 --- /dev/null +++ b/tests-system/nested-packages-wildcard-deps/lib.rsl @@ -0,0 +1 @@ +package lib diff --git a/tests-system/nested-packages-wildcard-deps/options b/tests-system/nested-packages-wildcard-deps/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/nested-packages-wildcard-deps/output b/tests-system/nested-packages-wildcard-deps/output new file mode 100644 index 00000000..fc3f4e64 --- /dev/null +++ b/tests-system/nested-packages-wildcard-deps/output @@ -0,0 +1 @@ +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-wildcard-deps/output.brief b/tests-system/nested-packages-wildcard-deps/output.brief new file mode 100644 index 00000000..fc3f4e64 --- /dev/null +++ b/tests-system/nested-packages-wildcard-deps/output.brief @@ -0,0 +1 @@ +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-wildcard-deps/output.json b/tests-system/nested-packages-wildcard-deps/output.json new file mode 100644 index 00000000..8a3b1e65 --- /dev/null +++ b/tests-system/nested-packages-wildcard-deps/output.json @@ -0,0 +1,9 @@ +{ + "B_001": { + "v": 1 + }, + "E_001": { + "w": 2 + } +} +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-wildcard-deps/output.smtlib b/tests-system/nested-packages-wildcard-deps/output.smtlib new file mode 100644 index 00000000..fc3f4e64 --- /dev/null +++ b/tests-system/nested-packages-wildcard-deps/output.smtlib @@ -0,0 +1 @@ +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-wildcard-deps/tracing b/tests-system/nested-packages-wildcard-deps/tracing new file mode 100644 index 00000000..c507a1d6 --- /dev/null +++ b/tests-system/nested-packages-wildcard-deps/tracing @@ -0,0 +1 @@ +LRM.Wildcard_Import diff --git a/tests-system/nested-packages-wildcard-empty/data.trlc b/tests-system/nested-packages-wildcard-empty/data.trlc new file mode 100644 index 00000000..2b723a9c --- /dev/null +++ b/tests-system/nested-packages-wildcard-empty/data.trlc @@ -0,0 +1,6 @@ +package app +import foo.* + +foo.Req R_001 { + title = "x" +} diff --git a/tests-system/nested-packages-wildcard-empty/foo.rsl b/tests-system/nested-packages-wildcard-empty/foo.rsl new file mode 100644 index 00000000..b6556f81 --- /dev/null +++ b/tests-system/nested-packages-wildcard-empty/foo.rsl @@ -0,0 +1,5 @@ +package foo + +type Req { + title String +} diff --git a/tests-system/nested-packages-wildcard-empty/options b/tests-system/nested-packages-wildcard-empty/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/nested-packages-wildcard-empty/output b/tests-system/nested-packages-wildcard-empty/output new file mode 100644 index 00000000..82ffe016 --- /dev/null +++ b/tests-system/nested-packages-wildcard-empty/output @@ -0,0 +1,4 @@ +import foo.* + ^^^ nested-packages-wildcard-empty/data.trlc:2: issue: wildcard import foo.* is equivalent to import foo since foo has no sub-packages [unused_imports] + | Consider replacing with: import foo +Processed 1 model and 1 requirement file and found 1 warning diff --git a/tests-system/nested-packages-wildcard-empty/output.brief b/tests-system/nested-packages-wildcard-empty/output.brief new file mode 100644 index 00000000..a36e3bac --- /dev/null +++ b/tests-system/nested-packages-wildcard-empty/output.brief @@ -0,0 +1 @@ +Processed 1 model and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-wildcard-empty/output.json b/tests-system/nested-packages-wildcard-empty/output.json new file mode 100644 index 00000000..704bf45f --- /dev/null +++ b/tests-system/nested-packages-wildcard-empty/output.json @@ -0,0 +1,6 @@ +{ + "R_001": { + "title": "x" + } +} +Processed 1 model and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-wildcard-empty/output.smtlib b/tests-system/nested-packages-wildcard-empty/output.smtlib new file mode 100644 index 00000000..82ffe016 --- /dev/null +++ b/tests-system/nested-packages-wildcard-empty/output.smtlib @@ -0,0 +1,4 @@ +import foo.* + ^^^ nested-packages-wildcard-empty/data.trlc:2: issue: wildcard import foo.* is equivalent to import foo since foo has no sub-packages [unused_imports] + | Consider replacing with: import foo +Processed 1 model and 1 requirement file and found 1 warning diff --git a/tests-system/nested-packages-wildcard-empty/tracing b/tests-system/nested-packages-wildcard-empty/tracing new file mode 100644 index 00000000..9ea51e69 --- /dev/null +++ b/tests-system/nested-packages-wildcard-empty/tracing @@ -0,0 +1,2 @@ +LRM.Wildcard_Import +LRM.Wildcard_Trivial diff --git a/tests-system/nested-packages-wildcard-self-cover/foo.rsl b/tests-system/nested-packages-wildcard-self-cover/foo.rsl new file mode 100644 index 00000000..f52652b1 --- /dev/null +++ b/tests-system/nested-packages-wildcard-self-cover/foo.rsl @@ -0,0 +1 @@ +package foo diff --git a/tests-system/nested-packages-wildcard-self-cover/foo_bar.rsl b/tests-system/nested-packages-wildcard-self-cover/foo_bar.rsl new file mode 100644 index 00000000..260599c9 --- /dev/null +++ b/tests-system/nested-packages-wildcard-self-cover/foo_bar.rsl @@ -0,0 +1,11 @@ +package foo.bar + +// The wildcard root foo covers the current package foo.bar (self-cover). +// This is permitted: it is not an illegal self-import, and the package +// must not be made to depend on itself. The reference to a sibling below +// keeps the wildcard import "used", so the linter stays quiet. +import foo.* + +type R { + c foo.baz.Color +} diff --git a/tests-system/nested-packages-wildcard-self-cover/foo_baz.rsl b/tests-system/nested-packages-wildcard-self-cover/foo_baz.rsl new file mode 100644 index 00000000..db983edb --- /dev/null +++ b/tests-system/nested-packages-wildcard-self-cover/foo_baz.rsl @@ -0,0 +1,3 @@ +package foo.baz + +enum Color { red green } diff --git a/tests-system/nested-packages-wildcard-self-cover/output b/tests-system/nested-packages-wildcard-self-cover/output new file mode 100644 index 00000000..660b91a7 --- /dev/null +++ b/tests-system/nested-packages-wildcard-self-cover/output @@ -0,0 +1 @@ +Processed 3 models and 0 requirement files and found no issues diff --git a/tests-system/nested-packages-wildcard-self-cover/output.brief b/tests-system/nested-packages-wildcard-self-cover/output.brief new file mode 100644 index 00000000..660b91a7 --- /dev/null +++ b/tests-system/nested-packages-wildcard-self-cover/output.brief @@ -0,0 +1 @@ +Processed 3 models and 0 requirement files and found no issues diff --git a/tests-system/nested-packages-wildcard-self-cover/output.json b/tests-system/nested-packages-wildcard-self-cover/output.json new file mode 100644 index 00000000..41e3f1e4 --- /dev/null +++ b/tests-system/nested-packages-wildcard-self-cover/output.json @@ -0,0 +1,2 @@ +{} +Processed 3 models and 0 requirement files and found no issues diff --git a/tests-system/nested-packages-wildcard-self-cover/output.smtlib b/tests-system/nested-packages-wildcard-self-cover/output.smtlib new file mode 100644 index 00000000..660b91a7 --- /dev/null +++ b/tests-system/nested-packages-wildcard-self-cover/output.smtlib @@ -0,0 +1 @@ +Processed 3 models and 0 requirement files and found no issues diff --git a/tests-system/nested-packages-wildcard-self-cover/tracing b/tests-system/nested-packages-wildcard-self-cover/tracing new file mode 100644 index 00000000..91af2162 --- /dev/null +++ b/tests-system/nested-packages-wildcard-self-cover/tracing @@ -0,0 +1,2 @@ +LRM.Wildcard_Self_Cover +LRM.Wildcard_Import diff --git a/tests-system/nested-packages-wildcard-undeclared/app.rsl b/tests-system/nested-packages-wildcard-undeclared/app.rsl new file mode 100644 index 00000000..677ac00e --- /dev/null +++ b/tests-system/nested-packages-wildcard-undeclared/app.rsl @@ -0,0 +1,6 @@ +package app +import missing.* + +type T { + v Integer +} diff --git a/tests-system/nested-packages-wildcard-undeclared/options b/tests-system/nested-packages-wildcard-undeclared/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/nested-packages-wildcard-undeclared/output b/tests-system/nested-packages-wildcard-undeclared/output new file mode 100644 index 00000000..0aad5a1f --- /dev/null +++ b/tests-system/nested-packages-wildcard-undeclared/output @@ -0,0 +1,3 @@ +import missing.* + ^^^^^^^ nested-packages-wildcard-undeclared/app.rsl:2: error: unknown symbol missing +Processed 1 model and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-wildcard-undeclared/output.brief b/tests-system/nested-packages-wildcard-undeclared/output.brief new file mode 100644 index 00000000..5fc53078 --- /dev/null +++ b/tests-system/nested-packages-wildcard-undeclared/output.brief @@ -0,0 +1,2 @@ +nested-packages-wildcard-undeclared/app.rsl:2:8: trlc error: unknown symbol missing +Processed 1 model and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-wildcard-undeclared/output.json b/tests-system/nested-packages-wildcard-undeclared/output.json new file mode 100644 index 00000000..2e520260 --- /dev/null +++ b/tests-system/nested-packages-wildcard-undeclared/output.json @@ -0,0 +1,4 @@ +import missing.* + ^^^^^^^ nested-packages-wildcard-undeclared/app.rsl:2: error: unknown symbol missing +{} +Processed 1 model and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-wildcard-undeclared/output.smtlib b/tests-system/nested-packages-wildcard-undeclared/output.smtlib new file mode 100644 index 00000000..0aad5a1f --- /dev/null +++ b/tests-system/nested-packages-wildcard-undeclared/output.smtlib @@ -0,0 +1,3 @@ +import missing.* + ^^^^^^^ nested-packages-wildcard-undeclared/app.rsl:2: error: unknown symbol missing +Processed 1 model and 0 requirement files and found 1 error diff --git a/tests-system/nested-packages-wildcard-undeclared/tracing b/tests-system/nested-packages-wildcard-undeclared/tracing new file mode 100644 index 00000000..c507a1d6 --- /dev/null +++ b/tests-system/nested-packages-wildcard-undeclared/tracing @@ -0,0 +1 @@ +LRM.Wildcard_Import diff --git a/tests-system/nested-packages-wildcard/com.rsl b/tests-system/nested-packages-wildcard/com.rsl new file mode 100644 index 00000000..7da49497 --- /dev/null +++ b/tests-system/nested-packages-wildcard/com.rsl @@ -0,0 +1 @@ +package com diff --git a/tests-system/nested-packages-wildcard/data.trlc b/tests-system/nested-packages-wildcard/data.trlc new file mode 100644 index 00000000..56dc2737 --- /dev/null +++ b/tests-system/nested-packages-wildcard/data.trlc @@ -0,0 +1,6 @@ +package app +import com.shared.* + +com.shared.si.Force F_001 { + v = 10 +} diff --git a/tests-system/nested-packages-wildcard/options b/tests-system/nested-packages-wildcard/options new file mode 100644 index 00000000..e69de29b diff --git a/tests-system/nested-packages-wildcard/output b/tests-system/nested-packages-wildcard/output new file mode 100644 index 00000000..fc3f4e64 --- /dev/null +++ b/tests-system/nested-packages-wildcard/output @@ -0,0 +1 @@ +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-wildcard/output.brief b/tests-system/nested-packages-wildcard/output.brief new file mode 100644 index 00000000..fc3f4e64 --- /dev/null +++ b/tests-system/nested-packages-wildcard/output.brief @@ -0,0 +1 @@ +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-wildcard/output.json b/tests-system/nested-packages-wildcard/output.json new file mode 100644 index 00000000..155cb506 --- /dev/null +++ b/tests-system/nested-packages-wildcard/output.json @@ -0,0 +1,6 @@ +{ + "F_001": { + "v": 10 + } +} +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-wildcard/output.smtlib b/tests-system/nested-packages-wildcard/output.smtlib new file mode 100644 index 00000000..fc3f4e64 --- /dev/null +++ b/tests-system/nested-packages-wildcard/output.smtlib @@ -0,0 +1 @@ +Processed 3 models and 1 requirement file and found no issues diff --git a/tests-system/nested-packages-wildcard/shared.rsl b/tests-system/nested-packages-wildcard/shared.rsl new file mode 100644 index 00000000..f1c5e601 --- /dev/null +++ b/tests-system/nested-packages-wildcard/shared.rsl @@ -0,0 +1,3 @@ +package com.shared + +enum Unit { kg newton } diff --git a/tests-system/nested-packages-wildcard/si.rsl b/tests-system/nested-packages-wildcard/si.rsl new file mode 100644 index 00000000..31ece4ad --- /dev/null +++ b/tests-system/nested-packages-wildcard/si.rsl @@ -0,0 +1,5 @@ +package com.shared.si + +type Force { + v Integer +} diff --git a/tests-system/nested-packages-wildcard/tracing b/tests-system/nested-packages-wildcard/tracing new file mode 100644 index 00000000..c507a1d6 --- /dev/null +++ b/tests-system/nested-packages-wildcard/tracing @@ -0,0 +1 @@ +LRM.Wildcard_Import diff --git a/tests-unit/BUILD.bazel b/tests-unit/BUILD.bazel index c3f6ab45..aee29340 100644 --- a/tests-unit/BUILD.bazel +++ b/tests-unit/BUILD.bazel @@ -18,11 +18,18 @@ py_test( deps = ["//trlc:trlc"], ) +py_test( + name = "test_nested_packages", + srcs = ["test_nested_packages.py"], + deps = ["//trlc:trlc"], +) + test_suite( name = "unit_tests", tests = [ ":test_ast_bysection", ":test_lexer", ":test_lexer_base", + ":test_nested_packages", ], ) diff --git a/tests-unit/test_nested_packages.py b/tests-unit/test_nested_packages.py new file mode 100644 index 00000000..a2dd4d96 --- /dev/null +++ b/tests-unit/test_nested_packages.py @@ -0,0 +1,400 @@ +import os +import shutil +import tempfile +import unittest + +from trlc.errors import Kind, Location, Message_Handler, TRLC_Error +from trlc.trlc import Source_Manager +from trlc import ast + + +class List_Handler(Message_Handler): + def __init__(self): + super().__init__() + self.messages = [] + + def emit( + self, location, kind, message, fatal=True, extrainfo=None, category=None + ): + self.messages.append((kind, message)) + if fatal: + raise TRLC_Error(location, kind, message) + + def has_error(self): + return any(k in (Kind.SYS_ERROR, Kind.USER_ERROR) for k, _ in self.messages) + + def error_messages(self): + return [m for k, m in self.messages if k in (Kind.SYS_ERROR, Kind.USER_ERROR)] + + def clear(self): + self.messages = [] + + +def make_source_manager(lint_mode=True): + mh = List_Handler() + return Source_Manager(mh=mh, lint_mode=lint_mode, error_recovery=False), mh + + +class Test_Nested_Packages(unittest.TestCase): + """Tests for nested (dotted) package names, e.g. foo.bar.""" + + def setUp(self): + self.tmp = tempfile.mkdtemp() + + def tearDown(self): + shutil.rmtree(self.tmp, ignore_errors=True) + + def write(self, name, content): + path = os.path.join(self.tmp, name) + with open(path, "w", encoding="utf-8") as f: + f.write(content) + return path + + # ------------------------------------------------------------------ + # Helper: run sm.process() and return the symbol table + # ------------------------------------------------------------------ + def process(self, sm): + return sm.process() + + # ------------------------------------------------------------------ + # Basic hierarchy tests + # ------------------------------------------------------------------ + + def test_basic_nested_package_declared(self): + """Declaring foo and foo.bar succeeds; both are in the stab.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("foo.rsl", "package foo\ntype Base {}\n")) + sm.register_rsl_file( + self.write("foo_bar.rsl", "package foo.bar\ntype Item {}\n") + ) + stab = self.process(sm) + self.assertIsNotNone(stab) + self.assertFalse(mh.has_error()) + foo = stab.lookup_assuming(mh, "foo", ast.Package) + self.assertIsNotNone(foo) + foo_bar = stab.lookup_assuming(mh, "foo.bar", ast.Package) + self.assertIsNotNone(foo_bar) + + def test_parent_linked_to_child(self): + """foo.bar.parent is foo, and foo.sub_packages contains foo.bar.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("foo.rsl", "package foo\ntype Base {}\n")) + sm.register_rsl_file( + self.write("foo_bar.rsl", "package foo.bar\ntype Item {}\n") + ) + stab = self.process(sm) + self.assertIsNotNone(stab) + foo = stab.lookup_assuming(mh, "foo", ast.Package) + foo_bar = stab.lookup_assuming(mh, "foo.bar", ast.Package) + self.assertIs(foo_bar.parent, foo) + child = foo.sub_packages.lookup_sub_package("bar") + self.assertIs(child, foo_bar) + + def test_deep_hierarchy(self): + """Three-level nesting a.b.c is supported.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("a.rsl", "package a\ntype Root {}\n")) + sm.register_rsl_file(self.write("a_b.rsl", "package a.b\ntype Mid {}\n")) + sm.register_rsl_file(self.write("a_b_c.rsl", "package a.b.c\ntype Leaf {}\n")) + stab = self.process(sm) + self.assertIsNotNone(stab) + self.assertFalse(mh.has_error()) + a = stab.lookup_assuming(mh, "a", ast.Package) + a_b = stab.lookup_assuming(mh, "a.b", ast.Package) + a_b_c = stab.lookup_assuming(mh, "a.b.c", ast.Package) + self.assertIs(a_b.parent, a) + self.assertIs(a_b_c.parent, a_b) + + # ------------------------------------------------------------------ + # Parent declaration required + # ------------------------------------------------------------------ + + def test_missing_parent_errors(self): + """Declaring foo.bar without declaring foo is an error.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("foo_bar.rsl", "package foo.bar\ntype T {}\n")) + stab = self.process(sm) + # Should produce an error about missing parent + self.assertTrue(mh.has_error()) + msgs = mh.error_messages() + self.assertTrue( + any("parent package foo" in m for m in msgs), + "Expected 'parent package foo' error, got: %s" % msgs, + ) + + # ------------------------------------------------------------------ + # Import and visibility + # ------------------------------------------------------------------ + + def test_import_nested_package(self): + """A .trlc file can import a nested package and use its types.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("ns.rsl", "package ns\ntype Dummy {}\n")) + sm.register_rsl_file( + self.write("ns_pkg.rsl", "package ns.pkg\ntype Req {\n x String\n}\n") + ) + sm.register_trlc_file( + self.write( + "data.trlc", + 'package data\nimport ns.pkg\nns.pkg.Req R_001 { x = "hello" }\n', + ) + ) + stab = self.process(sm) + self.assertIsNotNone(stab) + self.assertFalse(mh.has_error()) + + def test_unimported_nested_package_error(self): + """Using a nested package type without importing it is an error.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("foo.rsl", "package foo\ntype Base {}\n")) + sm.register_rsl_file( + self.write("foo_bar.rsl", "package foo.bar\ntype T {\n x String\n}\n") + ) + sm.register_trlc_file( + self.write("data.trlc", 'package data\nfoo.bar.T OBJ { x = "y" }\n') + ) + stab = self.process(sm) + self.assertTrue(mh.has_error()) + msgs = mh.error_messages() + self.assertTrue( + any("must be imported" in m for m in msgs), + "Expected 'must be imported' error, got: %s" % msgs, + ) + + def test_self_import_error(self): + """A package importing itself is an error.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("foo.rsl", "package foo\ntype Dummy {}\n")) + sm.register_rsl_file( + self.write( + "foo_bar.rsl", "package foo.bar\nimport foo.bar\ntype T { x String }\n" + ) + ) + stab = self.process(sm) + self.assertTrue(mh.has_error()) + msgs = mh.error_messages() + self.assertTrue( + any("cannot import itself" in m for m in msgs), + "Expected 'cannot import itself' error, got: %s" % msgs, + ) + + # ------------------------------------------------------------------ + # Name collision + # ------------------------------------------------------------------ + + def test_collision_between_nested_packages(self): + """foo.bar and Foo.Bar collide (sufficiently distinct check).""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("foo.rsl", "package foo\ntype D {}\n")) + sm.register_rsl_file(self.write("Foo.rsl", "package Foo\ntype D {}\n")) + sm.register_rsl_file(self.write("foo_bar.rsl", "package foo.bar\ntype T {}\n")) + sm.register_rsl_file(self.write("Foo_Bar.rsl", "package Foo.Bar\ntype T {}\n")) + stab = self.process(sm) + self.assertTrue(mh.has_error()) + + # ------------------------------------------------------------------ + # Wildcard imports (import foo.*) + # ------------------------------------------------------------------ + + def _warning_messages(self, mh): + return [m for _k, m in mh.messages] + + def test_wildcard_import_exposes_subtree(self): + """import com.shared.* exposes com.shared and its descendants.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("com.rsl", "package com\n")) + sm.register_rsl_file( + self.write("shared.rsl", "package com.shared\nenum U { a b }\n") + ) + sm.register_rsl_file( + self.write("si.rsl", "package com.shared.si\ntype F {\n v Integer\n}\n") + ) + sm.register_trlc_file( + self.write( + "data.trlc", + "package app\nimport com.shared.*\n" + "com.shared.si.F F_001 {\n v = 1\n}\n", + ) + ) + stab = self.process(sm) + self.assertIsNotNone(stab) + self.assertFalse(mh.has_error()) + + def test_wildcard_empty_subtree(self): + """import foo.* where foo has no children behaves like import foo.""" + sm, mh = make_source_manager() + sm.register_rsl_file( + self.write("foo.rsl", "package foo\ntype Req {\n x String\n}\n") + ) + sm.register_trlc_file( + self.write( + "data.trlc", + 'package app\nimport foo.*\nfoo.Req R_001 { x = "y" }\n', + ) + ) + stab = self.process(sm) + self.assertIsNotNone(stab) + self.assertFalse(mh.has_error()) + + def test_wildcard_undeclared_root_error(self): + """import missing.* where missing is not declared is an error.""" + sm, mh = make_source_manager() + sm.register_rsl_file( + self.write("app.rsl", "package app\nimport missing.*\ntype T {}\n") + ) + self.process(sm) + self.assertTrue(mh.has_error()) + + def test_wildcard_self_cover_allowed(self): + """A wildcard whose root covers the current package is permitted.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("foo.rsl", "package foo\n")) + sm.register_rsl_file( + self.write("foo_bar.rsl", "package foo.bar\nimport foo.*\ntype T {}\n") + ) + self.process(sm) + self.assertFalse( + any("cannot import itself" in m for m in self._warning_messages(mh)), + "Wildcard self-cover must not be a self-import error", + ) + + def test_wildcard_self_cover_no_self_cycle(self): + """A self-covering wildcard that is used must not create a spurious + circular dependency on the package itself.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("foo.rsl", "package foo\n")) + sm.register_rsl_file( + self.write("foo_baz.rsl", "package foo.baz\nenum Color { a b }\n") + ) + sm.register_rsl_file( + self.write( + "foo_bar.rsl", + "package foo.bar\nimport foo.*\ntype R {\n c foo.baz.Color\n}\n", + ) + ) + self.process(sm) + self.assertFalse( + any("circular" in m for m in mh.error_messages()), + "Self-covering wildcard must not produce a circular dependency, " + "got: %s" % mh.error_messages(), + ) + + def test_wildcard_unused_lint(self): + """An unused wildcard import is flagged by the linter.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("foo.rsl", "package foo\n")) + sm.register_rsl_file(self.write("sub.rsl", "package sub\ntype T {}\n")) + sm.register_rsl_file( + self.write("foobar.rsl", "package foo.bar\nimport sub.*\ntype R {}\n") + ) + self.process(sm) + self.assertTrue( + any( + "unused wildcard import sub.*" in m + for m in self._warning_messages(mh) + ), + "Expected unused wildcard warning, got: %s" + % self._warning_messages(mh), + ) + + def test_wildcard_redundant_lint(self): + """An explicit import covered by a wildcard is flagged as redundant.""" + sm, mh = make_source_manager() + sm.register_rsl_file(self.write("foo.rsl", "package foo\n")) + sm.register_rsl_file(self.write("sub.rsl", "package sub\n")) + sm.register_rsl_file( + self.write("subdeep.rsl", "package sub.deep\ntype U {\n v Integer\n}\n") + ) + sm.register_rsl_file( + self.write( + "foobar.rsl", + "package foo.bar\nimport sub.*\nimport sub.deep\n" + "type R {\n x sub.deep.U\n}\n", + ) + ) + self.process(sm) + self.assertTrue( + any("redundant import sub.deep" in m for m in self._warning_messages(mh)), + "Expected redundant import warning, got: %s" + % self._warning_messages(mh), + ) + + # ------------------------------------------------------------------ + # Sub-package vs member distinctness (S12) + # ------------------------------------------------------------------ + + def test_subpackage_member_collision(self): + """A sub-package leaf clashing with a parent member is an error.""" + sm, mh = make_source_manager() + sm.register_rsl_file( + self.write("foo.rsl", "package foo\ntype Bar {\n v Integer\n}\n") + ) + sm.register_rsl_file(self.write("foo_bar.rsl", "package foo.Bar\ntype W {}\n")) + self.process(sm) + self.assertTrue(mh.has_error()) + self.assertTrue( + any("clashes with a type or object" in m for m in mh.error_messages()), + "Expected sub-package/member clash error, got: %s" + % mh.error_messages(), + ) + + def test_shadow_resolve_type_and_subpackage(self): + """foo.T and foo.bar.T coexist and both resolve correctly.""" + sm, mh = make_source_manager() + sm.register_rsl_file( + self.write("foo.rsl", "package foo\ntype T {\n a Integer\n}\n") + ) + sm.register_rsl_file( + self.write("foo_bar.rsl", "package foo.bar\ntype T {\n b Integer\n}\n") + ) + sm.register_trlc_file( + self.write( + "data.trlc", + "package app\nimport foo\nimport foo.bar\n" + "foo.T X_001 {\n a = 1\n}\n" + "foo.bar.T Y_001 {\n b = 2\n}\n", + ) + ) + stab = self.process(sm) + self.assertIsNotNone(stab) + self.assertFalse(mh.has_error()) + + +class Test_Symbol_Table_Nested(unittest.TestCase): + """Tests for Symbol_Table methods added for nested packages.""" + + def setUp(self): + self.mh = List_Handler() + self.stab = ast.Symbol_Table.create_global_table(self.mh) + + def test_lookup_sub_package_returns_none_for_missing(self): + result = self.stab.lookup_sub_package("nonexistent") + self.assertIsNone(result) + + def test_register_with_key_and_lookup(self): + loc = Location("test.rsl") + pkg = ast.Package( + name="foo.bar", location=loc, builtin_stab=self.stab, declared_late=False + ) + # Register under leaf key "bar" + sub = ast.Symbol_Table() + sub.register_with_key(self.mh, pkg, "bar") + result = sub.lookup_sub_package("bar") + self.assertIs(result, pkg) + + def test_register_with_key_duplicate_raises(self): + loc = Location("test.rsl") + pkg1 = ast.Package( + name="foo.bar", location=loc, builtin_stab=self.stab, declared_late=False + ) + pkg2 = ast.Package( + name="foo.bar", location=loc, builtin_stab=self.stab, declared_late=False + ) + sub = ast.Symbol_Table() + sub.register_with_key(self.mh, pkg1, "bar") + with self.assertRaises(TRLC_Error): + sub.register_with_key(self.mh, pkg2, "bar") + + +if __name__ == "__main__": + unittest.main() diff --git a/trlc/ast.py b/trlc/ast.py index ff2acf98..0de53061 100644 --- a/trlc/ast.py +++ b/trlc/ast.py @@ -210,8 +210,17 @@ class Compilation_Unit(Node): :attribute package: the main package this file declares or contributes to :type: Package - :attribute imports: package imported by this file - :type: list[Package] + :attribute imports: packages explicitly imported by this file + :type: set[Package] + + :attribute wildcard_roots: roots of wildcard imports (``import foo.*``) + :type: set[Package] + + :attribute raw_imports: unresolved imports as (name, location, is_wildcard) + :type: list[tuple[str, Location, bool]] + + :attribute referenced_imports: imports actually used in this file + :type: set[Package] :attribute items: list of :type: list[Node] @@ -220,16 +229,20 @@ class Compilation_Unit(Node): def __init__(self, file_name): # lobster-exclude: Constructor only declares variables super().__init__(Location(file_name)) - self.package = None - self.imports = None - self.raw_imports = [] - self.items = [] + self.package = None + self.imports = None + self.wildcard_roots = set() + # list of (name : str, location : Location, is_wildcard : bool) + self.raw_imports = [] + self.referenced_imports = set() + self.items = [] def dump(self, indent=0): # pragma: no cover # lobster-exclude: Debugging feature self.write_indent(indent, f"Compilation_Unit ({self.location.file_name})") - for t_import in self.raw_imports: - self.write_indent(indent + 1, f"Import: {t_import.value}") + for name, _location, is_wildcard in self.raw_imports: + suffix = ".*" if is_wildcard else "" + self.write_indent(indent + 1, f"Import: {name}{suffix}") for n_item in self.items: n_item.dump(indent + 1) @@ -238,46 +251,87 @@ def set_package(self, pkg): assert isinstance(pkg, Package) self.package = pkg - def add_import(self, mh, t_import): + def add_import(self, mh, name, location, is_wildcard=False): # lobster-trace: LRM.Import_Visibility # lobster-trace: LRM.Self_Imports + # lobster-trace: LRM.Wildcard_Import + # lobster-trace: LRM.Wildcard_Self_Cover assert isinstance(mh, Message_Handler) - assert isinstance(t_import, Token) - assert t_import.kind == "IDENTIFIER" + assert isinstance(name, str) + assert isinstance(location, Location) + assert isinstance(is_wildcard, bool) - if t_import.value == self.package.name: - mh.error(t_import.location, + # An explicit self-import is an error. A wildcard whose root + # covers the current package is permitted: the current package + # stays implicitly visible (Wildcard_Self_Cover). + if name == self.package.name and not is_wildcard: + mh.error(location, "package %s cannot import itself" % self.package.name) - # Skip duplicates - for t_previous in self.raw_imports: - if t_previous.value == t_import.value: - mh.warning(t_import.location, - "duplicate import of package %s" % t_import.value) + # Skip duplicates (same name and same wildcard flavour) + for prev_name, _prev_location, prev_wildcard in self.raw_imports: + if prev_name == name and prev_wildcard == is_wildcard: + mh.warning(location, + "duplicate import of package %s%s" + % (name, ".*" if is_wildcard else "")) return - self.raw_imports.append(t_import) + self.raw_imports.append((name, location, is_wildcard)) def resolve_imports(self, mh, stab): # lobster-trace: LRM.Import_Visibility + # lobster-trace: LRM.Wildcard_Import assert isinstance(mh, Message_Handler) assert isinstance(stab, Symbol_Table) - self.imports = set() - for t_import in self.raw_imports: + self.imports = set() + self.wildcard_roots = set() + for name, location, is_wildcard in self.raw_imports: # We can ignore errors here, because that just means we # generate more error later. try: - a_import = stab.lookup(mh, t_import, Package) - self.imports.add(a_import) - a_import.set_ast_link(t_import) + a_import = stab.lookup_direct(mh, name, location, Package) except TRLC_Error: - pass + continue + if is_wildcard: + self.wildcard_roots.add(a_import) + else: + self.imports.add(a_import) + + def covered_by_wildcard(self, pkg): + # lobster-trace: LRM.Wildcard_Import + # A package is covered by a wildcard root if it is the root + # itself or any descendant of it. + assert isinstance(pkg, Package) + for root in self.wildcard_roots: + if pkg == root or pkg.name.startswith(root.name + "."): + return root + return None + + def mark_import_used(self, pkg): + """Record that a package was actually referenced in this file. + + Called during qualified-name resolution so the lint pass can + detect unused imports. If the package is visible through a + wildcard import, the wildcard root is marked used as well. + + :param pkg: the package that was used + :type pkg: Package + """ + assert isinstance(pkg, Package) + self.referenced_imports.add(pkg) + root = self.covered_by_wildcard(pkg) + if root is not None: + self.referenced_imports.add(root) def is_visible(self, n_pkg): # lobster-trace: LRM.Import_Visibility + # lobster-trace: LRM.Wildcard_Import + # lobster-trace: LRM.Nested_Visibility assert self.imports is not None assert isinstance(n_pkg, Package) - return n_pkg == self.package or n_pkg in self.imports + if n_pkg == self.package or n_pkg in self.imports: + return True + return self.covered_by_wildcard(n_pkg) is not None def add_item(self, node): # lobster-trace: LRM.RSL_File @@ -2762,6 +2816,12 @@ class Package(Entity): :attribute symbols: symbol table of the package :type: Symbol_Table + :attribute sub_packages: direct child packages of this package + :type: Symbol_Table + + :attribute parent: the parent package, or None for top-level packages + :type: Package + """ def __init__(self, name, location, builtin_stab, declared_late): # lobster-exclude: Constructor only declares variables @@ -2771,6 +2831,8 @@ def __init__(self, name, location, builtin_stab, declared_late): self.symbols = Symbol_Table() self.symbols.make_visible(builtin_stab) self.declared_late = declared_late + self.sub_packages = Symbol_Table() + self.parent = None def dump(self, indent=0): # pragma: no cover # lobster-exclude: Debugging feature @@ -3502,6 +3564,65 @@ def register(self, mh, entity): else: self.table[simple_name] = entity + def register_with_key(self, mh, entity, key_name): + """Register an entity under a different key name (leaf name). + + Used to register nested packages in their parent's ``sub_packages`` + table keyed by their leaf segment rather than their full name. + + :param mh: The message handler to use + :type mh: Message_Handler + + :param entity: the entity to register + :type entity: Entity + + :param key_name: the name to use as the lookup key + :type key_name: str + """ + # lobster-trace: LRM.Sufficiently_Distinct + assert isinstance(mh, Message_Handler) + assert isinstance(entity, Entity) + assert isinstance(key_name, str) + + simple_key = self.simplified_name(key_name) + + if simple_key in self.table: + existing = self.table[simple_key] + if existing.name == entity.name: + mh.error(entity.location, + "duplicate definition, previous definition at %s" % + mh.cross_file_reference(existing.location)) + else: + mh.error(entity.location, + "%s is too similar to %s, declared at %s" % + (entity.name, + existing.name, + mh.cross_file_reference(existing.location))) + else: + self.table[simple_key] = entity + + def lookup_sub_package(self, segment): + """Look up a direct child package by its leaf segment name. + + Used for traversing the nested package hierarchy, where the table + key is the simplified leaf segment but the entity name is the full + dotted package name. + + :param segment: the leaf name to look for (e.g. ``"bar"`` for \ + ``foo.bar``) + :type segment: str + + :returns: the child Package, or None if not found + :rtype: Package or None + """ + assert isinstance(segment, str) + simple_key = self.simplified_name(segment) + if simple_key in self.table: + entity = self.table[simple_key] + if isinstance(entity, Package): + return entity + return None + def __contains__(self, name): # lobster-trace: LRM.Described_Name_Equality return self.contains(name) diff --git a/trlc/lint.py b/trlc/lint.py index 9002cb86..f23ff923 100644 --- a/trlc/lint.py +++ b/trlc/lint.py @@ -221,34 +221,104 @@ def verify_array_type(self, n_typ): "be an optional %s instead." % n_typ.element_type.name) - def markup_ref(self, item, string_literals): - for string_literal in string_literals: - for reference in string_literal.references: - if reference.package.name == item.name: - return string_literal + def _find_import_location(self, cu, name, is_wildcard): + # lobster-exclude: Utility function + for raw_name, raw_loc, raw_wildcard in cu.raw_imports: + if raw_name == name and raw_wildcard == is_wildcard: + return raw_loc return None + def _import_in_markup(self, file, item, include_descendants = False): + # lobster-exclude: Utility function + # A package referenced in a markup string counts as used. For a + # wildcard root, a reference to any descendant package also counts. + for token in file.lexer.tokens: + literal = token.ast_link + if not isinstance(literal, ast.String_Literal): + continue + if not literal.has_references: + continue + for reference in literal.references: + if reference.package.name == item.name: + return True + if include_descendants and \ + reference.package.name.startswith(item.name + "."): + return True + return False + def verify_imports(self): + # lobster-trace: LRM.Wildcard_Import for file in self.mh.sm.all_files.values(): if not file.primary and not file.secondary: continue - if not file.cu.imports: + cu = file.cu + if not cu.imports and not cu.wildcard_roots: continue - for item in file.cu.imports: - import_tokens = [t for t in file.lexer.tokens - if t.value == item.name] - markup = self.markup_ref(item, - (m.ast_link for m in - file.lexer.tokens if - isinstance(m.ast_link, - ast.String_Literal) and - m.ast_link.has_references)) - if markup is not None: - import_tokens.append(markup) - if len(import_tokens) == 1: - import_tk = import_tokens[0] - self.mh.check(import_tk.location, - "unused import %s" % import_tk.value, - "unused_imports", - "Consider deleting this import" - " statement if not needed.") + + # Redundant explicit imports already covered by a wildcard import. + for item in cu.imports: + root = cu.covered_by_wildcard(item) + if root is None: + continue + imp_location = self._find_import_location(cu, item.name, False) + if imp_location is not None: + self.mh.check(imp_location, + "redundant import %s, already covered by" + " wildcard import %s.*" + % (item.name, root.name), + "unused_imports", + "Consider deleting this import statement.") + + # Unused explicit imports. + for item in cu.imports: + if item in cu.referenced_imports: + continue + # Skip the ones already reported as redundant above. + if cu.covered_by_wildcard(item) is not None: + continue + if self._import_in_markup(file, item): + continue + imp_location = self._find_import_location(cu, item.name, False) + if imp_location is not None: + self.mh.check(imp_location, + "unused import %s" % item.name, + "unused_imports", + "Consider deleting this import statement if" + " not needed.") + + # Unused wildcard imports (no package in the subtree referenced). + for root in cu.wildcard_roots: + if root in cu.referenced_imports: + continue + if self._import_in_markup(file, root, + include_descendants = True): + continue + imp_location = self._find_import_location(cu, root.name, True) + if imp_location is not None: + self.mh.check(imp_location, + "unused wildcard import %s.*" % root.name, + "unused_imports", + "Consider deleting this import statement if" + " not needed.") + + # Trivial wildcard imports (root has no sub-packages). + # lobster-trace: LRM.Wildcard_Trivial + for root in cu.wildcard_roots: + if not root.sub_packages.table: + # Skip if already reported as unused (deleting is better + # advice than replacing). + if root not in cu.referenced_imports: + continue + if self._import_in_markup(file, root, + include_descendants = True): + continue + imp_location = self._find_import_location( + cu, root.name, True) + if imp_location is not None: + self.mh.check( + imp_location, + "wildcard import %s.* is equivalent to import %s" + " since %s has no sub-packages" + % (root.name, root.name, root.name), + "unused_imports", + "Consider replacing with: import %s" % root.name) diff --git a/trlc/parser.py b/trlc/parser.py index 5c75a4ce..065a553e 100644 --- a/trlc/parser.py +++ b/trlc/parser.py @@ -346,6 +346,96 @@ def parse_described_name(self): else: return name, None, None + def parse_dotted_name(self): + # lobster-trace: LRM.Nested_Package_Names + # Consume IDENTIFIER { '.' IDENTIFIER } and return the full dotted + # name, the location of the first segment, and all tokens consumed. + self.match("IDENTIFIER") + parts = [self.ct.value] + first_location = self.ct.location + tokens = [self.ct] + + while self.peek("DOT"): + self.match("DOT") + tokens.append(self.ct) + self.match("IDENTIFIER") + parts.append(self.ct.value) + tokens.append(self.ct) + + return ".".join(parts), first_location, tokens + + def parse_import_name(self): + """Parse an import target: a dotted package name with an optional + trailing ``.*`` wildcard. + + Consumes ``IDENTIFIER { '.' IDENTIFIER } [ '.' '*' ]``. The ``*`` + lexes as an ``OPERATOR`` token with value ``"*"``. + + :returns: ``(full_name, first_location, tokens, is_wildcard)`` + :rtype: tuple[str, Location, list[Token], bool] + """ + # lobster-trace: LRM.Wildcard_Import + # lobster-trace: LRM.Nested_Package_Names + self.match("IDENTIFIER") + parts = [self.ct.value] + first_location = self.ct.location + tokens = [self.ct] + is_wildcard = False + + while self.peek("DOT"): + self.match("DOT") + t_dot = self.ct + if self.peek("OPERATOR") and self.nt.value == "*": + self.match("OPERATOR") + tokens.append(t_dot) + tokens.append(self.ct) + is_wildcard = True + break + tokens.append(t_dot) + self.match("IDENTIFIER") + parts.append(self.ct.value) + tokens.append(self.ct) + + return ".".join(parts), first_location, tokens, is_wildcard + + def descend_sub_packages(self, pkg, t_pkg): + # lobster-trace: LRM.Qualified_Name + # lobster-trace: LRM.Nested_Visibility + # Greedily consume `. segment` pairs, descending into sub-packages + # for as long as a segment names one. The first segment that is not + # a sub-package is the member name; we check that the package it + # belongs to is visible and mark the import used. Returns + # (leaf_package, t_member). If the dotted name ends on a package the + # trailing token (self.ct) is returned as the member, so the caller + # produces a sensible "unknown symbol" error. + # + # t_pkg is the token that named the current pkg; it is used as the + # location for the "package must be imported" error so the caret + # points at the offending package rather than the member. + while self.peek("DOT"): + self.match("DOT") + t_dot = self.ct + self.match("IDENTIFIER") + t_member = self.ct + child = pkg.sub_packages.lookup_sub_package(t_member.value) + if child is None: + pkg.set_ast_link(t_dot) + if not self.cu.is_visible(pkg): + self.mh.error(t_pkg.location, + "package must be imported before use") + self.cu.mark_import_used(pkg) + return pkg, t_member + child.set_ast_link(t_dot) + child.set_ast_link(t_member) + pkg = child + t_pkg = t_member + + if not self.cu.is_visible(pkg): + self.mh.error(t_pkg.location, + "package must be imported before use") + self.cu.mark_import_used(pkg) + return pkg, self.ct + def parse_qualified_name(self, scope, required_subclass=None, @@ -363,13 +453,8 @@ def parse_qualified_name(self, sym.set_ast_link(self.ct) if isinstance(sym, ast.Package): - if not self.cu.is_visible(sym): - self.mh.error(self.ct.location, - "package must be imported before use") - self.match("DOT") - sym.set_ast_link(self.ct) - self.match("IDENTIFIER") - return sym.symbols.lookup(self.mh, self.ct, required_subclass) + pkg, t_member = self.descend_sub_packages(sym, self.ct) + return pkg.symbols.lookup(self.mh, t_member, required_subclass) else: # Easiest way to generate the correct error message return scope.lookup(self.mh, self.ct, required_subclass) @@ -1793,16 +1878,11 @@ def parse_value(self, typ): self.match("IDENTIFIER") t_name = self.ct if self.peek("DOT"): - self.match("DOT") - t_dot = self.ct - self.match("IDENTIFIER") + # Parse a (potentially nested) package prefix followed by + # the object name via a greedy sub-package descent. the_pkg = self.stab.lookup(self.mh, t_name, ast.Package) the_pkg.set_ast_link(t_name) - the_pkg.set_ast_link(t_dot) - if not self.cu.is_visible(the_pkg): - self.mh.error(self.ct.location, - "package must be imported before use") - t_name = self.ct + the_pkg, t_name = self.descend_sub_packages(the_pkg, t_name) else: the_pkg = self.cu.package @@ -1969,26 +2049,28 @@ def parse_preamble(self, kind): # needed self.match_kw("package") t_pkg = self.ct - self.match("IDENTIFIER") + pkg_name, pkg_location, pkg_tokens = self.parse_dotted_name() if kind == "rsl": declare_package = True else: # lobster-trace: LRM.Late_Package_Declarations - declare_package = not self.stab.contains(self.ct.value) + declare_package = not self.stab.contains(pkg_name) if declare_package: # lobster-trace: LRM.Package_Declaration - pkg = ast.Package(name = self.ct.value, - location = self.ct.location, + pkg = ast.Package(name = pkg_name, + location = pkg_location, builtin_stab = self.stab, declared_late = kind == "trlc") self.stab.register(self.mh, pkg) else: - pkg = self.stab.lookup(self.mh, self.ct, ast.Package) + pkg = self.stab.lookup_direct(self.mh, pkg_name, pkg_location, + ast.Package) pkg.set_ast_link(t_pkg) - pkg.set_ast_link(self.ct) + for t in pkg_tokens: + pkg.set_ast_link(t) # lobster-trace: LRM.Current_Package self.cu.set_package(pkg) @@ -2000,9 +2082,14 @@ def parse_preamble(self, kind): if kind != "check": while self.peek_kw("import"): self.match_kw("import") - pkg.set_ast_link(self.ct) - self.match("IDENTIFIER") - self.cu.add_import(self.mh, self.ct) + t_import_kw = self.ct + imp_name, imp_location, imp_tokens, imp_wildcard = \ + self.parse_import_name() + pkg.set_ast_link(t_import_kw) + for t in imp_tokens: + pkg.set_ast_link(t) + self.cu.add_import(self.mh, imp_name, imp_location, + imp_wildcard) def parse_rsl_file(self): # lobster-trace: LRM.RSL_File diff --git a/trlc/trlc.py b/trlc/trlc.py index 5d7f5dd8..e280e6a6 100644 --- a/trlc/trlc.py +++ b/trlc/trlc.py @@ -347,6 +347,58 @@ def build_graph(self): {(imported_pkg.name , kind) for imported_pkg in parser.cu.imports} + # A wildcard import depends on the whole subtree rooted at + # the wildcard root, so the file-load closure pulls in every + # descendant package. The current package is excluded: a + # wildcard whose root covers the current package (see + # LRM.Wildcard_Self_Cover) must not make the package depend + # on itself, which would be reported as a spurious cycle. + # lobster-trace: LRM.Wildcard_Import + # lobster-trace: LRM.Wildcard_Self_Cover + if parser.cu.wildcard_roots: + for root in parser.cu.wildcard_roots: + for other in self.stab.values(ast.Package): + if other.name == pkg_name: + continue + if other.name == root.name or \ + other.name.startswith(root.name + "."): + graph[(pkg_name , kind)].add((other.name , kind)) + + # Build the package hierarchy for nested packages. Parents are + # always registered already (in the flat global table), so the + # iteration order does not matter; we sort by name purely for + # deterministic error output. + # lobster-trace: LRM.Parent_Package_Required + nested_packages = sorted( + (pkg for pkg in self.stab.values(ast.Package) if "." in pkg.name), + key=lambda p: p.name) + for pkg in nested_packages: + parent_name = pkg.name.rsplit(".", 1)[0] + leaf_name = pkg.name.rsplit(".", 1)[1] + parent_pkg = self.stab.lookup_sub_package(parent_name) + if not isinstance(parent_pkg, ast.Package): + ok = False + self.mh.error( + location = pkg.location, + message = ("parent package %s of nested package %s" + " has not been declared" + % (parent_name, pkg.name)), + fatal = False) + continue + try: + parent_pkg.sub_packages.register_with_key(self.mh, + pkg, + leaf_name) + except TRLC_Error: + ok = False + continue + pkg.parent = parent_pkg + # Add an implicit dependency: foo.bar rsl depends on foo rsl + for kind in ("rsl", "trlc"): + node = (pkg.name, kind) + if node in graph: + graph[node].add((parent_pkg.name, "rsl")) + # Build closure for our files work_list = {(parser.cu.package.name , "rsl") for parser in self.rsl_files.values() @@ -404,10 +456,10 @@ def parse_rsl_files(self) -> bool: offender = rsl_map[sorted_work_list[0]] names = {rsl_map[node].cu.package.name: rsl_map[node].cu.location - for node in sorted_work_list[1:]} + for node in sorted_work_list} self.mh.error( location = offender.cu.location, - message = ("circular inheritence between %s" % + message = ("circular inheritance between %s" % " | ".join(sorted(names))), explanation = "\n".join( sorted("%s is declared in %s" % @@ -465,6 +517,33 @@ def resolve_record_references(self) -> bool: return ok + def verify_subpackage_distinctness(self) -> bool: + """Check that sub-package leaf names are sufficiently distinct from + types or objects declared in the same parent package. + + Must run after RSL parsing, once every package's ``symbols`` table + is fully populated. Without this, the greedy qualified-name descent + (which consults ``sub_packages`` first) would silently shadow a + same-named member. + + :rtype: bool + """ + # lobster-trace: LRM.Subpackage_Member_Distinct + ok = True + for pkg in self.stab.values(ast.Package): + for child in pkg.sub_packages.table.values(): + leaf_name = child.name.rsplit(".", 1)[1] + simple_leaf = pkg.symbols.simplified_name(leaf_name) + if pkg.symbols.contains_raw(simple_leaf): + ok = False + self.mh.error( + location = child.location, + message = ("sub-package %s clashes with a type or" + " object of the same name in package %s" + % (leaf_name, pkg.name)), + fatal = False) + return ok + def perform_checks(self) -> bool: # lobster-trace: LRM.Order_Of_Evaluation_Unordered ok = True @@ -498,6 +577,10 @@ def process(self): # dependencies) ok &= self.parse_rsl_files() + # Now that all type declarations are known, check that sub-package + # names do not clash with members of their parent package. + ok &= self.verify_subpackage_distinctness() + if not self.error_recovery and not ok: # pragma: no cover self.callback_parse_end() return None