dart-lang / sdk

The Dart SDK, including the VM, dart2js, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10k stars 1.54k forks source link

Proposal lint for `@experimental` #55160

Open rrousselGit opened 4 months ago

rrousselGit commented 4 months ago

Currently, pkg:meta exposes @experimental. But it doesn't have an associated diagnostic, which makes it unreliable as a tool to warn users. This is a proposal to add some scenarios where @experimental could lead to a diagnostic when the associated API is used.

For the following, consider:

// package:foo/foo.dart
@experimental
void fn() {}

Case 1: Warn whenever an experimental API is used

By default, any reference to an experimental API would trigger a diagnostic:

void main() {
  fn(); // KO, fn is experimental
}

Case 2: Offer a way to opt-in to experiments

I'd suggest introducing a separate @useExperiments annotation, which could be placed on various locations. We could then have:

@useExperiments
import 'package:foo/foo.dart';

void main() {
  fn(); // OK, we manually opted in to experiments
}

Case 3: Warn if @useExperiments is unused

@useExperiments // KO, no experimental API from this package are used.
import 'package:foo/foo.dart';

void main() {}

Case 4: Enable project-wide opt-in to experiments in one of Dart's configuration files

Be it analysis_options.yaml or pubspec.yaml, those files could contain new fields related to experiments. For example in pubspecs we could have:

dependencies:
  foo: ^1.0.0

experiments:
  -  foo

This would then count as a global @useExperiment on all libraries within this package.

Going further: Named experiments

As it is, we would have no mean to differentiate between different kinds of experiments.
One solution could be to offer @Experimental(<String>['tags']), and @UseExperiment(<String>['tag'])

If we go down this path, I would suggest making that any "named" experiment can only be used if explicitly enabled using @UseExperiment([name]).

For instance with:

@Experimental(['offline'])
void fn() {}

A valid usage would be:

@UseExperiments(['offline'])
import 'package:foo/foo.dart';

void main() => fn();

But invalid usages would be:

@useExperiments
import 'package:foo/foo.dart';

void main() => fn(); // KO, named experiments need their code explicitly listed in `@UseExperiment`
@UseExperiments(['unrelated'])
import 'package:foo/foo.dart';

void main() => fn(); // KO, no "offline" code specified in `@UseExperiments`
bwilkerson commented 4 months ago

That's an interesting idea.

I'm going to move this issue to the SDK repository because all of our support for annotations declared in meta is implemented there, and this would be no exception.