Skip to content

Dart 3.13 shorthand factory constructors are parsed as unnamed ('new'), crashing PackageApiDiffer #266

Open

Description

@kevmoo

When analyzing a package that uses Dart 3.13 primary constructors and shorthand named factory constructor syntax (where the enclosing class prefix is omitted, e.g. factory fromSamples(...) instead of factory BenchmarkMetrics.fromSamples(...)), dart_apitool diff crashes with an ArgumentError:

Error while creating the diff: Invalid argument(s): Multiple new items found with the same structure as the old item ExecutableDeclaration(returnTypeName: BenchmarkMetrics, ..., name: new, ...): (ExecutableDeclaration(..., name: new, parameters: [...]), ExecutableDeclaration(..., name: new, parameters: [samplesNs, ...]), ExecutableDeclaration(..., name: new, parameters: [json]))
#0      PackageApiDiffer._diffIterables (package:dart_apitool/src/diff/package_api_differ.dart:1432:11)
#1      PackageApiDiffer._calculateExecutablesDiff (package:dart_apitool/src/diff/package_api_differ.dart:298:32)

Root Cause

  1. dart_apitool currently constrains analyzer: ^10.0.2 (Dart 3.9/3.10 era, predating Dart 3.13 primary and shorthand constructor syntax).
  2. Given a class with a primary constructor and shorthand factory constructors:
    final class const BenchmarkMetrics({
      required final double meanNs,
    }) {
      factory fromSamples(List<double> samplesNs) => ...;
      factory fromJson(Map<String, Object?> json) => ...;
    }
    analyzer 10.2.0 parses factory fromSamples(...) and factory fromJson(...) using the pre-3.13 grammar factory <typeIdentifier>(...) (an unnamed constructor with period == null and name == null).
  3. Because ConstructorElement.name is 'new' when period == null, InternalExecutableDeclaration.fromExecutableElement emits name: 'new' for the primary constructor and every shorthand factory constructor on the class.
  4. PackageApiDiffer._calculateExecutablesDiff then matches executables by oldEx.name == newEx.name, finds multiple constructors named 'new', and throws in _diffIterables (sameMatches.singleOrNull == null).

Resolution

Updating analyzer to ^12.0.0 (or migrating the analyzer frontend to package:api_summary, as explored in #258) resolves the constructor names properly (new, fromSamples, fromJson).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions