Description
The goal is to catch more error-prone code during static analysis; for example, code which is guaranteed to error at runtime. This often two secondary effects: (1) reducing errors in developer understanding of such code, and (2) reducing dynamic dispatch in code where a developer did not think dynamic dispatch was occurring.
We define a "raw type" as a type with omitted type argument(s). Dart fills in such types with their bounds, or dynamic if there are no bounds. Here are some examples:
void main() {
List a = [1, 2, 3];
a.forEach((n) => print(n.isEven));
fn(a);
}
void fn(List strings) => strings.forEach((s) => print(s.toUpperCase()));
On the first line of main
, a
is declared as a List<dynamic>
. The type of [1, 2, 3]
is inferred from List<dynamic>
. There is no implicit cast. On the second line, n.isEven
is a dynamic dispatch call, since the elements of a
are treated as dynamic. On the third line, when fn
is called, the runtime will throw a NoSuchMethodError, trying to call toUpperCase
on the numbers. There are currently no compile-time, static analysis checks that would reveal a problem.
The proposed "strict-raw-types" mode will report errors (e.g. Hints in the analyzer) at the declaration of most types with omitted type arguments. Here are some examples of "raw types" that would be reported:
- A variable declaration, like
List a = [1, 2, 3];
- A type argument, like
Future<List> a;
- A function parameter, like
main(List args) {}
,typedef cb = void Function(Future)
- A function return type, like
Future main() {}
,typedef cb = Future Function()
- A class declaration, like
class MyList extends List {}
,mixin Foo on List
There are some cases where a type will not be considered a "raw type":
- Omitted type arguments on types which are annotated with
@optionalTypeArgs
from the meta package. There are some classes which are often used in ways where type arguments are not known statically, or they are found in heterogeneous collections. If such a class is annotated with@optionalTypeArgs
, then it will not be reported as a "raw type." - A type on the right side of an
is
oras
expression; in experiments it was found that this just forced developers to writeis List<dynamic>
andas Map<dynamic, dynamic>
over and over and over. This increases verbosity, without improving correctness or understandability.
Raw types are currently one type of static analysis report found with the "implicit-dynamic: false" static analysis mode. However, multiple attempts to enable this mode on large code bases have shown it to be too onerous to comply with; "strict raw types" are just one piece of "inplicit-dynamic: false", and should be must easier to comply with.
This issue is a continuation of dart-lang/sdk#33749. Thanks to @munificent, @leafpetersen, and @matanlurey for specifying most of the above.