@@ -87,41 +87,46 @@ part file a problem.
87
87
Pre-feature part files inherit the entire import scope from the library file.
88
88
Each declaration of the library file and each part file is included in the
89
89
library’s declaration scope. It’s viable to think of part files as being
90
- textually included in the library file. There is a is even a rule against
90
+ textually included in the library file. There is even a rule against
91
91
declaring a ` part ` inclusion of the same file more than once, which matches
92
92
perfectly with that way of thinking.
93
93
94
94
## Feature
95
95
96
- This feature allows a part file to have ` import ` , ` export ` and ` part `
97
- directives of its own, where ` import ` directives only affect the part file
98
- itself, and its transitive part files. A library is defined by the source code
99
- of its library file and * all* transitively included part files, which can be an
100
- arbitrarily deep * tree* . A part file inherits the imports and import prefixes
101
- of its parent file (the library or part file that included it) into its
102
- top-level scope, but can choose to ignore or shadow those using its own
103
- imports.
96
+ This feature allows a part file to have ` import ` , ` export ` and ` part ` directives
97
+ of its own, where ` import ` directives only affect the part file itself, and its
98
+ transitive part files. A library is defined by the source code of its library
99
+ file and * all* transitively included part files, which can be an arbitrarily
100
+ deep * tree* . A part file inherits the imported declarations and import prefixes
101
+ of all its transitive parent files (the library or part files that included it),
102
+ but can choose to ignore or shadow those using its own imports.
104
103
105
104
The design goals and principles are:
106
105
107
106
* * Backwards compatible* : If a part file has no ` import ` , ` export ` or ` part `
108
107
directive, it works just like it always has.
109
108
* Because of that, it’s always safe to move one or more declarations into
110
- a new part file. _ (Although augmentation declarations modifies that
111
- slightly.)_
109
+ a new part file.
112
110
* Similarly it’s always possible and safe to combine a part file with no
113
111
` import ` s back into its parent file.
114
- * * Library member declarations are library-global* : All top-level
115
- declarations in the library file and all transitive part files are equal,
116
- and are all in scope in every file. They introduce declarations into the
117
- library’s declaration scope, which is the most significant scope in all
118
- files of the library. If there is any conflict, top-level declarations win!
112
+
113
+ _ (Augmentations modify both of these properties slightly, because order of
114
+ declarations also matter.)_
115
+
116
+ * * Library member declarations are library-global* : All top-level declarations
117
+ in the library file and all transitive part files are equal, and are all in
118
+ scope in every file. They introduce declarations into the library’s
119
+ declaration scope, which is the most significant scope in all files of the
120
+ library. If there is any conflict with imported names, top-level
121
+ declarations win!
122
+
119
123
* * The unit of ownership is the library* . It’s quite possible for one part
120
124
file to introduce a conflict with another part file. It always was, but
121
125
there are new ways too. If that happens, the library owner, who most likely
122
126
introduced the problem, is expected to fix it. There is no attempt to hide
123
127
name conflicts between declarations in separate tree-branches of the
124
128
library structure.
129
+
125
130
* * Import inheritance is a only suggestion* : Aka. other files’ imports cannot
126
131
break your code (at least if you’re not depending on them). A part file is
127
132
never restricted by the imports it inherits from its parent file. It can
@@ -132,6 +137,17 @@ The design goals and principles are:
132
137
why a macro should document any non-fresh names it introduces, so a library
133
138
using the macro can rename any declarations that would conflict._
134
139
140
+ * Because of that, it’s possible to convert an existing library into a
141
+ part file of another library. Since a library is self-contained and
142
+ imports all external names that it refers to, making it a part file will
143
+ not cause any conflict due to inherited imports. _ (Obviously still need
144
+ to avoid conflicts with top-level declarations.)_
145
+ * And similarly, if a part file * is* self-contained, it can be converted
146
+ into a separate library and imported back into the original library, or
147
+ it can be moved to another position in the part tree hierarchy. _ (Again
148
+ augmentations introduce complications, which is why it’s usually a good
149
+ idea to keep all augmentations inside the same part sub-tree).
150
+
135
151
### Grammar
136
152
137
153
We extend the grammar of part files to allow ` import ` , ` export ` and ` part ` file
@@ -147,7 +163,7 @@ other two. We restrict the `part of` directive to only allow the string version.
147
163
148
164
-- Added "<importOrExport>* <partDirective>*"
149
165
<partDeclaration> ::=
150
- <partHeader> <importOrExport>* <partDiretive >* (<metadata>
166
+ <partHeader> <importOrExport>* <partDirective >* (<metadata>
151
167
<topLevelDeclaration>)* <EOF>
152
168
```
153
169
@@ -398,54 +414,115 @@ In short:
398
414
399
415
## Language versioning and tooling
400
416
417
+ Dart language versioning is an extra-linguistic feature which allows the SDK
418
+ tooling to compile programs containing libraries written for different versions
419
+ of the language. As such, the language semantics (usually) do not refer to
420
+ language versions at all.
421
+
422
+ Similarly the language has no notion of a “package”, but tooling does consider
423
+ Dart files to belong to (at most) one package, and some of the files as
424
+ “having a ` package: ` URI”. This information is written into a metadata file,
425
+ ` package_config.json ` that is also used to resolve ` package: ` URIs, and to
426
+ assign default language versions to files that belong to a package.
427
+
428
+ Because of that, the restrictions in this section are not * language* rules,
429
+ instead they are restrictions enforced by the * tooling* in order to allow
430
+ multi-language-version programs to be compiled.
431
+
432
+ ### Pre-feature code interaction and migration
433
+
401
434
This feature is language versioned, so no existing code is affected at launch.
402
435
436
+ The only non-backwards compatible change is to disallow ` part of dotted.name; ` .
437
+ That use has been discouraged by the
438
+ [ ` use_string_in_part_of_directives ` ] [ string_part_of_lint ] lint, which was
439
+ introduced with Dart 2.19 in January 2023, and has been part of the official
440
+ “core” Dart lints since June 2023. The “core” lints are enforced more strongly
441
+ than the “recommended” lints, including counting against Pub score, so any
442
+ published code has had incentive to satisfy the lint.
443
+
444
+ The lint has a quick-fix, so migration can be achieved by enabling the lint
445
+ (directly, or by including the “recommended” or “core” lint sets, which is
446
+ already itself recommended practice) and running ` dart fix ` , which will change
447
+ any ` part of dotted.name; ` to the future-safer ` part of 'parent_file.dart'; ` .
448
+
449
+ All in all, there is very little expected migration since all actively
450
+ developed code, which is expected to use and follow recommended or core lints,
451
+ will already be compatible.
452
+
453
+ [ string_part_of_lint ] : https://dart.dev/tools/linter-rules/use_string_in_part_of_directives " use_string_in_part_of_directives lint "
454
+
455
+ We will enforce a set of rules that weren’t as clearly defined before
456
+ (see next section), and therefore maybe not strictly enforced, so there is a
457
+ risk that some pathologically designed library may break one of those rules.
458
+ Other than that, a pre-feature library can be used as post-feature library as
459
+ long as it satisfies these very reasonable rules.
460
+
403
461
The feature has no effect at the library boundary level, meaning the export
404
462
scope of a library, so pre-feature and post-feature libraries can safely
405
- coexist.
406
-
407
- As with pre-feature libraries, all files in a library must have the same
408
- associated * language version* . If any file has a language-version override
409
- marker (a line like ` // @dart=3.12 ` before any Dart code), then * every file* in
410
- the library * must* have a language override marker. _ (And they must still have
411
- the same language version, so it must be the same marker.)_
412
-
413
- Also, every file in a library must belong to the same * package* . The Dart
414
- language itself has no notion of packages, but the tooling uses a file’s
415
- package to derive its default language version. The Dart SDK will require that
416
- all files in a package belong to the same library, ensuring that they’ll always
417
- have the same language version. Also, if the library file is inside the ` lib/ `
418
- directory, so it has a ` package: ` URI as canonical URI, then so must all its
419
- sub-part files. We haven’t specified these requirements before,
420
- it has always been assumed, but technically it is possible to write programs
421
- where a part belongs to different package than their library. The Dart SDK’s
422
- multi-language-version support, which based on files belonging to packages,
423
- will not support libraries that are not entirely in a single package.
424
-
425
- That is, the extra restrictions enforced by Dart tools are:
426
-
427
- * If any file of a library has a ` // @dart= ` language version marker, then
428
- * all* files in that library must have a language version marker with
429
- * the same* language version.
430
- Or, phrased differently starting at the root:
431
- * If a library file has a language version marker,
432
- then it’s a compile-time error for any sub-part file
433
- to not have the same language version marker.
434
- * If a library file has no language version marker,
435
- then it’s a compile-time error for any sub-part file
436
- to have a language version marker.
437
-
438
- * If a library file belongs to a package,
439
- then all its sub-part files must belong to the same package.
440
- * If a library file has a ` package: ` URI (is located inside the package-URI
441
- directory specified by the package configuration, usually
442
- ` "packageUri": "lib/" ` of a ` package_config.json ` file),
443
- then all its sub-part files must also be inside that package-URI directory.
444
- * _ A library file outside of the ` lib/ ` directory can technically have
445
- part files inside the ` lib/ ` directory, specified using ` package: `
446
- URIs, but those part files can only be included in a program by
447
- referencing the library file, which means the part files might as well
448
- be placed outside of the ` lib/ ` directory._
463
+ coexist. A library can start using the feature without any effect on client
464
+ libraries. There is no need to worry about migration order.
465
+
466
+ ### Explicit sanity rules
467
+
468
+ The following rules are rules enforced by tooling, not the language, since they
469
+ rely on features that are not part of the language (files having a language
470
+ version, a language version marker, or belonging to a package).
471
+
472
+ All pre-feature libraries should already be following these rules, which exist
473
+ mainly ensure that different files of a library will * always* have the same
474
+ language versions. _ Some of these rules have not all been expressed explicitly
475
+ before, because they are considered blindingly obvious. We’re making them
476
+ explicit here, and will enforce the rules strictly for post-feature code, if we
477
+ didn’t already._
478
+
479
+ * It’s a ** compile-time error** if two Dart files of a library do not have the
480
+ same language version._ All Dart files in a library must have the same
481
+ language version._ Can be expressed locally as:
482
+ * It’s a compile-time error if the associated language version of a part
483
+ file is not the same as the language version of its parent file.
484
+
485
+ * It’s a ** compile-time error** if any file of a library has a
486
+ language-version override marker (a line like ` // @dart=3.12 ` before any
487
+ Dart code), and any * other* file of the same library does not have a
488
+ language-version override marker. _ While it’s still possible for that
489
+ library to currently have the same language version across all files, that
490
+ won’t stay true if the default language version for the package changes._
491
+ Can be expressed locally as:
492
+
493
+ * If a part file has a language version marker, then it’s a compile-time
494
+ error if its parent files does not have a language version marker. _ The
495
+ version marker it has must be for the same version due to the previous
496
+ rule._
497
+
498
+ * If a part file has no language version marker, then it’s a compile-time
499
+ error if its parent file has a language version marker.
500
+
501
+ * It’s a ** compile-time error** if two Dart files of a library do not belong
502
+ to the same package. _ Every file in a library must belong to the same
503
+ package to ensure that they always have the same default language version.
504
+ It’s also likely to break a lot of assumptions if they don’t._ Can be
505
+ expressed locally as:
506
+
507
+ * It’s a compile-time error if a part file does not belong to the same
508
+ package as its parent file.
509
+
510
+ The Dart SDK’s multi-language-version support, which based on files
511
+ belonging to packages, will not support libraries that are not entirely in a
512
+ single package.
513
+
514
+ * We * may* want to also make it a ** compile-time** error if two Dart files of
515
+ a library are not both inside the ` lib/ ` directory or both outside of it.
516
+ _ Having a parent file inside ` lib/ ` with a part file outside will not
517
+ compile if the parent file is accessed using a ` package: ` URI. Having a
518
+ parent file outside of ` lib/ ` with a part file inside works, but the part
519
+ file might as well be outside since the only way to use it is to go through
520
+ the parent file._ The only reason to maybe not enforce this rule would be a
521
+ file inside ` lib/ ` that is * never* accessed using a` package: ` URI, and which
522
+ depends on files outside of ` lib/ ` for something. If some frameworks do
523
+ that, maybe a Flutter ` main ` file, then we should just keep giving warnings
524
+ about the pattern. The ` lib/ ` directory should be self-contained because all
525
+ libraries in it can be accessed by other packages, and no other files can.
449
526
450
527
### User guidance tooling
451
528
@@ -466,24 +543,35 @@ can be applied.
466
543
467
544
It may very well be that annotations affecting declarations (which is typically
468
545
what annotations on library declarations do) have no benefit from being limited
469
- based on something as (so far) semantically arbitrary as source ordering.
546
+ based on something as (so far) semantically arbitrary as source ordering. But on
547
+ the other hand, users may choose to order source depending on properties that
548
+ annotations apply to. _ The analyzer may want to review annotations that apply to
549
+ a library for whether they can reasonably apply to any sub-tree of parts. For
550
+ example ` @Deprecated(…) ` could apply to every member in a sub-tree, allowing a
551
+ library to keep its deprecated API, and its necessary imports, separate from the
552
+ rest, so that it can all be removed as a single operation, and then marking all
553
+ that API as deprecated with one annotation._
470
554
471
555
##### An ` // ignore ` applying to a sub-tree
472
556
473
557
The analyzer recognizes ` // ignore: … ` comments as applying to the same or next
474
- line. For ignoring multiple warnings, there is a ` // ignore_for_file: … `
475
- comment which covers the entire file.
558
+ line. For ignoring multiple warnings, there is a ` // ignore_for_file: … ` comment
559
+ which covers the entire file. There is no ` ignore_for_library ` that would apply
560
+ to the entire library, including parts.
476
561
477
562
It can be considered whether to have an ` // ignore_for_all_files: … ` (or a
478
- better name) which applies to an entire subtree, not just the current file.
563
+ better name) which applies to an entire sub-tree, not just the current file, and
564
+ not the entire library. It would apply to the entire library if applied to the
565
+ library file.
479
566
480
567
It may very well be better to * not* that, and have each sub-part write its own
481
568
` // ignore_for_file: ... ` . That makes it very easy to see which ignores are in
482
569
effect for a file.
483
570
484
571
##### Invalid part file structure correction
485
572
486
- When analyzing an incomplete or invalid Dart program, any and all of the compile-time errors above may apply.
573
+ When analyzing an incomplete or invalid Dart program, any and all of the
574
+ compile-time errors above may apply.
487
575
488
576
It’s possible to have part files with parent-file cycles, part files with a
489
577
parent URI which doesn’t denote any existing file, or files with a ` part `
0 commit comments