Skip to content

Parse files with the language version the package declares - #187

Open
maxim-sysoev wants to merge 1 commit into
Workiva:masterfrom
maxim-sysoev:fix/parse-with-package-language-version
Open

Parse files with the language version the package declares#187
maxim-sysoev wants to merge 1 commit into
Workiva:masterfrom
maxim-sysoev:fix/parse-with-package-language-version

Conversation

@maxim-sysoev

Copy link
Copy Markdown

Problem

getDartDirectivePackageNames calls parseString without a feature set, so it falls back to FeatureSet.latestLanguageVersion() — the newest language version the bundled analyzer knows about. With analyzer 13 that version is unreleased, and it rejects the final modifier on formal parameters:

void f(final int a) {}
Error parsing: ./lib/example.dart
Content produced diagnostics when parsed:
  extraneous_modifier: Can't have modifier 'final' here. - 1:8

The run aborts with exit code 1 before a single dependency is checked, on code the SDK analyzer accepts without complaint.

The behaviour arrived with 5.0.6 (Allow up to analyzer 13). The parsing code itself is byte-identical to 5.0.5; only the resolved analyzer changed — 5.0.5 resolves analyzer 12.1.0 and passes, 5.0.6 resolves analyzer 13.3.0 and fails.

Pinning the language version explicitly confirms the cause — the same snippet parses cleanly under every released version:

feature set result
latestLanguageVersion() fails
language 3.9 / 3.10 / 3.11 / 3.12 passes

Why it hits projects that never write final on a parameter

freezed emits the modifier itself. A declaration containing no final at all generates a constructor that does, so every generated file in a freezed project fails to parse. In our monorepo five of twelve packages turned red overnight with no source change — CI installs the tool unpinned, so 5.0.6 was picked up automatically.

Excluding generated files via dart_dependency_validator.yaml removes the parse errors but defeats the check: dependencies used only from generated code are then reported as unused, which has to be silenced in turn.

Fix

Derive the feature set from the package's SDK constraint — the same way the analyzer determines a package's language version — and fall back to the previous behaviour when the constraint is missing or unbounded. The feature set is computed once per package and passed into the parse; the parameter is optional, so the public function stays source-compatible.

Tests

Adds a regression test to executable_test.dart: a project whose library uses final on both a positional and a named parameter now validates cleanly. The test fails on master and passes with this change.

The rest of the suite is unchanged: 4 tests fail both with and without this change on my machine (+84 -4 either way), so they look unrelated to it.

Suppressing diagnostics with throwIfDiagnostics: false was considered and rejected: it would also hide genuinely malformed files. With this change a truly broken file still reports expected_token diagnostics as before.

parseString falls back to FeatureSet.latestLanguageVersion() when no feature
set is given. On analyzer 13 that version is unreleased and rejects a `final`
modifier on formal parameters, so validation aborts with "Error parsing" on
code the SDK analyzer accepts. Packages using freezed hit this without writing
`final` themselves: the generator emits it for parameters with default values.

Derive the feature set from the package's SDK constraint, the same way the
analyzer determines a package's language version, and fall back to the latest
version when the constraint is missing or unbounded.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant