diff --git a/src/_data/linter_rules.json b/src/_data/linter_rules.json index 8054576a46..1477e81ab6 100644 --- a/src/_data/linter_rules.json +++ b/src/_data/linter_rules.json @@ -60,7 +60,8 @@ "incompatible": [ "avoid_types_on_closure_parameters", "omit_local_variable_types", - "omit_obvious_local_variable_types" + "omit_obvious_local_variable_types", + "omit_obvious_property_types" ], "sets": [], "fixStatus": "hasFix", @@ -320,7 +321,7 @@ "sets": [], "fixStatus": "noFix", "details": "**AVOID** using `FutureOr` as the type of a result. This type is\nproblematic because it may appear to encode that a result is either a\n`Future`, or the result should be discarded (when it is `void`).\nHowever, there is no safe way to detect whether we have one or the other\ncase (because an expression of type `void` can evaluate to any object\nwhatsoever, including a future of any type).\n\nIt is also conceptually unsound to have a type whose meaning is something\nlike \"ignore this object; also, take a look because it might be a future\".\n\nAn exception is made for contravariant occurrences of the type\n`FutureOr` (e.g., for the type of a formal parameter), and no\nwarning is emitted for these occurrences. The reason for this exception\nis that the type does not describe a result, it describes a constraint\non a value provided by others. Similarly, an exception is made for type\nalias declarations, because they may well be used in a contravariant\nposition (e.g., as the type of a formal parameter). Hence, in type alias\ndeclarations, only the type parameter bounds are checked.\n\nA replacement for the type `FutureOr` which is often useful is\n`Future?`. This type encodes that the result is either a\n`Future` or it is null, and there is no ambiguity at run time\nsince no object can have both types.\n\nIt may not always be possible to use the type `Future?` as a\nreplacement for the type `FutureOr`, because the latter is a\nsupertype of all types, and the former is not. In this case it may be a\nuseful remedy to replace `FutureOr` by the type `void`.\n\n**BAD:**\n```dart\nFutureOr m() {...}\n```\n\n**GOOD:**\n```dart\nFuture? m() {...}\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/4622", - "sinceDartSdk": "3.6-wip" + "sinceDartSdk": "3.6" }, { "name": "avoid_implementing_value_types", @@ -386,11 +387,11 @@ "categories": [ "style" ], - "state": "removed", + "state": "stable", "incompatible": [], "sets": [], - "fixStatus": "noFix", - "details": "**DON'T** check for `null` in custom `==` operators.\n\nAs `null` is a special value, no instance of any class (other than `Null`) can\nbe equivalent to it. Thus, it is redundant to check whether the other instance\nis `null`.\n\n**BAD:**\n```dart\nclass Person {\n final String? name;\n\n @override\n operator ==(Object? other) =>\n other != null && other is Person && name == other.name;\n}\n```\n\n**GOOD:**\n```dart\nclass Person {\n final String? name;\n\n @override\n operator ==(Object? other) => other is Person && name == other.name;\n}\n```\n\nThis rule has been removed.", + "fixStatus": "hasFix", + "details": "**NOTE:** This lint has been replaced by the\n`non_nullable_equals_parameter` warning and is deprecated.\nRemove all inclusions of this lint from your analysis options.\n\n**DON'T** check for `null` in custom `==` operators.\n\nAs `null` is a special value, no instance of any class (other than `Null`) can\nbe equivalent to it. Thus, it is redundant to check whether the other instance\nis `null`.\n\n**BAD:**\n```dart\nclass Person {\n final String? name;\n\n @override\n operator ==(Object? other) =>\n other != null && other is Person && name == other.name;\n}\n```\n\n**GOOD:**\n```dart\nclass Person {\n final String? name;\n\n @override\n operator ==(Object? other) => other is Person && name == other.name;\n}\n```", "sinceDartSdk": "2.0" }, { @@ -1708,7 +1709,22 @@ "sets": [], "fixStatus": "hasFix", "details": "Don't type annotate initialized local variables when the type is obvious.\n\nLocal variables, especially in modern code where functions tend to be small,\nhave very little scope. Omitting the type focuses the reader's attention on the\nmore important *name* of the variable and its initialized value. Hence, local\nvariable type annotations that are obvious should be omitted.\n\n**BAD:**\n```dart\nList> possibleDesserts(Set pantry) {\n List> desserts = >[];\n for (final List recipe in cookbook) {\n if (pantry.containsAll(recipe)) {\n desserts.add(recipe);\n }\n }\n\n return desserts;\n}\n\nconst cookbook = >[....];\n```\n\n**GOOD:**\n```dart\nList> possibleDesserts(Set pantry) {\n var desserts = >[];\n for (final List recipe in cookbook) {\n if (pantry.containsAll(recipe)) {\n desserts.add(recipe);\n }\n }\n\n return desserts;\n}\n\nconst cookbook = >[....];\n```\n\nSometimes the inferred type is not the type you want the variable to have. For\nexample, you may intend to assign values of other types later. You may also\nwish to write a type annotation explicitly because the type of the initializing\nexpression is non-obvious and it will be helpful for future readers of the\ncode to document this type. Or you may wish to commit to a specific type such\nthat future updates of dependencies (in nearby code, in imports, anywhere)\nwill not silently change the type of that variable, thus introducing\ncompile-time errors or run-time bugs in locations where this variable is used.\nIn those cases, go ahead and annotate the variable with the type you want.\n\n**GOOD:**\n```dart\nWidget build(BuildContext context) {\n Widget result = someGenericFunction(42) ?? Text('You won!');\n if (applyPadding) {\n result = Padding(padding: EdgeInsets.all(8.0), child: result);\n }\n return result;\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/3480.", - "sinceDartSdk": "3.6-wip" + "sinceDartSdk": "3.6" + }, + { + "name": "omit_obvious_property_types", + "description": "Omit obvious type annotations for top-level and static variables.", + "categories": [ + "style" + ], + "state": "experimental", + "incompatible": [ + "always_specify_types" + ], + "sets": [], + "fixStatus": "hasFix", + "details": "Don't type annotate initialized top-level or static variables when the type is\nobvious.\n\n**BAD:**\n```dart\nfinal int myTopLevelVariable = 7;\n\nclass A {\n static String myStaticVariable = 'Hello';\n}\n```\n\n**GOOD:**\n```dart\nfinal myTopLevelVariable = 7;\n\nclass A {\n static myStaticVariable = 'Hello';\n}\n```\n\nSometimes the inferred type is not the type you want the variable to have. For\nexample, you may intend to assign values of other types later. You may also\nwish to write a type annotation explicitly because the type of the initializing\nexpression is non-obvious and it will be helpful for future readers of the\ncode to document this type. Or you may wish to commit to a specific type such\nthat future updates of dependencies (in nearby code, in imports, anywhere)\nwill not silently change the type of that variable, thus introducing\ncompile-time errors or run-time bugs in locations where this variable is used.\nIn those cases, go ahead and annotate the variable with the type you want.\n\n**GOOD:**\n```dart\nfinal num myTopLevelVariable = 7;\n\nclass A {\n static String? myStaticVariable = 'Hello';\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": "one_member_abstracts", @@ -2044,7 +2060,9 @@ "style" ], "state": "stable", - "incompatible": [], + "incompatible": [ + "unnecessary_final" + ], "sets": [], "fixStatus": "hasFix", "details": "**DO** prefer declaring for-each loop variables as final if they are not\nreassigned later in the code.\n\nDeclaring for-each loop variables as final when possible is a good practice\nbecause it helps avoid accidental reassignments and allows the compiler to do\noptimizations.\n\n**BAD:**\n```dart\nfor (var element in elements) { // LINT\n print('Element: $element');\n}\n```\n\n**GOOD:**\n```dart\nfor (final element in elements) {\n print('Element: $element');\n}\n```\n\n**GOOD:**\n```dart\nfor (var element in elements) {\n element = element + element;\n print('Element: $element');\n}\n```", @@ -2623,7 +2641,20 @@ "sets": [], "fixStatus": "hasFix", "details": "Do type annotate initialized local variables when the type is non-obvious.\n\nType annotations on local variables can serve as a request for type inference,\ndocumenting the expected outcome of the type inference step, and declaratively\nallowing the compiler and analyzer to solve the possibly complex task of\nfinding type arguments and annotations in the initializing expression that\nyield the desired result.\n\nType annotations on local variables can also inform readers about the type\nof the initializing expression, which will allow them to proceed reading the\nsubsequent lines of code with known good information about the type of the\ngiven variable (which may not be immediately evident by looking at the\ninitializing 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, myComplexExpression as int.\n\n**BAD:**\n```dart\nList> possibleDesserts(Set pantry) {\n var desserts = genericFunctionDeclaredFarAway([42], 'Something');\n for (final recipe in cookbook) {\n if (pantry.containsAll(recipe)) {\n desserts.add(recipe);\n }\n }\n\n return desserts;\n}\n\nconst List> cookbook = ...;\n```\n\n**GOOD:**\n```dart\nList> possibleDesserts(Set pantry) {\n List> desserts = genericFunctionDeclaredFarAway(\n [42],\n 'Something',\n );\n for (final List recipe in cookbook) {\n if (pantry.containsAll(recipe)) {\n desserts.add(recipe);\n }\n }\n\n return desserts;\n}\n\nconst List> cookbook = ...;\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/3480.", - "sinceDartSdk": "3.6-wip" + "sinceDartSdk": "3.6" + }, + { + "name": "specify_nonobvious_property_types", + "description": "Specify non-obvious type annotations for top-level and static variables.", + "categories": [ + "style" + ], + "state": "experimental", + "incompatible": [], + "sets": [], + "fixStatus": "hasFix", + "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": "super_goes_last", @@ -2841,7 +2872,8 @@ "state": "stable", "incompatible": [ "prefer_final_locals", - "prefer_final_parameters" + "prefer_final_parameters", + "prefer_final_in_for_each" ], "sets": [], "fixStatus": "hasFix", @@ -3183,13 +3215,26 @@ "categories": [ "errorProne" ], - "state": "stable", + "state": "removed", "incompatible": [], "sets": [], "fixStatus": "noFix", - "details": "**AVOID**\n\n* assigning directly to the `href` field of an AnchorElement\n* assigning directly to the `src` field of an EmbedElement, IFrameElement, or\n ScriptElement\n* assigning directly to the `srcdoc` field of an IFrameElement\n* calling the `createFragment` method of Element\n* calling the `open` method of Window\n* calling the `setInnerHtml` method of Element\n* calling the `Element.html` constructor\n* calling the `DocumentFragment.html` constructor\n\n\n**BAD:**\n```dart\nvar script = ScriptElement()..src = 'foo.js';\n```", + "details": "**NOTE:** This lint is deprecated and will be removed in a future release.\nRemove all inclusions of this lint from your analysis options.\n\n**AVOID**\n\n* assigning directly to the `href` field of an AnchorElement\n* assigning directly to the `src` field of an EmbedElement, IFrameElement, or\n ScriptElement\n* assigning directly to the `srcdoc` field of an IFrameElement\n* calling the `createFragment` method of Element\n* calling the `open` method of Window\n* calling the `setInnerHtml` method of Element\n* calling the `Element.html` constructor\n* calling the `DocumentFragment.html` constructor\n\n\n**BAD:**\n```dart\nvar script = ScriptElement()..src = 'foo.js';\n```\n\nThis rule has been removed.", "sinceDartSdk": "2.4" }, + { + "name": "unsafe_variance", + "description": "Unsafe type: Has a type variable in a non-covariant position.", + "categories": [ + "errorProne" + ], + "state": "experimental", + "incompatible": [], + "sets": [], + "fixStatus": "noFix", + "details": "Don't declare non-covariant members.\n\nAn instance variable whose type contains a type parameter of the\nenclosing class, mixin, or enum in a non-covariant position is\nlikely to cause run-time failures due to failing type\nchecks. For example, in `class C {...}`, an instance variable\nof the form `void Function(X) myVariable;` may cause this kind\nof run-time failure.\n\nThe same is true for a getter or method whose return type has a\nnon-covariant occurrence of a type parameter of the enclosing\ndeclaration.\n\nThis lint flags this kind of member declaration.\n\n**BAD:**\n```dart\nclass C {\n final bool Function(X) fun; // LINT\n C(this.fun);\n}\n\nvoid main() {\n C c = C((i) => i.isEven);\n c.fun(10); // Throws.\n}\n```\n\nThe problem is that `X` occurs as a parameter type in the type\nof `fun`. A better approach is to ensure that the non-covariant\nmember `fun` is _only_ used on `this`. We cannot strictly\nenforce this, but we can make it private and add a forwarding\nmethod `fun`:\n\n**BETTER:**\n```dart\nclass C {\n // ignore: unsafe_variance\n final bool Function(X) _fun;\n bool fun(X x) => _fun(x);\n C(this.fun);\n}\n\nvoid main() {\n C c = C((i) => i.isEven);\n c.fun(10); // Succeeds.\n}\n```\n\nA fully safe approach requires a feature that Dart does not yet\nhave, namely statically checked variance. With that, we could\nspecify that the type parameter `X` is invariant (`inout X`).\n\nAnother possibility is to declare the variable to have a safe\nbut more general type. It is then safe to use the variable\nitself, but every invocation will have to be checked at run\ntime:\n\n**HONEST:**\n```dart\nclass C {\n final bool Function(Never) fun;\n C(this.fun);\n}\n\nvoid main() {\n C c = C((i) => i.isEven);\n var cfun = c.fun; // Local variable, enables promotion.\n if (cfun is bool Function(int)) cfun(10); // Succeeds.\n if (cfun is bool Function(bool)) cfun(true); // Not called.\n}\n```", + "sinceDartSdk": "3.7-wip" + }, { "name": "use_build_context_synchronously", "description": "Do not use `BuildContext` across asynchronous gaps.", @@ -3476,7 +3521,7 @@ "sets": [], "fixStatus": "hasFix", "details": "**DO** use truncating division, '~/', instead of regular division ('/') followed\nby 'toInt()'.\n\nDart features a \"truncating division\" operator which is the same operation as\ndivision followed by truncation, but which is more concise and expressive, and\nmay be more performant on some platforms, for certain inputs.\n\n**BAD:**\n```dart\nvar x = (2 / 3).toInt();\n```\n\n**GOOD:**\n```dart\nvar x = 2 ~/ 3;\n```", - "sinceDartSdk": "3.6-wip" + "sinceDartSdk": "3.6" }, { "name": "valid_regexps", diff --git a/src/content/tools/diagnostic-messages.md b/src/content/tools/diagnostic-messages.md index e0f20cfd31..fe79b30b88 100644 --- a/src/content/tools/diagnostic-messages.md +++ b/src/content/tools/diagnostic-messages.md @@ -10710,6 +10710,14 @@ abstract class C { ### invalid_null_aware_operator +_The element can't be null, so the null-aware operator '?' is unnecessary._ + +_The map entry key can't be null, so the null-aware operator '?' is +unnecessary._ + +_The map entry value can't be null, so the null-aware operator '?' is +unnecessary._ + _The receiver can't be 'null' because of short-circuiting, so the null-aware operator '{0}' can't be used._ @@ -10770,6 +10778,14 @@ because of the cast to `String`, which is a non-nullable type. If `o` ever has the value `null`, the cast will fail and the invocation of `length` will not happen. +The following code produces this diagnostic because `s` can't be `null`: + +```dart +List makeSingletonList(String s) { + return [[!?!]s]; +} +``` + #### Common fixes Replace the null-aware operator with a non-null-aware equivalent; for @@ -23234,8 +23250,6 @@ void f() { ### unused_element -_A value for optional parameter '{0}' isn't ever given._ - _The declaration '{0}' isn't referenced._ #### Description @@ -23245,8 +23259,6 @@ referenced in the library that contains the declaration. The following kinds of declarations are analyzed: - Private top-level declarations and all of their members - Private members of public declarations -- Optional parameters of private functions for which a value is never - passed Not all references to an element will mark it as "used": - Assigning a value to a top-level variable (with a standard `=` @@ -23266,6 +23278,23 @@ produces this diagnostic: class [!_C!] {} ``` +#### Common fixes + +If the declaration isn't needed, then remove it. + +If the declaration is intended to be used, then add the code to use it. + +### unused_element_parameter + +_A value for optional parameter '{0}' isn't ever given._ + +#### Description + +The analyzer produces this diagnostic when a value is never passed for an +optional parameter declared within a private declaration. + +#### Example + Assuming that no code in the library passes a value for `y` in any invocation of `_m`, the following code produces this diagnostic: @@ -25990,7 +26019,7 @@ interop value and an unrelated JS interop type that will always be true and won' #### Description -The analyzer produces this diagnostic when an `is` test has either +The analyzer produces this diagnostic when an `is` test has either: - a JS interop type on the right-hand side, whether directly or as a type argument to another type, or - a JS interop value on the left-hand side. @@ -29119,6 +29148,117 @@ bool f(String s) { } ``` +### unsafe_variance + +_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. + +#### Example + +**BAD:** +```dart +class C { + final bool Function([!X!]) fun; // LINT + C(this.fun); +} + +void main() { + C c = C((i) => i.isEven); + c.fun(10); // Throws. +} +``` + +The problem is that `X` occurs as a parameter type in the type +of `fun`. + +#### 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: + +**BETTER:** +```dart +class C { + // ignore: unsafe_variance + final bool Function(X) _fun; + bool fun(X x) => _fun(x); + C(this._fun); +} + +void main() { + C c = C((i) => i.isEven); + c.fun(10); // Succeeds. +} +``` + +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: + +**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. +} +``` + +With this approach, `C` is not a subtype of `C`, so +`c` must have a different declared 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: + +**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. +} +``` + ### use_build_context_synchronously _Don't use 'BuildContext's across async gaps, guarded by an unrelated 'mounted'