Tangentially related to #31410.
While migrating swaths of internal code (probably in the tens-of-thousands-of-lines, myself), one of the most common errors users are running into when migrating to Dart2 are so-called “naked” raw types (is there a better name?). For example:
// Works as the user intended.
// List<int> x = <int>[1];
var x = [1];
// Does _not_ work as the user intended.
// List<dynamic> x = <dynamic>[1];
List x = [1];
Here is a more extreme example:
typedef Iterable<CacheEntry<K, V>> CacheEvictionFunction<K, V>(
List<CacheEntry<K, V>> entries,
int entriesToBeTrimmed,
);
class CacheConfig {
// In Dart1 this was "fine".
// In Dart2 this fails, quite late, at runtime:
//
// CacheEvictionFunction<dynamic, dynamic> "inferred"
final CacheEvictionFunction _evictionImpl;
CacheConfig(this._evictionImpl);
}
My
- Be a compile-time error (“You must specify a type or “)
- Work similar to Java’s
<?>
(wildcard operator). That would mean:
// --no-implicit-dynamic
//
// List<int> x = <int>[1];
List x = [1];
// --no-implicit-dynamic
//
// Compile-time error: Must specify a type 'T' for List<T>
List x;
// --no-implicit-dynamic
//
// OK.
List<dynamic> x;
… if we are accepting name nominations, I nominate --strict-raw-types
.
Java calls them “raw types”, which I hear on the Dart team sometimes.
I agree, it should be a compile error. We need to be clear about the several different places a raw type (or a thing that looks like a raw type) can appear. Here’s the behavior I’d like:
As for naming the flag, maybe
--strict-raw-types
?Hit another case internally. Accidental omission of type parameters extending/implementing:
This is totally valid code, and nothing is flagged in the analyzer or otherwise.
At runtime, trying to use a
WizBang
as aHasValue<String>
(or similar) fails. It took quite a bit of groking to finally understand thatHasValue
had a type parameterT
that was omitted. I’d like to see our hypothetical--strict-raw-types
warn if a type with type parameters is implemented without bounds or “passing through” params:Hit a particularly nasty case internally that took 3 of us ~2 hours to resolve:
See if you can spot the source of the error!
This is because raw type of
Response
is aResponse<Context>
, not aResponse<SubContext>
. Even though there is very judicious use of type arguments across (as far as the internal team thought), it is actually weaker than usingvar
(usingvar response1 =
actually fixes the bug).Why? Because
response.callback
is statically saying give me aCallback<Context>
, though, at runtime, we’ve encoded aCallback<SubContext>
, and contravariance law says this is a bad. Wamp wamp.Good news: my CL https://dart-review.googlesource.com/c/sdk/+/75629 is updated and sent for review. Thanks to @leafpetersen for clarifications about the desired behavior and @srawlins for the offer to help review and/or take over the CL if it needs significant changes.
We’ll need to add official documentation for this, but for now the instructions are:
strict-inference
andstrict-raw-types
can be configured via the language section of the analysis_options.yaml file, for example: