Skip to content

With "Unquoted Imports" disallow unquoted part of library names. #4262

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 13, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 105 additions & 83 deletions accepted/future-releases/unquoted-imports/feature-specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Author: Bob Nystrom

Status: Accepted

Version 0.4 (see [CHANGELOG](#CHANGELOG) at end)
Version 0.5 (see [CHANGELOG](#CHANGELOG) at end)

Experiment flag: unquoted-imports

Expand Down Expand Up @@ -341,20 +341,80 @@ after the directives outside of the path. These are all valid:

```dart
import /* Weird but OK. */ some/path;
export some/path; // Hi there.
part some/path // Before the semicolon? Really?
import some/path; // Hi there.
export some/path // Before the semicolon? Really?
;
```

The syntax that results from the above few sections is simple to tokenize and
parse while looking like a single opaque "unquoted string" to users and tools.

### Part of directives

In Dart today, `part of` directives can already contain unquoted dotted
identifiers, like:

```dart
// some_lib.dart
library some.lib;

part 'some_part.dart';

// some_part.dart
part of some.lib;
```

This is a legacy syntax from when library names were more widely used in Dart.
Library names in `part of` directives have been deprecated for many years
because the syntax doesn't work well with many tools. How is a given tool
supposed to know where to find the library that happens to contain a `library`
directive with that name? The quoted URI syntax was added later specifically to
address that point and users are encouraged by documentation and lints to use
the quoted syntax.

Looking at a corpus of 122,420 files:

```
-- Directive (443733 total) --
352744 ( 79.495%): import =========================================
55471 ( 12.501%): export =======
17823 ( 4.017%): part ===
17695 ( 3.988%): part of ===
```

So `part of` directives are fairly rare to begin with. Of them, most use the
recommended URI syntax and would not be affected by this change:

```
-- Part of (17695 total) --
13229 ( 74.761%): uri ===================================
4466 ( 25.239%): library name ============
```

If we reinterpreted that existing syntax to refer to another package instead of
a library within the current package, user breakage is likely and would be very
confusing.

To avoid potential user confusion and breakage, the new syntax in this proposal
isn't allowed in `part or` or `part` directives. We disallow it in `part of`
to avoid confusion with the legacy syntax, and we disallow it in `part` to be
symmetric with `part of`.

Further, we make a **language-versioned breaking change** and remove support for
unquoted library identifiers in `part of` directives completely. That way, a
user never sees syntax in a `part of` file that *looks* like a package path but
means something else.

*Since part files should realistically always be part of the same package that
contains the library that owns them, the new unquoted syntax is of limited use
for part files anyway. Quoted relative URIs are shorter and more idiomatic.*

## Syntax

The normative stuff starts now. Here is the proposal:

We add a new rule and hang it off the existing `uri` rule already used by import
and export directives:
We replace the existing `uri` rule with the following rule along with a couple
of helper rules:

```
uri ::= stringLiteral | packagePath
Expand All @@ -371,22 +431,33 @@ between any of the `segmentComponent`, `/`, or `.` tokens in a `packagePath`.
*In other words, there can be nothing except the terminals themselves from the
first `segmentComponent` in the `packagePath` to the last.*

*An import, export, or part directive can continue to use a `stringLiteral` for
the quoted form (which is what they will do for relative references). But they
can also use a `packagePath`, which is a slash-separated series of segments,
each of which is a series of dot-separated components.*
*An import or export directive can continue to use a `stringLiteral` for the
quoted form (which is what they will do for relative references). But they can
also use a `packagePath`, which is a slash-separated series of segments, each of
which is a series of dot-separated components.*

### Disallowing unquoted part and part-of directives.

### Part directive lookahead
We change the `partDirective` and `partHeader` rules to:

*There are two directives for working with part files, `part` and `part of`.
This means that when the parser sees `part of`, it doesn't immediately know if
it is looking at a `part` directive followed by an unquoted identifier like
`part of;` or `part of.some/other.thing;` versus a `part of` directive like
`part of thing;` or `part of 'uri.dart';` It must lookahead past the `of`
identifier to see if the next token is `;`, `.`, `/`, or another identifier.*
```
partDirective ::= metadata 'part' stringLiteral ';'
partHeader ::= metadata 'part' 'of' stringLiteral ';'
```

*As part of [augmentations][], we are also planning to [expand the power of part
files][enhanced parts]. That includes supporting configurable part directives
(but not configurable part-of. When/if that happens, this grammar will need to
be tweaked to something like:*

```
partDirective ::= metadata 'part' configurableQuotedUri ';'
configurableQuotedUri ::= stringLiteral configurationQuotedUri*
configurationQuotedUri ::= 'if' '(' uriTest ')' stringLiteral
```

*This may add some complexity to parsing, but should be minor. Dart's grammar
has other places that require much more (sometimes unbounded) lookahead.*
[augmentations]: https://github.com/dart-lang/language/blob/main/working/augmentation-libraries/feature-specification.md
[enhanced parts]: https://github.com/dart-lang/language/blob/main/working/augmentation-libraries/parts_with_imports.md

## Static semantics

Expand Down Expand Up @@ -461,8 +532,7 @@ There are no runtime semantics for this feature.

## Compatibility

This feature is fully backwards compatible for `import`, `export`, and `part`
directives.
This feature is fully backwards compatible for `import` and `export` directives.

For all directives, we still allow quoted "dart:" and "package:" imports. Users
may be compelled to use the existing syntax in uncommon corner cases where the
Expand All @@ -474,73 +544,21 @@ migrated to the new style and the old quoted forms will be essentially vestigial
syntax (similar to names after `library` directives). A future version of Dart
may make a breaking change and remove support for the old syntax.

### Part-of directives

The `part of` directive allows a library name after `of` instead of a string
literal. With this proposal, that syntax is now ambiguous. Is it interpreted
as a library name, or as an unquoted URI that should be converted to a URI?
In other words, given:

```dart
part of foo.bar;
```

Is the file saying it's a part of the library containing `library foo.bar;` or
that it's part of the library found at URI `package:foo.bar/bar.dart`?

Library names in `part of` directives have been deprecated for many years
because the syntax doesn't work well with many tools. How is a given tool
supposed to know where to find the library that happens to contain a `library`
directive with that name? The quoted URI syntax was added later specifically to
address that point and users are encouraged by documentation and lints to use
the quoted syntax.

Looking at a corpus of 122,420 files:

```
-- Directive (443733 total) --
352744 ( 79.495%): import =========================================
55471 ( 12.501%): export =======
17823 ( 4.017%): part ===
17695 ( 3.988%): part of ===
```

So `part of` directives are fairly rare to begin with. Of them, most use the
recommended URI syntax and would not be affected by this change:

```
-- Part of (17695 total) --
13229 ( 74.761%): uri ===================================
4466 ( 25.239%): library name ============
```

In total, only about 1% of directives are `part of` with a library name:

```
-- URI (443733 total) --
352744 ( 79.495%): import ===========================
55471 ( 12.501%): export =====
17823 ( 4.017%): part ==
13229 ( 2.981%): part of with uri =
4466 ( 1.006%): part of with library name =
```

Given that, I propose that we make a **breaking change** and remove support for
the long-deprecated library name syntax from `part of` directives. An unquoted
series of identifiers after `part of` then gets unambiguously interpreted as
this proposal's semantics. In other words, `part of foo.bar;` is part of the
library at `package:foo/bar.dart`, not part of the library with name `foo.bar`.

Users affected by the breakage can and should update their `part of` directive
to point to the URI of the library that the file is a part of, using either the
quoted or unquoted syntax.
This proposal makes a **breaking change** to disallow unquoted library names in
`part of` directives.

### Language versioning

To avoid breaking existing `part of` directives, this change is language
versioned. Only libraries whose language version is at or above the version that
this proposal ships in can use this new unquoted syntax in `part of` or any
other directive.
versioned.

Only libraries whose language version is at or above the version that this
proposal ships in can use this new unquoted syntax in `import` and `export`
directives.

In language versions before the version this feature ships in, code may
continue to use the old unquoted library name syntax in `part of` directives
but users are encouraged to migrate away from that syntax.

## Tooling

Expand Down Expand Up @@ -570,6 +588,10 @@ new unquoted style whenever an existing directive could use it.

## Changelog

### 0.5

- Require `part` and `part of` directives to use quoted paths (#4038).

### 0.4

- Allow reserved words and built-in identifiers as path components (#3984).
Expand Down
Loading