@@ -29147,6 +29147,117 @@ bool f(String s) {
2914729147}
2914829148```
2914929149
29150+ ### unsafe_variance
29151+
29152+ _This type is unsafe: a type parameter occurs in a non-covariant position._
29153+
29154+ #### Description
29155+
29156+ This lint warns against declaring non-covariant members.
29157+
29158+ An instance variable whose type contains a type parameter of the
29159+ enclosing class, mixin, or enum in a non-covariant position is
29160+ likely to cause run-time failures due to failing type
29161+ checks. For example, in `class C<X> {...}`, an instance variable
29162+ of the form `void Function(X) myVariable;` may cause this kind
29163+ of run-time failure.
29164+
29165+ The same is true for a getter or method whose return type has a
29166+ non-covariant occurrence of a type parameter of the enclosing
29167+ declaration.
29168+
29169+ This lint flags this kind of member declaration.
29170+
29171+ #### Example
29172+
29173+ **BAD:**
29174+ ```dart
29175+ class C<X> {
29176+ final bool Function([!X!]) fun; // LINT
29177+ C(this.fun);
29178+ }
29179+
29180+ void main() {
29181+ C<num> c = C<int>((i) => i.isEven);
29182+ c.fun(10); // Throws.
29183+ }
29184+ ```
29185+
29186+ The problem is that `X` occurs as a parameter type in the type
29187+ of `fun`.
29188+
29189+ #### Common fixes
29190+
29191+ One way to reduce the potential for run-time type errors is to
29192+ ensure that the non-covariant member `fun` is _only_ used on
29193+ `this`. We cannot strictly enforce this, but we can make it
29194+ private and add a forwarding method `fun` such that we can check
29195+ locally in the same library that this constraint is satisfied:
29196+
29197+ **BETTER:**
29198+ ```dart
29199+ class C<X> {
29200+ // ignore: unsafe_variance
29201+ final bool Function(X) _fun;
29202+ bool fun(X x) => _fun(x);
29203+ C(this._fun);
29204+ }
29205+
29206+ void main() {
29207+ C<num> c = C<int>((i) => i.isEven);
29208+ c.fun(10); // Succeeds.
29209+ }
29210+ ```
29211+
29212+ A fully safe approach requires a feature that Dart does not yet
29213+ have, namely statically checked variance. With that, we could
29214+ specify that the type parameter `X` is invariant (`inout X`).
29215+
29216+ It is possible to emulate invariance without support for statically
29217+ checked variance. This puts some restrictions on the creation of
29218+ subtypes, but faithfully provides the typing that `inout` would
29219+ give:
29220+
29221+ **GOOD:**
29222+ ```dart
29223+ typedef Inv<X> = X Function(X);
29224+ typedef C<X> = _C<X, Inv<X>>;
29225+
29226+ class _C<X, Invariance extends Inv<X>> {
29227+ // ignore: unsafe_variance
29228+ final bool Function(X) fun; // Safe!
29229+ _C(this.fun);
29230+ }
29231+
29232+ void main() {
29233+ C<int> c = C<int>((i) => i.isEven);
29234+ c.fun(10); // Succeeds.
29235+ }
29236+ ```
29237+
29238+ With this approach, `C<int>` is not a subtype of `C<num>`, so
29239+ `c` must have a different declared type.
29240+
29241+ Another possibility is to declare the variable to have a safe
29242+ but more general type. It is then safe to use the variable
29243+ itself, but every invocation will have to be checked at run
29244+ time:
29245+
29246+ **HONEST:**
29247+ ```dart
29248+ class C<X> {
29249+ final bool Function(Never) fun;
29250+ C(this.fun);
29251+ }
29252+
29253+ void main() {
29254+ C<num> c = C<int>((int i) => i.isEven);
29255+ var cfun = c.fun; // Local variable, enables promotion.
29256+ if (cfun is bool Function(int)) cfun(10); // Succeeds.
29257+ if (cfun is bool Function(bool)) cfun(true); // Not called.
29258+ }
29259+ ```
29260+
2915029261### use_build_context_synchronously
2915129262
2915229263_Don't use 'BuildContext's across async gaps, guarded by an unrelated 'mounted'
0 commit comments