From 150a0a6d78a51fba81d15d5a896333dd4c2191af Mon Sep 17 00:00:00 2001 From: Parker Lougheed Date: Tue, 7 Jan 2025 16:36:29 +0800 Subject: [PATCH] Update upstream docs for latest lint and diagnostic releases --- src/_data/linter_rules.json | 27 +++ src/content/tools/diagnostic-messages.md | 265 ++++++++++++++++------- 2 files changed, 215 insertions(+), 77 deletions(-) diff --git a/src/_data/linter_rules.json b/src/_data/linter_rules.json index 670d382ff6..a6fcce0b1f 100644 --- a/src/_data/linter_rules.json +++ b/src/_data/linter_rules.json @@ -2656,6 +2656,19 @@ "details": "Do type annotate initialized top-level or static variables when the type is\nnon-obvious.\n\nType annotations on top-level or static variables can serve as a request for\ntype inference, documenting the expected outcome of the type inference step,\nand declaratively allowing the compiler and analyzer to solve the possibly\ncomplex task of finding type arguments and annotations in the initializing\nexpression that yield the desired result.\n\nType annotations on top-level or static variables can also inform readers about\nthe type of the initializing expression, which will allow them to proceed\nreading the locations in code where this variable is used with known good\ninformation about the type of the given variable (which may not be immediately\nevident by looking at the initializing expression).\n\nAn expression is considered to have a non-obvious type when it does not\nhave an obvious type.\n\nAn expression e has an obvious type in the following cases:\n\n- e is a non-collection literal. For instance, 1, true, 'Hello, $name!'.\n- e is a collection literal with actual type arguments. For instance,\n {}.\n- e is a list literal or a set literal where at least one element has an\n obvious type, and all elements have the same type. For instance, [1, 2] and\n { [true, false], [] }, but not [1, 1.5].\n- e is a map literal where all key-value pair have a key with an obvious type\n and a value with an obvious type, and all keys have the same type, and all\n values have the same type. For instance, { #a: [] }, but not\n {1: 1, 2: true}.\n- e is an instance creation expression whose class part is not raw. For\n instance C(14) if C is a non-generic class, or C(14) if C accepts one\n type argument, but not C(14) if C accepts one or more type arguments.\n- e is a cascade whose target has an obvious type. For instance,\n 1..isEven..isEven has an obvious type because 1 has an obvious type.\n- e is a type cast. For instance, myComplexpression as int.\n\n**BAD:**\n```dart\nfinal myTopLevelVariable =\n genericFunctionWrittenByOtherFolks(with, args);\n\nclass A {\n static var myStaticVariable =\n myTopLevelVariable.update('foo', null);\n}\n```\n\n**GOOD:**\n```dart\nfinal Map myTopLevelVariable =\n genericFunctionWrittenByOtherFolks(with, args);\n\nclass A {\n static Map myStaticVariable =\n myTopLevelVariable.update('foo', null);\n}\n```\n\n**This rule is experimental.** It is being evaluated, and it may be changed\nor removed. Feedback on its behavior is welcome! The main issue is here:\nhttps://github.com/dart-lang/linter/issues/5101.", "sinceDartSdk": "3.7-wip" }, + { + "name": "strict_top_level_inference", + "description": "Specify type annotations.", + "categories": [ + "style" + ], + "state": "stable", + "incompatible": [], + "sets": [], + "fixStatus": "hasFix", + "details": "Do type annotate top-level and class-like member declarations, where types\nare not inferred from super-interfaces or initializers.\n\nThe lint warns about every omitted return type, parameter type, and\nvariable type of a top-level declaration or class-like-namespace-level\ndeclaration (static or instance member or constructor declaration), which\nis not given a type by inference, and which therefore defaults to dynamic.\n\nThe only omitted types that can be given a type by top-level inference,\nare those of variable declarations with initializer expressions, and\nreturn and parameter types of instance members that override a consistent\ncombined super-interface signature.\n\nSetters do not need a return type, as it is always assumed to be `void`.\n\n**BAD:**\n```dart\nvar _zeroPointCache;\nclass Point {\n get zero => ...;\n final x, y;\n Point(x, y) {}\n closest(b, c) => distance(b) <= distance(c) ? b : c;\n distance(other) => ...;\n}\n_sq(v) => v * v;\n```\n\n**GOOD:**\n```dart\nPoint? _zeroPointCache;\nclass Point {\n Point get zero => ...;\n final int x, y;\n Point(int x, int y) {}\n closest(Point b, Point c) =>\n distance(b) <= distance(c) ? b : c;\n distance(Point other) => ...;\n}\nint _sq(int v) => v * v;\n```", + "sinceDartSdk": "3.7-wip" + }, { "name": "super_goes_last", "description": "Place the `super` call last in a constructor initialization list.", @@ -3179,6 +3192,20 @@ "details": "Unnecessary `toList()` in spreads.\n\n**BAD:**\n```dart\nchildren: [\n ...['foo', 'bar', 'baz'].map((String s) => Text(s)).toList(),\n]\n```\n\n**GOOD:**\n```dart\nchildren: [\n ...['foo', 'bar', 'baz'].map((String s) => Text(s)),\n]\n```", "sinceDartSdk": "2.18" }, + { + "name": "unnecessary_underscores", + "description": "Unnecessary underscores can be removed.", + "categories": [ + "brevity", + "style" + ], + "state": "stable", + "incompatible": [], + "sets": [], + "fixStatus": "needsFix", + "details": "**AVOID** using multiple underscores when a single wildcard will do.\n\n**BAD:**\n```dart\nvoid function(int __) { }\n```\n\n**GOOD:**\n```dart\nvoid function(int _) { }\n```", + "sinceDartSdk": "3.7-wip" + }, { "name": "unreachable_from_main", "description": "Unreachable top-level members in executable libraries.", diff --git a/src/content/tools/diagnostic-messages.md b/src/content/tools/diagnostic-messages.md index f959a26fa6..f86ff232fd 100644 --- a/src/content/tools/diagnostic-messages.md +++ b/src/content/tools/diagnostic-messages.md @@ -14425,6 +14425,118 @@ final class C extends Struct { } ``` +### native_function_missing_type + +_The native type of this function couldn't be inferred so it must be specified +in the annotation._ + +#### Description + +The analyzer produces this diagnostic when a `@Native`-annotated function +requires a type hint on the annotation to infer the native function type. + +Dart types like `int` and `double` have multiple possible native +representations. Since the native type needs to be known at compile time +to generate correct bindings and call instructions for the function, an +explicit type must be given. + +For more information about FFI, see [C interop using dart:ffi][ffi]. + +#### Example + +The following code produces this diagnostic because the function `f()` has +the return type `int`, but doesn't have an explicit type parameter on the +`Native` annotation: + +```dart +import 'dart:ffi'; + +@Native() +external int [!f!](); +``` + +#### Common fixes + +Add the corresponding type to the annotation. For instance, if `f()` was +declared to return an `int32_t` in C, the Dart function should be declared +as: + +```dart +import 'dart:ffi'; + +@Native() +external int f(); +``` + +### negative_variable_dimension + +_The variable dimension of a variable-length array must be non-negative._ + +#### Description + +The analyzer produces this diagnostic in two cases. + +The first is when the variable dimension given in an +`Array.variableWithVariableDimension` annotation is negative. The variable +dimension is the first argument in the annotation. + +The second is when the variable dimension given in an +`Array.variableMulti` annotation is negative. The variable dimension is +specified in the `variableDimension` argument of the annotation. + +For more information about FFI, see [C interop using dart:ffi][ffi]. + +#### Examples + +The following code produces this diagnostic because a variable dimension +of `-1` was provided in the `Array.variableWithVariableDimension` +annotation: + +```dart +import 'dart:ffi'; + +final class MyStruct extends Struct { + @Array.variableWithVariableDimension([!-1!]) + external Array a0; +} +``` + +The following code produces this diagnostic because a variable dimension +of `-1` was provided in the `Array.variableMulti` annotation: + +```dart +import 'dart:ffi'; + +final class MyStruct2 extends Struct { + @Array.variableMulti(variableDimension: [!-1!], [1, 2]) + external Array>> a0; +} +``` + +#### Common fixes + +Change the variable dimension with zero (`0`) or a positive number: + +```dart +import 'dart:ffi'; + +final class MyStruct extends Struct { + @Array.variableWithVariableDimension(1) + external Array a0; +} +``` + +Change the variable dimension with zero (`0`) or a positive number: + +```dart +import 'dart:ffi'; + +final class MyStruct2 extends Struct { + @Array.variableMulti(variableDimension: 1, [1, 2]) + external Array>> a0; +} +``` + ### new_with_undefined_constructor_default _The class '{0}' doesn't have an unnamed constructor._ @@ -15719,7 +15831,7 @@ For more information about FFI, see [C interop using dart:ffi][ffi]. #### Example The following code produces this diagnostic because an array dimension of -`-1` was provided: +`-8` was provided: ```dart import 'dart:ffi'; @@ -15743,7 +15855,8 @@ final class MyStruct extends Struct { } ``` -If this is a variable length inline array, change the annotation to `Array.variable()`: +If this is a variable length inline array, change the annotation to +`Array.variable()`: ```dart import 'dart:ffi'; @@ -29099,6 +29212,32 @@ List toLowercase(List strings) { } ``` +### unnecessary_underscores + +_Unnecessary use of multiple underscores._ + +#### Description + +The analyzer produces this diagnostic when an unused variable is named +with multiple underscores (for example `__`). +A single `_` wildcard variable can be used instead. + +#### Example + +The following code produces this diagnostic because the `__` parameter is unused: + +```dart +void function(int [!__!]) { } +``` + +#### Common fixes + +Replace the name with a single underscore: + +```dart +void function(int _) { } +``` + ### unrelated_type_equality_checks _The type of the operand ('{0}') isn't a subtype or a supertype of the value @@ -29154,111 +29293,83 @@ _This type is unsafe: a type parameter occurs in a non-covariant position._ #### Description -This lint warns against declaring non-covariant members. - -An instance variable whose type contains a type parameter of the -enclosing class, mixin, or enum in a non-covariant position is -likely to cause run-time failures due to failing type -checks. For example, in `class C {...}`, an instance variable -of the form `void Function(X) myVariable;` may cause this kind -of run-time failure. - -The same is true for a getter or method whose return type has a -non-covariant occurrence of a type parameter of the enclosing -declaration. - -This lint flags this kind of member declaration. +The analyzer produces this diagnostic when an instance member has a result +type which is [contravariant or invariant](https://dart.dev/resources/glossary#variance) +in a type parameter of the enclosing declaration. The result type of a +variable is its type, and the result type of a getter or method is its +return type. This lint warns against such members because they are likely +to cause a failing type check at run time, with no static warning or error +at the call site. #### Example -**BAD:** +The following code produces this diagnostic because `X` occurs +as a parameter type in the type of `f`, which is a +contravariant occurrence of this type parameter: + ```dart class C { - final bool Function([!X!]) fun; // LINT - C(this.fun); -} - -void main() { - C c = C((i) => i.isEven); - c.fun(10); // Throws. + bool Function([!X!]) f; + C(this.f); } ``` -The problem is that `X` occurs as a parameter type in the type -of `fun`. +This is unsafe: If `c` has static type `C` and run-time type `C` +then `c.f` will throw. Hence, every invocation `c.f(a)` will also throw, +even in the case where `a` has a correct type as an argument to `c.f`. #### Common fixes -One way to reduce the potential for run-time type errors is to -ensure that the non-covariant member `fun` is _only_ used on -`this`. We cannot strictly enforce this, but we can make it -private and add a forwarding method `fun` such that we can check -locally in the same library that this constraint is satisfied: +If the linted member is or can be private then you may be able +to enforce that it is never accessed on any other receiver than `this`. +This is sufficient to ensure that that the run-time type error does not +occur. For example: -**BETTER:** ```dart class C { + // NB: Ensure manually that `_f` is only accessed on `this`. // ignore: unsafe_variance - final bool Function(X) _fun; - bool fun(X x) => _fun(x); - C(this._fun); -} + bool Function(X) _f; -void main() { - C c = C((i) => i.isEven); - c.fun(10); // Succeeds. + C(this._f); + + // We can write a forwarding method to allow clients to call `_f`. + bool f(X x) => _f(x); } ``` -A fully safe approach requires a feature that Dart does not yet -have, namely statically checked variance. With that, we could -specify that the type parameter `X` is invariant (`inout X`). - -It is possible to emulate invariance without support for statically -checked variance. This puts some restrictions on the creation of -subtypes, but faithfully provides the typing that `inout` would -give: +You can eliminate the unsafe variance by using a more general type for +the linted member. In this case you may need to check the run-time type +and perform a downcast at call sites. -**GOOD:** ```dart -typedef Inv = X Function(X); -typedef C = _C>; - -class _C> { - // ignore: unsafe_variance - final bool Function(X) fun; // Safe! - _C(this.fun); -} - -void main() { - C c = C((i) => i.isEven); - c.fun(10); // Succeeds. +class C { + bool Function(Never) f; + C(this.f); } ``` -With this approach, `C` is not a subtype of `C`, so -`c` must have a different declared type. +If `c` has static type `C` then you may test the type. For example, +`c.f is bool Function(num)`. You may safely call it with an argument of +type `num` if it has that type. -Another possibility is to declare the variable to have a safe -but more general type. It is then safe to use the variable -itself, but every invocation will have to be checked at run -time: +You can also eliminate the unsafe variance by using a much more general +type like `Function`, which is essentially the type `dynamic` for +functions. -**HONEST:** ```dart class C { - final bool Function(Never) fun; - C(this.fun); -} - -void main() { - C c = C((int i) => i.isEven); - var cfun = c.fun; // Local variable, enables promotion. - if (cfun is bool Function(int)) cfun(10); // Succeeds. - if (cfun is bool Function(bool)) cfun(true); // Not called. + Function f; + C(this.f); } ``` +This will make `c.f(a)` dynamically safe: It will throw if and only if the +argument `a` does not have the type required by the function. This is +better than the original version because it will not throw because of a +mismatched static type. It only throws when it _must_ throw for soundness +reasons. + ### use_build_context_synchronously _Don't use 'BuildContext's across async gaps, guarded by an unrelated 'mounted' @@ -29272,7 +29383,7 @@ The analyzer produces this diagnostic when a `BuildContext` is referenced by a `StatefulWidget` after an asynchronous gap without first checking the `mounted` property. -Storing a `BuildContext` for later use can lead to difficult to diagnose +Storing a `BuildContext` for later use can lead to difficult-to-diagnose crashes. Asynchronous gaps implicitly store a `BuildContext`, making them easy to overlook for diagnosis.