69ed86fda6
Change-Id: If6006f5df03f90c50eb2860a3fa17118c8941b36 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/510820 Commit-Queue: Keerti Parthasarathy <keertip@google.com> Reviewed-by: Keerti Parthasarathy <keertip@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
17955 lines
482 KiB
YAML
17955 lines
482 KiB
YAML
# Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
|
|
# for details. All rights reserved. Use of this source code is governed by a
|
|
# BSD-style license that can be found in the LICENSE file.
|
|
|
|
# After editing this file to add a new lint rule or code,
|
|
# make sure to regenerate supporting classes by running:
|
|
#
|
|
# dart run pkg/linter/tool/generate_lints.dart
|
|
# dart run pkg/analyzer/tool/messages/generate.dart
|
|
|
|
# There is a fixed set of categories:
|
|
#
|
|
# * binarySize - rules that help to minimize binary size.
|
|
# * brevity - rules that encourage brevity in the source code.
|
|
# * documentationCommentMaintenance - rules that help to maintain documentation
|
|
# comments.
|
|
# * effectiveDart - rules that align with the Effective Dart style guide.
|
|
# * errorProne - rules that protect against error-prone code.
|
|
# * flutter - rules that help to write Flutter code.
|
|
# * languageFeatureUsage - rules that promote language feature usage.
|
|
# * memoryLeaks - rules that protect against possibly memory-leaking code.
|
|
# * nonPerformant - rules that protect against non-performant code.
|
|
# * pub - pub-related rules.
|
|
# * publicInterface - rules that promote a healthy public interface.
|
|
# * style - matters of style, largely derived from Effective Dart.
|
|
# * unintentional - rules that protect against code that probably doesn't do
|
|
# what you think it does, or that shouldn't be used as it is.
|
|
# * unusedCode - rules that protect against unused code.
|
|
# * web - rules that help to write code deployed to the web.
|
|
|
|
LinterLintCode:
|
|
alwaysDeclareReturnTypesOfFunctions:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: alwaysDeclareReturnTypes
|
|
problemMessage: "The function '#p0' should have a return type but doesn't."
|
|
correctionMessage: "Try adding a return type to the function."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a method or function doesn't
|
|
have an explicit return type.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the function `f`
|
|
doesn't have a return type:
|
|
|
|
```dart
|
|
[!f!]() {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add an explicit return type:
|
|
|
|
```dart
|
|
void f() {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** declare method return types.
|
|
|
|
When declaring a method or function *always* specify a return type.
|
|
Declaring return types for functions helps improve your codebase by allowing the
|
|
analyzer to more adequately check your code for errors that could occur during
|
|
runtime.
|
|
|
|
**BAD:**
|
|
```dart
|
|
main() { }
|
|
|
|
_bar() => _Foo();
|
|
|
|
class _Foo {
|
|
_foo() => 42;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void main() { }
|
|
|
|
_Foo _bar() => _Foo();
|
|
|
|
class _Foo {
|
|
int _foo() => 42;
|
|
}
|
|
|
|
typedef predicate = bool Function(Object o);
|
|
```
|
|
alwaysDeclareReturnTypesOfMethods:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: alwaysDeclareReturnTypes
|
|
problemMessage: "The method '#p0' should have a return type but doesn't."
|
|
correctionMessage: "Try adding a return type to the method."
|
|
hasPublishedDocs: true
|
|
alwaysPutControlBodyOnNewLine:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Statement should be on a separate line."
|
|
correctionMessage: "Try moving the statement to a new line."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the code being controlled by a
|
|
control flow statement (`if`, `for`, `while`, or `do`) is on the same line
|
|
as the control flow statement.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `return` statement
|
|
is on the same line as the `if` that controls whether the `return` will be
|
|
executed:
|
|
|
|
```dart
|
|
void f(bool b) {
|
|
if (b) [!return!];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Put the controlled statement onto a separate, indented, line:
|
|
|
|
```dart
|
|
void f(bool b) {
|
|
if (b)
|
|
return;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
From the [style guide for the flutter repo](https://flutter.dev/style-guide/):
|
|
|
|
**DO** separate the control structure expression from its statement.
|
|
|
|
Don't put the statement part of an `if`, `for`, `while`, `do` on the same line
|
|
as the expression, even if it is short. Doing so makes it unclear that there
|
|
is relevant code there. This is especially important for early returns.
|
|
|
|
**BAD:**
|
|
```dart
|
|
if (notReady) return;
|
|
|
|
if (notReady)
|
|
return;
|
|
else print('ok')
|
|
|
|
while (condition) i += 1;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (notReady)
|
|
return;
|
|
|
|
if (notReady)
|
|
return;
|
|
else
|
|
print('ok')
|
|
|
|
while (condition)
|
|
i += 1;
|
|
```
|
|
|
|
Note that this rule can conflict with the
|
|
[Dart formatter](https://dart.dev/tools/dart-format), and should not be enabled
|
|
when the Dart formatter is used.
|
|
alwaysPutRequiredNamedParametersFirst:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Required named parameters should be before optional named parameters."
|
|
correctionMessage: "Try moving the required named parameter to be before any optional named parameters."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when required named parameters occur
|
|
after optional named parameters.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the required parameter
|
|
`x` is after the optional parameter `y`:
|
|
|
|
```dart
|
|
void f({int? y, required int [!x!]}) {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Reorder the parameters so that all required named parameters are before
|
|
any optional named parameters:
|
|
|
|
```dart
|
|
void f({required int x, int? y}) {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** specify `required` on named parameter before other named parameters.
|
|
|
|
**BAD:**
|
|
```dart
|
|
m({b, c, required a}) ;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
m({required a, b, c}) ;
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
m({b, c, @required a}) ;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
m({@required a, b, c}) ;
|
|
```
|
|
alwaysRequireNonNullNamedParameters:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
stable: "2.0"
|
|
removed: "3.3"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
NOTE: This rule is removed in Dart 3.3.0; it is no longer functional.
|
|
|
|
**DO** specify `@required` on named parameters without a default value on which
|
|
an `assert(param != null)` is done.
|
|
|
|
**BAD:**
|
|
```dart
|
|
m1({a}) {
|
|
assert(a != null);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
m1({@required a}) {
|
|
assert(a != null);
|
|
}
|
|
|
|
m2({a: 1}) {
|
|
assert(a != null);
|
|
}
|
|
```
|
|
|
|
NOTE: Only asserts at the start of the bodies will be taken into account.
|
|
alwaysSpecifyTypesAddType:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: alwaysSpecifyTypes
|
|
problemMessage: "Missing type annotation."
|
|
correctionMessage: "Try adding a type annotation."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when
|
|
an explicit type annotation is missing.
|
|
|
|
This includes in the following cases:
|
|
|
|
- A variable declaration that uses `var`, `final`, or `const`
|
|
without a specific type.
|
|
- A function parameter lacks a type annotation.
|
|
- A collection literal lacks explicit type arguments.
|
|
- A reference to a generic class, mixin, or enum lacks type arguments.
|
|
|
|
Types annotated with `@optionalTypeArgs` from `package:meta` are
|
|
exempt from requiring type arguments.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because
|
|
the top-level variable uses `var` without an explicit type:
|
|
|
|
```dart
|
|
[!var!] x = 1;
|
|
```
|
|
|
|
The following code produces this diagnostic because
|
|
the parameter `p` lacks a type annotation:
|
|
|
|
```dart
|
|
void f([!p!]) {}
|
|
```
|
|
|
|
The following code produces this diagnostic because
|
|
the set literal lacks an explicit type argument:
|
|
|
|
```dart
|
|
Set<int> items = [!{!]1, 2};
|
|
```
|
|
|
|
The following code produces this diagnostic because
|
|
the generic type `C` is used without specifying its type argument:
|
|
|
|
```dart
|
|
class C<T> {}
|
|
|
|
[!C!] c = C<int>();
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add an explicit type annotation to variable declarations:
|
|
|
|
```dart
|
|
int x = 1;
|
|
```
|
|
|
|
Add a type annotation to parameters:
|
|
|
|
```dart
|
|
void f(int p) {}
|
|
```
|
|
|
|
Add explicit type arguments to collection literals:
|
|
|
|
```dart
|
|
Set<int> items = <int>{1, 2};
|
|
```
|
|
|
|
Add type arguments to generic type references:
|
|
|
|
```dart
|
|
class C<T> {}
|
|
|
|
C<int> c = C<int>();
|
|
```
|
|
|
|
If a type argument is meant to be optional,
|
|
annotate the declaration with `@optionalTypeArgs` from
|
|
`package:meta` to exempt it from this lint:
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
@optionalTypeArgs
|
|
class C<T> {}
|
|
|
|
C c = C();
|
|
```
|
|
deprecatedDetails: |-
|
|
From the [style guide for the flutter repo](https://flutter.dev/style-guide/):
|
|
|
|
**DO** specify type annotations.
|
|
|
|
Avoid `var` when specifying that a type is unknown and short-hands that elide
|
|
type annotations. Use `dynamic` if you are being explicit that the type is
|
|
unknown. Use `Object` if you are being explicit that you want an object that
|
|
implements `==` and `hashCode`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var foo = 10;
|
|
final bar = Bar();
|
|
const quux = 20;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
int foo = 10;
|
|
final Bar bar = Bar();
|
|
String baz = 'hello';
|
|
const int quux = 20;
|
|
```
|
|
|
|
NOTE: Using the `@optionalTypeArgs` annotation in the `meta` package, API
|
|
authors can special-case type parameters whose type needs to be dynamic but whose
|
|
declaration should be treated as optional. For example, suppose you have a
|
|
`Key` object whose type parameter you'd like to treat as optional. Using the
|
|
`@optionalTypeArgs` would look like this:
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
@optionalTypeArgs
|
|
class Key<T> {
|
|
...
|
|
}
|
|
|
|
void main() {
|
|
Key s = Key(); // OK!
|
|
}
|
|
```
|
|
alwaysSpecifyTypesReplaceKeyword:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
sharedName: alwaysSpecifyTypes
|
|
problemMessage: "Missing type annotation."
|
|
correctionMessage: "Try replacing '#p0' with '#p1'."
|
|
hasPublishedDocs: false
|
|
alwaysSpecifyTypesSpecifyType:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: alwaysSpecifyTypes
|
|
problemMessage: "Missing type annotation."
|
|
correctionMessage: "Try specifying the type '#p0'."
|
|
hasPublishedDocs: false
|
|
alwaysSpecifyTypesSplitToTypes:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: alwaysSpecifyTypes
|
|
problemMessage: "Missing type annotation."
|
|
correctionMessage: "Try splitting the declaration and specify the different type annotations."
|
|
hasPublishedDocs: false
|
|
alwaysUsePackageImports:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'package:' imports for files in the 'lib' directory."
|
|
correctionMessage: "Try converting the URI to a 'package:' URI."
|
|
state:
|
|
stable: "2.10"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an `import` in a library inside
|
|
the `lib` directory uses a relative path to import another library inside
|
|
the `lib` directory of the same package.
|
|
|
|
#### Example
|
|
|
|
Given that a file named `a.dart` and the code below are both inside the
|
|
`lib` directory of the same package, the following code produces this
|
|
diagnostic because a relative URI is used to import `a.dart`:
|
|
|
|
```dart
|
|
%ignore=uri_does_not_exist
|
|
import [!'a.dart'!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use a package import:
|
|
|
|
```dart
|
|
%ignore=uri_does_not_exist
|
|
import 'package:p/a.dart';
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** avoid relative imports for files in `lib/`.
|
|
|
|
When mixing relative and absolute imports it's possible to create confusion
|
|
where the same member gets imported in two different ways. One way to avoid
|
|
that is to ensure you consistently use absolute imports for files within the
|
|
`lib/` directory.
|
|
|
|
This is the opposite of 'prefer_relative_imports'.
|
|
|
|
You can also use 'avoid_relative_lib_imports' to disallow relative imports of
|
|
files within `lib/` directory outside of it (for example `test/`).
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'baz.dart';
|
|
|
|
import 'src/bag.dart'
|
|
|
|
import '../lib/baz.dart';
|
|
|
|
...
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'package:foo/bar.dart';
|
|
|
|
import 'package:foo/baz.dart';
|
|
|
|
import 'package:foo/src/baz.dart';
|
|
...
|
|
```
|
|
analyzerElementModelTrackingBad:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Bad tracking annotation for this member."
|
|
state:
|
|
internal: "3.10"
|
|
categories: [publicInterface]
|
|
deprecatedDetails: "For internal use only."
|
|
hasPublishedDocs: false
|
|
analyzerElementModelTrackingMoreThanOne:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "There can be only one tracking annotation."
|
|
state:
|
|
internal: "3.10"
|
|
categories: [publicInterface]
|
|
deprecatedDetails: "For internal use only."
|
|
hasPublishedDocs: false
|
|
analyzerElementModelTrackingZero:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "No required tracking annotation."
|
|
state:
|
|
internal: "3.10"
|
|
categories: [publicInterface]
|
|
deprecatedDetails: "For internal use only."
|
|
hasPublishedDocs: false
|
|
analyzerPublicApiBadPartDirective:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Part directives in the analyzer public API should point to files in the analyzer public API."
|
|
hasPublishedDocs: false
|
|
state:
|
|
internal: "3.10"
|
|
categories: [publicInterface]
|
|
deprecatedDetails: "For internal use only."
|
|
comment: |-
|
|
Lint issued if a file in the analyzer public API contains a `part`
|
|
directive that points to a file that's not in the analyzer public API.
|
|
|
|
The rationale for this lint is that if such a `part` directive were to
|
|
exist, it would cause all the members of the part file to become part of
|
|
the analyzer's public API, even though they don't appear to be public API.
|
|
|
|
Note that the analyzer doesn't make very much use of `part` directives,
|
|
but it may do so in the future once augmentations and enhanced parts are
|
|
supported.
|
|
analyzerPublicApiBadType:
|
|
type: lint
|
|
parameters:
|
|
String types: "list of types, separated by `, `"
|
|
problemMessage: "Element makes use of type(s) which is not part of the analyzer public API: #types."
|
|
hasPublishedDocs: false
|
|
state:
|
|
internal: "3.10"
|
|
categories: [publicInterface]
|
|
deprecatedDetails: "For internal use only."
|
|
comment: |-
|
|
Lint issued if a method, function, getter, or setter in the analyzer
|
|
public API makes use of a type that's not part of the analyzer public API,
|
|
or if a non-public type appears in an `extends`, `implements`, `with`, or
|
|
`on` clause.
|
|
|
|
The reason this is a problem is that it makes it possible for analyzer
|
|
clients to implicitly reference analyzer internal types. This can happen
|
|
in many ways; here are some examples:
|
|
|
|
- If `C` is a public API class that implements `B`, and `B` is a private
|
|
class with a getter called `x`, then a client can access `B.x` via `C`.
|
|
|
|
- If `f` has return type `T`, and `T` is a private class with a getter
|
|
called `x`, then a client can access `T.x` via `f().x`.
|
|
|
|
- If `f` has type `void Function(T)`, and `T` is a private class with a
|
|
getter called `x`, then a client can access `T.x` via
|
|
`var g = f; g = (t) { print(t.x); }`.
|
|
|
|
This lint can be suppressed either with an `ignore` comment, or by marking
|
|
the referenced type with `@AnalyzerPublicApi(...)`. The advantage of
|
|
marking the referenced type with `@AnalyzerPublicApi(...)` is that it
|
|
causes the members of referenced type to be checked by this lint.
|
|
analyzerPublicApiExperimentalInconsistency:
|
|
type: lint
|
|
parameters:
|
|
String types: "list of types, separated by `, `"
|
|
problemMessage: "Element makes use of experimental type(s), but is not itself marked with `@experimental`: #types."
|
|
hasPublishedDocs: false
|
|
state:
|
|
internal: "3.10"
|
|
categories: [publicInterface]
|
|
deprecatedDetails: "For internal use only."
|
|
comment: |-
|
|
Lint issued if an element in the analyzer public API makes use
|
|
of a type that's annotated `@experimental`, but the element
|
|
itself is not annotated `@experimental`.
|
|
|
|
The reason this is a problem is that it makes it possible for
|
|
analyzer clients to implicitly reference analyzer experimental
|
|
types. This can happen in many ways; here are some examples:
|
|
|
|
- If `C` is a non-experimental public API class that implements
|
|
`B`, and `B` is an experimental public API class with a getter
|
|
called `x`, then a client can access `B.x` via `C`.
|
|
|
|
- If `f` has return type `T`, and `T` is an experimental public
|
|
API class with a getter called `x`, then a client can access
|
|
`T.x` via `f().x`.
|
|
|
|
- If `f` has type `void Function(T)`, and `T` is an experimental
|
|
public API class with a getter called `x`, then a client can
|
|
access `T.x` via `var g = f; g = (t) { print(t.x); }`.
|
|
analyzerPublicApiExportsNonPublicName:
|
|
type: lint
|
|
parameters:
|
|
String elements: "List of elements, separated by `, `"
|
|
problemMessage: "Export directive exports element(s) that are not part of the analyzer public API: #elements."
|
|
hasPublishedDocs: false
|
|
state:
|
|
internal: "3.10"
|
|
categories: [publicInterface]
|
|
deprecatedDetails: "For internal use only."
|
|
comment: |-
|
|
Lint issued if a file in the analyzer public API contains an `export`
|
|
directive that exports a name that's not part of the analyzer public API.
|
|
|
|
This lint can be suppressed either with an `ignore` comment, or by marking
|
|
the exported declaration with `@AnalyzerPublicApi(...)`. The advantage of
|
|
marking the exported declaration with `@AnalyzerPublicApi(...)` is that it
|
|
causes the members of the exported declaration to be checked by this lint.
|
|
analyzerPublicApiImplInPublicApi:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: 'Declarations in the analyzer public API should not end in "Impl".'
|
|
hasPublishedDocs: false
|
|
state:
|
|
internal: "3.10"
|
|
categories: [publicInterface]
|
|
deprecatedDetails: "For internal use only."
|
|
comment: |-
|
|
Lint issued if a top level declaration in the analyzer public API has a
|
|
name ending in `Impl`.
|
|
|
|
Such declarations are not meant to be members of the analyzer public API,
|
|
so if they are either declared outside of `package:analyzer/src`, or
|
|
marked with `@AnalyzerPublicApi(...)`, that is almost certainly a mistake.
|
|
annotateOverrides:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The member '#p0' overrides an inherited member but isn't annotated with '@override'."
|
|
correctionMessage: "Try adding the '@override' annotation."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a member overrides an inherited
|
|
member, but isn't annotated with `@override`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the method `m` in the
|
|
class `B` overrides the method with the same name in class `A`, but isn't
|
|
marked as an intentional override:
|
|
|
|
```dart
|
|
class A {
|
|
void m() {}
|
|
}
|
|
|
|
class B extends A {
|
|
void [!m!]() {}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the member in the subclass is intended to override the member in the
|
|
superclass, then add an `@override` annotation:
|
|
|
|
```dart
|
|
class A {
|
|
void m() {}
|
|
}
|
|
|
|
class B extends A {
|
|
@override
|
|
void m() {}
|
|
}
|
|
```
|
|
|
|
If the member in the subclass is not intended to override the member in
|
|
the superclass, then rename one of the members:
|
|
|
|
```dart
|
|
class A {
|
|
void m() {}
|
|
}
|
|
|
|
class B extends A {
|
|
void m2() {}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** annotate overridden methods and fields.
|
|
|
|
This practice improves code readability and helps protect against
|
|
unintentionally overriding superclass members.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Cat {
|
|
int get lives => 9;
|
|
}
|
|
|
|
class Lucky extends Cat {
|
|
final int lives = 14;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
abstract class Dog {
|
|
String get breed;
|
|
void bark() {}
|
|
}
|
|
|
|
class Husky extends Dog {
|
|
@override
|
|
final String breed = 'Husky';
|
|
@override
|
|
void bark() {}
|
|
}
|
|
```
|
|
annotateRedeclares:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The member '#p0' is redeclaring but isn't annotated with '@redeclare'."
|
|
correctionMessage: "Try adding the '@redeclare' annotation."
|
|
state:
|
|
experimental: "3.2"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an extension type member redeclares
|
|
a member from an implemented interface without the `@redeclare` annotation.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `f` method in the
|
|
extension type E redeclares the `f` method from `C` without the `@redeclare` annotation:
|
|
|
|
```dart
|
|
class C {
|
|
void f() { }
|
|
}
|
|
|
|
extension type E(C c) implements C {
|
|
void [!f!]() { }
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add the `@redeclare` annotation to make the redeclaration explicit:
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
class C {
|
|
void f() { }
|
|
}
|
|
|
|
extension type E(C c) implements C {
|
|
@redeclare
|
|
void f() { }
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** annotate redeclared members.
|
|
|
|
This practice improves code readability and helps protect against
|
|
unintentionally redeclaring members or being surprised when a member ceases to
|
|
redeclare (due for example to a rename refactoring).
|
|
|
|
**BAD:**
|
|
```dart
|
|
class C {
|
|
void f() { }
|
|
}
|
|
|
|
extension type E(C c) implements C {
|
|
void f() {
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
class C {
|
|
void f() { }
|
|
}
|
|
|
|
extension type E(C c) implements C {
|
|
@redeclare
|
|
void f() {
|
|
...
|
|
}
|
|
}
|
|
```
|
|
asyncReturnWithNoAwait:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Returning a 'Future' without 'await'."
|
|
correctionMessage: "Try adding an 'await' or making the body non-async."
|
|
state:
|
|
stable: "3.13"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `Future` is returned from an
|
|
`async` function without using `await`.
|
|
|
|
Returning an unawaited `Future` from an `async` function means any
|
|
exception thrown by the `Future` might not propagate as expected. Having
|
|
the exception thrown at the `await` site helps identify the source of the
|
|
exception and makes it easier to debug.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because it returns the `Future`
|
|
from `futureString` without using `await`:
|
|
|
|
```dart
|
|
Future<String> futureString(Future<String> value) async {
|
|
[!return!] value;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add `await` before the returned `Future`:
|
|
|
|
```dart
|
|
Future<String> futureString(Future<String> value) async {
|
|
return await value;
|
|
}
|
|
```
|
|
|
|
If you don't need to use `await`, then remove `async` from the function body:
|
|
|
|
```dart
|
|
Future<String> futureString(Future<String> value) {
|
|
return value;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use `await` when returning a `Future` from an `async` function.
|
|
|
|
**BAD:**
|
|
```dart
|
|
Future<String> futureString(Future<String> value) async {
|
|
return value;
|
|
}
|
|
Future<int> futureInt(Future<int> value) async => value;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Future<String> futureString(Future<String> value) async {
|
|
return await value;
|
|
}
|
|
Future<int> futureInt(Future<int> value) => value;
|
|
```
|
|
avoidAnnotatingWithDynamic:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary 'dynamic' type annotation."
|
|
correctionMessage: "Try removing the type 'dynamic'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a parameter has an explicit
|
|
`dynamic` type annotation. Because `dynamic` is the default type for
|
|
parameters without a type annotation, the annotation is unnecessary.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the parameter `p`
|
|
is explicitly annotated with `dynamic`:
|
|
|
|
```dart
|
|
void f([!dynamic p!]) {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the `dynamic` type annotation:
|
|
|
|
```dart
|
|
void f(p) {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** annotating with `dynamic` when not required.
|
|
|
|
As `dynamic` is the assumed return value of a function or method, it is usually
|
|
not necessary to annotate it.
|
|
|
|
**BAD:**
|
|
```dart
|
|
dynamic lookUpOrDefault(String name, Map map, dynamic defaultValue) {
|
|
var value = map[name];
|
|
if (value != null) return value;
|
|
return defaultValue;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
lookUpOrDefault(String name, Map map, defaultValue) {
|
|
var value = map[name];
|
|
if (value != null) return value;
|
|
return defaultValue;
|
|
}
|
|
```
|
|
avoidAs:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
stable: "2.0"
|
|
removed: "3.0"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
NOTE: This rule was removed from the SDK in Dart 3; it is no longer functional.
|
|
Its advice is compiler-specific and mostly obsolete with null safety.
|
|
|
|
**AVOID** using `as`.
|
|
|
|
If you know the type is correct, use an assertion or assign to a more
|
|
narrowly-typed variable (this avoids the type check in release mode; `as` is not
|
|
compiled out in release mode). If you don't know whether the type is
|
|
correct, check using `is` (this avoids the exception that `as` raises).
|
|
|
|
**BAD:**
|
|
```dart
|
|
(pm as Person).firstName = 'Seth';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (pm is Person)
|
|
pm.firstName = 'Seth';
|
|
```
|
|
|
|
but certainly not
|
|
|
|
**BAD:**
|
|
```dart
|
|
try {
|
|
(pm as Person).firstName = 'Seth';
|
|
} on CastError { }
|
|
```
|
|
|
|
Note that an exception is made in the case of `dynamic` since the cast has no
|
|
performance impact.
|
|
|
|
**OK:**
|
|
```dart
|
|
HasScrollDirection scrollable = renderObject as dynamic;
|
|
```
|
|
avoidBoolLiteralsInConditionalExpressions:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Conditional expressions with a 'bool' literal can be simplified."
|
|
correctionMessage: "Try rewriting the expression to use either '&&' or '||'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a conditional expression uses a
|
|
boolean literal, and both branches are booleans. You can simplify these
|
|
expressions using `&&` or `||`.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the then expression
|
|
is the `bool` literal `true`:
|
|
|
|
```dart
|
|
bool f(bool a, bool b) => [!a ? true : b!];
|
|
```
|
|
|
|
The following code produces this diagnostic because the else expression
|
|
is the `bool` literal `false`:
|
|
|
|
```dart
|
|
bool f(bool a, bool b) => [!a ? b : false!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the conditional expression with the equivalent logical expression.
|
|
If the then expression is a boolean literal, replace it with one of the following:
|
|
|
|
```dart
|
|
bool f(bool a, bool b) => a || b; // replaces: a ? true : b
|
|
bool g(bool a, bool b) => !a && b; // replaces: a ? false : b
|
|
```
|
|
|
|
If the else expression is a boolean literal, replace it with one of the following:
|
|
|
|
```dart
|
|
bool f(bool a, bool b) => !a || b; // replaces: a ? b : true
|
|
bool g(bool a, bool b) => a && b; // replaces: a ? b : false
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** `bool` literals in conditional expressions.
|
|
|
|
**BAD:**
|
|
```dart
|
|
condition ? true : boolExpression
|
|
condition ? false : boolExpression
|
|
condition ? boolExpression : true
|
|
condition ? boolExpression : false
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
condition || boolExpression
|
|
!condition && boolExpression
|
|
!condition || boolExpression
|
|
condition && boolExpression
|
|
```
|
|
|
|
avoidCatchesWithoutOnClauses:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Catch clause should use 'on' to specify the type of exception being caught."
|
|
correctionMessage: "Try adding an 'on' clause before the 'catch'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `catch` clause lacks an
|
|
`on` clause to specify the exception type. Catching all exceptions
|
|
can mask unexpected errors and make debugging more difficult.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `catch` clause
|
|
has no `on` clause to specify the exception type:
|
|
|
|
```dart
|
|
void f() {
|
|
try {
|
|
int.parse('?');
|
|
} [!catch!] (e) {
|
|
print(e);
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add an `on` clause to specify the type of exception to catch:
|
|
|
|
```dart
|
|
void f() {
|
|
try {
|
|
int.parse('?');
|
|
} on FormatException catch (e) {
|
|
print(e);
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/usage#avoid-catches-without-on-clauses):
|
|
|
|
**AVOID** catches without on clauses.
|
|
|
|
Using catch clauses without on clauses make your code prone to encountering
|
|
unexpected errors that won't be thrown (and thus will go unnoticed).
|
|
|
|
**BAD:**
|
|
```dart
|
|
try {
|
|
somethingRisky()
|
|
} catch(e) {
|
|
doSomething(e);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
try {
|
|
somethingRisky()
|
|
} on Exception catch(e) {
|
|
doSomething(e);
|
|
}
|
|
```
|
|
|
|
A few exceptional cases are allowed:
|
|
|
|
* If the body of the catch rethrows the exception.
|
|
* If the caught exception is "directly used" in an argument to `Future.error`,
|
|
`Completer.completeError`, or `FlutterError.reportError`, or any function with
|
|
a return type of `Never`.
|
|
* If the caught exception is "directly used" in a new throw-expression.
|
|
|
|
In these cases, "directly used" means that the exception is referenced within
|
|
the relevant code (like within an argument). If the exception variable is
|
|
referenced _before_ the relevant code, for example to instantiate a wrapper
|
|
exception, the variable is not "directly used."
|
|
avoidCatchingErrorsClass:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: avoidCatchingErrors
|
|
problemMessage: "The type 'Error' should not be caught."
|
|
correctionMessage: "Try removing the catch or catching an 'Exception' instead."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DON'T** explicitly catch `Error` or types that implement it.
|
|
|
|
Errors differ from Exceptions in that Errors can be analyzed and prevented prior
|
|
to runtime. It should almost never be necessary to catch an error at runtime.
|
|
|
|
**BAD:**
|
|
```dart
|
|
try {
|
|
somethingRisky();
|
|
} on Error catch(e) {
|
|
doSomething(e);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
try {
|
|
somethingRisky();
|
|
} on Exception catch(e) {
|
|
doSomething(e);
|
|
}
|
|
```
|
|
avoidCatchingErrorsSubclass:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: avoidCatchingErrors
|
|
problemMessage: "The type '#p0' should not be caught because it is a subclass of 'Error'."
|
|
correctionMessage: "Try removing the catch or catching an 'Exception' instead."
|
|
hasPublishedDocs: false
|
|
avoidClassesWithOnlyStaticMembers:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Classes should define instance members."
|
|
correctionMessage: "Try adding instance behavior or moving the members out of the class."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [effectiveDart, languageFeatureUsage, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/design#avoid-defining-a-class-that-contains-only-static-members):
|
|
|
|
**AVOID** defining a class that contains only static members.
|
|
|
|
Creating classes with the sole purpose of providing utility or otherwise static
|
|
methods is discouraged. Dart allows functions to exist outside of classes for
|
|
this very reason.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class DateUtils {
|
|
static DateTime mostRecent(List<DateTime> dates) {
|
|
return dates.reduce((a, b) => a.isAfter(b) ? a : b);
|
|
}
|
|
}
|
|
|
|
class _Favorites {
|
|
static const mammal = 'weasel';
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
DateTime mostRecent(List<DateTime> dates) {
|
|
return dates.reduce((a, b) => a.isAfter(b) ? a : b);
|
|
}
|
|
|
|
const _favoriteMammal = 'weasel';
|
|
```
|
|
avoidDoubleAndIntChecks:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Explicit check for double or int."
|
|
correctionMessage: "Try removing the check."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne, web]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an `if` statement
|
|
tests the type of a variable with `is double` and the
|
|
following `else if` tests the same variable with `is int`.
|
|
|
|
When compiled to JavaScript, both `int` and `double` values
|
|
are represented as JavaScript `Number` values.
|
|
Because every `int` value also satisfies the `is double` check,
|
|
the `is int` branch is unreachable.
|
|
|
|
To learn more about how Dart represents numbers on different platforms,
|
|
including when compiled to JavaScript, check out
|
|
[Numbers in Dart](https://dart.dev/resources/language/number-representation).
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because
|
|
`x` is type tested with `is double` and then `is int`,
|
|
which makes the `is int` branch unreachable when compiled to JavaScript:
|
|
|
|
```dart
|
|
void f(Object x) {
|
|
if (x is double) {
|
|
print('double');
|
|
} else if ([!x is int!]) {
|
|
print('int');
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the distinction between `int` and `double` isn't meaningful,
|
|
then check if the type is `num` instead:
|
|
|
|
```dart
|
|
void f(Object x) {
|
|
if (x is num) {
|
|
print('num');
|
|
}
|
|
}
|
|
```
|
|
|
|
If both branches are needed and it's ok for
|
|
integer `double` values (such as `3.0`) to take the `is int` branch,
|
|
then check `is int` first. This ensures that
|
|
integer values take the `is int` branch on all platforms:
|
|
|
|
```dart
|
|
void f(Object x) {
|
|
if (x is int) {
|
|
print('int');
|
|
} else if (x is double) {
|
|
print('double');
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** to check if type is `double` or `int`.
|
|
|
|
When compiled to JS, integer values are represented as floats. That can lead to
|
|
some unexpected behavior when using either `is` or `is!` where the type is
|
|
either `int` or `double`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
f(num x) {
|
|
if (x is double) {
|
|
...
|
|
} else if (x is int) {
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
f(dynamic x) {
|
|
if (x is num) {
|
|
...
|
|
} else {
|
|
...
|
|
}
|
|
}
|
|
```
|
|
avoidDynamicCalls:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Method invocation or property access on a 'dynamic' target."
|
|
correctionMessage: "Try giving the target a type."
|
|
state:
|
|
experimental: "2.12"
|
|
stable: "2.14"
|
|
categories: [binarySize, errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a member of a class is accessed
|
|
on an expression whose type is `dynamic`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the getter `length` is
|
|
being invoked on `s`, which has the type `dynamic`:
|
|
|
|
```dart
|
|
void f(dynamic s) {
|
|
[!s!].length;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Provide enough type information that the expression has a type other than
|
|
`dynamic`:
|
|
|
|
```dart
|
|
void f(String s) {
|
|
s.length;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** avoid method calls or accessing properties on an object that is either
|
|
explicitly or implicitly statically typed `dynamic`. Dynamic calls are treated
|
|
slightly different in every runtime environment and compiler, but most
|
|
production modes (and even some development modes) have both compile size and
|
|
runtime performance penalties associated with dynamic calls.
|
|
|
|
Additionally, targets typed `dynamic` disables most static analysis, meaning it
|
|
is easier to lead to a runtime `NoSuchMethodError` or `TypeError` than properly
|
|
statically typed Dart code.
|
|
|
|
There is an exception to methods and properties that exist on `Object?`:
|
|
- `a.hashCode`
|
|
- `a.runtimeType`
|
|
- `a.noSuchMethod(someInvocation)`
|
|
- `a.toString()`
|
|
|
|
... these members are dynamically dispatched in the web-based runtimes, but not
|
|
in the VM-based ones. Additionally, they are so common that it would be very
|
|
punishing to disallow `any.toString()` or `any == true`, for example.
|
|
|
|
Note that despite `Function` being a type, the semantics are close to identical
|
|
to `dynamic`, and calls to an object that is typed `Function` will also trigger
|
|
this lint.
|
|
|
|
Dynamic calls are allowed on cast expressions (`as dynamic` or `as Function`).
|
|
|
|
**BAD:**
|
|
```dart
|
|
void explicitDynamicType(dynamic object) {
|
|
print(object.foo());
|
|
}
|
|
|
|
void implicitDynamicType(object) {
|
|
print(object.foo());
|
|
}
|
|
|
|
abstract class SomeWrapper {
|
|
T doSomething<T>();
|
|
}
|
|
|
|
void inferredDynamicType(SomeWrapper wrapper) {
|
|
var object = wrapper.doSomething();
|
|
print(object.foo());
|
|
}
|
|
|
|
void callDynamic(dynamic function) {
|
|
function();
|
|
}
|
|
|
|
void functionType(Function function) {
|
|
function();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void explicitType(Fooable object) {
|
|
object.foo();
|
|
}
|
|
|
|
void castedType(dynamic object) {
|
|
(object as Fooable).foo();
|
|
}
|
|
|
|
abstract class SomeWrapper {
|
|
T doSomething<T>();
|
|
}
|
|
|
|
void inferredType(SomeWrapper wrapper) {
|
|
var object = wrapper.doSomething<Fooable>();
|
|
object.foo();
|
|
}
|
|
|
|
void functionTypeWithParameters(Function() function) {
|
|
function();
|
|
}
|
|
```
|
|
avoidEmptyElse:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Empty statements are not allowed in an 'else' clause."
|
|
correctionMessage: "Try removing the empty statement or removing the else clause."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the statement after an `else`
|
|
is an empty statement (a semicolon).
|
|
|
|
For more information, see the documentation for
|
|
[`avoid_empty_else`](https://dart.dev/diagnostics/avoid_empty_else).
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the statement
|
|
following the `else` is an empty statement:
|
|
|
|
```dart
|
|
void f(int x, int y) {
|
|
if (x > y)
|
|
print("1");
|
|
else [!;!]
|
|
print("2");
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the statement after the empty statement is intended to be executed only
|
|
when the condition is `false`, then remove the empty statement:
|
|
|
|
```dart
|
|
void f(int x, int y) {
|
|
if (x > y)
|
|
print("1");
|
|
else
|
|
print("2");
|
|
}
|
|
```
|
|
|
|
If there is no code that is intended to be executed only when the
|
|
condition is `false`, then remove the whole `else` clause:
|
|
|
|
```dart
|
|
void f(int x, int y) {
|
|
if (x > y)
|
|
print("1");
|
|
print("2");
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** empty statements in the `else` clause of `if` statements.
|
|
|
|
**BAD:**
|
|
```dart
|
|
if (x > y)
|
|
print('1');
|
|
else ;
|
|
print('2');
|
|
```
|
|
|
|
If you want a statement that follows the empty clause to _conditionally_ run,
|
|
remove the dangling semicolon to include it in the `else` clause.
|
|
Optionally, also enclose the else's statement in a block.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (x > y)
|
|
print('1');
|
|
else
|
|
print('2');
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (x > y) {
|
|
print('1');
|
|
} else {
|
|
print('2');
|
|
}
|
|
```
|
|
|
|
If you want a statement that follows the empty clause to _unconditionally_ run,
|
|
remove the `else` clause.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (x > y) print('1');
|
|
|
|
print('2');
|
|
```
|
|
avoidEqualsAndHashCodeOnMutableClasses:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The method '#p0' should not be overridden in classes not annotated with '@immutable'."
|
|
correctionMessage: "Try removing the override or annotating the class with '@immutable'."
|
|
state:
|
|
stable: "2.6"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/design#avoid-defining-custom-equality-for-mutable-classes):
|
|
|
|
**AVOID** overloading operator == and hashCode on classes not marked `@immutable`.
|
|
|
|
If a class is not immutable, overloading `operator ==` and `hashCode` can
|
|
lead to unpredictable and undesirable behavior when used in collections.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class B {
|
|
String key;
|
|
const B(this.key);
|
|
@override
|
|
operator ==(other) => other is B && other.key == key;
|
|
@override
|
|
int get hashCode => key.hashCode;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
@immutable
|
|
class A {
|
|
final String key;
|
|
const A(this.key);
|
|
@override
|
|
operator ==(other) => other is A && other.key == key;
|
|
@override
|
|
int get hashCode => key.hashCode;
|
|
}
|
|
```
|
|
|
|
NOTE: The lint checks the use of the `@immutable` annotation, and will trigger
|
|
even if the class is otherwise not mutable. Thus:
|
|
|
|
**BAD:**
|
|
```dart
|
|
class C {
|
|
final String key;
|
|
const C(this.key);
|
|
@override
|
|
operator ==(other) => other is C && other.key == key;
|
|
@override
|
|
int get hashCode => key.hashCode;
|
|
}
|
|
```
|
|
avoidEscapingInnerQuotes:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
problemMessage: "Unnecessary escape of '#p0'."
|
|
correctionMessage: "Try changing the outer quotes to '#p1'."
|
|
state:
|
|
stable: "2.8"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Avoid escaping inner quotes by converting surrounding quotes.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var s = 'It\'s not fun';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var s = "It's not fun";
|
|
```
|
|
avoidFieldInitializersInConstClasses:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Fields in 'const' classes should not have initializers."
|
|
correctionMessage: "Try converting the field to a getter or initialize the field in the constructors."
|
|
state:
|
|
stable: "2.0"
|
|
# TODO(srawlins): This rule has nothing to do with style. It is to reduce
|
|
# runtime memory usage. But we don't have a Category for that yet.
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**AVOID** field initializers in const classes.
|
|
|
|
Instead of `final x = const expr;`, you should write `get x => const expr;` and
|
|
not allocate a useless field. As of April 2018 this is true for the VM, but not
|
|
for code that will be compiled to JS.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
final a = const [];
|
|
const A();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {
|
|
get a => const [];
|
|
const A();
|
|
}
|
|
```
|
|
avoidFinalParameters:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Parameters should not be marked as 'final'."
|
|
correctionMessage: "Try removing the keyword 'final'."
|
|
state:
|
|
stable: "2.16"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
NOTE: This rule is removed in Dart 3.13.0; it is no longer functional.
|
|
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a parameter declaration is
|
|
marked with the `final` keyword.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the
|
|
parameter `a` is marked as `final`:
|
|
|
|
```dart
|
|
// @dart=3.12
|
|
void f([!final!] String a) {
|
|
print(a);
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the
|
|
closure parameter `a` is marked as `final`:
|
|
|
|
```dart
|
|
// @dart=3.12
|
|
var f = ([!final!] int a) => print(a);
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the `final` keyword from the parameter declaration:
|
|
|
|
```dart
|
|
void f(String a) {
|
|
print(a);
|
|
}
|
|
```
|
|
|
|
```dart
|
|
var f = (int a) => print(a);
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** declaring parameters as `final`.
|
|
|
|
Declaring parameters as `final` can lead to unnecessarily verbose code,
|
|
especially when using the "parameter_assignments" rule.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void goodParameter(final String label) { // LINT
|
|
print(label);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void badParameter(String label) { // OK
|
|
print(label);
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void goodExpression(final int value) => print(value); // LINT
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void badExpression(int value) => print(value); // OK
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
[1, 4, 6, 8].forEach((final value) => print(value + 2)); // LINT
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
[1, 4, 6, 8].forEach((value) => print(value + 2)); // OK
|
|
```
|
|
avoidFunctionLiteralsInForeachCalls:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Function literals shouldn't be passed to 'forEach'."
|
|
correctionMessage: "Try using a 'for' loop."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the argument to
|
|
`Iterable.forEach` is a closure.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the argument to the
|
|
invocation of `forEach` is a closure:
|
|
|
|
```dart
|
|
void f(Iterable<String> s) {
|
|
s.[!forEach!]((e) => print(e));
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the closure can be replaced by a tear-off, then replace the closure:
|
|
|
|
```dart
|
|
void f(Iterable<String> s) {
|
|
s.forEach(print);
|
|
}
|
|
```
|
|
|
|
If the closure can't be replaced by a tear-off, then use a `for` loop to
|
|
iterate over the elements:
|
|
|
|
```dart
|
|
void f(Iterable<String> s) {
|
|
for (var e in s) {
|
|
print(e);
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** using `forEach` with a function literal.
|
|
|
|
The `for` loop enables a developer to be clear and explicit as to their intent.
|
|
A return in the body of the `for` loop returns from the body of the function,
|
|
where as a return in the body of the `forEach` closure only returns a value
|
|
for that iteration of the `forEach`. The body of a `for` loop can contain
|
|
`await`s, while the closure body of a `forEach` cannot.
|
|
|
|
**BAD:**
|
|
```dart
|
|
people.forEach((person) {
|
|
...
|
|
});
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
for (var person in people) {
|
|
...
|
|
}
|
|
```
|
|
avoidFutureorVoid:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't use the type 'FutureOr<void>'."
|
|
correctionMessage: "Try using 'Future<void>?' or 'void'."
|
|
state:
|
|
experimental: "3.6"
|
|
categories: [errorProne, unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the type `FutureOr<void>`
|
|
is used as the type of a result (to be precise: it is used in a
|
|
position that isn't contravariant). The type `FutureOr<void>` is
|
|
problematic because it may appear to encode that a result is either a
|
|
`Future<void>`, or the result should be discarded (when it is
|
|
`void`). However, there is no safe way to detect whether we have one
|
|
or the other case because an expression of type `void` can evaluate
|
|
to any object whatsoever, including a future of any type.
|
|
|
|
It is also conceptually unsound to have a type whose meaning is
|
|
something like "ignore this object; also, take a look because it
|
|
might be a future".
|
|
|
|
An exception is made for contravariant occurrences of the type
|
|
`FutureOr<void>` (e.g., for the type of a formal parameter), and no
|
|
warning is emitted for these occurrences. The reason for this
|
|
exception is that the type does not describe a result, it describes a
|
|
constraint on a value provided by others. Similarly, an exception is
|
|
made for type alias declarations, because they may well be used in a
|
|
contravariant position (e.g., as the type of a formal
|
|
parameter). Hence, in type alias declarations, only the type
|
|
parameter bounds are checked.
|
|
|
|
#### Example
|
|
|
|
```dart
|
|
import 'dart:async';
|
|
|
|
[!FutureOr<void>!] m() => null;
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
A replacement for the type `FutureOr<void>` which is often useful is
|
|
`Future<void>?`. This type encodes that a result is either a
|
|
`Future<void>` or it is null, and there is no ambiguity at run time
|
|
since no object can have both types.
|
|
|
|
It may not always be possible to use the type `Future<void>?` as a
|
|
replacement for the type `FutureOr<void>`, because the latter is a
|
|
supertype of all types, and the former is not. In this case it may be a
|
|
useful remedy to replace `FutureOr<void>` by the type `void`.
|
|
deprecatedDetails: |-
|
|
**AVOID** using `FutureOr<void>` as the type of a result. This type is
|
|
problematic because it may appear to encode that a result is either a
|
|
`Future<void>`, or the result should be discarded (when it is `void`).
|
|
However, there is no safe way to detect whether we have one or the other
|
|
case (because an expression of type `void` can evaluate to any object
|
|
whatsoever, including a future of any type).
|
|
|
|
It is also conceptually unsound to have a type whose meaning is something
|
|
like "ignore this object; also, take a look because it might be a future".
|
|
|
|
An exception is made for contravariant occurrences of the type
|
|
`FutureOr<void>` (e.g., for the type of a formal parameter), and no
|
|
warning is emitted for these occurrences. The reason for this exception
|
|
is that the type does not describe a result, it describes a constraint
|
|
on a value provided by others. Similarly, an exception is made for type
|
|
alias declarations, because they may well be used in a contravariant
|
|
position (e.g., as the type of a formal parameter). Hence, in type alias
|
|
declarations, only the type parameter bounds are checked.
|
|
|
|
A replacement for the type `FutureOr<void>` which is often useful is
|
|
`Future<void>?`. This type encodes that the result is either a
|
|
`Future<void>` or it is null, and there is no ambiguity at run time
|
|
since no object can have both types.
|
|
|
|
It may not always be possible to use the type `Future<void>?` as a
|
|
replacement for the type `FutureOr<void>`, because the latter is a
|
|
supertype of all types, and the former is not. In this case it may be a
|
|
useful remedy to replace `FutureOr<void>` by the type `void`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
FutureOr<void> m() {...}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Future<void>? m() {...}
|
|
```
|
|
|
|
**This rule is experimental.**
|
|
It is being evaluated, and it might be changed or removed.
|
|
Feedback on its behavior is welcome! The primary relevant issue is
|
|
[dart-lang/sdk#59292](https://github.com/dart-lang/sdk/issues/59232).
|
|
avoidImplementingValueTypes:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Classes that override '==' should not be implemented."
|
|
correctionMessage: "Try removing the class from the 'implements' clause."
|
|
state:
|
|
stable: "2.1"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DON'T** implement classes that override `==`.
|
|
|
|
The `==` operator is contractually required to be an equivalence relation;
|
|
that is, symmetrically for all objects `o1` and `o2`, `o1 == o2` and `o2 == o1`
|
|
must either both be true, or both be false.
|
|
|
|
> _NOTE_: Dart does not have true _value types_, so instead we consider a class
|
|
> that implements `==` as a _proxy_ for identifying value types.
|
|
|
|
When using `implements`, you do not inherit the method body of `==`, making it
|
|
nearly impossible to follow the contract of `==`. Classes that override `==`
|
|
typically are usable directly in tests _without_ creating mocks or fakes as
|
|
well. For example, for a given class `Size`:
|
|
|
|
```dart
|
|
class Size {
|
|
final int inBytes;
|
|
const Size(this.inBytes);
|
|
|
|
@override
|
|
bool operator ==(Object other) => other is Size && other.inBytes == inBytes;
|
|
|
|
@override
|
|
int get hashCode => inBytes.hashCode;
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
class CustomSize implements Size {
|
|
final int inBytes;
|
|
const CustomSize(this.inBytes);
|
|
|
|
int get inKilobytes => inBytes ~/ 1000;
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'package:test/test.dart';
|
|
import 'size.dart';
|
|
|
|
class FakeSize implements Size {
|
|
int inBytes = 0;
|
|
}
|
|
|
|
void main() {
|
|
test('should not throw on a size >1Kb', () {
|
|
expect(() => someFunction(FakeSize()..inBytes = 1001), returnsNormally);
|
|
});
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class ExtendedSize extends Size {
|
|
ExtendedSize(int inBytes) : super(inBytes);
|
|
|
|
int get inKilobytes => inBytes ~/ 1000;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'package:test/test.dart';
|
|
import 'size.dart';
|
|
|
|
void main() {
|
|
test('should not throw on a size >1Kb', () {
|
|
expect(() => someFunction(Size(1001)), returnsNormally);
|
|
});
|
|
}
|
|
```
|
|
avoidInitToNull:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Redundant initialization to 'null'."
|
|
correctionMessage: "Try removing the initializer."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a nullable variable is
|
|
explicitly initialized to `null`. The variable can be a local variable,
|
|
field, or top-level variable.
|
|
|
|
A variable or field that isn't explicitly initialized automatically gets
|
|
initialized to `null`. There's no concept of "uninitialized memory" in
|
|
Dart.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the variable `f` is
|
|
explicitly initialized to `null`:
|
|
|
|
```dart
|
|
class C {
|
|
int? [!f = null!];
|
|
|
|
void m() {
|
|
if (f != null) {
|
|
print(f);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the unnecessary initialization:
|
|
|
|
```dart
|
|
class C {
|
|
int? f;
|
|
|
|
void m() {
|
|
if (f != null) {
|
|
print(f);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/usage#dont-explicitly-initialize-variables-to-null):
|
|
|
|
**DON'T** explicitly initialize variables to `null`.
|
|
|
|
If a variable has a non-nullable type or is `final`,
|
|
Dart reports a compile error if you try to use it
|
|
before it has been definitely initialized.
|
|
If the variable is nullable and not `const` or `final`,
|
|
then it is implicitly initialized to `null` for you.
|
|
There's no concept of "uninitialized memory" in Dart
|
|
and no need to explicitly initialize a variable to `null` to be "safe".
|
|
Adding `= null` is redundant and unneeded.
|
|
|
|
**BAD:**
|
|
```dart
|
|
Item? bestDeal(List<Item> cart) {
|
|
Item? bestItem = null;
|
|
|
|
for (final item in cart) {
|
|
if (bestItem == null || item.price < bestItem.price) {
|
|
bestItem = item;
|
|
}
|
|
}
|
|
|
|
return bestItem;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Item? bestDeal(List<Item> cart) {
|
|
Item? bestItem;
|
|
|
|
for (final item in cart) {
|
|
if (bestItem == null || item.price < bestItem.price) {
|
|
bestItem = item;
|
|
}
|
|
}
|
|
|
|
return bestItem;
|
|
}
|
|
```
|
|
avoidJsRoundedInts:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Integer literal can't be represented exactly when compiled to JavaScript."
|
|
correctionMessage: "Try using a 'BigInt' to represent the value."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne, web]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an `int` literal's value
|
|
can't be represented exactly as a JavaScript `Number`.
|
|
|
|
When Dart code is compiled to JavaScript,
|
|
integers whose values fall outside the range `-(2^53 - 1)` to `2^53 - 1`
|
|
might be rounded to the nearest representable value.
|
|
This silently changes the literal's value on the web.
|
|
|
|
To learn more about how Dart represents numbers on different platforms,
|
|
including when compiled to JavaScript, check out
|
|
[Numbers in Dart](https://dart.dev/resources/language/number-representation).
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because
|
|
the literal `9007199254740993` (2^53 + 1) can't be
|
|
represented exactly as a JavaScript `Number` is rounded to
|
|
`9007199254740992` at run time when compiled to JavaScript:
|
|
|
|
```dart
|
|
final i = [!9007199254740993!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the value needs to be exact on all platforms,
|
|
including when compiled to JavaScript,
|
|
then use a `BigInt` to represent it:
|
|
|
|
```dart
|
|
final i = BigInt.parse('9007199254740993');
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** integer literals that cannot be represented exactly when compiled to
|
|
JavaScript.
|
|
|
|
When a program is compiled to JavaScript `int` and `double` become JavaScript
|
|
Numbers. Too large integers (`value < Number.MIN_SAFE_INTEGER` or
|
|
`value > Number.MAX_SAFE_INTEGER`) may be rounded to the closest Number value.
|
|
|
|
For instance `1000000000000000001` cannot be represented exactly as a JavaScript
|
|
Number, so `1000000000000000000` will be used instead.
|
|
|
|
**BAD:**
|
|
```dart
|
|
int value = 9007199254740995;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
BigInt value = BigInt.parse('9007199254740995');
|
|
```
|
|
avoidMultipleDeclarationsPerLine:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Multiple variables declared on a single line."
|
|
correctionMessage: "Try splitting the variable declarations into multiple lines."
|
|
state:
|
|
stable: "2.13"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DON'T** declare multiple variables on a single line.
|
|
|
|
**BAD:**
|
|
```dart
|
|
String? foo, bar, baz;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
String? foo;
|
|
String? bar;
|
|
String? baz;
|
|
```
|
|
avoidNullChecksInEqualityOperators:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary null comparison in implementation of '=='."
|
|
correctionMessage: "Try removing the comparison."
|
|
state:
|
|
stable: "2.0"
|
|
deprecated: "3.11"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**NOTE:** This lint has been replaced by the
|
|
`non_nullable_equals_parameter` warning and is deprecated.
|
|
Remove all inclusions of this lint from your analysis options.
|
|
|
|
**DON'T** check for `null` in custom `==` operators.
|
|
|
|
As `null` is a special value, no instance of any class (other than `Null`) can
|
|
be equivalent to it. Thus, it is redundant to check whether the other instance
|
|
is `null`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Person {
|
|
final String? name;
|
|
|
|
@override
|
|
operator ==(Object? other) =>
|
|
other != null && other is Person && name == other.name;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Person {
|
|
final String? name;
|
|
|
|
@override
|
|
operator ==(Object? other) => other is Person && name == other.name;
|
|
}
|
|
```
|
|
avoidPositionalBooleanParameters:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "'bool' parameters should be named parameters."
|
|
correctionMessage: "Try converting the parameter to a named parameter."
|
|
state:
|
|
experimental: "2.0"
|
|
stable: "2.2"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a non-overriding, public
|
|
function has a positional parameter of type `bool`.
|
|
|
|
Positional boolean parameters can cause ambiguity when
|
|
the function is invoked because the function of the
|
|
specified boolean value might not be clear from the value alone.
|
|
Named parameters make their intent explicit.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because
|
|
the function `f` has a positional parameter with a type of `bool`:
|
|
|
|
```dart
|
|
void f([!bool b!]) {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Convert the positional parameter to a named parameter:
|
|
|
|
```dart
|
|
void f({bool b = false}) {}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/design#avoid-positional-boolean-parameters):
|
|
|
|
**AVOID** positional boolean parameters.
|
|
|
|
Positional boolean parameters are a bad practice because they are very
|
|
ambiguous. Using named boolean parameters is much more readable because it
|
|
inherently describes what the boolean value represents.
|
|
|
|
**BAD:**
|
|
```dart
|
|
Task(true);
|
|
Task(false);
|
|
ListBox(false, true, true);
|
|
Button(false);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Task.oneShot();
|
|
Task.repeating();
|
|
ListBox(scroll: true, showScrollbars: true);
|
|
Button(ButtonState.enabled);
|
|
```
|
|
avoidPrint:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't invoke 'print' in production code."
|
|
correctionMessage: "Try using a logging framework."
|
|
state:
|
|
stable: "2.5"
|
|
categories: [unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the function `print` is invoked
|
|
in production code.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the function `print`
|
|
can't be invoked in production:
|
|
|
|
```dart
|
|
void f(int x) {
|
|
[!print!]('x = $x');
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If you're writing code that uses Flutter, then use the function
|
|
[`debugPrint`][debugPrint], guarded by a test
|
|
using [`kDebugMode`][kDebugMode]:
|
|
|
|
```dart
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
void f(int x) {
|
|
if (kDebugMode) {
|
|
debugPrint('x = $x');
|
|
}
|
|
}
|
|
```
|
|
|
|
If you're writing code that doesn't use Flutter, then use a logging
|
|
service, such as [`package:logging`][package-logging], to write the
|
|
information.
|
|
deprecatedDetails: |-
|
|
**DO** avoid `print` calls in production code.
|
|
|
|
For production code, consider using a logging framework.
|
|
If you are using Flutter, you can use `debugPrint`
|
|
or surround `print` calls with a check for `kDebugMode`
|
|
|
|
**BAD:**
|
|
```dart
|
|
void f(int x) {
|
|
print('debug: $x');
|
|
...
|
|
}
|
|
```
|
|
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f(int x) {
|
|
debugPrint('debug: $x');
|
|
...
|
|
}
|
|
```
|
|
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f(int x) {
|
|
log('log: $x');
|
|
...
|
|
}
|
|
```
|
|
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f(int x) {
|
|
if (kDebugMode) {
|
|
print('debug: $x');
|
|
}
|
|
...
|
|
}
|
|
```
|
|
avoidPrivateTypedefFunctions:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The typedef is unnecessary because it is only used in one place."
|
|
correctionMessage: "Try inlining the type or using it in other places."
|
|
state:
|
|
stable: "2.0"
|
|
deprecated: "3.13"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**NOTE:** As a private typedef can make some code more readable,
|
|
this lint rule has been deprecated as of Dart 3.13 and is
|
|
set to be removed in a future release of the Dart SDK.
|
|
Remove all inclusions of this lint from your analysis options.
|
|
If you wish to keep enforcing it, consider implementing the
|
|
rule with an [analyzer plugin](https://dart.dev/tools/analyzer-plugins).
|
|
|
|
**AVOID** private typedef functions used only once. Prefer inline function
|
|
syntax.
|
|
|
|
**BAD:**
|
|
```dart
|
|
typedef void _F();
|
|
m(_F f);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
m(void Function() f);
|
|
```
|
|
avoidRedundantArgumentValues:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The value of the argument is redundant because it matches the default value."
|
|
correctionMessage: "Try removing the argument."
|
|
state:
|
|
stable: "2.8"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an argument is
|
|
passed to an optional parameter of a function, but the
|
|
argument's value is the same as the parameter's default value.
|
|
The duplicative argument can often be removed without
|
|
changing the behavior of the code.
|
|
|
|
When invoking methods, because the redundancy check is based on the
|
|
static type of the invocation target, there can be false positives when
|
|
a method override changes a parameter's default value.
|
|
If the static type has one default value specified but the
|
|
actual runtime type has another, the diagnostic might not be accurate.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the
|
|
named argument `b` has the same value as its default value of `true`:
|
|
|
|
```dart
|
|
void g({bool b = true}) {}
|
|
|
|
void f() {
|
|
g(b: [!true!]);
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the
|
|
positional argument `1` has the same value as
|
|
the default value of the second parameter of `g`:
|
|
|
|
```dart
|
|
void g([int? a, int? b = 1]) {}
|
|
|
|
void f() {
|
|
g(0, [!1!]);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the redundant argument from the invocation:
|
|
|
|
```dart
|
|
void g({bool b = true}) {}
|
|
|
|
void f() {
|
|
g();
|
|
}
|
|
```
|
|
|
|
```dart
|
|
void g([int? a, int? b = 1]) {}
|
|
|
|
void f() {
|
|
g(0);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** pass an argument that matches the corresponding parameter's default
|
|
value.
|
|
|
|
Note that a method override can change the default value of a parameter, so that
|
|
an argument may be equal to one default value, and not the other. Take, for
|
|
example, two classes, `A` and `B` where `B` is a subclass of `A`, and `B`
|
|
overrides a method declared on `A`, and that method has a parameter with one
|
|
default value in `A`'s declaration, and a different default value in `B`'s
|
|
declaration. If the static type of the target of the invoked method is `B`, and
|
|
`B`'s default value matches the argument, then the argument can be omitted (and
|
|
if the argument value is different, then a lint is not reported). If, however,
|
|
the static type of the target of the invoked method is `A`, then a lint may be
|
|
reported, but we cannot know statically which method is invoked, so the reported
|
|
lint may be a false positive. Such cases can be ignored inline with a comment
|
|
like `// ignore: avoid_redundant_argument_values`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void f({bool valWithDefault = true, bool? val}) {
|
|
...
|
|
}
|
|
|
|
void main() {
|
|
f(valWithDefault: true);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f({bool valWithDefault = true, bool? val}) {
|
|
...
|
|
}
|
|
|
|
void main() {
|
|
f(valWithDefault: false);
|
|
f();
|
|
}
|
|
```
|
|
avoidRelativeLibImports:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Can't use a relative path to import a library in 'lib'."
|
|
correctionMessage: "Try fixing the relative path or changing the import to a 'package:' import."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the URI in an `import`
|
|
directive has `lib` in the path.
|
|
|
|
#### Example
|
|
|
|
Assuming that there is a file named `a.dart` in the `lib` directory:
|
|
|
|
```dart
|
|
%uri="lib/a.dart"
|
|
class A {}
|
|
```
|
|
|
|
The following code produces this diagnostic because the import contains a
|
|
path that includes `lib`:
|
|
|
|
```dart
|
|
import [!'../lib/a.dart'!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rewrite the import to not include `lib` in the URI:
|
|
|
|
```dart
|
|
import 'a.dart';
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** avoid relative imports for files in `lib/`.
|
|
|
|
When mixing relative and absolute imports it's possible to create confusion
|
|
where the same member gets imported in two different ways. An easy way to avoid
|
|
that is to ensure you have no relative imports that include `lib/` in their
|
|
paths.
|
|
|
|
You can also use 'always_use_package_imports' to disallow relative imports
|
|
between files within `lib/`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'package:foo/bar.dart';
|
|
|
|
import '../lib/baz.dart';
|
|
|
|
...
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'package:foo/bar.dart';
|
|
|
|
import 'baz.dart';
|
|
|
|
...
|
|
```
|
|
avoidRenamingMethodParameters:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
problemMessage: "The parameter name '#p0' doesn't match the name '#p1' in the overridden method."
|
|
correctionMessage: "Try changing the name to '#p1'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [documentationCommentMaintenance]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a method that overrides a
|
|
method from a superclass changes the names of the parameters.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the parameter of the
|
|
method `m` in `B` is named `b`, which is different from the name of the
|
|
overridden method's parameter in `A`:
|
|
|
|
```dart
|
|
class A {
|
|
void m(int a) {}
|
|
}
|
|
|
|
class B extends A {
|
|
@override
|
|
void m(int [!b!]) {}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rename one of the parameters so that they are the same:
|
|
|
|
```dart
|
|
class A {
|
|
void m(int a) {}
|
|
}
|
|
|
|
class B extends A {
|
|
@override
|
|
void m(int a) {}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** rename parameters of overridden methods.
|
|
|
|
Methods that override another method, but do not have their own documentation
|
|
comment, will inherit the overridden method's comment when `dart doc` produces
|
|
documentation. If the inherited method contains the name of the parameter (in
|
|
square brackets), then `dart doc` cannot link it correctly.
|
|
|
|
**BAD:**
|
|
```dart
|
|
abstract class A {
|
|
m(a);
|
|
}
|
|
|
|
abstract class B extends A {
|
|
m(b);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
abstract class A {
|
|
m(a);
|
|
}
|
|
|
|
abstract class B extends A {
|
|
m(a);
|
|
}
|
|
```
|
|
avoidReturnTypesOnSetters:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary return type on a setter."
|
|
correctionMessage: "Try removing the return type."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a setter has an explicit return
|
|
type.
|
|
|
|
Setters never return a value, so declaring the return type of one is
|
|
redundant.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the setter `s` has an
|
|
explicit return type (`void`):
|
|
|
|
```dart
|
|
[!void!] set s(int p) {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the return type:
|
|
|
|
```dart
|
|
set s(int p) {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** return types on setters.
|
|
|
|
As setters do not return a value, declaring the return type of one is redundant.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void set speed(int ms);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
set speed(int ms);
|
|
```
|
|
avoidReturningNull:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
stable: "2.0"
|
|
removed: "3.3"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
NOTE: This rule is removed in Dart 3.3.0; it is no longer functional.
|
|
|
|
**AVOID** returning null from members whose return type is bool, double, int,
|
|
or num.
|
|
|
|
Functions that return primitive types such as bool, double, int, and num are
|
|
generally expected to return non-nullable values. Thus, returning null where a
|
|
primitive type was expected can lead to runtime exceptions.
|
|
|
|
**BAD:**
|
|
```dart
|
|
bool getBool() => null;
|
|
num getNum() => null;
|
|
int getInt() => null;
|
|
double getDouble() => null;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
bool getBool() => false;
|
|
num getNum() => -1;
|
|
int getInt() => -1;
|
|
double getDouble() => -1.0;
|
|
```
|
|
avoidReturningNullForFuture:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
stable: "2.1"
|
|
removed: "3.3"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
NOTE: This rule is removed in Dart 3.3.0; it is no longer functional.
|
|
|
|
**AVOID** returning null for Future.
|
|
|
|
It is almost always wrong to return `null` for a `Future`. Most of the time the
|
|
developer simply forgot to put an `async` keyword on the function.
|
|
avoidReturningNullForVoidFromFunction:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: avoidReturningNullForVoid
|
|
problemMessage: "Don't return 'null' from a function with a return type of 'void'."
|
|
correctionMessage: "Try removing the 'null'."
|
|
state:
|
|
stable: "2.1"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a function that has a return
|
|
type of `void` explicitly returns `null`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because there is an explicit
|
|
return of `null` in a `void` function:
|
|
|
|
```dart
|
|
void f() {
|
|
[!return null;!]
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the unnecessary explicit `null`:
|
|
|
|
```dart
|
|
void f() {
|
|
return;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** returning `null` for `void`.
|
|
|
|
In a large variety of languages `void` as return type is used to indicate that
|
|
a function doesn't return anything. Dart allows returning `null` in functions
|
|
with `void` return type but it also allow using `return;` without specifying any
|
|
value. To have a consistent way you should not return `null` and only use an
|
|
empty return.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void f1() {
|
|
return null;
|
|
}
|
|
Future<void> f2() async {
|
|
return null;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f1() {
|
|
return;
|
|
}
|
|
Future<void> f2() async {
|
|
return;
|
|
}
|
|
```
|
|
avoidReturningNullForVoidFromMethod:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: avoidReturningNullForVoid
|
|
problemMessage: "Don't return 'null' from a method with a return type of 'void'."
|
|
correctionMessage: "Try removing the 'null'."
|
|
hasPublishedDocs: true
|
|
avoidReturningThis:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't return 'this' from a method."
|
|
correctionMessage: "Try changing the return type to 'void' and removing the return."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/design#avoid-returning-this-from-methods-just-to-enable-a-fluent-interface):
|
|
|
|
**AVOID** returning this from methods just to enable a fluent interface.
|
|
|
|
Returning `this` from a method is redundant; Dart has a cascade operator which
|
|
allows method chaining universally.
|
|
|
|
Returning `this` is allowed for:
|
|
|
|
- operators
|
|
- methods with a return type different of the current class
|
|
- methods defined in parent classes / mixins or interfaces
|
|
- methods defined in extensions
|
|
|
|
**BAD:**
|
|
```dart
|
|
var buffer = StringBuffer()
|
|
.write('one')
|
|
.write('two')
|
|
.write('three');
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var buffer = StringBuffer()
|
|
..write('one')
|
|
..write('two')
|
|
..write('three');
|
|
```
|
|
avoidSettersWithoutGetters:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Setter has no corresponding getter."
|
|
correctionMessage: "Try adding a corresponding getter or removing the setter."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DON'T** define a setter without a corresponding getter.
|
|
|
|
Defining a setter without defining a corresponding getter can lead to logical
|
|
inconsistencies. Doing this could allow you to set a property to some value,
|
|
but then upon observing the property's value, it could easily be different.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Bad {
|
|
int l, r;
|
|
|
|
set length(int newLength) {
|
|
r = l + newLength;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Good {
|
|
int l, r;
|
|
|
|
int get length => r - l;
|
|
|
|
set length(int newLength) {
|
|
r = l + newLength;
|
|
}
|
|
}
|
|
```
|
|
avoidShadowingTypeParameters:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
problemMessage: "The type parameter '#p0' shadows a type parameter from the enclosing #p1."
|
|
correctionMessage: "Try renaming one of the type parameters."
|
|
state:
|
|
stable: "2.1"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a type parameter shadows a type
|
|
parameter from an enclosing declaration.
|
|
|
|
Shadowing a type parameter with a different type parameter can lead to
|
|
subtle bugs that are difficult to debug.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the type parameter `T`
|
|
defined by the method `m` shadows the type parameter `T` defined by the
|
|
class `C`:
|
|
|
|
```dart
|
|
class C<T> {
|
|
void m<[!T!]>() {}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rename one of the type parameters:
|
|
|
|
```dart
|
|
class C<T> {
|
|
void m<S>() {}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** shadowing type parameters.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A<T> {
|
|
void fn<T>() {}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A<T> {
|
|
void fn<U>() {}
|
|
}
|
|
```
|
|
avoidSingleCascadeInExpressionStatements:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Unnecessary cascade expression."
|
|
correctionMessage: "Try using the operator '#p0'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a single cascade operator is
|
|
used and the value of the expression isn't being used for anything (such
|
|
as being assigned to a variable or being passed as an argument).
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the value of the
|
|
cascade expression `s..length` isn't being used:
|
|
|
|
```dart
|
|
void f(String s) {
|
|
[!s..length!];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the cascade operator with a simple access operator:
|
|
|
|
```dart
|
|
void f(String s) {
|
|
s.length;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** single cascade in expression statements.
|
|
|
|
**BAD:**
|
|
```dart
|
|
o..m();
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
o.m();
|
|
```
|
|
avoidSlowAsyncIo:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use of an async 'dart:io' method."
|
|
correctionMessage: "Try using the synchronous version of the method."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [nonPerformant]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an asynchronous file I/O method
|
|
with a synchronous equivalent is used.
|
|
|
|
The following are the specific flagged asynchronous methods:
|
|
|
|
- `Directory.exists`
|
|
- `Directory.stat`
|
|
- `File.lastModified`
|
|
- `File.exists`
|
|
- `File.stat`
|
|
- `FileSystemEntity.isDirectory`
|
|
- `FileSystemEntity.isFile`
|
|
- `FileSystemEntity.isLink`
|
|
- `FileSystemEntity.type`
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the async method
|
|
`exists` is invoked:
|
|
|
|
```dart
|
|
import 'dart:io';
|
|
|
|
Future<void> g(File f) async {
|
|
await [!f.exists()!];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use the synchronous version of the method:
|
|
|
|
```dart
|
|
import 'dart:io';
|
|
|
|
void g(File f) {
|
|
f.existsSync();
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** using the following asynchronous file I/O methods because they are
|
|
much slower than their synchronous counterparts.
|
|
|
|
* `Directory.exists`
|
|
* `Directory.stat`
|
|
* `File.lastModified`
|
|
* `File.exists`
|
|
* `File.stat`
|
|
* `FileSystemEntity.isDirectory`
|
|
* `FileSystemEntity.isFile`
|
|
* `FileSystemEntity.isLink`
|
|
* `FileSystemEntity.type`
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'dart:io';
|
|
|
|
Future<Null> someFunction() async {
|
|
var file = File('/path/to/my/file');
|
|
var now = DateTime.now();
|
|
if ((await file.lastModified()).isBefore(now)) print('before'); // LINT
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'dart:io';
|
|
|
|
Future<Null> someFunction() async {
|
|
var file = File('/path/to/my/file');
|
|
var now = DateTime.now();
|
|
if (file.lastModifiedSync().isBefore(now)) print('before'); // OK
|
|
}
|
|
```
|
|
avoidTypeToString:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Using 'toString' on a 'Type' is not safe in production code."
|
|
correctionMessage: "Try a normal type check or compare the 'runtimeType' directly."
|
|
state:
|
|
stable: "2.12"
|
|
categories: [unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the method `toString` is
|
|
invoked on a value whose static type is `Type`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the method `toString`
|
|
is invoked on the `Type` returned by `runtimeType`:
|
|
|
|
```dart
|
|
bool isC(Object o) => o.runtimeType.[!toString!]() == 'C';
|
|
|
|
class C {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If it's essential that the type is exactly the same, then use an explicit
|
|
comparison:
|
|
|
|
```dart
|
|
bool isC(Object o) => o.runtimeType == C;
|
|
|
|
class C {}
|
|
```
|
|
|
|
If it's alright for instances of subtypes of the type to return `true`,
|
|
then use a type check:
|
|
|
|
```dart
|
|
bool isC(Object o) => o is C;
|
|
|
|
class C {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** avoid calls to <Type>.toString() in production code, since it does not
|
|
contractually return the user-defined name of the Type (or underlying class).
|
|
Development-mode compilers where code size is not a concern use the full name,
|
|
but release-mode compilers often choose to minify these symbols.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void bar(Object other) {
|
|
if (other.runtimeType.toString() == 'Bar') {
|
|
doThing();
|
|
}
|
|
}
|
|
|
|
Object baz(Thing myThing) {
|
|
return getThingFromDatabase(key: myThing.runtimeType.toString());
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void bar(Object other) {
|
|
if (other is Bar) {
|
|
doThing();
|
|
}
|
|
}
|
|
|
|
class Thing {
|
|
String get thingTypeKey => ...
|
|
}
|
|
|
|
Object baz(Thing myThing) {
|
|
return getThingFromDatabase(key: myThing.thingTypeKey);
|
|
}
|
|
```
|
|
avoidTypesAsParameterNamesFormalParameter:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: avoidTypesAsParameterNames
|
|
problemMessage: "The parameter name '#p0' matches a visible type name."
|
|
correctionMessage: "Try adding a name for the parameter or changing the parameter name to not match an existing type."
|
|
hasPublishedDocs: true
|
|
avoidTypesAsParameterNamesTypeParameter:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: avoidTypesAsParameterNames
|
|
problemMessage: "The type parameter name '#p0' matches a visible type name."
|
|
state:
|
|
stable: "2.0"
|
|
correctionMessage: "Try changing the type parameter name to not match an existing type."
|
|
categories: [unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the name of a parameter in a
|
|
parameter list is the same as a visible type (a type whose name is in
|
|
scope).
|
|
|
|
This often indicates that the intended name of the parameter is missing,
|
|
causing the name of the type to be used as the name of the parameter
|
|
rather than the type of the parameter. Even when that's not the case (the
|
|
name of the parameter is intentional), the name of the parameter will
|
|
shadow the existing type, which can lead to bugs that are difficult to
|
|
diagnose.
|
|
|
|
The analyzer also produces this diagnostic when the name of a type
|
|
parameter in a type parameter list is the same as a type whose name is
|
|
in scope. It is again recommended that the type parameter is renamed
|
|
such that the error-prone shadowing is avoided.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the function `f` has a
|
|
parameter named `int`, which shadows the type `int` from `dart:core`:
|
|
|
|
```dart
|
|
void f([!int!]) {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the parameter name is missing, then add a name for the parameter:
|
|
|
|
```dart
|
|
void f(int x) {}
|
|
```
|
|
|
|
If the parameter is intended to have an implicit type of `dynamic`, then
|
|
rename the parameter so that it doesn't shadow the name of any visible type:
|
|
|
|
```dart
|
|
void f(int_) {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** using a parameter name that is the same as an existing type.
|
|
|
|
**BAD:**
|
|
```dart
|
|
m(f(int));
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
m(f(int v));
|
|
```
|
|
avoidTypesOnClosureParameters:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary type annotation on a function expression parameter."
|
|
correctionMessage: "Try removing the type annotation."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a parameter of a
|
|
function expression (closure) has an explicit type annotation, and the
|
|
closure is in a context where the parameter type can be inferred.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because
|
|
the parameter `x` in the closure passed to `l.map` has an
|
|
explicit type annotation of `int`, which is unnecessary because
|
|
the type can be inferred from the list's type:
|
|
|
|
```dart
|
|
void f(List<int> l) {
|
|
l.map(([!int!] x) => x.isEven);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the type annotation:
|
|
|
|
```dart
|
|
void f(List<int> l) {
|
|
l.map((x) => x.isEven);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** annotating types for function expression parameters.
|
|
|
|
Annotating types for function expression parameters is usually unnecessary
|
|
because the parameter types can almost always be inferred from the context,
|
|
thus making the practice redundant.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var names = people.map((Person person) => person.name);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var names = people.map((person) => person.name);
|
|
```
|
|
avoidUnnecessaryContainers:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary instance of 'Container'."
|
|
correctionMessage: "Try removing the 'Container' (but not its children) from the widget tree."
|
|
state:
|
|
stable: "2.7"
|
|
categories: [flutter, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a widget tree contains an
|
|
instance of `Container` and the only argument to the constructor is
|
|
`child:`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the invocation of the
|
|
`Container` constructor only has a `child:` argument:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget buildRow() {
|
|
return [!Container!](
|
|
child: Row(
|
|
children: [
|
|
Text('a'),
|
|
Text('b'),
|
|
],
|
|
)
|
|
);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If you intended to provide other arguments to the constructor, then add
|
|
them:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget buildRow() {
|
|
return Container(
|
|
color: Colors.red.shade100,
|
|
child: Row(
|
|
children: [
|
|
Text('a'),
|
|
Text('b'),
|
|
],
|
|
)
|
|
);
|
|
}
|
|
```
|
|
|
|
If no other arguments are needed, then unwrap the child widget:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget buildRow() {
|
|
return Row(
|
|
children: [
|
|
Text('a'),
|
|
Text('b'),
|
|
],
|
|
);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** wrapping widgets in unnecessary containers.
|
|
|
|
Wrapping a widget in `Container` with no other parameters set has no effect
|
|
and makes code needlessly more complex.
|
|
|
|
**BAD:**
|
|
```dart
|
|
Widget buildRow() {
|
|
return Container(
|
|
child: Row(
|
|
children: <Widget>[
|
|
const MyLogo(),
|
|
const Expanded(
|
|
child: Text('...'),
|
|
),
|
|
],
|
|
)
|
|
);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Widget buildRow() {
|
|
return Row(
|
|
children: <Widget>[
|
|
const MyLogo(),
|
|
const Expanded(
|
|
child: Text('...'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
```
|
|
avoidUnstableFinalFields:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
experimental: "3.3"
|
|
removed: "3.3"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
This rule has been removed.
|
|
avoidUnusedConstructorParameters:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The parameter '#p0' is not used in the constructor."
|
|
correctionMessage: "Try using the parameter or removing it."
|
|
state:
|
|
stable: "2.0"
|
|
# TODO(srawlins): This isn't even just about unintentional syntax; unused
|
|
# parameters can represent code bloat.
|
|
categories: [unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a constructor parameter
|
|
isn't used in the constructor body or initializer list, and isn't an
|
|
initializing formal parameter (`this.x`), a super parameter (`super.x`),
|
|
or a declaring parameter.
|
|
|
|
Parameters that are only declared but never referenced are
|
|
likely unintentional and add unnecessary complexity to the constructor.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because
|
|
the parameter `x` isn't used in the constructor:
|
|
|
|
```dart
|
|
class C {
|
|
C([!int x!]);
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because
|
|
the parameter `c` isn't used in the constructor initializer list:
|
|
|
|
```dart
|
|
class C {
|
|
int x;
|
|
|
|
C(int a, int b, [!int c!]): x = a + b {
|
|
print(x);
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the parameter is meant to initialize a field,
|
|
then use an initializing formal parameter:
|
|
|
|
```dart
|
|
class C {
|
|
int x;
|
|
|
|
C(this.x);
|
|
}
|
|
```
|
|
|
|
If the parameter isn't needed, then remove it:
|
|
|
|
```dart
|
|
class C {
|
|
int x;
|
|
|
|
C(int a, int b): x = a + b {
|
|
print(x);
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** defining unused parameters in constructors.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class BadOne {
|
|
BadOne(int unusedParameter, [String unusedPositional]);
|
|
}
|
|
|
|
class BadTwo {
|
|
int c;
|
|
|
|
BadTwo(int a, int b, int x) {
|
|
c = a + b;
|
|
}
|
|
}
|
|
```
|
|
avoidVoidAsync:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "An 'async' function should have a 'Future' return type when it doesn't return a value."
|
|
correctionMessage: "Try changing the return type."
|
|
state:
|
|
stable: "2.1"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** mark `async` functions as returning `Future<void>`.
|
|
|
|
When declaring an `async` method or function which does not return a value,
|
|
declare that it returns `Future<void>` and not just `void`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void f() async {}
|
|
void f2() async => null;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Future<void> f() async {}
|
|
Future<void> f2() async => null;
|
|
```
|
|
|
|
**EXCEPTION:**
|
|
|
|
An exception is made for top-level `main` functions, where the `Future`
|
|
annotation *can* (and generally should) be dropped in favor of `void`.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Future<void> f() async {}
|
|
|
|
void main() async {
|
|
await f();
|
|
}
|
|
```
|
|
avoidWebLibrariesInFlutter:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't use web-only libraries outside Flutter web plugins."
|
|
correctionMessage: "Try finding a different library for your needs."
|
|
state:
|
|
experimental: "2.6"
|
|
stable: "2.14"
|
|
categories: [errorProne, flutter, web]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a library in a package that
|
|
isn't a web plugin contains an import of a web-only library:
|
|
- `dart:html`
|
|
- `dart:js`
|
|
- `dart:js_util`
|
|
- `dart:js_interop`
|
|
- `dart:js_interop_unsafe`
|
|
- `package:js`
|
|
- `package:web`
|
|
|
|
#### Example
|
|
|
|
When found in a package that isn't a web plugin, the following code
|
|
produces this diagnostic because it imports `dart:html`:
|
|
|
|
```dart
|
|
%ignore=unused_import
|
|
import [!'dart:html'!];
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
class C {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the package isn't intended to be a web plugin, then remove the import:
|
|
|
|
```dart
|
|
%ignore=unused_import
|
|
import 'package:flutter/material.dart';
|
|
|
|
class C {}
|
|
```
|
|
|
|
If the package is intended to be a web plugin, then add the following
|
|
lines to the `pubspec.yaml` file of the package:
|
|
|
|
```yaml
|
|
flutter:
|
|
plugin:
|
|
platforms:
|
|
web:
|
|
pluginClass: HelloPlugin
|
|
fileName: hello_web.dart
|
|
```
|
|
|
|
See [Developing packages & plugins](https://flutter.dev/to/develop-packages)
|
|
for more information.
|
|
deprecatedDetails: |-
|
|
**AVOID** using web libraries, `dart:html`, `dart:js` and
|
|
`dart:js_util` in Flutter packages that are not web plugins. These libraries are
|
|
not supported outside of a web context; functionality that depends on them will
|
|
fail at runtime in Flutter mobile, and their use is generally discouraged in
|
|
Flutter web.
|
|
|
|
Web library access *is* allowed in:
|
|
|
|
* plugin packages that declare `web` as a supported context
|
|
|
|
otherwise, imports of `dart:html`, `dart:js` and `dart:js_util` are disallowed.
|
|
awaitOnlyFutures:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Uses 'await' on an instance of '#p0', which is not a subtype of 'Future'."
|
|
correctionMessage: "Try removing the 'await' or changing the expression."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the expression after `await`
|
|
has any type other than `Future<T>`, `FutureOr<T>`, `Future<T>?`,
|
|
`FutureOr<T>?` or `dynamic`.
|
|
|
|
An exception is made for the expression `await null` because it is a
|
|
common way to introduce a microtask delay.
|
|
|
|
Unless the expression can produce a `Future`, the `await` is unnecessary
|
|
and can cause a reader to assume a level of asynchrony that doesn't exist.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the expression after
|
|
`await` has the type `int`:
|
|
|
|
```dart
|
|
void f() async {
|
|
[!await!] 23;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the `await`:
|
|
|
|
```dart
|
|
void f() async {
|
|
23;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** using await on anything which is not a future.
|
|
|
|
Await is allowed on the types: `Future<X>`, `FutureOr<X>`, `Future<X>?`,
|
|
`FutureOr<X>?` and `dynamic`.
|
|
|
|
Further, using `await null` is specifically allowed as a way to introduce a
|
|
microtask delay.
|
|
|
|
**BAD:**
|
|
```dart
|
|
main() async {
|
|
print(await 23);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
main() async {
|
|
await null; // If a delay is really intended.
|
|
print(23);
|
|
}
|
|
```
|
|
camelCaseExtensions:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The extension name '#p0' isn't an UpperCamelCase identifier."
|
|
correctionMessage: "Try changing the name to follow the UpperCamelCase style."
|
|
state:
|
|
stable: "2.6"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the name of an extension
|
|
doesn't use the 'UpperCamelCase' naming convention.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the name of the
|
|
extension doesn't start with an uppercase letter:
|
|
|
|
```dart
|
|
extension [!stringExtension!] on String {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the extension needs to have a name (needs to be visible outside this
|
|
library), then rename the extension so that it has a valid name:
|
|
|
|
```dart
|
|
extension StringExtension on String {}
|
|
```
|
|
|
|
If the extension doesn't need to have a name, then remove the name of the
|
|
extension:
|
|
|
|
```dart
|
|
extension on String {}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/style#do-name-extensions-using-uppercamelcase):
|
|
|
|
**DO** name extensions using `UpperCamelCase`.
|
|
|
|
Extensions should capitalize the first letter of each word (including
|
|
the first word), and use no separators.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
extension MyFancyList<T> on List<T> {
|
|
// ...
|
|
}
|
|
|
|
extension SmartIterable<T> on Iterable<T> {
|
|
// ...
|
|
}
|
|
```
|
|
camelCaseTypes:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The type name '#p0' isn't an UpperCamelCase identifier."
|
|
correctionMessage: "Try changing the name to follow the UpperCamelCase style."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the name of a type (a class,
|
|
mixin, enum, or typedef) doesn't use the 'UpperCamelCase' naming
|
|
convention.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the name of the class
|
|
doesn't start with an uppercase letter:
|
|
|
|
```dart
|
|
class [!c!] {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rename the type so that it has a valid name:
|
|
|
|
```dart
|
|
class C {}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/style#do-name-types-using-uppercamelcase):
|
|
|
|
**DO** name types using UpperCamelCase.
|
|
|
|
Classes and typedefs should capitalize the first letter of each word (including
|
|
the first word), and use no separators.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class SliderMenu {
|
|
// ...
|
|
}
|
|
|
|
class HttpRequest {
|
|
// ...
|
|
}
|
|
|
|
typedef num Adder(num x, num y);
|
|
```
|
|
cancelSubscriptions:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Uncancelled instance of 'StreamSubscription'."
|
|
correctionMessage: "Try invoking 'cancel' in the function in which the 'StreamSubscription' was created."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne, memoryLeaks]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an instance of
|
|
`StreamSubscription` is created but the method `cancel` isn't invoked.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `subscription`
|
|
isn't canceled:
|
|
|
|
```dart
|
|
import 'dart:async';
|
|
|
|
void f(Stream stream) {
|
|
// ignore: unused_local_variable
|
|
var [!subscription = stream.listen((_) {})!];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Cancel the subscription:
|
|
|
|
```dart
|
|
import 'dart:async';
|
|
|
|
void f(Stream stream) {
|
|
var subscription = stream.listen((_) {});
|
|
subscription.cancel();
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** invoke `cancel` on instances of `dart:async` `StreamSubscription`.
|
|
|
|
Cancelling instances of StreamSubscription prevents memory leaks and unexpected
|
|
behavior.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
StreamSubscription _subscriptionA; // LINT
|
|
void init(Stream stream) {
|
|
_subscriptionA = stream.listen((_) {});
|
|
}
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction() {
|
|
StreamSubscription _subscriptionF; // LINT
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class B {
|
|
StreamSubscription _subscriptionB; // OK
|
|
void init(Stream stream) {
|
|
_subscriptionB = stream.listen((_) {});
|
|
}
|
|
|
|
void dispose(filename) {
|
|
_subscriptionB.cancel();
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunctionOK() {
|
|
StreamSubscription _subscriptionB; // OK
|
|
_subscriptionB.cancel();
|
|
}
|
|
```
|
|
|
|
**Known limitations**
|
|
|
|
This rule does not track all patterns of StreamSubscription instantiations and
|
|
cancellations. See [linter#317](https://github.com/dart-lang/sdk/issues/57387)
|
|
for more information.
|
|
cascadeInvocations:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary duplication of receiver."
|
|
correctionMessage: "Try using a cascade to avoid the duplication."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, languageFeatureUsage, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** Use the cascading style when successively invoking methods on the same
|
|
reference.
|
|
|
|
**BAD:**
|
|
```dart
|
|
SomeClass someReference = SomeClass();
|
|
someReference.firstMethod();
|
|
someReference.secondMethod();
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
SomeClass someReference = SomeClass();
|
|
...
|
|
someReference.firstMethod();
|
|
someReference.aProperty = value;
|
|
someReference.secondMethod();
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
SomeClass someReference = SomeClass()
|
|
..firstMethod()
|
|
..aProperty = value
|
|
..secondMethod();
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
SomeClass someReference = SomeClass();
|
|
...
|
|
someReference
|
|
..firstMethod()
|
|
..aProperty = value
|
|
..secondMethod();
|
|
```
|
|
castNullableToNonNullable:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't cast a nullable value to a non-nullable type."
|
|
correctionMessage: "Try adding a not-null assertion ('!') to make the type non-nullable."
|
|
state:
|
|
stable: "2.12"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DON'T** cast a nullable value to a non nullable type. This hides a null check
|
|
and most of the time it is not what is expected.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {}
|
|
class B extends A {}
|
|
|
|
A? a;
|
|
var v = a as B;
|
|
var v = a as A;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {}
|
|
class B extends A {}
|
|
|
|
A? a;
|
|
var v = a! as B;
|
|
var v = a!;
|
|
```
|
|
closeSinks:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unclosed instance of 'Sink'."
|
|
correctionMessage: "Try invoking 'close' in the function in which the 'Sink' was created."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne, memoryLeaks]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an instance of `Sink` is
|
|
created but the method `close` isn't invoked.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `sink` isn't
|
|
closed:
|
|
|
|
```dart
|
|
import 'dart:io';
|
|
|
|
void g(File f) {
|
|
var [!sink = f.openWrite()!];
|
|
sink.write('x');
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Close the sink:
|
|
|
|
```dart
|
|
import 'dart:io';
|
|
|
|
void g(File f) {
|
|
var sink = f.openWrite();
|
|
sink.write('x');
|
|
sink.close();
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** invoke `close` on instances of `dart:core` `Sink`.
|
|
|
|
Closing instances of Sink prevents memory leaks and unexpected behavior.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
IOSink _sinkA;
|
|
void init(filename) {
|
|
_sinkA = File(filename).openWrite(); // LINT
|
|
}
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction() {
|
|
IOSink _sinkF; // LINT
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class B {
|
|
IOSink _sinkB;
|
|
void init(filename) {
|
|
_sinkB = File(filename).openWrite(); // OK
|
|
}
|
|
|
|
void dispose(filename) {
|
|
_sinkB.close();
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunctionOK() {
|
|
IOSink _sinkFOK; // OK
|
|
_sinkFOK.close();
|
|
}
|
|
```
|
|
|
|
**Known limitations**
|
|
|
|
This rule does not track all patterns of Sink instantiations and
|
|
closures. See [sdk#57882](https://github.com/dart-lang/sdk/issues/57882)
|
|
for more information.
|
|
collectionMethodsUnrelatedType:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
problemMessage: "The argument type '#p0' isn't related to '#p1'."
|
|
correctionMessage: "Try changing the argument or element type to match."
|
|
state:
|
|
stable: "2.19"
|
|
categories: [unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when any one of several methods in
|
|
the core libraries are invoked with arguments of an inappropriate type.
|
|
These methods are ones that don't provide a specific enough type for the
|
|
parameter to allow the normal type checking to catch the error.
|
|
|
|
The arguments that are checked are:
|
|
- an argument to `Iterable<E>.contains` should be related to `E`
|
|
- an argument to `List<E>.remove` should be related to `E`
|
|
- an argument to `Map<K, V>.containsKey` should be related to `K`
|
|
- an argument to `Map<K, V>.containsValue` should be related to `V`
|
|
- an argument to `Map<K, V>.remove` should be related to `K`
|
|
- an argument to `Map<K, V>.[]` should be related to `K`
|
|
- an argument to `Queue<E>.remove` should be related to `E`
|
|
- an argument to `Set<E>.lookup` should be related to `E`
|
|
- an argument to `Set<E>.remove` should be related to `E`
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the argument to
|
|
`contains` is a `String`, which isn't assignable to `int`, the element
|
|
type of the list `l`:
|
|
|
|
```dart
|
|
bool f(List<int> l) => l.contains([!'1'!]);
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the element type is correct, then change the argument to have the same
|
|
type:
|
|
|
|
```dart
|
|
bool f(List<int> l) => l.contains(1);
|
|
```
|
|
|
|
If the argument type is correct, then change the element type:
|
|
|
|
```dart
|
|
bool f(List<String> l) => l.contains('1');
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** invoke certain collection method with an argument with an unrelated
|
|
type.
|
|
|
|
Doing this will invoke `==` on the collection's elements and most likely will
|
|
return `false`.
|
|
|
|
An argument passed to a collection method should relate to the collection type
|
|
as follows:
|
|
|
|
* an argument to `Iterable<E>.contains` should be related to `E`
|
|
* an argument to `List<E>.remove` should be related to `E`
|
|
* an argument to `Map<K, V>.containsKey` should be related to `K`
|
|
* an argument to `Map<K, V>.containsValue` should be related to `V`
|
|
* an argument to `Map<K, V>.remove` should be related to `K`
|
|
* an argument to `Map<K, V>.[]` should be related to `K`
|
|
* an argument to `Queue<E>.remove` should be related to `E`
|
|
* an argument to `Set<E>.lookup` should be related to `E`
|
|
* an argument to `Set<E>.remove` should be related to `E`
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction() {
|
|
var list = <int>[];
|
|
if (list.contains('1')) print('someFunction'); // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction() {
|
|
var set = <int>{};
|
|
set.remove('1'); // LINT
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction() {
|
|
var list = <int>[];
|
|
if (list.contains(1)) print('someFunction'); // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction() {
|
|
var set = <int>{};
|
|
set.remove(1); // OK
|
|
}
|
|
```
|
|
combinatorsOrdering:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Sort combinator names alphabetically."
|
|
correctionMessage: "Try sorting the combinator names alphabetically."
|
|
state:
|
|
stable: "2.19"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** sort combinator names alphabetically.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'a.dart' show B, A hide D, C;
|
|
export 'a.dart' show B, A hide D, C;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'a.dart' show A, B hide C, D;
|
|
export 'a.dart' show A, B hide C, D;
|
|
```
|
|
commentReferences:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The referenced name isn't visible in scope."
|
|
correctionMessage: "Try adding an import for the referenced name."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [documentationCommentMaintenance]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** reference only in-scope identifiers in doc comments.
|
|
|
|
If you surround identifiers like variable, method, or type names in square
|
|
brackets, then tools like your IDE and
|
|
[`dart doc`](https://dart.dev/tools/dart-doc) can link to them. For this to
|
|
work, ensure that all identifiers in docs wrapped in brackets are in scope.
|
|
|
|
For example, assuming `outOfScopeId` is out of scope:
|
|
|
|
**BAD:**
|
|
```dart
|
|
/// Returns whether [value] is larger than [outOfScopeId].
|
|
bool isOutOfRange(int value) { ... }
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
/// Returns the larger of [a] or [b].
|
|
int max_int(int a, int b) { ... }
|
|
```
|
|
|
|
Note that the square bracket comment format is designed to allow comments to
|
|
refer to declarations using a fairly natural format but does not allow
|
|
*arbitrary expressions*. In particular, code references within square brackets
|
|
can consist of any of the following:
|
|
|
|
- A bare identifier which is in-scope for the comment (see the spec for what is
|
|
"in-scope" in doc comments). Examples include `[print]` and `[Future]`.
|
|
- Two identifiers separated by a period (a "prefixed identifier"), such that the
|
|
first identifier acts as a namespacing identifier, such as a class property
|
|
name or method name prefixed by the containing class's name, or a top-level
|
|
identifier prefixed by an import prefix. Examples include `[Future.new]` (an
|
|
unnamed constructor), `[Future.value]` (a constructor), `[Future.wait]` (a
|
|
static method), `[Future.then]` (an instance method), `[math.max]` (given that
|
|
'dart:async' is imported with a `max` prefix).
|
|
- A prefixed identifier followed by a pair of parentheses, used to disambiguate
|
|
named constructors from instance members (whose names are allowed to collide).
|
|
Examples include `[Future.value()]`.
|
|
- Three identifiers separated by two periods, such that the first identifier is
|
|
an import prefix name, the second identifier is a top-level element like a
|
|
class or an extension, and the third identifier is a member of that top-level
|
|
element. Examples include `[async.Future.then]` (given that 'dart:async' is
|
|
imported with an `async` prefix).
|
|
|
|
**Known limitations**
|
|
|
|
The `comment_references` lint rule aligns with the Dart analyzer's notion of
|
|
comment references, which is occasionally distinct from Dartdoc's notion of
|
|
comment references. The lint rule may report comment references which Dartdoc
|
|
can resolve, even though the analyzer cannot. See
|
|
[sdk#57783](https://github.com/dart-lang/sdk/issues/57783) for more
|
|
information.
|
|
conditionalUriDoesNotExist:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The target of the conditional URI '#p0' doesn't exist."
|
|
correctionMessage: "Try creating the file referenced by the URI, or try using a URI for a file that does exist."
|
|
state:
|
|
stable: "2.16"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DON'T** reference files that do not exist in conditional imports.
|
|
|
|
Code may fail at runtime if the condition evaluates such that the missing file
|
|
needs to be imported.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'file_that_does_exist.dart'
|
|
if (condition) 'file_that_does_not_exist.dart';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'file_that_does_exist.dart'
|
|
if (condition) 'file_that_also_does_exist.dart';
|
|
```
|
|
constantIdentifierNames:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The constant name '#p0' isn't a lowerCamelCase identifier."
|
|
correctionMessage: "Try changing the name to follow the lowerCamelCase style."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the name of a constant doesn't
|
|
follow the lowerCamelCase naming convention.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the name of the
|
|
top-level variable isn't a lowerCamelCase identifier:
|
|
|
|
```dart
|
|
const [!EMPTY_STRING!] = '';
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rewrite the name to follow the lowerCamelCase naming convention:
|
|
|
|
```dart
|
|
const emptyString = '';
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** using lowerCamelCase for constant names.
|
|
|
|
In new code, use `lowerCamelCase` for constant variables, including enum values.
|
|
|
|
In existing code that uses `ALL_CAPS_WITH_UNDERSCORES` for constants, you may
|
|
continue to use all caps to stay consistent.
|
|
|
|
**BAD:**
|
|
```dart
|
|
const PI = 3.14;
|
|
const kDefaultTimeout = 1000;
|
|
final URL_SCHEME = RegExp('^([a-z]+):');
|
|
|
|
class Dice {
|
|
static final NUMBER_GENERATOR = Random();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
const pi = 3.14;
|
|
const defaultTimeout = 1000;
|
|
final urlScheme = RegExp('^([a-z]+):');
|
|
|
|
class Dice {
|
|
static final numberGenerator = Random();
|
|
}
|
|
```
|
|
controlFlowInFinally:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Use of '#p0' in a 'finally' clause."
|
|
correctionMessage: "Try restructuring the code."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `finally` clause contains a
|
|
`return`, `break`, or `continue` statement.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because there is a `return`
|
|
statement inside a `finally` block:
|
|
|
|
```dart
|
|
void f(void Function() g) {
|
|
try {
|
|
g();
|
|
} catch (e) {
|
|
print(e);
|
|
} finally {
|
|
[!return;!]
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the statement isn't needed, then remove the statement, and remove the
|
|
`finally` clause if the block is empty:
|
|
|
|
```dart
|
|
void f(void Function() g) {
|
|
try {
|
|
g();
|
|
} catch (e) {
|
|
print(e);
|
|
}
|
|
}
|
|
```
|
|
|
|
If the statement is needed, then move the statement outside the `finally`
|
|
block:
|
|
|
|
```dart
|
|
void f(void Function() g) {
|
|
try {
|
|
g();
|
|
} catch (e) {
|
|
print(e);
|
|
}
|
|
return;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** control flow leaving `finally` blocks.
|
|
|
|
Using control flow in `finally` blocks will inevitably cause unexpected behavior
|
|
that is hard to debug.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class BadReturn {
|
|
double nonCompliantMethod() {
|
|
try {
|
|
return 1 / 0;
|
|
} catch (e) {
|
|
print(e);
|
|
} finally {
|
|
return 1.0; // LINT
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
class BadContinue {
|
|
double nonCompliantMethod() {
|
|
for (var o in [1, 2]) {
|
|
try {
|
|
print(o / 0);
|
|
} catch (e) {
|
|
print(e);
|
|
} finally {
|
|
continue; // LINT
|
|
}
|
|
}
|
|
return 1.0;
|
|
}
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
class BadBreak {
|
|
double nonCompliantMethod() {
|
|
for (var o in [1, 2]) {
|
|
try {
|
|
print(o / 0);
|
|
} catch (e) {
|
|
print(e);
|
|
} finally {
|
|
break; // LINT
|
|
}
|
|
}
|
|
return 1.0;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Ok {
|
|
double compliantMethod() {
|
|
var i = 5;
|
|
try {
|
|
i = 1 / 0;
|
|
} catch (e) {
|
|
print(e); // OK
|
|
}
|
|
return i;
|
|
}
|
|
}
|
|
```
|
|
curlyBracesInFlowControlStructures:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Statements in #p0 should be enclosed in a block."
|
|
correctionMessage: "Try wrapping the statement in a block."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a control structure (`if`,
|
|
`for`, `while`, or `do` statement) has a statement other than a block.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `then` statement
|
|
is not enclosed in a block:
|
|
|
|
```dart
|
|
int f(bool b) {
|
|
if (b)
|
|
[!return 1;!]
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add braces around the statement that should be a block:
|
|
|
|
```dart
|
|
int f(bool b) {
|
|
if (b) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use curly braces for all flow control structures.
|
|
|
|
Doing so avoids the [dangling else](https://en.wikipedia.org/wiki/Dangling_else)
|
|
problem.
|
|
|
|
**BAD:**
|
|
```dart
|
|
if (overflowChars != other.overflowChars)
|
|
return overflowChars < other.overflowChars;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (isWeekDay) {
|
|
print('Bike to work!');
|
|
} else {
|
|
print('Go dancing or read a book!');
|
|
}
|
|
```
|
|
|
|
There is one exception to this: an `if` statement with no `else` clause where
|
|
the entire `if` statement (including the condition and the body) fits in one
|
|
line. In that case, you may leave off the braces if you prefer:
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (arg == null) return defaultValue;
|
|
```
|
|
|
|
If the body wraps to the next line, though, use braces:
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (overflowChars != other.overflowChars) {
|
|
return overflowChars < other.overflowChars;
|
|
}
|
|
```
|
|
danglingLibraryDocComments:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Dangling library doc comment."
|
|
correctionMessage: "Add a 'library' directive after the library comment."
|
|
state:
|
|
stable: "2.19"
|
|
categories: [documentationCommentMaintenance]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a documentation comment that
|
|
appears to be library documentation isn't followed by a `library`
|
|
directive. More specifically, it is produced when a documentation comment
|
|
appears before the first directive in the library, assuming that it isn't
|
|
a `library` directive, or before the first top-level declaration and is
|
|
separated from the declaration by one or more blank lines.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because there's a
|
|
documentation comment before the first `import` directive:
|
|
|
|
```dart
|
|
[!/// This is a great library.!]
|
|
import 'dart:core';
|
|
```
|
|
|
|
The following code produces this diagnostic because there's a
|
|
documentation comment before the first class declaration, but there's a
|
|
blank line between the comment and the declaration.
|
|
|
|
```dart
|
|
[!/// This is a great library.!]
|
|
|
|
class C {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the comment is library documentation, then add a `library` directive
|
|
without a name:
|
|
|
|
```dart
|
|
/// This is a great library.
|
|
library;
|
|
|
|
import 'dart:core';
|
|
```
|
|
|
|
If the comment is documentation for the following declaration, then remove
|
|
the blank line:
|
|
|
|
```dart
|
|
/// This is a great library.
|
|
class C {}
|
|
```
|
|
deprecatedDetails: |-
|
|
Attach library doc comments (with `///`) to library directives, rather than
|
|
leaving them dangling near the top of a library.
|
|
|
|
**BAD:**
|
|
```dart
|
|
/// This is a great library.
|
|
import 'package:math';
|
|
```
|
|
|
|
```dart
|
|
/// This is a great library.
|
|
|
|
class C {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
/// This is a great library.
|
|
library;
|
|
|
|
import 'package:math';
|
|
|
|
class C {}
|
|
```
|
|
|
|
**NOTE:** An unnamed library, like `library;` above, is only supported in Dart
|
|
2.19 and later. Code which might run in earlier versions of Dart will need to
|
|
provide a name in the `library` directive.
|
|
dependOnReferencedPackages:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The imported package '#p0' isn't a dependency of the importing package."
|
|
correctionMessage: "Try adding a dependency for '#p0' in the 'pubspec.yaml' file."
|
|
state:
|
|
stable: "2.14"
|
|
categories: [pub]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a package import refers to a
|
|
package that is not specified in the `pubspec.yaml` file.
|
|
|
|
Depending explicitly on packages that you reference ensures they will
|
|
always exist and allows you to put a dependency constraint on them to
|
|
guard against breaking changes.
|
|
|
|
#### Example
|
|
|
|
Given a `pubspec.yaml` file containing the following:
|
|
|
|
```yaml
|
|
dependencies:
|
|
meta: ^3.0.0
|
|
```
|
|
|
|
The following code produces this diagnostic because there is no dependency
|
|
on the package `a`:
|
|
|
|
```dart
|
|
%ignore=uri_does_not_exist
|
|
import [!'package:a/a.dart'!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Whether the dependency should be a regular dependency or dev dependency
|
|
depends on whether the package is referenced from a public library (one
|
|
under either `lib` or `bin`), or only private libraries, (such as one
|
|
under `test`).
|
|
|
|
If the package is referenced from at least one public library, then add a
|
|
regular dependency on the package to the `pubspec.yaml` file under the
|
|
`dependencies` field:
|
|
|
|
```yaml
|
|
dependencies:
|
|
a: ^1.0.0
|
|
meta: ^3.0.0
|
|
```
|
|
|
|
If the package is referenced only from private libraries, then add a
|
|
dev dependency on the package to the `pubspec.yaml` file under the
|
|
`dev_dependencies` field:
|
|
|
|
```yaml
|
|
dependencies:
|
|
meta: ^3.0.0
|
|
dev_dependencies:
|
|
a: ^1.0.0
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** depend on referenced packages.
|
|
|
|
When importing a package, add a dependency on it to your pubspec.
|
|
|
|
Depending explicitly on packages that you reference ensures they will always
|
|
exist and allows you to put a dependency constraint on them to guard you
|
|
against breaking changes.
|
|
|
|
Whether this should be a regular dependency or dev_dependency depends on if it
|
|
is referenced from a public file (one under either `lib` or `bin`), or some
|
|
other private file.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'package:a/a.dart';
|
|
```
|
|
|
|
```yaml
|
|
dependencies:
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'package:a/a.dart';
|
|
```
|
|
|
|
```yaml
|
|
dependencies:
|
|
a: ^1.0.0
|
|
```
|
|
deprecatedConsistencyConstructor:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: deprecatedConsistency
|
|
problemMessage: "Constructors in a deprecated class should be deprecated."
|
|
correctionMessage: "Try marking the constructor as deprecated."
|
|
state:
|
|
stable: "2.13"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** apply `@Deprecated()` consistently:
|
|
|
|
- if a class is deprecated, its constructors should also be deprecated.
|
|
- if a field is deprecated, the constructor parameter pointing to it should also
|
|
be deprecated.
|
|
- if a constructor parameter pointing to a field is deprecated, the field should
|
|
also be deprecated.
|
|
|
|
**BAD:**
|
|
```dart
|
|
@deprecated
|
|
class A {
|
|
A();
|
|
}
|
|
|
|
class B {
|
|
B({this.field});
|
|
@deprecated
|
|
Object field;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
@deprecated
|
|
class A {
|
|
@deprecated
|
|
A();
|
|
}
|
|
|
|
class B {
|
|
B({@deprecated this.field});
|
|
@deprecated
|
|
Object field;
|
|
}
|
|
|
|
class C extends B {
|
|
C({@deprecated super.field});
|
|
}
|
|
```
|
|
deprecatedConsistencyField:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: deprecatedConsistency
|
|
problemMessage: "Fields that are initialized by a deprecated parameter should be deprecated."
|
|
correctionMessage: "Try marking the field as deprecated."
|
|
hasPublishedDocs: false
|
|
deprecatedConsistencyParameter:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: deprecatedConsistency
|
|
problemMessage: "Parameters that initialize a deprecated field should be deprecated."
|
|
correctionMessage: "Try marking the parameter as deprecated."
|
|
hasPublishedDocs: false
|
|
deprecatedMemberUseFromSamePackageWithMessage:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
sharedName: deprecatedMemberUseFromSamePackage
|
|
problemMessage: "'#p0' is deprecated and shouldn't be used. #p1"
|
|
correctionMessage: "Try replacing the use of the deprecated member with the replacement, if a replacement is specified."
|
|
state:
|
|
stable: "3.0"
|
|
categories: [languageFeatureUsage]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Elements that are annotated with `@Deprecated` should not be referenced from
|
|
within the package in which they are declared.
|
|
|
|
**AVOID** using deprecated elements.
|
|
|
|
...
|
|
|
|
**BAD:**
|
|
```dart
|
|
// Declared in one library:
|
|
class Foo {
|
|
@Deprecated("Use 'm2' instead")
|
|
void m1() {}
|
|
|
|
void m2({
|
|
@Deprecated('This is an old parameter') int? p,
|
|
})
|
|
}
|
|
|
|
@Deprecated('Do not use')
|
|
int x = 0;
|
|
|
|
// In the same or another library, but within the same package:
|
|
void m(Foo foo) {
|
|
foo.m1();
|
|
foo.m2(p: 7);
|
|
x = 1;
|
|
}
|
|
```
|
|
|
|
Deprecated elements can be used from within _other_ deprecated elements, in
|
|
order to allow for the deprecation of a collection of APIs together as one unit.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
// Declared in one library:
|
|
class Foo {
|
|
@Deprecated("Use 'm2' instead")
|
|
void m1() {}
|
|
|
|
void m2({
|
|
@Deprecated('This is an old parameter') int? p,
|
|
})
|
|
}
|
|
|
|
@Deprecated('Do not use')
|
|
int x = 0;
|
|
|
|
// In the same or another library, but within the same package:
|
|
@Deprecated('Do not use')
|
|
void m(Foo foo) {
|
|
foo.m1();
|
|
foo.m2(p: 7);
|
|
x = 1;
|
|
}
|
|
```
|
|
deprecatedMemberUseFromSamePackageWithoutMessage:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: deprecatedMemberUseFromSamePackage
|
|
problemMessage: "'#p0' is deprecated and shouldn't be used."
|
|
correctionMessage: "Try replacing the use of the deprecated member with the replacement, if a replacement is specified."
|
|
hasPublishedDocs: false
|
|
diagnosticDescribeAllProperties:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The public property isn't described by either 'debugFillProperties' or 'debugDescribeChildren'."
|
|
correctionMessage: "Try describing the property."
|
|
state:
|
|
stable: "2.3"
|
|
categories: [errorProne, flutter]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a class that implements
|
|
`Diagnosticable` has a public property that isn't added as a property in
|
|
either a `debugFillProperties` or `debugDescribeChildren` method.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the property `p2`
|
|
isn't added in the `debugFillProperties` method:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
class C extends Widget {
|
|
bool get p1 => true;
|
|
|
|
bool get [!p2!] => false;
|
|
|
|
@override
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
super.debugFillProperties(properties);
|
|
properties.add(DiagnosticsProperty<bool>('p1', p1));
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If there isn't on override of either the `debugFillProperties` or
|
|
`debugDescribeChildren` method, then add one.
|
|
|
|
Add a description of the property in the `debugFillProperties` or
|
|
`debugDescribeChildren` method:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
class C extends Widget {
|
|
bool get p1 => true;
|
|
|
|
bool get p2 => false;
|
|
|
|
@override
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
super.debugFillProperties(properties);
|
|
properties.add(DiagnosticsProperty<bool>('p1', p1));
|
|
properties.add(DiagnosticsProperty<bool>('p2', p2));
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** reference all public properties in `debug` method implementations.
|
|
|
|
Implementers of `Diagnosticable` should reference all public properties in
|
|
a `debugFillProperties(...)` or `debugDescribeChildren(...)` method
|
|
implementation to improve debuggability at runtime.
|
|
|
|
Public properties are defined as fields and getters that are
|
|
|
|
* not package-private (e.g., prefixed with `_`)
|
|
* not `static` or overriding
|
|
* not themselves `Widget`s or collections of `Widget`s
|
|
|
|
In addition, the "debug" prefix is treated specially for properties in Flutter.
|
|
For the purposes of diagnostics, a property `foo` and a prefixed property
|
|
`debugFoo` are treated as effectively describing the same property and it is
|
|
sufficient to refer to one or the other.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Absorber extends Widget {
|
|
bool get absorbing => _absorbing;
|
|
bool _absorbing;
|
|
bool get ignoringSemantics => _ignoringSemantics;
|
|
bool _ignoringSemantics;
|
|
@override
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
super.debugFillProperties(properties);
|
|
properties.add(DiagnosticsProperty<bool>('absorbing', absorbing));
|
|
// Missing reference to ignoringSemantics
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Absorber extends Widget {
|
|
bool get absorbing => _absorbing;
|
|
bool _absorbing;
|
|
bool get ignoringSemantics => _ignoringSemantics;
|
|
bool _ignoringSemantics;
|
|
@override
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
super.debugFillProperties(properties);
|
|
properties.add(DiagnosticsProperty<bool>('absorbing', absorbing));
|
|
properties.add(DiagnosticsProperty<bool>('ignoringSemantics', ignoringSemantics));
|
|
}
|
|
}
|
|
```
|
|
directivesOrderingAlphabetical:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: directivesOrdering
|
|
problemMessage: "Sort directive sections alphabetically."
|
|
correctionMessage: "Try sorting the directives."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** follow the directive ordering conventions in
|
|
[Effective Dart](https://dart.dev/effective-dart/style#ordering):
|
|
|
|
**DO** place `dart:` imports before other imports.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'package:bar/bar.dart';
|
|
import 'package:foo/foo.dart';
|
|
|
|
import 'dart:async'; // LINT
|
|
import 'dart:html'; // LINT
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'dart:html'; // OK
|
|
import 'package:bar/bar.dart';
|
|
|
|
import 'dart:async'; // LINT
|
|
import 'package:foo/foo.dart';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'dart:async'; // OK
|
|
import 'dart:html'; // OK
|
|
|
|
import 'package:bar/bar.dart';
|
|
import 'package:foo/foo.dart';
|
|
```
|
|
|
|
**DO** place `package:` imports before relative imports.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'a.dart';
|
|
import 'b.dart';
|
|
|
|
import 'package:bar/bar.dart'; // LINT
|
|
import 'package:foo/foo.dart'; // LINT
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'package:bar/bar.dart'; // OK
|
|
import 'a.dart';
|
|
|
|
import 'package:foo/foo.dart'; // LINT
|
|
import 'b.dart';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'package:bar/bar.dart'; // OK
|
|
import 'package:foo/foo.dart'; // OK
|
|
|
|
import 'a.dart';
|
|
import 'b.dart';
|
|
```
|
|
|
|
**DO** specify exports in a separate section after all imports.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'src/error.dart';
|
|
export 'src/error.dart'; // LINT
|
|
import 'src/string_source.dart';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'src/error.dart';
|
|
import 'src/string_source.dart';
|
|
|
|
export 'src/error.dart'; // OK
|
|
```
|
|
|
|
**DO** sort sections alphabetically.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'package:foo/bar.dart'; // OK
|
|
import 'package:bar/bar.dart'; // LINT
|
|
|
|
import 'a/b.dart'; // OK
|
|
import 'a.dart'; // LINT
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'package:bar/bar.dart'; // OK
|
|
import 'package:foo/bar.dart'; // OK
|
|
|
|
import 'a.dart'; // OK
|
|
import 'a/b.dart'; // OK
|
|
```
|
|
directivesOrderingDart:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: directivesOrdering
|
|
problemMessage: "Place 'dart:' #p0 before other #p0."
|
|
correctionMessage: "Try sorting the directives."
|
|
hasPublishedDocs: false
|
|
directivesOrderingExports:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: directivesOrdering
|
|
problemMessage: "Specify exports in a separate section after all imports."
|
|
correctionMessage: "Try sorting the directives."
|
|
hasPublishedDocs: false
|
|
directivesOrderingPackageBeforeRelative:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: directivesOrdering
|
|
problemMessage: "Place 'package:' #p0 before relative #p0."
|
|
correctionMessage: "Try sorting the directives."
|
|
hasPublishedDocs: false
|
|
discardedFutures:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "'Future'-returning calls in a non-'async' function."
|
|
correctionMessage: "Try converting the enclosing function to be 'async' and then 'await' the future, or wrap the expression in 'unawaited'."
|
|
state:
|
|
stable: "2.18"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Making asynchronous calls in non-`async` functions is usually the sign of a
|
|
programming error. In general these functions should be marked `async` and such
|
|
futures should likely be awaited (as enforced by `unawaited_futures`).
|
|
|
|
**DON'T** invoke asynchronous functions in non-`async` blocks.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void recreateDir(String path) {
|
|
deleteDir(path);
|
|
createDir(path);
|
|
}
|
|
|
|
Future<void> deleteDir(String path) async {}
|
|
|
|
Future<void> createDir(String path) async {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Future<void> recreateDir(String path) async {
|
|
await deleteDir(path);
|
|
await createDir(path);
|
|
}
|
|
|
|
Future<void> deleteDir(String path) async {}
|
|
|
|
Future<void> createDir(String path) async {}
|
|
```
|
|
doNotUseEnvironment:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Invalid use of an environment declaration."
|
|
correctionMessage: "Try removing the environment declaration usage."
|
|
state:
|
|
stable: "2.9"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Using values derived from the environment at compile-time, creates
|
|
hidden global state and makes applications hard to understand and maintain.
|
|
|
|
**DON'T** use `fromEnvironment` or `hasEnvironment` factory constructors.
|
|
|
|
**BAD:**
|
|
```dart
|
|
const loggingLevel =
|
|
bool.hasEnvironment('logging') ? String.fromEnvironment('logging') : null;
|
|
```
|
|
documentIgnores:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Missing documentation explaining why the diagnostic is ignored."
|
|
correctionMessage: "Try adding a comment immediately above the ignore comment."
|
|
state:
|
|
stable: "3.5"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** document all ignored diagnostic reports.
|
|
|
|
**BAD:**
|
|
```dart
|
|
// ignore: unused_element
|
|
int _x = 1;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
// This private field will be used later.
|
|
// ignore: unused_element
|
|
int _x = 1;
|
|
```
|
|
emptyCatches:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Empty catch block."
|
|
correctionMessage: "Try adding statements to the block, adding a comment to the block, or removing the 'catch' clause."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the block in a `catch` clause
|
|
is empty.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the catch block is
|
|
empty:
|
|
|
|
```dart
|
|
void f() {
|
|
try {
|
|
print('Hello');
|
|
} catch (exception) [!{}!]
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the exception shouldn't be ignored, then add code to handle the
|
|
exception:
|
|
|
|
```dart
|
|
void f() {
|
|
try {
|
|
print('We can print.');
|
|
} catch (exception) {
|
|
print("We can't print.");
|
|
}
|
|
}
|
|
```
|
|
|
|
If the exception is intended to be ignored, then add a comment explaining
|
|
why:
|
|
|
|
```dart
|
|
void f() {
|
|
try {
|
|
print('We can print.');
|
|
} catch (exception) {
|
|
// Nothing to do.
|
|
}
|
|
}
|
|
```
|
|
|
|
If the exception is intended to be ignored and there isn't any good
|
|
explanation for why, then rename the exception parameter:
|
|
|
|
```dart
|
|
void f() {
|
|
try {
|
|
print('We can print.');
|
|
} catch (_) {}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** empty catch blocks.
|
|
|
|
In general, empty catch blocks should be avoided. In cases where they are
|
|
intended, a comment should be provided to explain why exceptions are being
|
|
caught and suppressed. Alternatively, the exception identifier can be named with
|
|
underscores (e.g., `_`) to indicate that we intend to skip it.
|
|
|
|
**BAD:**
|
|
```dart
|
|
try {
|
|
...
|
|
} catch(exception) { }
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
try {
|
|
...
|
|
} catch(e) {
|
|
// ignored, really.
|
|
}
|
|
|
|
// Alternatively:
|
|
try {
|
|
...
|
|
} catch(_) { }
|
|
|
|
// Better still:
|
|
try {
|
|
...
|
|
} catch(e) {
|
|
doSomething(e);
|
|
}
|
|
```
|
|
emptyConstructorBodies:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Empty constructor bodies should be written using a ';' rather than '{}'."
|
|
correctionMessage: "Try replacing the constructor body with ';'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a constructor has an empty
|
|
block body.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the constructor for
|
|
`C` has a block body that is empty:
|
|
|
|
```dart
|
|
class C {
|
|
C() [!{}!]
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the block with a semicolon:
|
|
|
|
```dart
|
|
class C {
|
|
C();
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/usage#do-use--instead-of--for-empty-constructor-bodies):
|
|
|
|
**DO** use `;` instead of `{}` for empty constructor bodies.
|
|
|
|
In Dart, a constructor with an empty body can be terminated with just a
|
|
semicolon. This is required for const constructors. For consistency and
|
|
brevity, other constructors should also do this.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Point {
|
|
int x, y;
|
|
Point(this.x, this.y) {}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Point {
|
|
int x, y;
|
|
Point(this.x, this.y);
|
|
}
|
|
```
|
|
emptyContainerBodies:
|
|
type: lint
|
|
parameters:
|
|
String containerKind: The kind of the container, such as 'class' or 'enum'.
|
|
problemMessage: "Empty #containerKind bodies should be written using a ';' rather than '{}'."
|
|
correctionMessage: "Try replacing the #containerKind body with ';'."
|
|
state:
|
|
experimental: "3.13"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Use `;` instead of `{}` for empty container (class, enum, etc.) bodies.
|
|
emptyStatements:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary empty statement."
|
|
correctionMessage: "Try removing the empty statement or restructuring the code."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an empty statement is found.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the statement
|
|
controlled by the `while` loop is an empty statement:
|
|
|
|
```dart
|
|
void f(bool condition) {
|
|
while (condition)[!;!]
|
|
g();
|
|
}
|
|
|
|
void g() {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If there are no statements that need to be controlled, then remove both
|
|
the empty statement and the control structure it's part of (being careful
|
|
that any other code being removed doesn't have a side-effect that needs to
|
|
be preserved):
|
|
|
|
```dart
|
|
void f(bool condition) {
|
|
g();
|
|
}
|
|
|
|
void g() {}
|
|
```
|
|
|
|
If there are no statements that need to be controlled but the control
|
|
structure is still required for other reasons, then replace the empty
|
|
statement with a block to make the structure of the code more obvious:
|
|
|
|
```dart
|
|
void f(bool condition) {
|
|
while (condition) {}
|
|
g();
|
|
}
|
|
|
|
void g() {}
|
|
```
|
|
|
|
If there are statements that need to be controlled, remove the empty
|
|
statement and adjust the code so that the appropriate statements are being
|
|
controlled, possibly adding a block:
|
|
|
|
```dart
|
|
void f(bool condition) {
|
|
while (condition) {
|
|
g();
|
|
}
|
|
}
|
|
|
|
void g() {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** empty statements.
|
|
|
|
Empty statements almost always indicate a bug.
|
|
|
|
For example,
|
|
|
|
**BAD:**
|
|
```dart
|
|
if (complicated.expression.foo());
|
|
bar();
|
|
```
|
|
|
|
Formatted with `dart format` the bug becomes obvious:
|
|
|
|
```dart
|
|
if (complicated.expression.foo()) ;
|
|
bar();
|
|
|
|
```
|
|
|
|
Better to avoid the empty statement altogether.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (complicated.expression.foo())
|
|
bar();
|
|
```
|
|
enableNullSafety:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
stable: "2.19"
|
|
removed: "3.0"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
NOTE: This rule is removed in Dart 3.0.0; it is no longer functional.
|
|
|
|
**DO** use sound null safety, by not specifying a dart version lower than `2.12`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
// @dart=2.8
|
|
a() {
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
b() {
|
|
}
|
|
```
|
|
eolAtEndOfFile:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Missing a newline at the end of the file."
|
|
correctionMessage: "Try adding a newline at the end of the file."
|
|
state:
|
|
stable: "2.14"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** put a single newline at the end of non-empty files.
|
|
|
|
**BAD:**
|
|
```dart
|
|
a {
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
b {
|
|
}
|
|
<-- newline
|
|
```
|
|
eraseDartTypeExtensionTypes:
|
|
# TODO(nshahan): Update.
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unsafe use of 'DartType' in an 'is' check."
|
|
correctionMessage: "Ensure DartType extension types are erased by using a helper method."
|
|
state:
|
|
internal: "3.3"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Experimental WIP lint to help ensure `DartType` accesses are safe in the dev_compiler.
|
|
|
|
**For internal use only.**
|
|
exhaustiveCases:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Missing case clauses for some constants in '#p0'."
|
|
correctionMessage: "Try adding case clauses for the missing constants."
|
|
state:
|
|
stable: "2.9"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Switching on instances of enum-like classes should be exhaustive.
|
|
|
|
Enum-like classes are defined as concrete (non-abstract) classes that have:
|
|
* only private non-factory constructors
|
|
* two or more static const fields whose type is the enclosing class and
|
|
* no subclasses of the class in the defining library
|
|
|
|
**DO** define case clauses for all constants in enum-like classes.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class EnumLike {
|
|
final int i;
|
|
const EnumLike._(this.i);
|
|
|
|
static const e = EnumLike._(1);
|
|
static const f = EnumLike._(2);
|
|
static const g = EnumLike._(3);
|
|
}
|
|
|
|
void bad(EnumLike e) {
|
|
// Missing case.
|
|
switch(e) { // LINT
|
|
case EnumLike.e :
|
|
print('e');
|
|
break;
|
|
case EnumLike.f :
|
|
print('f');
|
|
break;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class EnumLike {
|
|
final int i;
|
|
const EnumLike._(this.i);
|
|
|
|
static const e = EnumLike._(1);
|
|
static const f = EnumLike._(2);
|
|
static const g = EnumLike._(3);
|
|
}
|
|
|
|
void ok(EnumLike e) {
|
|
// All cases covered.
|
|
switch(e) { // OK
|
|
case EnumLike.e :
|
|
print('e');
|
|
break;
|
|
case EnumLike.f :
|
|
print('f');
|
|
break;
|
|
case EnumLike.g :
|
|
print('g');
|
|
break;
|
|
}
|
|
}
|
|
```
|
|
fileNames:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The file name '#p0' isn't a lower_case_with_underscores identifier."
|
|
correctionMessage: "Try changing the name to follow the lower_case_with_underscores style."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the name of a `.dart` file
|
|
doesn't use lower_case_with_underscores.
|
|
|
|
#### Example
|
|
|
|
A file named `SliderMenu.dart` produces this diagnostic because the file
|
|
name uses the UpperCamelCase convention.
|
|
|
|
#### Common fixes
|
|
|
|
Rename the file to use the lower_case_with_underscores convention, such as
|
|
`slider_menu.dart`.
|
|
deprecatedDetails: |-
|
|
**DO** name source files using `lowercase_with_underscores`.
|
|
|
|
Some file systems are not case-sensitive, so many projects require filenames to
|
|
be all lowercase. Using a separating character allows names to still be readable
|
|
in that form. Using underscores as the separator ensures that the name is still
|
|
a valid Dart identifier, which may be helpful if the language later supports
|
|
symbolic imports.
|
|
|
|
**BAD:**
|
|
|
|
* `SliderMenu.dart`
|
|
* `filesystem.dart`
|
|
* `file-system.dart`
|
|
|
|
**GOOD:**
|
|
|
|
* `slider_menu.dart`
|
|
* `file_system.dart`
|
|
|
|
Files without a strict `.dart` extension are ignored. For example:
|
|
|
|
**OK:**
|
|
|
|
* `file-system.g.dart`
|
|
* `SliderMenu.css.dart`
|
|
|
|
The lint `library_names` can be used to enforce the same kind of naming on the
|
|
library.
|
|
flutterStyleTodos:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "To-do comment doesn't follow the Flutter style."
|
|
correctionMessage: "Try following the Flutter style for to-do comments."
|
|
state:
|
|
stable: "2.1"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** use Flutter TODO format.
|
|
|
|
From the [Flutter
|
|
docs](https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#comments):
|
|
|
|
> TODOs should include the string TODO in all caps, followed by the GitHub
|
|
username of the person with the best context about the problem referenced by the
|
|
TODO in parenthesis. A TODO is not a commitment that the person referenced will
|
|
fix the problem, it is intended to be the person with enough context to explain
|
|
the problem. Thus, when you create a TODO, it is almost always your username
|
|
that is given.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
// TODO(username): message.
|
|
// TODO(username): message, https://URL-to-issue.
|
|
```
|
|
hashAndEquals:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
problemMessage: "Missing a corresponding override of '#p0'."
|
|
correctionMessage: "Try overriding '#p0' or removing '#p1'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a class or mixin either
|
|
overrides the definition of `==` but doesn't override the definition of
|
|
`hashCode`, or conversely overrides the definition of `hashCode` but
|
|
doesn't override the definition of `==`.
|
|
|
|
Both the `==` operator and the `hashCode` property of objects must be
|
|
consistent for a common hash map implementation to function properly. As a
|
|
result, when overriding either method, both should be overridden.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the class `C`
|
|
overrides the `==` operator but doesn't override the getter `hashCode`:
|
|
|
|
```dart
|
|
class C {
|
|
final int value;
|
|
|
|
C(this.value);
|
|
|
|
@override
|
|
bool operator [!==!](Object other) =>
|
|
other is C &&
|
|
other.runtimeType == runtimeType &&
|
|
other.value == value;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If you need to override one of the members, then add an override of the
|
|
other:
|
|
|
|
```dart
|
|
class C {
|
|
final int value;
|
|
|
|
C(this.value);
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
other is C &&
|
|
other.runtimeType == runtimeType &&
|
|
other.value == value;
|
|
|
|
@override
|
|
int get hashCode => value.hashCode;
|
|
}
|
|
```
|
|
|
|
If you don't need to override either of the members, then remove the
|
|
unnecessary override:
|
|
|
|
```dart
|
|
class C {
|
|
final int value;
|
|
|
|
C(this.value);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** override `hashCode` if overriding `==` and prefer overriding `==` if
|
|
overriding `hashCode`.
|
|
|
|
Every object in Dart has a `hashCode`. Both the `==` operator and the
|
|
`hashCode` property of objects must be consistent in order for a common hash
|
|
map implementation to function properly. Thus, when overriding `==`, the
|
|
`hashCode` should also be overridden to maintain consistency. Similarly, if
|
|
`hashCode` is overridden, `==` should be also.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Bad {
|
|
final int value;
|
|
Bad(this.value);
|
|
|
|
@override
|
|
bool operator ==(Object other) => other is Bad && other.value == value;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Better {
|
|
final int value;
|
|
Better(this.value);
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
other is Better &&
|
|
other.runtimeType == runtimeType &&
|
|
other.value == value;
|
|
|
|
@override
|
|
int get hashCode => value.hashCode;
|
|
}
|
|
```
|
|
implementationImports:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Import of a library in the 'lib/src' directory of another package."
|
|
correctionMessage: "Try importing a public library that exports this library, or removing the import."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an import references a library
|
|
that's inside the `lib/src` directory of a different package, which
|
|
violates [the convention for pub
|
|
packages](https://dart.dev/tools/pub/package-layout#implementation-files).
|
|
|
|
#### Example
|
|
|
|
The following code, assuming that it isn't part of the `ffi` package,
|
|
produces this diagnostic because the library being imported is inside the
|
|
top-level `src` directory:
|
|
|
|
```dart
|
|
%ignore=uri_does_not_exist
|
|
import [!'package:ffi/src/allocation.dart'!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the library being imported contains code that's part of the public API,
|
|
then import the public library that exports the public API:
|
|
|
|
```dart
|
|
%ignore=unused_import
|
|
import 'package:ffi/ffi.dart';
|
|
```
|
|
|
|
If the library being imported isn't part of the public API of the package,
|
|
then either find a different way to accomplish your goal, assuming that
|
|
it's possible, or open an issue asking the package authors to make it part
|
|
of the public API.
|
|
deprecatedDetails: |-
|
|
From the [pub package layout doc](https://dart.dev/tools/pub/package-layout#implementation-files):
|
|
|
|
**DON'T** import implementation files from another package.
|
|
|
|
The libraries inside `lib` are publicly visible: other packages are free to
|
|
import them. But much of a package's code is internal implementation libraries
|
|
that should only be imported and used by the package itself. Those go inside a
|
|
subdirectory of `lib` called `src`. You can create subdirectories in there if
|
|
it helps you organize things.
|
|
|
|
You are free to import libraries that live in `lib/src` from within other Dart
|
|
code in the same package (like other libraries in `lib`, scripts in `bin`,
|
|
and tests) but you should never import from another package's `lib/src`
|
|
directory. Those files are not part of the package's public API, and they
|
|
might change in ways that could break your code.
|
|
|
|
**BAD:**
|
|
```dart
|
|
// In 'road_runner'
|
|
import 'package:acme/src/internals.dart';
|
|
```
|
|
implicitCallTearoffs:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Implicit tear-off of the 'call' method."
|
|
correctionMessage: "Try explicitly tearing off the 'call' method."
|
|
state:
|
|
stable: "2.19"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an object with a `call` method
|
|
is assigned to a function-typed variable, implicitly tearing off the
|
|
`call` method.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because an instance of
|
|
`Callable` is passed to a function expecting a `Function`:
|
|
|
|
```dart
|
|
class Callable {
|
|
void call() {}
|
|
}
|
|
|
|
void callIt(void Function() f) {
|
|
f();
|
|
}
|
|
|
|
void f() {
|
|
callIt([!Callable()!]);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Explicitly tear off the `call` method:
|
|
|
|
```dart
|
|
class Callable {
|
|
void call() {}
|
|
}
|
|
|
|
void callIt(void Function() f) {
|
|
f();
|
|
}
|
|
|
|
void f() {
|
|
callIt(Callable().call);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO**
|
|
Explicitly tear off `.call` methods from objects when assigning to a Function
|
|
type. There is less magic with an explicit tear off. Future language versions
|
|
may remove the implicit call tear off.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Callable {
|
|
void call() {}
|
|
}
|
|
void callIt(void Function() f) {
|
|
f();
|
|
}
|
|
|
|
callIt(Callable());
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Callable {
|
|
void call() {}
|
|
}
|
|
void callIt(void Function() f) {
|
|
f();
|
|
}
|
|
|
|
callIt(Callable().call);
|
|
```
|
|
implicitReopen:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
Object p2: undocumented
|
|
Object p3: undocumented
|
|
problemMessage: "The #p0 '#p1' reopens '#p2' because it is not marked '#p3'."
|
|
correctionMessage: "Try marking '#p1' '#p3' or annotating it with '@reopen'."
|
|
state:
|
|
experimental: "3.0"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Using an `interface`, `base`, `final`, or `sealed` modifier on a class,
|
|
or a `base` modifier on a mixin,
|
|
authors can control whether classes and mixins allow being implemented,
|
|
extended, and/or mixed in from outside of the library where they're defined.
|
|
In some cases, it's possible for an author to inadvertently relax these controls
|
|
and implicitly "reopen" a class. (A similar reopening cannot occur with a mixin.)
|
|
|
|
This lint guards against unintentionally reopening a class by requiring such
|
|
cases to be made explicit with the
|
|
[`@reopen`](https://pub.dev/documentation/meta/latest/meta/reopen-constant.html)
|
|
annotation in `package:meta`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
interface class I {}
|
|
|
|
class C extends I {} // LINT
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
interface class I {}
|
|
|
|
final class C extends I {}
|
|
```
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
interface class I {}
|
|
|
|
@reopen
|
|
class C extends I {}
|
|
```
|
|
initializeInFieldDeclaration:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Field should be initialized in the field declaration."
|
|
correctionMessage: "Try initializing the field where it's declared."
|
|
state:
|
|
experimental: "3.13"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Initialize fields where they're declared when possible.
|
|
invalidCasePatterns:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "This expression is not valid in a 'case' clause in Dart 3.0."
|
|
correctionMessage: "Try refactoring the expression to be valid in 3.0."
|
|
state:
|
|
experimental: "3.0"
|
|
categories: [languageFeatureUsage]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Some case expressions that are valid in Dart 2.19 and below will become an error
|
|
or have changed semantics when a library is upgraded to 3.0. This lint flags
|
|
those expressions in order to ease migration to Dart 3.0.
|
|
|
|
Some valid switch cases in 2.19 will become compile errors in Dart 3.0:
|
|
|
|
* Set literals
|
|
* Parenthesized expressions
|
|
* Calls to `identical()`.
|
|
* Unary operator expressions `!`, `-`, or `~` (except for `-` before an integer
|
|
literal, which is a valid pattern and is fine)
|
|
* Binary operator expressions `!=`, `==`, `&`, `|`, `^`, `~/`, `>>`, `>>>`,
|
|
`<<`, `+`, `-`, `*`, `/`, `%`, `<`, `<=`, `>`, `>=`, `??`.
|
|
* Conditional operator `?:`
|
|
* `.length` calls on strings
|
|
* `is` and `is!` expressions
|
|
|
|
Examples of all of them:
|
|
|
|
```dart
|
|
switch (obj) {
|
|
case {1}: // Set literal.
|
|
case (1): // Parenthesized expression.
|
|
case identical(1, 2): // `identical()` call.
|
|
case -pi: // Unary operator.
|
|
case 1 + 2: // Binary operator.
|
|
case true ? 1 : 2: // Conditional operator.
|
|
case 'hi'.length: // .length call.
|
|
case i is int: // is expression.
|
|
}
|
|
```
|
|
|
|
Some valid switch cases in 2.19 are also syntactically valid patterns, but the
|
|
pattern matching behavior may be different from the current constant equality
|
|
behavior. They are:
|
|
|
|
**List and map literals.** A list or map literal can appear as a constant in a
|
|
case:
|
|
|
|
```dart
|
|
switch (obj) {
|
|
case [1, 2]: ...
|
|
case {'k': 'v'}: ...
|
|
}
|
|
```
|
|
|
|
Currently, the case will only match if the incoming value has the same identity
|
|
as the constant. So:
|
|
|
|
```dart
|
|
test(List<int> list) {
|
|
switch (list) {
|
|
case [1, 2]: print('Matched'); break;
|
|
default: print('Did not match'); break;
|
|
}
|
|
}
|
|
|
|
main() {
|
|
test(const [1, 2]); // Prints "Matched".
|
|
test([1, 2]); // Prints "Did not match".
|
|
}
|
|
```
|
|
|
|
With patterns, a list or map literal becomes a list or map pattern. The pattern
|
|
destructures the incoming object and matches if the subpatterns all match. In
|
|
other words, list and map pattern match using something more like deep equality.
|
|
|
|
With Dart 3.0, the above program prints "Matched" twice.
|
|
|
|
**Constant constructor calls.** Similar to collections, you can construct a
|
|
constant instance of a class in a case:
|
|
|
|
```dart
|
|
class Point {
|
|
final int x;
|
|
final int y;
|
|
const Point({this.x, this.y});
|
|
}
|
|
|
|
test(Point p) {
|
|
switch (p) {
|
|
case Point(x: 1, y: 2): print('Matched'); break;
|
|
default: print('Did not match'); break;
|
|
}
|
|
}
|
|
|
|
main() {
|
|
test(const Point(1, 2)); // Prints "Matched".
|
|
test(Point(1, 2)); // Prints "Did not match".
|
|
}
|
|
```
|
|
|
|
Again, like collections, the case currently only matches if the incoming value
|
|
has the same identity. With patterns, the `Point(...)` syntax becomes an object
|
|
pattern that destructures the incoming point, calls the `x` and `y` getters on
|
|
it and then matches the results of those against the corresponding subpatterns.
|
|
|
|
In this example, it will print "Matched" twice.
|
|
|
|
Note that object patterns only support named fields. So any constant constructor
|
|
in a case today that has positional arguments will become a compile-time error
|
|
when parsed as a pattern. A constant constructor call with no arguments is a
|
|
valid object pattern and only does a type test:
|
|
|
|
```dart
|
|
class Thing {
|
|
const Thing();
|
|
}
|
|
|
|
test(Thing t) {
|
|
switch (t) {
|
|
case Thing(): print('Matched'); break;
|
|
default: print('Did not match'); break;
|
|
}
|
|
}
|
|
|
|
main() {
|
|
test(const Thing()); // Prints "Matched".
|
|
test(Thing()); // Prints "Did not match".
|
|
}
|
|
```
|
|
|
|
When interpreted as a pattern, this prints "Matched" twice.
|
|
|
|
**Wildcards.** Today, you can have a constant named `_`:
|
|
|
|
```dart
|
|
test(int n) {
|
|
const _ = 3;
|
|
switch (n) {
|
|
case _: print('Matched'); break;
|
|
default: print('Did not match'); break;
|
|
}
|
|
}
|
|
|
|
main() {
|
|
test(3); // Prints "Matched".
|
|
test(5); // Prints "Did not match".
|
|
}
|
|
```
|
|
|
|
With patterns, the identifier `_` is treated as a pattern that matches all
|
|
values, so this prints "Matched" twice.
|
|
|
|
**Logic operators.** The logic operators `&&` and `||` are valid constant
|
|
expressions and also valid patterns. As a constant expression, they simply
|
|
evaluate the expression to a boolean and match if the incoming value is equal to
|
|
that boolean value. So:
|
|
|
|
```dart
|
|
test(bool b) {
|
|
switch (b) {
|
|
case true && false: print('Matched'); break;
|
|
default: print('Did not match'); break;
|
|
}
|
|
}
|
|
|
|
main() {
|
|
test(false); // Prints "Matched".
|
|
test(true); // Prints "Did not match".
|
|
}
|
|
```
|
|
|
|
With Dart 3.0, these become patterns. The above example prints "Did not match"
|
|
twice because no boolean value can be both true and false.
|
|
|
|
Many of invalid cases can be mechanically changed to something that is valid
|
|
both in Dart today and valid and means the same in Dart 3.0.
|
|
|
|
**Parenthesized expressions:** Provided the inner expression is one that's not
|
|
broken in Dart 3.0, just discard the parentheses.
|
|
|
|
**List literals, map literals, set literals, and constant constructor calls:**
|
|
Put `const` before the literal or call. This turns it into a constant pattern
|
|
which preserves the current behavior:
|
|
|
|
**BAD:**
|
|
|
|
```dart
|
|
case [1, 2]:
|
|
case {'k': 'v'}:
|
|
case {1, 2}:
|
|
case Point(1, 2):
|
|
```
|
|
|
|
**GOOD:**
|
|
|
|
```dart
|
|
case const [1, 2]:
|
|
case const {'k': 'v'}:
|
|
case const {1, 2}:
|
|
case const Point(1, 2):
|
|
```
|
|
|
|
* **Wildcards:** Rename the constant from `_` to something else. Since the name
|
|
is private, this can be done locally in the library without affecting other
|
|
code.
|
|
|
|
* **Everything else:** For any other invalid expression, you have to hoist the
|
|
expression out into a new named constant. For example, if you have code like
|
|
this:
|
|
|
|
|
|
**BAD:**
|
|
|
|
```dart
|
|
switch (n) {
|
|
case 1 + 2: ...
|
|
}
|
|
```
|
|
|
|
It can be fixed by changing it to:
|
|
|
|
**GOOD:**
|
|
|
|
```dart
|
|
const three = 1 + 2;
|
|
|
|
switch (n) {
|
|
case three: ...
|
|
}
|
|
```
|
|
invalidRuntimeCheckWithJsInteropTypesCatchClauseJsInteropType:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: invalidRuntimeCheckWithJsInteropTypes
|
|
problemMessage: "Catch clause with type '#p0' checks whether the caught value is a JS interop type, which might not be platform-consistent."
|
|
correctionMessage: "Remove the type in the catch clause and try using 'isA' from 'dart:js_interop' within the catch block to check if the value is a JS interop type."
|
|
hasPublishedDocs: false
|
|
invalidRuntimeCheckWithJsInteropTypesDartAsJs:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
sharedName: invalidRuntimeCheckWithJsInteropTypes
|
|
problemMessage: "Cast from '#p0' to '#p1' casts a Dart value to a JS interop type, which might not be platform-consistent."
|
|
correctionMessage: "Try using conversion methods from 'dart:js_interop' to convert between Dart types and JS interop types."
|
|
state:
|
|
stable: "3.5"
|
|
categories: [errorProne, web]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
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.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the JS interop type
|
|
`JSBoolean` is on the right-hand side of an `is` test:
|
|
|
|
```dart
|
|
import 'dart:js_interop';
|
|
|
|
bool f(Object b) => [!b is JSBoolean!];
|
|
```
|
|
|
|
The following code produces this diagnostic because the JS interop type
|
|
`JSString` is used as a type argument on the right-hand side of an `is`
|
|
test:
|
|
|
|
```dart
|
|
import 'dart:js_interop';
|
|
|
|
bool f(List<Object> l) => [!l is List<JSString>!];
|
|
```
|
|
|
|
The following code produces this diagnostic because the JS interop value
|
|
`a` is on the left-hand side of an `is` test:
|
|
|
|
```dart
|
|
import 'dart:js_interop';
|
|
|
|
bool f(JSAny a) => [!a is String!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use a JS interop helper, such as `isA`, to check the underlying type of
|
|
JS interop values:
|
|
|
|
```dart
|
|
import 'dart:js_interop';
|
|
|
|
void f(Object b) => b.jsify()?.isA<JSBoolean>();
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use `is` checks where the type is a JS interop type.
|
|
|
|
**DON'T** use `is` checks where the type is a generic Dart type that has JS
|
|
interop type arguments.
|
|
|
|
**DON'T** use `is` checks with a JS interop value.
|
|
|
|
`dart:js_interop` types have runtime types that are different based on whether
|
|
you are compiling to JS or to Wasm. Therefore, runtime type checks may result in
|
|
different behavior. Runtime checks also do not necessarily check that a JS
|
|
interop value is a particular JavaScript type.
|
|
|
|
**BAD:**
|
|
```dart
|
|
extension type HTMLElement(JSObject o) {}
|
|
extension type HTMLDivElement(JSObject o) implements HTMLElement {}
|
|
|
|
void compute(JSAny a, bool b, List<JSObject> lo, List<String> ls, JSObject o,
|
|
HTMLElement e) {
|
|
a is String; // LINT, checking that a JS value is a Dart type
|
|
b is JSBoolean; // LINT, checking that a Dart value is a JS type
|
|
a is JSString; // LINT, checking that a JS value is a different JS interop
|
|
// type
|
|
o is JSNumber; // LINT, checking that a JS value is a different JS interop
|
|
// type
|
|
lo is List<String>; // LINT, JS interop type argument and Dart type argument
|
|
// are incompatible
|
|
ls is List<JSString>; // LINT, Dart type argument and JS interop type argument
|
|
// are incompatible
|
|
lo is List<JSArray>; // LINT, comparing JS interop type argument with
|
|
// different JS interop type argument
|
|
lo is List<JSNumber>; // LINT, comparing JS interop type argument with
|
|
// different JS interop type argument
|
|
o is HTMLElement; // LINT, true because both are JSObjects but doesn't check
|
|
// that it's a JS HTMLElement
|
|
e is HTMLDivElement; // LINT, true because both are JSObjects but doesn't
|
|
// check that it's a JS HTMLDivElement
|
|
}
|
|
```
|
|
|
|
Prefer using JS interop helpers like `isA` from `dart:js_interop` to check the
|
|
underlying type of JS interop values.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
extension type HTMLElement(JSObject o) implements JSObject {}
|
|
extension type HTMLDivElement(JSObject o) implements HTMLElement {}
|
|
|
|
void compute(JSAny a, List<JSAny> l, JSObject o, HTMLElement e, Object obj) {
|
|
a.isA<JSString>; // OK, uses JS interop to check it is a JS string
|
|
l[0].isA<JSString>; // OK, uses JS interop to check it is a JS string
|
|
o.isA<HTMLElement>(); // OK, uses JS interop to check `o` is an HTMLElement
|
|
e.isA<HTMLDivElement>(); // OK, uses JS interop to check `e` is an
|
|
// HTMLDivElement
|
|
obj.isA<JSObject>(); // OK, uses JS interop to check `obj` is a JSObject
|
|
}
|
|
```
|
|
|
|
**DON'T** use `as` to cast a JS interop value to an unrelated Dart type or an
|
|
unrelated Dart value to a JS interop type.
|
|
|
|
**DON'T** use `as` to cast a JS interop value to a JS interop type represented
|
|
by an incompatible `dart:js_interop` type.
|
|
|
|
**BAD:**
|
|
```dart
|
|
extension type Window(JSObject o) {}
|
|
|
|
void compute(String s, JSBoolean b, Window w, List<String> l,
|
|
List<JSObject> lo) {
|
|
s as JSString; // LINT, casting Dart type to JS interop type
|
|
b as bool; // LINT, casting JS interop type to Dart type
|
|
b as JSNumber; // LINT, JSBoolean and JSNumber are incompatible
|
|
b as Window; // LINT, JSBoolean and JSObject are incompatible
|
|
w as JSBoolean; // LINT, JSObject and JSBoolean are incompatible
|
|
l as List<JSString>; // LINT, casting Dart value with Dart type argument to
|
|
// Dart type with JS interop type argument
|
|
lo as List<String>; // LINT, casting Dart value with JS interop type argument
|
|
// to Dart type with Dart type argument
|
|
lo as List<JSBoolean>; // LINT, casting Dart value with JS interop type
|
|
// argument to Dart type with incompatible JS interop
|
|
// type argument
|
|
}
|
|
```
|
|
|
|
Prefer using `dart:js_interop` conversion methods to convert a JS interop value
|
|
to a Dart value and vice versa.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
extension type Window(JSObject o) {}
|
|
extension type Document(JSObject o) {}
|
|
|
|
void compute(String s, JSBoolean b, Window w, JSArray<JSString> a,
|
|
List<String> ls, JSObject o, List<JSAny> la) {
|
|
s.toJS; // OK, converts the Dart type to a JS type
|
|
b.toDart; // OK, converts the JS type to a Dart type
|
|
a.toDart; // OK, converts the JS type to a Dart type
|
|
w as Document; // OK, but no runtime check that `w` is a JS Document
|
|
ls.map((e) => e.toJS).toList(); // OK, converts the Dart types to JS types
|
|
o as JSArray<JSString>; // OK, JSObject and JSArray are compatible
|
|
la as List<JSString>; // OK, JSAny and JSString are compatible
|
|
(o as Object) as JSObject; // OK, Object is a supertype of JSAny
|
|
}
|
|
|
|
**DON'T** use a JS interop type in a catch clause. If the code in the try
|
|
block is expected to throw a JS value, don't use a type at all in the
|
|
catch clause.
|
|
|
|
**BAD:**
|
|
```dart
|
|
@JS()
|
|
external void throwSomething();
|
|
|
|
extension type JSError(JSObject _) implements JSObject {}
|
|
|
|
void compute() {
|
|
try {
|
|
throwSomething();
|
|
} on JSString catch (e) { // LINT, checking that the caught value is a JS type
|
|
print('string $e');
|
|
} on JSError catch (e) { // LINT, checking that the caught value is a JS
|
|
// interop type
|
|
print('JSError: $e');
|
|
} on List<JSNumber> catch (e) { // LINT, type contains a JS interop type
|
|
print('List<JSNumber>: $e');
|
|
} on bool catch (e) { // NO LINT, but may be platform-dependent
|
|
print('bool: $e');
|
|
}
|
|
}
|
|
```
|
|
|
|
Use `isA` from `dart:js_interop` to check if a value is a JS interop type
|
|
within the catch block. If the code in the try block is expected to throw
|
|
a JS value, leave the catch clause untyped and check for all JS interop
|
|
types first using `isA` before checking for Dart types.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
@JS()
|
|
external void throwSomething();
|
|
|
|
extension type JSError(JSObject _) implements JSObject {}
|
|
|
|
void compute() {
|
|
try {
|
|
throwSomething();
|
|
} catch (e) {
|
|
if (e.isA<JSString>()) { // OK, uses JS interop to check it is a JS string
|
|
print('string: $e');
|
|
} else if (e.isA<JSError>()) { // OK, uses JS interop to check it is a JSError
|
|
print('Error: $e');
|
|
} else if (e.isA<JSAny>()) { // OK, checks all interop types before Dart types
|
|
print('JSAny: $e');
|
|
} else if (e is bool) { // OK, `e` can't be a JS value
|
|
print('boolean: $e');
|
|
}
|
|
}
|
|
}
|
|
```
|
|
invalidRuntimeCheckWithJsInteropTypesDartIsJs:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
sharedName: invalidRuntimeCheckWithJsInteropTypes
|
|
problemMessage: "Runtime check between '#p0' and '#p1' checks whether a Dart value is a JS interop type, which might not be platform-consistent."
|
|
correctionMessage: "Try using 'isA' from 'dart:js_interop' to check if the Dart value is a JS interop type."
|
|
hasPublishedDocs: false
|
|
invalidRuntimeCheckWithJsInteropTypesJsAsDart:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
sharedName: invalidRuntimeCheckWithJsInteropTypes
|
|
problemMessage: "Cast from '#p0' to '#p1' casts a JS interop value to a Dart type, which might not be platform-consistent."
|
|
correctionMessage: "Try using conversion methods from 'dart:js_interop' to convert between JS interop types and Dart types."
|
|
hasPublishedDocs: false
|
|
invalidRuntimeCheckWithJsInteropTypesJsAsIncompatibleJs:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
sharedName: invalidRuntimeCheckWithJsInteropTypes
|
|
problemMessage: "Cast from '#p0' to '#p1' casts a JS interop value to an incompatible JS interop type, which might not be platform-consistent."
|
|
hasPublishedDocs: false
|
|
invalidRuntimeCheckWithJsInteropTypesJsIsDart:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
sharedName: invalidRuntimeCheckWithJsInteropTypes
|
|
problemMessage: "Runtime check between '#p0' and '#p1' checks whether a JS interop value is a Dart type, which might not be platform-consistent."
|
|
hasPublishedDocs: false
|
|
invalidRuntimeCheckWithJsInteropTypesJsIsInconsistentJs:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
sharedName: invalidRuntimeCheckWithJsInteropTypes
|
|
problemMessage: "Runtime check between '#p0' and '#p1' involves a non-trivial runtime check between two JS interop types that might not be platform-consistent."
|
|
correctionMessage: "Try using a JS interop member like 'isA' from 'dart:js_interop' to check the underlying type of JS interop values."
|
|
hasPublishedDocs: false
|
|
invalidRuntimeCheckWithJsInteropTypesJsIsUnrelatedJs:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
sharedName: invalidRuntimeCheckWithJsInteropTypes
|
|
problemMessage: "Runtime check between '#p0' and '#p1' involves a runtime check between a JS interop value and an unrelated JS interop type that will always be true and won't check the underlying type."
|
|
correctionMessage: "Try using a JS interop member like 'isA' from 'dart:js_interop' to check the underlying type of JS interop values, or make the JS interop type a supertype using 'implements'."
|
|
hasPublishedDocs: false
|
|
invariantBooleans:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
stable: "2.0"
|
|
removed: "3.0"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
NOTE: This rule is removed in Dart 3.0.0; it is no longer functional.
|
|
|
|
**DON'T** test for conditions that can be inferred at compile time or test the
|
|
same condition twice.
|
|
|
|
Conditional statements using a condition which cannot be anything but `false`
|
|
have the effect of making blocks of code non-functional. If the condition
|
|
cannot evaluate to anything but `true`, the conditional statement is completely
|
|
redundant, and makes the code less readable.
|
|
It is quite likely that the code does not match the programmer's intent.
|
|
Either the condition should be removed or it should be updated so that it does
|
|
not always evaluate to `true` or `false` and does not perform redundant tests.
|
|
This rule will hint to the test conflicting with the linted one.
|
|
|
|
**BAD:**
|
|
```dart
|
|
// foo can't be both equal and not equal to bar in the same expression
|
|
if(foo == bar && something && foo != bar) {...}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void compute(int foo) {
|
|
if (foo == 4) {
|
|
doSomething();
|
|
// we know foo is equal to 4 at this point, so the next condition is always false
|
|
if (foo > 4) {...}
|
|
...
|
|
}
|
|
...
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void compute(bool foo) {
|
|
if (foo) {
|
|
return;
|
|
}
|
|
doSomething();
|
|
// foo is always false here
|
|
if (foo){...}
|
|
...
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void nestedOK() {
|
|
if (foo == bar) {
|
|
foo = baz;
|
|
if (foo != bar) {...}
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void nestedOk2() {
|
|
if (foo == bar) {
|
|
return;
|
|
}
|
|
|
|
foo = baz;
|
|
if (foo == bar) {...} // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void nestedOk5() {
|
|
if (foo != null) {
|
|
if (bar != null) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (bar != null) {...} // OK
|
|
}
|
|
```
|
|
iterableContainsUnrelatedType:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
stable: "2.0"
|
|
removed: "3.3"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
NOTE: This rule is removed in Dart 3.3.0; it is no longer functional.
|
|
|
|
**DON'T** invoke `contains` on `Iterable` with an instance of different type
|
|
than the parameter type.
|
|
|
|
Doing this will invoke `==` on its elements and most likely will return `false`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction() {
|
|
var list = <int>[];
|
|
if (list.contains('1')) print('someFunction'); // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction3() {
|
|
List<int> list = <int>[];
|
|
if (list.contains('1')) print('someFunction3'); // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction8() {
|
|
List<DerivedClass2> list = <DerivedClass2>[];
|
|
DerivedClass3 instance;
|
|
if (list.contains(instance)) print('someFunction8'); // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
abstract class SomeIterable<E> implements Iterable<E> {}
|
|
|
|
abstract class MyClass implements SomeIterable<int> {
|
|
bool badMethod(String thing) => this.contains(thing); // LINT
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction10() {
|
|
var list = [];
|
|
if (list.contains(1)) print('someFunction10'); // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction1() {
|
|
var list = <int>[];
|
|
if (list.contains(1)) print('someFunction1'); // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction4() {
|
|
List<int> list = <int>[];
|
|
if (list.contains(1)) print('someFunction4'); // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction5() {
|
|
List<ClassBase> list = <ClassBase>[];
|
|
DerivedClass1 instance;
|
|
if (list.contains(instance)) print('someFunction5'); // OK
|
|
}
|
|
|
|
abstract class ClassBase {}
|
|
|
|
class DerivedClass1 extends ClassBase {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction6() {
|
|
List<Mixin> list = <Mixin>[];
|
|
DerivedClass2 instance;
|
|
if (list.contains(instance)) print('someFunction6'); // OK
|
|
}
|
|
|
|
abstract class ClassBase {}
|
|
|
|
abstract class Mixin {}
|
|
|
|
class DerivedClass2 extends ClassBase with Mixin {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction7() {
|
|
List<Mixin> list = <Mixin>[];
|
|
DerivedClass3 instance;
|
|
if (list.contains(instance)) print('someFunction7'); // OK
|
|
}
|
|
|
|
abstract class ClassBase {}
|
|
|
|
abstract class Mixin {}
|
|
|
|
class DerivedClass3 extends ClassBase implements Mixin {}
|
|
```
|
|
joinReturnWithAssignment:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Assignment could be inlined in 'return' statement."
|
|
correctionMessage: "Try inlining the assigned value in the 'return' statement."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** join return statement with assignment when possible.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
B _lazyInstance;
|
|
static B get instance {
|
|
_lazyInstance ??= B(); // LINT
|
|
return _lazyInstance;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {
|
|
B _lazyInstance;
|
|
static B get instance => _lazyInstance ??= B();
|
|
}
|
|
```
|
|
leadingNewlinesInMultilineStrings:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Missing a newline at the beginning of a multiline string."
|
|
correctionMessage: "Try adding a newline at the beginning of the string."
|
|
state:
|
|
stable: "2.8"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Multiline strings are easier to read when they start with a newline (a newline
|
|
starting a multiline string is ignored).
|
|
|
|
**BAD:**
|
|
```dart
|
|
var s1 = '''{
|
|
"a": 1,
|
|
"b": 2
|
|
}''';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var s1 = '''
|
|
{
|
|
"a": 1,
|
|
"b": 2
|
|
}''';
|
|
|
|
var s2 = '''This one-liner multiline string is ok. It usually allows to escape both ' and " in the string.''';
|
|
```
|
|
libraryAnnotations:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "This annotation should be attached to a library directive."
|
|
correctionMessage: "Try attaching the annotation to a library directive."
|
|
state:
|
|
stable: "2.19"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an annotation that applies to
|
|
a whole library isn't associated with a `library` directive.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `TestOn`
|
|
annotation, which applies to the whole library, is associated with an
|
|
`import` directive rather than a `library` directive:
|
|
|
|
```dart
|
|
[!@TestOn('browser')!]
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Associate the annotation with a `library` directive, adding one if
|
|
necessary:
|
|
|
|
```dart
|
|
@TestOn('browser')
|
|
library;
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {}
|
|
```
|
|
deprecatedDetails: |-
|
|
Attach library annotations to library directives, rather than
|
|
some other library-level element.
|
|
|
|
**BAD:**
|
|
```dart
|
|
@TestOn('browser')
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
@TestOn('browser')
|
|
library;
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {}
|
|
```
|
|
|
|
**NOTE:** An unnamed library, like `library;` above, is only supported in Dart
|
|
2.19 and later. Code which might run in earlier versions of Dart will need to
|
|
provide a name in the `library` directive.
|
|
libraryNames:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The library name '#p0' isn't a lower_case_with_underscores identifier."
|
|
correctionMessage: "Try changing the name to follow the lower_case_with_underscores style."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the name of a library doesn't
|
|
use the lower_case_with_underscores naming convention.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the library name
|
|
`libraryName` isn't a lower_case_with_underscores identifier:
|
|
|
|
```dart
|
|
library [!libraryName!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the library name is not required, then remove the library name:
|
|
|
|
```dart
|
|
library;
|
|
```
|
|
|
|
If the library name is required, then convert it to use the
|
|
lower_case_with_underscores naming convention:
|
|
|
|
```dart
|
|
library library_name;
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** name libraries using `lowercase_with_underscores`.
|
|
|
|
Some file systems are not case-sensitive, so many projects require filenames to
|
|
be all lowercase. Using a separating character allows names to still be readable
|
|
in that form. Using underscores as the separator ensures that the name is still
|
|
a valid Dart identifier, which may be helpful if the language later supports
|
|
symbolic imports.
|
|
|
|
**BAD:**
|
|
```dart
|
|
library peg-parser;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
library peg_parser;
|
|
```
|
|
|
|
The lint `file_names` can be used to enforce the same kind of naming on the
|
|
file.
|
|
libraryPrefixes:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The prefix '#p0' isn't a lower_case_with_underscores identifier."
|
|
correctionMessage: "Try changing the prefix to follow the lower_case_with_underscores style."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an import prefix doesn't use
|
|
the lower_case_with_underscores naming convention.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the prefix
|
|
`ffiSupport` isn't a lower_case_with_underscores identifier:
|
|
|
|
```dart
|
|
%ignore=unused_import
|
|
import 'package:ffi/ffi.dart' as [!ffiSupport!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Convert the prefix to use the lower_case_with_underscores naming
|
|
convention:
|
|
|
|
```dart
|
|
%ignore=unused_import
|
|
import 'package:ffi/ffi.dart' as ffi_support;
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use `lowercase_with_underscores` when specifying a library prefix.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'dart:math' as Math;
|
|
import 'dart:json' as JSON;
|
|
import 'package:js/js.dart' as JS;
|
|
import 'package:javascript_utils/javascript_utils.dart' as jsUtils;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'dart:math' as math;
|
|
import 'dart:json' as json;
|
|
import 'package:js/js.dart' as js;
|
|
import 'package:javascript_utils/javascript_utils.dart' as js_utils;
|
|
```
|
|
libraryPrivateTypesInPublicApi:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Invalid use of a private type in a public API."
|
|
correctionMessage: "Try making the private type public, or making the API that uses the private type also be private."
|
|
state:
|
|
stable: "2.14"
|
|
categories: [publicInterface]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a type that is not part of the
|
|
public API of a library is referenced in the public API of that library.
|
|
|
|
Using a private type in a public API can make the API unusable outside the
|
|
defining library.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the parameter `c` of
|
|
the public function `f` has a type that is library private (`_C`):
|
|
|
|
```dart
|
|
void f([!_C!] c) {}
|
|
|
|
class _C {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the API doesn't need to be used outside the defining library, then make
|
|
it private:
|
|
|
|
```dart
|
|
%ignore=unused_element
|
|
void _f(_C c) {}
|
|
|
|
class _C {}
|
|
```
|
|
|
|
If the API needs to be part of the public API of the library, then either
|
|
use a different type that's public, or make the referenced type public:
|
|
|
|
```dart
|
|
void f(C c) {}
|
|
|
|
class C {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** using library private types in public APIs.
|
|
|
|
For the purposes of this lint, a public API is considered to be any top-level or
|
|
member declaration unless the declaration is library private or contained in a
|
|
declaration that's library private. The following uses of types are checked:
|
|
|
|
- the return type of a function or method,
|
|
- the type of any parameter of a function or method,
|
|
- the bound of a type parameter to any function, method, class, mixin,
|
|
extension's extended type, or type alias,
|
|
- the type of any top level variable or field,
|
|
- any type used in the declaration of a type alias (for example
|
|
`typedef F = _Private Function();`), or
|
|
- any type used in the `on` clause of an extension or a mixin
|
|
|
|
**BAD:**
|
|
```dart
|
|
f(_Private p) { ... }
|
|
class _Private {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
f(String s) { ... }
|
|
```
|
|
linesLongerThan80Chars:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The line length exceeds the 80-character limit."
|
|
correctionMessage: "Try breaking the line across multiple lines."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**AVOID** lines longer than 80 characters
|
|
|
|
Readability studies show that long lines of text are harder to read because your
|
|
eye has to travel farther when moving to the beginning of the next line. This is
|
|
why newspapers and magazines use multiple columns of text.
|
|
|
|
If you really find yourself wanting lines longer than 80 characters, our
|
|
experience is that your code is likely too verbose and could be a little more
|
|
compact. The main offender is usually `VeryLongCamelCaseClassNames`. Ask
|
|
yourself, “Does each word in that type name tell me something critical or
|
|
prevent a name collision?” If not, consider omitting it.
|
|
|
|
Note that `dart format` does 99% of this for you, but the last 1% is you. It
|
|
does not split long string literals to fit in 80 columns, so you have to do
|
|
that manually.
|
|
|
|
We make an exception for URIs and file paths. When those occur in comments or
|
|
strings (usually in imports and exports), they may remain on a single line even
|
|
if they go over the line limit. This makes it easier to search source files for
|
|
a given path.
|
|
listRemoveUnrelatedType:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
stable: "2.0"
|
|
removed: "3.3"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
NOTE: This rule is removed in Dart 3.3.0; it is no longer functional.
|
|
|
|
**DON'T** invoke `remove` on `List` with an instance of different type than
|
|
the parameter type.
|
|
|
|
Doing this will invoke `==` on its elements and most likely will
|
|
return `false`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction() {
|
|
var list = <int>[];
|
|
if (list.remove('1')) print('someFunction'); // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction3() {
|
|
List<int> list = <int>[];
|
|
if (list.remove('1')) print('someFunction3'); // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction8() {
|
|
List<DerivedClass2> list = <DerivedClass2>[];
|
|
DerivedClass3 instance;
|
|
if (list.remove(instance)) print('someFunction8'); // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
abstract class SomeList<E> implements List<E> {}
|
|
|
|
abstract class MyClass implements SomeList<int> {
|
|
bool badMethod(String thing) => this.remove(thing); // LINT
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction10() {
|
|
var list = [];
|
|
if (list.remove(1)) print('someFunction10'); // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction1() {
|
|
var list = <int>[];
|
|
if (list.remove(1)) print('someFunction1'); // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction4() {
|
|
List<int> list = <int>[];
|
|
if (list.remove(1)) print('someFunction4'); // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction5() {
|
|
List<ClassBase> list = <ClassBase>[];
|
|
DerivedClass1 instance;
|
|
if (list.remove(instance)) print('someFunction5'); // OK
|
|
}
|
|
|
|
abstract class ClassBase {}
|
|
|
|
class DerivedClass1 extends ClassBase {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction6() {
|
|
List<Mixin> list = <Mixin>[];
|
|
DerivedClass2 instance;
|
|
if (list.remove(instance)) print('someFunction6'); // OK
|
|
}
|
|
|
|
abstract class ClassBase {}
|
|
|
|
abstract class Mixin {}
|
|
|
|
class DerivedClass2 extends ClassBase with Mixin {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction7() {
|
|
List<Mixin> list = <Mixin>[];
|
|
DerivedClass3 instance;
|
|
if (list.remove(instance)) print('someFunction7'); // OK
|
|
}
|
|
|
|
abstract class ClassBase {}
|
|
|
|
abstract class Mixin {}
|
|
|
|
class DerivedClass3 extends ClassBase implements Mixin {}
|
|
```
|
|
literalOnlyBooleanExpressions:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The Boolean expression has a constant value."
|
|
correctionMessage: "Try changing the expression."
|
|
state:
|
|
experimental: "2.0"
|
|
stable: "2.2"
|
|
categories: [unusedCode]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the value of the condition in
|
|
an `if` or loop statement is known to be either always `true` or always
|
|
`false`. An exception is made for a `while` loop whose condition is the
|
|
Boolean literal `true`.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the condition will
|
|
always evaluate to `true`:
|
|
|
|
```dart
|
|
void f() {
|
|
[!if (true) {
|
|
print('true');
|
|
}!]
|
|
}
|
|
```
|
|
|
|
The lint will evaluate a subset of expressions that are composed of
|
|
constants, so the following code will also produce this diagnostic because
|
|
the condition will always evaluate to `false`:
|
|
|
|
```dart
|
|
void g(int i) {
|
|
[!if (1 == 0 || 3 > 4) {
|
|
print('false');
|
|
}!]
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the condition is wrong, then correct the condition so that it's value
|
|
can't be known at compile time:
|
|
|
|
```dart
|
|
void g(int i) {
|
|
if (i == 0 || i > 4) {
|
|
print('false');
|
|
}
|
|
}
|
|
```
|
|
|
|
If the condition is correct, then simplify the code to not evaluate the
|
|
condition:
|
|
|
|
```dart
|
|
void f() {
|
|
print('true');
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** test for conditions composed only by literals, since the value can be
|
|
inferred at compile time.
|
|
|
|
Conditional statements using a condition which cannot be anything but FALSE have
|
|
the effect of making blocks of code non-functional. If the condition cannot
|
|
evaluate to anything but `true`, the conditional statement is completely
|
|
redundant, and makes the code less readable.
|
|
It is quite likely that the code does not match the programmer's intent.
|
|
Either the condition should be removed or it should be updated so that it does
|
|
not always evaluate to `true` or `false`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void bad() {
|
|
if (true) {} // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void bad() {
|
|
if (true && 1 != 0) {} // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void bad() {
|
|
if (1 != 0 && true) {} // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void bad() {
|
|
if (1 < 0 && true) {} // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void bad() {
|
|
if (true && false) {} // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void bad() {
|
|
if (1 != 0) {} // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void bad() {
|
|
if (true && 1 != 0 || 3 < 4) {} // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void bad() {
|
|
if (1 != 0 || 3 < 4 && true) {} // LINT
|
|
}
|
|
```
|
|
|
|
**NOTE:** that an exception is made for the common `while (true) { }` idiom,
|
|
which is often reasonably preferred to the equivalent `for (;;)`.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void good() {
|
|
while (true) {
|
|
// Do stuff.
|
|
}
|
|
}
|
|
```
|
|
matchingSuperParameters:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
problemMessage: "The super parameter named '#p0'' does not share the same name as the corresponding parameter in the super constructor, '#p1'."
|
|
correctionMessage: "Try using the name of the corresponding parameter in the super constructor."
|
|
state:
|
|
stable: "3.0"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** use super parameter names that match their corresponding super
|
|
constructor's parameter names.
|
|
|
|
**BAD:**
|
|
|
|
```dart
|
|
class Rectangle {
|
|
final int width;
|
|
final int height;
|
|
|
|
Rectangle(this.width, this.height);
|
|
}
|
|
|
|
class ColoredRectangle extends Rectangle {
|
|
final Color color;
|
|
|
|
ColoredRectangle(
|
|
this.color,
|
|
super.height, // Bad, actually corresponds to the `width` parameter.
|
|
super.width, // Bad, actually corresponds to the `height` parameter.
|
|
);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
|
|
```dart
|
|
class Rectangle {
|
|
final int width;
|
|
final int height;
|
|
|
|
Rectangle(this.width, this.height);
|
|
}
|
|
|
|
class ColoredRectangle extends Rectangle {
|
|
final Color color;
|
|
|
|
ColoredRectangle(
|
|
this.color,
|
|
super.width,
|
|
super.height,
|
|
);
|
|
}
|
|
```
|
|
missingCodeBlockLanguageInDocComment:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The code block is missing a specified language."
|
|
correctionMessage: "Try adding a language to the code block."
|
|
state:
|
|
stable: "3.4"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** specify the language used in the code block of a doc comment.
|
|
|
|
To enable proper syntax highlighting of Markdown code blocks,
|
|
[`dart doc`](https://dart.dev/tools/dart-doc) strongly recommends code blocks to
|
|
specify the language used after the initial code fence.
|
|
|
|
See [highlight.js](https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md)
|
|
for the list of languages supported by `dart doc`.
|
|
To disable syntax highlighting or if no language is suitable,
|
|
you can specify `none` as the language.
|
|
|
|
**BAD:**
|
|
```dart
|
|
/// ```
|
|
/// void main() {}
|
|
/// ```
|
|
class A {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
/// ```dart
|
|
/// void main() {}
|
|
/// ```
|
|
class A {}
|
|
```
|
|
missingWhitespaceBetweenAdjacentStrings:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Missing whitespace between adjacent strings."
|
|
correctionMessage: "Try adding whitespace between the strings."
|
|
state:
|
|
stable: "2.8"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic for a pair of adjacent string
|
|
literals unless either the left-hand string ends in whitespace or the
|
|
right-hand string begins with whitespace.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because neither the left nor
|
|
the right string literal includes a space to separate the words that will
|
|
be joined:
|
|
|
|
```dart
|
|
var s =
|
|
[!'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed'!]
|
|
'do eiusmod tempor incididunt ut labore et dolore magna';
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add whitespace at the end of the left-hand literal or at the beginning of
|
|
the right-hand literal:
|
|
|
|
```dart
|
|
var s =
|
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed '
|
|
'do eiusmod tempor incididunt ut labore et dolore magna';
|
|
```
|
|
deprecatedDetails: |-
|
|
Add a trailing whitespace to prevent missing whitespace between adjacent
|
|
strings.
|
|
|
|
With long text split across adjacent strings it's easy to forget a whitespace
|
|
between strings.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var s =
|
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed'
|
|
'do eiusmod tempor incididunt ut labore et dolore magna';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var s =
|
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed '
|
|
'do eiusmod tempor incididunt ut labore et dolore magna';
|
|
```
|
|
noAdjacentStringsInList:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't use adjacent strings in a list literal."
|
|
correctionMessage: "Try adding a comma between the strings."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when two string literals are
|
|
adjacent in a list literal. Adjacent strings in Dart are concatenated
|
|
together to form a single string, but the intent might be for each string
|
|
to be a separate element in the list.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the strings `'a'` and
|
|
`'b'` are adjacent:
|
|
|
|
```dart
|
|
List<String> list = [[!'a' 'b'!], 'c'];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the two strings are intended to be separate elements of the list, then
|
|
add a comma between them:
|
|
|
|
```dart
|
|
List<String> list = ['a', 'b', 'c'];
|
|
```
|
|
|
|
If the two strings are intended to be a single concatenated string, then
|
|
either manually merge the strings:
|
|
|
|
```dart
|
|
List<String> list = ['ab', 'c'];
|
|
```
|
|
|
|
Or use the `+` operator to concatenate the strings:
|
|
|
|
```dart
|
|
List<String> list = ['a' + 'b', 'c'];
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use adjacent strings in a list.
|
|
|
|
This can indicate a forgotten comma.
|
|
|
|
**BAD:**
|
|
```dart
|
|
List<String> list = <String>[
|
|
'a'
|
|
'b',
|
|
'c',
|
|
];
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
List<String> list = <String>[
|
|
'a' +
|
|
'b',
|
|
'c',
|
|
];
|
|
```
|
|
noDefaultCases:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Invalid use of 'default' member in a switch."
|
|
correctionMessage: "Try enumerating all the possible values of the switch expression."
|
|
state:
|
|
experimental: "2.9"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Switches on enums and enum-like classes should not use a `default` clause.
|
|
|
|
Enum-like classes are defined as concrete (non-abstract) classes that have:
|
|
* only private non-factory constructors
|
|
* two or more static const fields whose type is the enclosing class and
|
|
* no subclasses of the class in the defining library
|
|
|
|
**DO** define default behavior outside switch statements.
|
|
|
|
**BAD:**
|
|
```dart
|
|
switch (testEnum) {
|
|
case TestEnum.A:
|
|
return '123';
|
|
case TestEnum.B:
|
|
return 'abc';
|
|
default:
|
|
return null;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
switch (testEnum) {
|
|
case TestEnum.A:
|
|
return '123';
|
|
case TestEnum.B:
|
|
return 'abc';
|
|
}
|
|
// Default here.
|
|
return null;
|
|
```
|
|
noDuplicateCaseValues:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
problemMessage: "The value of the case clause ('#p0') is equal to the value of an earlier case clause ('#p1')."
|
|
correctionMessage: "Try removing or changing the value."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when two or more `case` clauses in
|
|
the same `switch` statement have the same value.
|
|
|
|
Any `case` clauses after the first can't be executed, so having duplicate
|
|
`case` clauses is misleading.
|
|
|
|
This diagnostic is often the result of either a typo or a change to the
|
|
value of a constant.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because two case clauses have
|
|
the same value (1):
|
|
|
|
```dart
|
|
// @dart = 2.14
|
|
void f(int v) {
|
|
switch (v) {
|
|
case 1:
|
|
break;
|
|
case [!1!]:
|
|
break;
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If one of the clauses should have a different value, then change the value
|
|
of the clause:
|
|
|
|
```dart
|
|
void f(int v) {
|
|
switch (v) {
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
break;
|
|
}
|
|
}
|
|
```
|
|
|
|
If the value is correct, then merge the statements into a single clause:
|
|
|
|
```dart
|
|
void f(int v) {
|
|
switch (v) {
|
|
case 1:
|
|
break;
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use more than one case with same value.
|
|
|
|
This is usually a typo or changed value of constant.
|
|
|
|
**BAD:**
|
|
```dart
|
|
const int A = 1;
|
|
switch (v) {
|
|
case 1:
|
|
case 2:
|
|
case A:
|
|
case 2:
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
const int A = 1;
|
|
switch (v) {
|
|
case A:
|
|
case 2:
|
|
}
|
|
```
|
|
|
|
NOTE: this lint only reports duplicate cases in libraries opted in to Dart 2.19
|
|
and below. In Dart 3.0 and after, duplicate cases are reported as dead code
|
|
by the analyzer.
|
|
noDynamicCasts:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Implicit cast from 'dynamic'."
|
|
correctionMessage: "Try adding an explicit cast or changing the target type."
|
|
state:
|
|
stable: "3.13"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** avoid implicit casts from `dynamic`.
|
|
|
|
Assigning a `dynamic`-typed expression to a non-`dynamic`, non-`Object?`
|
|
target is a form of implicit casting. It is better to make such a cast
|
|
explicit, so that it is visible to developers.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void f(dynamic x) {
|
|
int y = x;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f(dynamic x) {
|
|
int y = x as int;
|
|
}
|
|
```
|
|
noLeadingUnderscoresForLibraryPrefixes:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The library prefix '#p0' starts with an underscore."
|
|
correctionMessage: "Try renaming the prefix to not start with an underscore."
|
|
state:
|
|
stable: "2.16"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the name of a prefix declared
|
|
on an import starts with an underscore.
|
|
|
|
Library prefixes are inherently not visible outside the declaring library,
|
|
so a leading underscore indicating private adds no value.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the prefix `_core`
|
|
starts with an underscore:
|
|
|
|
```dart
|
|
import 'dart:core' as [!_core!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the underscore:
|
|
|
|
```dart
|
|
import 'dart:core' as core;
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use a leading underscore for library prefixes.
|
|
There is no concept of "private" for library prefixes. When one of those has a
|
|
name that starts with an underscore, it sends a confusing signal to the reader.
|
|
To avoid that, don't use leading underscores in those names.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'dart:core' as _core;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'dart:core' as core;
|
|
```
|
|
noLeadingUnderscoresForLocalIdentifiers:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The local variable '#p0' starts with an underscore."
|
|
correctionMessage: "Try renaming the variable to not start with an underscore."
|
|
state:
|
|
stable: "2.16"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the name of a local variable
|
|
starts with an underscore.
|
|
|
|
Local variables are inherently not visible outside the declaring library,
|
|
so a leading underscore indicating private adds no value.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the parameter `_s`
|
|
starts with an underscore:
|
|
|
|
```dart
|
|
int f(String [!_s!]) => _s.length;
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the underscore:
|
|
|
|
```dart
|
|
int f(String s) => s.length;
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use a leading underscore for identifiers that aren't private. Dart
|
|
uses a leading underscore in an identifier to mark members and top-level
|
|
declarations as private. This trains users to associate a leading underscore
|
|
with one of those kinds of declarations. They see `_` and think "private".
|
|
There is no concept of "private" for local variables or parameters. When one of
|
|
those has a name that starts with an underscore, it sends a confusing signal to
|
|
the reader. To avoid that, don't use leading underscores in those names.
|
|
|
|
**EXCEPTION:**: An unused parameter can be named `_`, `__`, `___`, etc. This is
|
|
common practice in callbacks where you are passed a value but you don't need
|
|
to use it. Giving it a name that consists solely of underscores is the idiomatic
|
|
way to indicate that the value isn't used.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void print(String _name) {
|
|
var _size = _name.length;
|
|
...
|
|
}
|
|
```
|
|
**GOOD:**
|
|
|
|
```dart
|
|
void print(String name) {
|
|
var size = name.length;
|
|
...
|
|
}
|
|
```
|
|
|
|
**OK:**
|
|
|
|
```dart
|
|
[1,2,3].map((_) => print('Hello'));
|
|
```
|
|
noLiteralBoolComparisons:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary comparison to a boolean literal."
|
|
correctionMessage: "Remove the comparison and use the negate `!` operator if necessary."
|
|
state:
|
|
stable: "3.0"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/usage#dont-use-true-or-false-in-equality-operations):
|
|
|
|
**DON'T** use `true` or `false` in equality operations.
|
|
|
|
This lint applies only if the expression is of a non-nullable `bool` type.
|
|
|
|
**BAD:**
|
|
```dart
|
|
if (someBool == true) {
|
|
print('true!');
|
|
}
|
|
while (someBool == false) {
|
|
print('still false!');
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (someBool) {
|
|
print('true!');
|
|
}
|
|
while (!someBool) {
|
|
print('still false!');
|
|
}
|
|
```
|
|
noLogicInCreateState:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't put any logic in 'createState'."
|
|
correctionMessage: "Try moving the logic out of 'createState'."
|
|
state:
|
|
stable: "2.8"
|
|
categories: [errorProne, flutter]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an implementation of
|
|
`createState` in a subclass of `StatefulWidget` contains any logic other
|
|
than the return of the result of invoking a zero argument constructor.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the constructor
|
|
invocation has arguments:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
class MyWidget extends StatefulWidget {
|
|
@override
|
|
MyState createState() => [!MyState(0)!];
|
|
}
|
|
|
|
class MyState extends State {
|
|
int x;
|
|
|
|
MyState(this.x);
|
|
|
|
Widget build(BuildContext context) => MyWidget();
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rewrite the code so that `createState` doesn't contain any logic:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
class MyWidget extends StatefulWidget {
|
|
@override
|
|
MyState createState() => MyState();
|
|
}
|
|
|
|
class MyState extends State {
|
|
int x = 0;
|
|
|
|
MyState();
|
|
|
|
Widget build(BuildContext context) => MyWidget();
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** put any logic in `createState()`.
|
|
|
|
Implementations of `createState()` should return a new instance
|
|
of a State object and do nothing more. Since state access is preferred
|
|
via the `widget` field, passing data to `State` objects using custom
|
|
constructor parameters should also be avoided and so further, the State
|
|
constructor is required to be passed no arguments.
|
|
|
|
**BAD:**
|
|
```dart
|
|
MyState global;
|
|
|
|
class MyStateful extends StatefulWidget {
|
|
@override
|
|
MyState createState() {
|
|
global = MyState();
|
|
return global;
|
|
}
|
|
}
|
|
```
|
|
|
|
```dart
|
|
class MyStateful extends StatefulWidget {
|
|
@override
|
|
MyState createState() => MyState()..field = 42;
|
|
}
|
|
```
|
|
|
|
```dart
|
|
class MyStateful extends StatefulWidget {
|
|
@override
|
|
MyState createState() => MyState(42);
|
|
}
|
|
```
|
|
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class MyStateful extends StatefulWidget {
|
|
@override
|
|
MyState createState() {
|
|
return MyState();
|
|
}
|
|
}
|
|
```
|
|
noRawTypes:
|
|
type: lint
|
|
parameters:
|
|
Type type: the name of the generic type
|
|
problemMessage: "The generic type '#type' should have explicit type arguments but doesn't."
|
|
correctionMessage: "Use explicit type arguments for '#type'."
|
|
state:
|
|
stable: "3.13"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a generic type is used without
|
|
explicit type arguments, meaning it is a raw type.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the generic class `C`
|
|
is used without a type argument:
|
|
|
|
```dart
|
|
class C<T> {}
|
|
[!C!] c = C<int>();
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Provide explicit type arguments:
|
|
|
|
```dart
|
|
class C<T> {}
|
|
C<int> c = C<int>();
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use raw types.
|
|
|
|
A raw type is a type annotation for a generic type which omits type
|
|
arguments. Developers may mistakenly believe that the type arguments are
|
|
inferred, but in fact each type parameter's bound is used, which can lead
|
|
to lost type information.
|
|
|
|
**BAD:**
|
|
```dart
|
|
List list = [1, 2, 3];
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
List<int> list = [1, 2, 3];
|
|
```
|
|
noRuntimetypeTostring:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Using 'toString' on a 'Type' is not safe in production code."
|
|
correctionMessage: "Try removing the usage of 'toString' or restructuring the code."
|
|
state:
|
|
stable: "2.8"
|
|
categories: [nonPerformant]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Calling `toString` on a runtime type is a non-trivial operation that can
|
|
negatively impact performance. It's better to avoid it.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
String toString() => '$runtimeType()';
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {
|
|
String toString() => 'A()';
|
|
}
|
|
```
|
|
|
|
This lint has some exceptions where performance is not a problem or where real
|
|
type information is more important than performance:
|
|
|
|
* in an assertion
|
|
* in a throw expression
|
|
* in a catch clause
|
|
* in a mixin declaration
|
|
* in an abstract class declaration
|
|
noSelfAssignments:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The variable or property is being assigned to itself."
|
|
correctionMessage: "Try removing the assignment that has no direct effect."
|
|
state:
|
|
stable: "3.1"
|
|
categories: [unintentional]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DON'T** assign a variable to itself. Usually this is a mistake.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class C {
|
|
int x;
|
|
|
|
C(int x) {
|
|
x = x;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class C {
|
|
int x;
|
|
|
|
C(int x) : x = x;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class C {
|
|
int x;
|
|
|
|
C(int x) {
|
|
this.x = x;
|
|
}
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
class C {
|
|
int _x = 5;
|
|
|
|
int get x => _x;
|
|
|
|
set x(int x) {
|
|
_x = x;
|
|
_customUpdateLogic();
|
|
}
|
|
|
|
void _customUpdateLogic() {
|
|
print('updated');
|
|
}
|
|
|
|
void example() {
|
|
x = x;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class C {
|
|
int _x = 5;
|
|
|
|
int get x => _x;
|
|
|
|
set x(int x) {
|
|
_x = x;
|
|
_customUpdateLogic();
|
|
}
|
|
|
|
void _customUpdateLogic() {
|
|
print('updated');
|
|
}
|
|
|
|
void example() {
|
|
_customUpdateLogic();
|
|
}
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
class C {
|
|
int x = 5;
|
|
|
|
void update(C other) {
|
|
this.x = this.x;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class C {
|
|
int x = 5;
|
|
|
|
void update(C other) {
|
|
this.x = other.x;
|
|
}
|
|
}
|
|
```
|
|
noSoloTests:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't commit soloed tests."
|
|
correctionMessage: "Try removing the 'soloTest' annotation or 'solo_' prefix."
|
|
hasPublishedDocs: true
|
|
state:
|
|
internal: "3.10"
|
|
categories: [errorProne]
|
|
deprecatedDetails: "For internal use only."
|
|
noTrailingSpaces:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't create string literals with trailing spaces in tests."
|
|
correctionMessage: 'Try removing the trailing spaces.'
|
|
hasPublishedDocs: true
|
|
state:
|
|
internal: "3.10"
|
|
categories: [errorProne]
|
|
deprecatedDetails: "For internal use only."
|
|
noWildcardVariableUses:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The referenced identifier is a wildcard."
|
|
correctionMessage: "Use an identifier name that is not a wildcard."
|
|
state:
|
|
stable: "3.1"
|
|
categories: [languageFeatureUsage, unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when either a parameter or local
|
|
variable whose name consists of only underscores is referenced. Such
|
|
names will become non-binding in a future version of the Dart language,
|
|
making the reference illegal.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the name of the
|
|
parameter consists of two underscores:
|
|
|
|
```dart
|
|
// @dart = 3.6
|
|
void f(int __) {
|
|
print([!__!]);
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the name of the
|
|
local variable consists of a single underscore:
|
|
|
|
```dart
|
|
// @dart = 3.6
|
|
void f() {
|
|
int _ = 0;
|
|
print([!_!]);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the variable or parameter is intended to be referenced, then give it a
|
|
name that has at least one non-underscore character:
|
|
|
|
```dart
|
|
void f(int p) {
|
|
print(p);
|
|
}
|
|
```
|
|
|
|
If the variable or parameter is not intended to be referenced, then
|
|
replace the reference with a different expression:
|
|
|
|
```dart
|
|
void f() {
|
|
print(0);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use wildcard parameters or variables.
|
|
|
|
Wildcard parameters and local variables
|
|
(e.g. underscore-only names like `_`, `__`, `___`, etc.) will
|
|
become non-binding in a future version of the Dart language.
|
|
Any existing code that uses wildcard parameters or variables will
|
|
break. In anticipation of this change, and to make adoption easier,
|
|
this lint disallows wildcard and variable parameter uses.
|
|
|
|
|
|
**BAD:**
|
|
```dart
|
|
var _ = 1;
|
|
print(_); // LINT
|
|
```
|
|
|
|
```dart
|
|
void f(int __) {
|
|
print(__); // LINT multiple underscores too
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
for (var _ in [1, 2, 3]) count++;
|
|
```
|
|
|
|
```dart
|
|
var [a, _, b, _] = [1, 2, 3, 4];
|
|
```
|
|
nonConstantIdentifierNames:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The variable name '#p0' isn't a lowerCamelCase identifier."
|
|
correctionMessage: "Try changing the name to follow the lowerCamelCase style."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the name of a class member,
|
|
top-level declaration, variable, parameter, named parameter, or named
|
|
constructor that isn't declared to be `const`, doesn't use the
|
|
lowerCamelCase convention.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the top-level variable
|
|
`Count` doesn't start with a lowercase letter:
|
|
|
|
```dart
|
|
var [!Count!] = 0;
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Change the name in the declaration to follow the lowerCamelCase
|
|
convention:
|
|
|
|
```dart
|
|
var count = 0;
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** name non-constant identifiers using lowerCamelCase.
|
|
|
|
Class members, top-level definitions, variables, parameters, named parameters
|
|
and named constructors should capitalize the first letter of each word
|
|
except the first word, and use no separators.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var item;
|
|
|
|
HttpRequest httpRequest;
|
|
|
|
align(clearItems) {
|
|
// ...
|
|
}
|
|
```
|
|
noopPrimitiveOperations:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The expression has no effect and can be removed."
|
|
correctionMessage: "Try removing the expression."
|
|
state:
|
|
stable: "2.14"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Some operations on primitive types are idempotent and can be removed.
|
|
|
|
**BAD:**
|
|
|
|
```dart
|
|
doubleValue.toDouble();
|
|
|
|
intValue.toInt();
|
|
intValue.round();
|
|
intValue.ceil();
|
|
intValue.floor();
|
|
intValue.truncate();
|
|
|
|
string.toString();
|
|
string = 'hello\n'
|
|
''
|
|
'world';
|
|
|
|
'string with ${x.toString()}';
|
|
```
|
|
|
|
Note that the empty string literals at the beginning or end of a string are
|
|
allowed, as they are typically used to format the string literal across multiple
|
|
lines:
|
|
|
|
```dart
|
|
// OK
|
|
string = ''
|
|
'hello\n'
|
|
'world\n';
|
|
|
|
// OK
|
|
string = 'hello\n'
|
|
'world\n'
|
|
'';
|
|
```
|
|
nullCheckOnNullableTypeParameter:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The null check operator shouldn't be used on a variable whose type is a potentially nullable type parameter."
|
|
correctionMessage: "Try explicitly testing for 'null'."
|
|
state:
|
|
experimental: "2.12"
|
|
stable: "2.17"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a null check operator is used
|
|
on a variable whose type is `T?`, where `T` is a type parameter that
|
|
allows the type argument to be nullable (either has no bound or has a
|
|
bound that is nullable).
|
|
|
|
Given a generic type parameter `T` which has a nullable bound, it is very
|
|
easy to introduce erroneous null checks when working with a variable of
|
|
type `T?`. Specifically, it is not uncommon to have `T? x;` and want to
|
|
assert that `x` has been set to a valid value of type `T`. A common
|
|
mistake is to do so using `x!`. This is almost always incorrect, because
|
|
if `T` is a nullable type, `x` may validly hold `null` as a value of type
|
|
`T`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because `t` has the type `T?`
|
|
and `T` allows the type argument to be nullable (because it has no
|
|
`extends` clause):
|
|
|
|
```dart
|
|
T f<T>(T? t) => t[!!!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use the type parameter to cast the variable:
|
|
|
|
```dart
|
|
T f<T>(T? t) => t as T;
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use `null` check on a potentially nullable type parameter.
|
|
|
|
Given a generic type parameter `T` which has a nullable bound (e.g., the default
|
|
bound of `Object?`), it is very easy to introduce erroneous `null` checks when
|
|
working with a variable of type `T?`. Specifically, it is not uncommon to have
|
|
`T? x;` and want to assert that `x` has been set to a valid value of type `T`.
|
|
A common mistake is to do so using `x!`. This is almost always incorrect, since
|
|
if `T` is a nullable type, `x` may validly hold `null` as a value of type `T`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
T run<T>(T callback()) {
|
|
T? result;
|
|
(() { result = callback(); })();
|
|
return result!;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
T run<T>(T callback()) {
|
|
T? result;
|
|
(() { result = callback(); })();
|
|
return result as T;
|
|
}
|
|
```
|
|
nullClosures:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Closure can't be 'null' because it might be invoked."
|
|
correctionMessage: "Try providing a non-null closure."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DON'T** pass `null` as an argument where a closure is expected.
|
|
|
|
Often a closure that is passed to a method will only be called conditionally,
|
|
so that tests and "happy path" production calls do not reveal that `null` will
|
|
result in an exception being thrown.
|
|
|
|
This rule only catches null literals being passed where closures are expected
|
|
in the following locations:
|
|
|
|
#### Constructors
|
|
|
|
* From `dart:async`
|
|
* `Future` at the 0th positional parameter
|
|
* `Future.microtask` at the 0th positional parameter
|
|
* `Future.sync` at the 0th positional parameter
|
|
* `Timer` at the 0th positional parameter
|
|
* `Timer.periodic` at the 1st positional parameter
|
|
* From `dart:core`
|
|
* `List.generate` at the 1st positional parameter
|
|
|
|
#### Static functions
|
|
|
|
* From `dart:async`
|
|
* `scheduleMicrotask` at the 0th positional parameter
|
|
* `Future.doWhile` at the 0th positional parameter
|
|
* `Future.forEach` at the 0th positional parameter
|
|
* `Future.wait` at the named parameter `cleanup`
|
|
* `Timer.run` at the 0th positional parameter
|
|
|
|
#### Instance methods
|
|
|
|
* From `dart:async`
|
|
* `Future.then` at the 0th positional parameter
|
|
* `Future.complete` at the 0th positional parameter
|
|
* From `dart:collection`
|
|
* `Queue.removeWhere` at the 0th positional parameter
|
|
* `Queue.retain
|
|
* `Iterable.firstWhere` at the 0th positional parameter, and the named
|
|
parameter `orElse`
|
|
* `Iterable.forEach` at the 0th positional parameter
|
|
* `Iterable.fold` at the 1st positional parameter
|
|
* `Iterable.lastWhere` at the 0th positional parameter, and the named
|
|
parameter `orElse`
|
|
* `Iterable.map` at the 0th positional parameter
|
|
* `Iterable.reduce` at the 0th positional parameter
|
|
* `Iterable.singleWhere` at the 0th positional parameter, and the named
|
|
parameter `orElse`
|
|
* `Iterable.skipWhile` at the 0th positional parameter
|
|
* `Iterable.takeWhile` at the 0th positional parameter
|
|
* `Iterable.where` at the 0th positional parameter
|
|
* `List.removeWhere` at the 0th positional parameter
|
|
* `List.retainWhere` at the 0th positional parameter
|
|
* `String.replaceAllMapped` at the 1st positional parameter
|
|
* `String.replaceFirstMapped` at the 1st positional parameter
|
|
* `String.splitMapJoin` at the named parameters `onMatch` and `onNonMatch`
|
|
|
|
**BAD:**
|
|
```dart
|
|
[1, 3, 5].firstWhere((e) => e.isOdd, orElse: null);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
[1, 3, 5].firstWhere((e) => e.isOdd, orElse: () => null);
|
|
```
|
|
omitLocalVariableTypes:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary type annotation on a local variable."
|
|
correctionMessage: "Try removing the type annotation."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DON'T** redundantly type annotate initialized local variables.
|
|
|
|
Local variables, especially in modern code where functions tend to be small,
|
|
have very little scope. Omitting the type focuses the reader's attention on the
|
|
more important *name* of the variable and its initialized value.
|
|
|
|
**BAD:**
|
|
```dart
|
|
List<List<Ingredient>> possibleDesserts(Set<Ingredient> pantry) {
|
|
List<List<Ingredient>> desserts = <List<Ingredient>>[];
|
|
for (final List<Ingredient> recipe in cookbook) {
|
|
if (pantry.containsAll(recipe)) {
|
|
desserts.add(recipe);
|
|
}
|
|
}
|
|
|
|
return desserts;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
List<List<Ingredient>> possibleDesserts(Set<Ingredient> pantry) {
|
|
var desserts = <List<Ingredient>>[];
|
|
for (final recipe in cookbook) {
|
|
if (pantry.containsAll(recipe)) {
|
|
desserts.add(recipe);
|
|
}
|
|
}
|
|
|
|
return desserts;
|
|
}
|
|
```
|
|
|
|
Sometimes the inferred type is not the type you want the variable to have. For
|
|
example, you may intend to assign values of other types later. In that case,
|
|
annotate the variable with the type you want.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Widget build(BuildContext context) {
|
|
Widget result = Text('You won!');
|
|
if (applyPadding) {
|
|
result = Padding(padding: EdgeInsets.all(8.0), child: result);
|
|
}
|
|
return result;
|
|
}
|
|
```
|
|
omitObviousLocalVariableTypes:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Omit the type annotation on a local variable when the type is obvious."
|
|
correctionMessage: "Try removing the type annotation."
|
|
state:
|
|
experimental: "3.6"
|
|
stable: "3.11"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Don't type annotate initialized local variables when
|
|
the type is [obvious](https://dart.dev/glossary/obviously-typed).
|
|
|
|
Local variables, especially in modern code where functions
|
|
tend to be small, have very little scope.
|
|
Omitting the type focuses the reader's attention on the
|
|
more important *name* of the variable and its initialized value.
|
|
Hence, local variable type annotations that are
|
|
[obvious](https://dart.dev/glossary/obviously-typed) should be omitted.
|
|
|
|
**BAD:**
|
|
```dart
|
|
List<List<Ingredient>> possibleDesserts(Set<Ingredient> pantry) {
|
|
List<List<Ingredient>> desserts = <List<Ingredient>>[];
|
|
for (final List<Ingredient> recipe in cookbook) {
|
|
if (pantry.containsAll(recipe)) {
|
|
desserts.add(recipe);
|
|
}
|
|
}
|
|
|
|
return desserts;
|
|
}
|
|
|
|
const cookbook = <List<Ingredient>>[....];
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
List<List<Ingredient>> possibleDesserts(Set<Ingredient> pantry) {
|
|
var desserts = <List<Ingredient>>[];
|
|
for (final List<Ingredient> recipe in cookbook) {
|
|
if (pantry.containsAll(recipe)) {
|
|
desserts.add(recipe);
|
|
}
|
|
}
|
|
|
|
return desserts;
|
|
}
|
|
|
|
const cookbook = <List<Ingredient>>[....];
|
|
```
|
|
|
|
Sometimes the inferred type is not the type you want the variable to have. For
|
|
example, you may intend to assign values of other types later. You may also
|
|
wish to write a type annotation explicitly because the type of the initializing
|
|
expression is non-obvious and it will be helpful for future readers of the
|
|
code to document this type. Or you may wish to commit to a specific type such
|
|
that future updates of dependencies (in nearby code, in imports, anywhere)
|
|
will not silently change the type of that variable, thus introducing
|
|
compile-time errors or run-time bugs in locations where this variable is used.
|
|
In those cases, go ahead and annotate the variable with the type you want.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Widget build(BuildContext context) {
|
|
Widget result = someGenericFunction(42) ?? Text('You won!');
|
|
if (applyPadding) {
|
|
result = Padding(padding: EdgeInsets.all(8.0), child: result);
|
|
}
|
|
return result;
|
|
}
|
|
```
|
|
omitObviousPropertyTypes:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The type annotation isn't needed because it is obvious."
|
|
correctionMessage: "Try removing the type annotation."
|
|
state:
|
|
experimental: "3.7"
|
|
stable: "3.11"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Don't type annotate initialized top-level or static variables when
|
|
the type is [obvious](https://dart.dev/glossary/obviously-typed).
|
|
|
|
**BAD:**
|
|
```dart
|
|
final int myTopLevelVariable = 7;
|
|
|
|
class A {
|
|
static String myStaticVariable = 'Hello';
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
final myTopLevelVariable = 7;
|
|
|
|
class A {
|
|
static myStaticVariable = 'Hello';
|
|
}
|
|
```
|
|
|
|
Sometimes the inferred type is not the type you want the variable to have. For
|
|
example, you may intend to assign values of other types later. You may also
|
|
wish to write a type annotation explicitly because the type of the initializing
|
|
expression is non-obvious and it will be helpful for future readers of the
|
|
code to document this type. Or you may wish to commit to a specific type such
|
|
that future updates of dependencies (in nearby code, in imports, anywhere)
|
|
will not silently change the type of that variable, thus introducing
|
|
compile-time errors or run-time bugs in locations where this variable is used.
|
|
In those cases, go ahead and annotate the variable with the type you want.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
final num myTopLevelVariable = 7;
|
|
|
|
class A {
|
|
static String? myStaticVariable = 'Hello';
|
|
}
|
|
```
|
|
oneMemberAbstracts:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Unnecessary use of an abstract class."
|
|
correctionMessage: "Try making '#p0' a top-level function and removing the class."
|
|
state:
|
|
stable: "2.0"
|
|
deprecated: "3.13"
|
|
categories: [effectiveDart, languageFeatureUsage, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**NOTE:** As one member abstracts are often a stepping stone,
|
|
this lint rule has been deprecated as of Dart 3.13 and is
|
|
set to be removed in a future release of the Dart SDK.
|
|
Remove all inclusions of this lint from your analysis options.
|
|
If you wish to keep enforcing it, consider implementing the
|
|
rule with an [analyzer plugin](https://dart.dev/tools/analyzer-plugins).
|
|
|
|
From [Effective Dart](https://dart.dev/effective-dart/design#avoid-defining-a-one-member-abstract-class-when-a-simple-function-will-do):
|
|
|
|
**AVOID** defining a one-member abstract class when a simple function will do.
|
|
|
|
Unlike Java, Dart has first-class functions, closures, and a nice light syntax
|
|
for using them. If all you need is something like a callback, just use a
|
|
function. If you're defining a class and it only has a single abstract member
|
|
with a meaningless name like `call` or `invoke`, there is a good chance
|
|
you just want a function.
|
|
|
|
**BAD:**
|
|
```dart
|
|
abstract class Predicate {
|
|
bool test(item);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
typedef Predicate = bool Function(item);
|
|
```
|
|
onlyThrowErrors:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't throw instances of classes that don't extend either 'Exception' or 'Error'."
|
|
correctionMessage: "Try throwing a different class of object."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the value being thrown isn't a
|
|
subclass of either `Exception` or `Error`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the string `'f'` is
|
|
being thrown:
|
|
|
|
```dart
|
|
void f() => throw [!'f'!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the value with an instance of a subclass of either `Exception` or
|
|
`Error`:
|
|
|
|
```dart
|
|
void f() => throw ArgumentError('f');
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** throw only instances of classes that extend `dart.core.Error` or
|
|
`dart.core.Exception`.
|
|
|
|
Throwing instances that do not extend `Error` or `Exception` is a bad practice;
|
|
doing this is usually a hack for something that should be implemented more
|
|
thoroughly.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void throwString() {
|
|
throw 'hello world!'; // LINT
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void throwArgumentError() {
|
|
Error error = ArgumentError('oh!');
|
|
throw error; // OK
|
|
}
|
|
```
|
|
overriddenFields:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Field overrides a field inherited from '#p0'."
|
|
correctionMessage: "Try removing the field, overriding the getter and setter if necessary."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a class defines a field that
|
|
overrides a field from a superclass.
|
|
|
|
Overriding a field with another field causes the object to have two
|
|
distinct fields, but because the fields have the same name only one of the
|
|
fields can be referenced in a given scope. That can lead to confusion
|
|
where a reference to one of the fields can be mistaken for a reference to
|
|
the other.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the field `f` in `B`
|
|
shadows the field `f` in `A`:
|
|
|
|
```dart
|
|
class A {
|
|
int f = 1;
|
|
}
|
|
|
|
class B extends A {
|
|
@override
|
|
int [!f!] = 2;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the two fields are representing the same property, then remove the
|
|
field from the subclass:
|
|
|
|
```dart
|
|
class A {
|
|
int f = 1;
|
|
}
|
|
|
|
class B extends A {}
|
|
```
|
|
|
|
If the two fields should be distinct, then rename one of the fields:
|
|
|
|
```dart
|
|
class A {
|
|
int f = 1;
|
|
}
|
|
|
|
class B extends A {
|
|
int g = 2;
|
|
}
|
|
```
|
|
|
|
If the two fields are related in some way, but can't be the same, then
|
|
find a different way to implement the semantics you need.
|
|
deprecatedDetails: |-
|
|
**DON'T** override fields.
|
|
|
|
Overriding fields is almost always done unintentionally. Regardless, it is a
|
|
bad practice to do so.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Base {
|
|
Object field = 'lorem';
|
|
|
|
Object something = 'change';
|
|
}
|
|
|
|
class Bad1 extends Base {
|
|
@override
|
|
final field = 'ipsum'; // LINT
|
|
}
|
|
|
|
class Bad2 extends Base {
|
|
@override
|
|
Object something = 'done'; // LINT
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Base {
|
|
Object field = 'lorem';
|
|
|
|
Object something = 'change';
|
|
}
|
|
|
|
class Ok extends Base {
|
|
Object newField; // OK
|
|
|
|
final Object newFinal = 'ignore'; // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
abstract class BaseLoggingHandler {
|
|
Base transformer;
|
|
}
|
|
|
|
class LogPrintHandler implements BaseLoggingHandler {
|
|
@override
|
|
Derived transformer; // OK
|
|
}
|
|
```
|
|
packageApiDocs:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Missing documentation for public API."
|
|
correctionMessage: "Try adding a documentation comment."
|
|
state:
|
|
stable: "2.0"
|
|
removed: "3.7"
|
|
categories: [effectiveDart, publicInterface]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**NOTE:** This lint has been removed because it is has not
|
|
been fully functional since at least Dart 2.0.
|
|
Remove all inclusions of this lint from your analysis options.
|
|
|
|
**DO** provide doc comments for all public APIs.
|
|
|
|
As described in the [pub package layout doc](https://dart.dev/tools/pub/package-layout#implementation-files),
|
|
public APIs consist in everything in your package's `lib` folder, minus
|
|
implementation files in `lib/src`, adding elements explicitly exported with an
|
|
`export` directive.
|
|
|
|
For example, given `lib/foo.dart`:
|
|
```dart
|
|
export 'src/bar.dart' show Bar;
|
|
export 'src/baz.dart';
|
|
|
|
class Foo { }
|
|
|
|
class _Foo { }
|
|
```
|
|
its API includes:
|
|
|
|
* `Foo` (but not `_Foo`)
|
|
* `Bar` (exported) and
|
|
* all *public* elements in `src/baz.dart`
|
|
|
|
All public API members should be documented with `///` doc-style comments.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Bar {
|
|
void bar();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
/// A Foo.
|
|
abstract class Foo {
|
|
/// Start foo-ing.
|
|
void start() => _start();
|
|
|
|
_start();
|
|
}
|
|
```
|
|
|
|
Advice for writing good doc comments can be found in the
|
|
[Doc Writing Guidelines](https://dart.dev/effective-dart/documentation).
|
|
packageNames:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The package name '#p0' isn't a lower_case_with_underscores identifier."
|
|
correctionMessage: "Try changing the name to follow the lower_case_with_underscores style."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the name of a package doesn't
|
|
use the lower_case_with_underscores naming convention.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the name of the
|
|
package uses the lowerCamelCase naming convention:
|
|
|
|
```yaml
|
|
name: [!somePackage!]
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rewrite the name of the package using the lower_case_with_underscores
|
|
naming convention:
|
|
|
|
```yaml
|
|
name: some_package
|
|
```
|
|
deprecatedDetails: |-
|
|
From the [Pubspec format description](https://dart.dev/tools/pub/pubspec):
|
|
|
|
**DO** use `lowercase_with_underscores` for package names.
|
|
|
|
Package names should be all lowercase, with underscores to separate words,
|
|
`just_like_this`. Use only basic Latin letters and Arabic digits: \[a-z0-9\_\].
|
|
Also, make sure the name is a valid Dart identifier -- that it doesn't start
|
|
with digits and isn't a reserved word.
|
|
packagePrefixedLibraryNames:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The library name is not a dot-separated path prefixed by the package name."
|
|
correctionMessage: "Try changing the name to '#p0'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a library has a name that
|
|
doesn't follow these guidelines:
|
|
|
|
- Prefix all library names with the package name.
|
|
- Make the entry library have the same name as the package.
|
|
- For all other libraries in a package, after the package name add the
|
|
dot-separated path to the library's Dart file.
|
|
- For libraries under `lib`, omit the top directory name.
|
|
|
|
For example, given a package named `my_package`, here are the library
|
|
names for various files in the package:
|
|
|
|
```dart
|
|
// In lib/my_package.dart
|
|
library my_package;
|
|
|
|
// In lib/other.dart
|
|
library my_package.other;
|
|
|
|
// In lib/foo/bar.dart
|
|
library my_package.foo.bar;
|
|
|
|
// In example/foo/bar.dart
|
|
library my_package.example.foo.bar;
|
|
|
|
// In lib/src/private.dart
|
|
library my_package.src.private;
|
|
```
|
|
|
|
#### Example
|
|
|
|
Assuming that the file containing the following code is not in a file
|
|
named `special.dart` in the `lib` directory of a package named `something`
|
|
(which would be an exception to the rule), the analyzer produces this
|
|
diagnostic because the name of the library doesn't conform to the
|
|
guidelines above:
|
|
|
|
```dart
|
|
library [!something.special!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Change the name of the library to conform to the guidelines.
|
|
deprecatedDetails: |-
|
|
**DO** prefix library names with the package name and a dot-separated path.
|
|
|
|
This guideline helps avoid the warnings you get when two libraries have the same
|
|
name. Here are the rules we recommend:
|
|
|
|
* Prefix all library names with the package name.
|
|
* Make the entry library have the same name as the package.
|
|
* For all other libraries in a package, after the package name add the
|
|
dot-separated path to the library's Dart file.
|
|
* For libraries under `lib`, omit the top directory name.
|
|
|
|
For example, say the package name is `my_package`. Here are the library names
|
|
for various files in the package:
|
|
|
|
**GOOD:**
|
|
```dart
|
|
// In lib/my_package.dart
|
|
library my_package;
|
|
|
|
// In lib/other.dart
|
|
library my_package.other;
|
|
|
|
// In lib/foo/bar.dart
|
|
library my_package.foo.bar;
|
|
|
|
// In example/foo/bar.dart
|
|
library my_package.example.foo.bar;
|
|
|
|
// In lib/src/private.dart
|
|
library my_package.src.private;
|
|
```
|
|
parameterAssignments:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Invalid assignment to the parameter '#p0'."
|
|
correctionMessage: "Try using a local variable in place of the parameter."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a
|
|
parameter is reassigned within a function body.
|
|
Reassigning parameters can make it harder to understand
|
|
what value a parameter holds at any given point in a function.
|
|
|
|
All types of assignments are considered, including:
|
|
|
|
- Direct variable assignments (with `=`).
|
|
- Null-aware assignments (with `??=`).
|
|
- Compound assignments (such as with `+=`).
|
|
- Increment and decrement operators (such as with `++`).
|
|
- Variable pattern assignments (such as in `var (a, b) = (1, 2);`).
|
|
|
|
There's an exception for a single null-aware assignment (`??=`) at
|
|
the beginning of a function for nullable parameters that don't have
|
|
a default value, as this is a common pattern for providing defaults.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the
|
|
parameter `x` is reassigned a new value:
|
|
|
|
```dart
|
|
void f(int x) {
|
|
[!x = 4!];
|
|
print(x);
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the
|
|
parameter `x` is modified with a postfix increment:
|
|
|
|
```dart
|
|
void f(int x) {
|
|
[!x++!];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Introduce a local variable to hold the modified value
|
|
instead of reassigning the parameter:
|
|
|
|
```dart
|
|
void f(int x) {
|
|
var y = x + 4;
|
|
print(y);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** assign new values to parameters of methods or functions.
|
|
|
|
Assigning new values to parameters is generally a bad practice unless an
|
|
operator such as `??=` is used. Otherwise, arbitrarily reassigning parameters
|
|
is usually a mistake.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void badFunction(int parameter) { // LINT
|
|
parameter = 4;
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void badFunction(int required, {int optional: 42}) { // LINT
|
|
optional ??= 8;
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void badFunctionPositional(int required, [int optional = 42]) { // LINT
|
|
optional ??= 8;
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
void badMethod(int parameter) { // LINT
|
|
parameter = 4;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void ok(String parameter) {
|
|
print(parameter);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void actuallyGood(int required, {int optional}) { // OK
|
|
optional ??= ...;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void actuallyGoodPositional(int required, [int optional]) { // OK
|
|
optional ??= ...;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {
|
|
void ok(String parameter) {
|
|
print(parameter);
|
|
}
|
|
}
|
|
```
|
|
preferAdjacentStringConcatenation:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "String literals shouldn't be concatenated by the '+' operator."
|
|
correctionMessage: "Try removing the operator to use adjacent strings."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the `+` operator is used to
|
|
concatenate two string literals.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because two string literals
|
|
are being concatenated by using the `+` operator:
|
|
|
|
```dart
|
|
var s = 'a' [!+!] 'b';
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the operator:
|
|
|
|
```dart
|
|
var s = 'a' 'b';
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use adjacent strings to concatenate string literals.
|
|
|
|
**BAD:**
|
|
```dart
|
|
raiseAlarm(
|
|
'ERROR: Parts of the spaceship are on fire. Other ' +
|
|
'parts are overrun by martians. Unclear which are which.');
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
raiseAlarm(
|
|
'ERROR: Parts of the spaceship are on fire. Other '
|
|
'parts are overrun by martians. Unclear which are which.');
|
|
```
|
|
preferAssertsInInitializerLists:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Assert should be in the initializer list."
|
|
correctionMessage: "Try moving the assert to the initializer list."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the body of a constructor
|
|
begins with one or more assert statements.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the body of the
|
|
constructor begins with an assert statement:
|
|
|
|
```dart
|
|
class C {
|
|
C(int i) {
|
|
[!assert!](i != 0);
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Move the assert to the initializer list, removing the body if there are
|
|
only assert statements in it:
|
|
|
|
```dart
|
|
class C {
|
|
C(int i) : assert(i != 0);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** put asserts in initializer lists.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
A(int a) {
|
|
assert(a != 0);
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {
|
|
A(int a) : assert(a != 0);
|
|
}
|
|
```
|
|
preferAssertsWithMessage:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Missing a message in an assert."
|
|
correctionMessage: "Try adding a message to the assert."
|
|
state:
|
|
stable: "2.3"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an assert statement doesn't
|
|
have a message.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because there's no message
|
|
in the assert statement:
|
|
|
|
```dart
|
|
void f(String s) {
|
|
[!assert(s.isNotEmpty);!]
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add a message to the assert statement:
|
|
|
|
```dart
|
|
void f(String s) {
|
|
assert(s.isNotEmpty, 'The argument must not be empty.');
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
When assertions fail it's not always simple to understand why. Adding a message
|
|
to the `assert` helps the developer to understand why the AssertionError occurs.
|
|
|
|
**BAD:**
|
|
```dart
|
|
f(a) {
|
|
assert(a != null);
|
|
}
|
|
|
|
class A {
|
|
A(a) : assert(a != null);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
f(a) {
|
|
assert(a != null, 'a must not be null');
|
|
}
|
|
|
|
class A {
|
|
A(a) : assert(a != null, 'a must not be null');
|
|
}
|
|
```
|
|
preferBoolInAsserts:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
stable: "2.0"
|
|
removed: "3.0"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
NOTE: This rule is removed in Dart 3.0.0; it is no longer functional.
|
|
|
|
**DO** use a boolean for assert conditions.
|
|
|
|
Not using booleans in assert conditions can lead to code where it isn't clear
|
|
what the intention of the assert statement is.
|
|
|
|
**BAD:**
|
|
```dart
|
|
assert(() {
|
|
f();
|
|
return true;
|
|
});
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
assert(() {
|
|
f();
|
|
return true;
|
|
}());
|
|
```
|
|
preferCollectionLiterals:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary constructor invocation."
|
|
correctionMessage: "Try using a collection literal."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a constructor is used to create
|
|
a list, map, or set, but a literal would produce the same result.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the constructor for
|
|
`Map` is being used to create a map that could also be created using a
|
|
literal:
|
|
|
|
```dart
|
|
var m = [!Map<String, String>()!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use the literal representation:
|
|
|
|
```dart
|
|
var m = <String, String>{};
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use collection literals when possible.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var addresses = Map<String, String>();
|
|
var uniqueNames = Set<String>();
|
|
var ids = LinkedHashSet<int>();
|
|
var coordinates = LinkedHashMap<int, int>();
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var addresses = <String, String>{};
|
|
var uniqueNames = <String>{};
|
|
var ids = <int>{};
|
|
var coordinates = <int, int>{};
|
|
```
|
|
|
|
**EXCEPTIONS:**
|
|
|
|
When a `LinkedHashSet` or `LinkedHashMap` is expected, a collection literal is
|
|
not preferred (or allowed).
|
|
|
|
```dart
|
|
void main() {
|
|
LinkedHashSet<int> linkedHashSet = LinkedHashSet.from([1, 2, 3]); // OK
|
|
LinkedHashMap linkedHashMap = LinkedHashMap(); // OK
|
|
|
|
printSet(LinkedHashSet<int>()); // LINT
|
|
printHashSet(LinkedHashSet<int>()); // OK
|
|
|
|
printMap(LinkedHashMap<int, int>()); // LINT
|
|
printHashMap(LinkedHashMap<int, int>()); // OK
|
|
}
|
|
|
|
void printSet(Set<int> ids) => print('$ids!');
|
|
void printHashSet(LinkedHashSet<int> ids) => printSet(ids);
|
|
void printMap(Map map) => print('$map!');
|
|
void printHashMap(LinkedHashMap map) => printMap(map);
|
|
```
|
|
preferConditionalAssignment:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The 'if' statement could be replaced by a null-aware assignment."
|
|
correctionMessage: "Try using the '??=' operator to conditionally assign a value."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an assignment to a variable is
|
|
conditional based on whether the variable has the value `null` and the
|
|
`??=` operator could be used instead.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the parameter `s` is
|
|
being compared to `null` in order to determine whether to assign a
|
|
different value:
|
|
|
|
```dart
|
|
int f(String? s) {
|
|
[!if (s == null) {
|
|
s = '';
|
|
}!]
|
|
return s.length;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use the `??=` operator instead of an explicit `if` statement:
|
|
|
|
```dart
|
|
int f(String? s) {
|
|
s ??= '';
|
|
return s.length;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** using `??=` over testing for `null`.
|
|
|
|
As Dart has the `??=` operator, it is advisable to use it where applicable to
|
|
improve the brevity of your code.
|
|
|
|
**BAD:**
|
|
```dart
|
|
String get fullName {
|
|
if (_fullName == null) {
|
|
_fullName = getFullUserName(this);
|
|
}
|
|
return _fullName;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
String get fullName {
|
|
return _fullName ??= getFullUserName(this);
|
|
}
|
|
```
|
|
preferConstConstructors:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'const' with the constructor to improve performance."
|
|
correctionMessage: "Try adding the 'const' keyword to the constructor invocation."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an invocation of a const
|
|
constructor isn't either preceded by `const` or in a [constant context][].
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the invocation of the
|
|
`const` constructor is neither prefixed by `const` nor in a
|
|
[constant context][]:
|
|
|
|
```dart
|
|
class C {
|
|
const C();
|
|
}
|
|
|
|
C c = [!C()!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the context can be made a [constant context][], then do so:
|
|
|
|
```dart
|
|
class C {
|
|
const C();
|
|
}
|
|
|
|
const C c = C();
|
|
```
|
|
|
|
If the context can't be made a [constant context][], then add `const`
|
|
before the constructor invocation:
|
|
|
|
```dart
|
|
class C {
|
|
const C();
|
|
}
|
|
|
|
C c = const C();
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** using `const` for instantiating constant constructors.
|
|
|
|
If a constructor can be invoked as const to produce a canonicalized instance,
|
|
it's preferable to do so.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
const A();
|
|
}
|
|
|
|
void accessA() {
|
|
A a = new A();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {
|
|
const A();
|
|
}
|
|
|
|
void accessA() {
|
|
A a = const A();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {
|
|
final int x;
|
|
|
|
const A(this.x);
|
|
}
|
|
|
|
A foo(int x) => new A(x);
|
|
```
|
|
preferConstConstructorsInImmutables:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Constructors in '@immutable' classes should be declared as 'const'."
|
|
correctionMessage: "Try adding 'const' to the constructor declaration."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a non-`const` constructor is
|
|
found in a class that has the `@immutable` annotation.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the constructor in `C`
|
|
isn't declared as `const` even though `C` has the `@immutable` annotation:
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
@immutable
|
|
class C {
|
|
final f;
|
|
|
|
[!C!](this.f);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the class really is intended to be immutable, then add the `const`
|
|
modifier to the constructor:
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
@immutable
|
|
class C {
|
|
final f;
|
|
|
|
const C(this.f);
|
|
}
|
|
```
|
|
|
|
If the class is mutable, then remove the `@immutable` annotation:
|
|
|
|
```dart
|
|
class C {
|
|
final f;
|
|
|
|
C(this.f);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** declaring `const` constructors on `@immutable` classes.
|
|
|
|
If a class is immutable, it is usually a good idea to make its constructor a
|
|
`const` constructor.
|
|
|
|
**BAD:**
|
|
```dart
|
|
@immutable
|
|
class A {
|
|
final a;
|
|
A(this.a);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
@immutable
|
|
class A {
|
|
final a;
|
|
const A(this.a);
|
|
}
|
|
```
|
|
preferConstDeclarations:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'const' for final variables initialized to a constant value."
|
|
correctionMessage: "Try replacing 'final' with 'const'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a top-level variable, static
|
|
field, or local variable is marked as `final` and is initialized to a
|
|
constant value.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the top-level variable
|
|
`v` is both `final` and initialized to a constant value:
|
|
|
|
```dart
|
|
[!final v = const <int>[]!];
|
|
```
|
|
|
|
The following code produces this diagnostic because the static field `f`
|
|
is both `final` and initialized to a constant value:
|
|
|
|
```dart
|
|
class C {
|
|
static [!final f = const <int>[]!];
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the local variable `v`
|
|
is both `final` and initialized to a constant value:
|
|
|
|
```dart
|
|
void f() {
|
|
[!final v = const <int>[]!];
|
|
print(v);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the keyword `final` with `const` and remove `const` from the
|
|
initializer:
|
|
|
|
```dart
|
|
class C {
|
|
static const f = <int>[];
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** using `const` for constant-valued declarations.
|
|
|
|
Constant declarations are more hot-reload friendly and allow
|
|
values to be used in other constant expressions.
|
|
|
|
**BAD:**
|
|
```dart
|
|
final o = const <int>[];
|
|
|
|
class A {
|
|
static final o = const <int>[];
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
const o = <int>[];
|
|
|
|
class A {
|
|
static const o = <int>[];
|
|
}
|
|
```
|
|
preferConstLiteralsToCreateImmutables:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'const' literals as arguments to constructors of '@immutable' classes."
|
|
correctionMessage: "Try adding 'const' before the literal."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a non-const list, map, or set
|
|
literal is passed as an argument to a constructor declared in a class
|
|
annotated with `@immutable`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the list literal
|
|
(`[1]`) is being passed to a constructor in an immutable class but isn't
|
|
a constant list:
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
@immutable
|
|
class C {
|
|
final f;
|
|
|
|
const C(this.f);
|
|
}
|
|
|
|
C c = C([![1]!]);
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the context can be made a [constant context][], then do so:
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
@immutable
|
|
class C {
|
|
final f;
|
|
|
|
const C(this.f);
|
|
}
|
|
|
|
const C c = C([1]);
|
|
```
|
|
|
|
If the context can't be made a [constant context][] but the constructor
|
|
can be invoked using `const`, then add `const` before the constructor
|
|
invocation:
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
@immutable
|
|
class C {
|
|
final f;
|
|
|
|
const C(this.f);
|
|
}
|
|
|
|
C c = const C([1]);
|
|
```
|
|
|
|
If the context can't be made a [constant context][] and the constructor
|
|
can't be invoked using `const`, then add the keyword `const` before the
|
|
collection literal:
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
@immutable
|
|
class C {
|
|
final f;
|
|
|
|
const C(this.f);
|
|
}
|
|
|
|
C c = C(const [1]);
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** using `const` for instantiating list, map and set literals used as
|
|
parameters in immutable class instantiations.
|
|
|
|
**BAD:**
|
|
```dart
|
|
@immutable
|
|
class A {
|
|
A(this.v);
|
|
final v;
|
|
}
|
|
|
|
A a1 = new A([1]);
|
|
A a2 = new A({});
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
A a1 = new A(const [1]);
|
|
A a2 = new A(const {});
|
|
```
|
|
preferConstructorsOverStaticMethods:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Static method should be a constructor."
|
|
correctionMessage: "Try converting the method into a constructor."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a static method returns a newly
|
|
created instance of the class and could, therefore, be a constructor.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the static method
|
|
`all` could be a constructor:
|
|
|
|
```dart
|
|
class C {
|
|
final int a, b, c;
|
|
C(this.a, this.b, this.c);
|
|
static C [!all!](int i) => C(i, i, i);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Convert the static method to a named constructor:
|
|
|
|
```dart
|
|
class C {
|
|
final int a, b, c;
|
|
C(this.a, this.b, this.c);
|
|
C.all(int i) : a = i, b = i, c = i;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** defining constructors instead of static methods to create instances.
|
|
|
|
In most cases, it makes more sense to use a named constructor rather than a
|
|
static method because it makes instantiation clearer.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Point {
|
|
num x, y;
|
|
Point(this.x, this.y);
|
|
static Point polar(num theta, num radius) {
|
|
return Point(radius * math.cos(theta),
|
|
radius * math.sin(theta));
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Point {
|
|
num x, y;
|
|
Point(this.x, this.y);
|
|
Point.polar(num theta, num radius)
|
|
: x = radius * math.cos(theta),
|
|
y = radius * math.sin(theta);
|
|
}
|
|
```
|
|
preferContainsAlwaysFalse:
|
|
# TODO(brianwilkerson): Should be warning rather than lint,
|
|
# as represents a bug rather than style preference.
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferContains
|
|
problemMessage: "Always 'false' because 'indexOf' is always greater than or equal to -1."
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
preferContainsAlwaysTrue:
|
|
# TODO(brianwilkerson): Should be warning rather than lint,
|
|
# as represents a bug rather than style preference.
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferContains
|
|
problemMessage: "Always 'true' because 'indexOf' is always greater than or equal to -1."
|
|
hasPublishedDocs: false
|
|
preferContainsUseContains:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferContains
|
|
problemMessage: "Unnecessary use of 'indexOf' to test for containment."
|
|
correctionMessage: "Try using 'contains'."
|
|
state:
|
|
stable: "2.0"
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the method `indexOf` is used and
|
|
the result is only compared with `-1` or `0` in a way where the semantics
|
|
are equivalent to using `contains`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the condition in the
|
|
`if` statement is checking to see whether the list contains the string:
|
|
|
|
```dart
|
|
void f(List<String> l, String s) {
|
|
if ([!l.indexOf(s) < 0!]) {
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use `contains` instead, negating the condition when necessary:
|
|
|
|
```dart
|
|
void f(List<String> l, String s) {
|
|
if (l.contains(s)) {
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use `indexOf` to see if a collection contains an element.
|
|
|
|
Calling `indexOf` to see if a collection contains something is difficult to read
|
|
and may have poor performance.
|
|
|
|
Instead, prefer `contains`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
if (lunchBox.indexOf('sandwich') == -1) return 'so hungry...';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (!lunchBox.contains('sandwich')) return 'so hungry...';
|
|
```
|
|
preferDoubleQuotes:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of single quotes."
|
|
correctionMessage: "Try using double quotes unless the string contains double quotes."
|
|
state:
|
|
stable: "2.4"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a string literal uses single
|
|
quotes (`'`) when it could use double quotes (`"`) without needing extra
|
|
escapes and without hurting readability.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the string literal
|
|
uses single quotes but doesn't need to:
|
|
|
|
```dart
|
|
void f(String name) {
|
|
print([!'Hello $name'!]);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use double quotes in place of single quotes:
|
|
|
|
```dart
|
|
void f(String name) {
|
|
print("Hello $name");
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use double quotes where they wouldn't require additional escapes.
|
|
|
|
That means strings with a double quote may use apostrophes so that the double
|
|
quote isn't escaped (note: we don't lint the other way around, ie, a double
|
|
quoted string with an escaped double quote is not flagged).
|
|
|
|
It's also rare, but possible, to have strings within string interpolations. In
|
|
this case, it's much more readable to use a single quote somewhere. So single
|
|
quotes are allowed either within, or containing, an interpolated string literal.
|
|
Arguably strings within string interpolations should be its own type of lint.
|
|
|
|
**BAD:**
|
|
```dart
|
|
useStrings(
|
|
'should be double quote',
|
|
r'should be double quote',
|
|
r\'''should be double quotes\''')
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
useStrings(
|
|
"should be double quote",
|
|
r"should be double quote",
|
|
r"""should be double quotes""",
|
|
'ok with " inside',
|
|
'nested \${a ? "strings" : "can"} be wrapped by a double quote',
|
|
"and nested \${a ? 'strings' : 'can be double quoted themselves'}");
|
|
```
|
|
preferEqualForDefaultValues:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
stable: "2.0"
|
|
removed: "3.0"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
NOTE: This rule is removed in Dart 3.0; it is no longer functional.
|
|
|
|
**DO** use `=` to separate a named parameter from its default value.
|
|
|
|
**BAD:**
|
|
```dart
|
|
m({a: 1})
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
m({a = 1})
|
|
```
|
|
preferExpressionFunctionBodies:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of a block function body."
|
|
correctionMessage: "Try using an expression function body."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the body of a function consists
|
|
of a single return statement with an expression.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the body of `f` has a
|
|
single return statement:
|
|
|
|
```dart
|
|
int f() [!{
|
|
return 0;
|
|
}!]
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the body is complete, then replace the body with an expression body:
|
|
|
|
```dart
|
|
int f() => 0;
|
|
```
|
|
|
|
If the body isn't complete, then add the missing statements.
|
|
deprecatedDetails: |-
|
|
**CONSIDER** using => for short members whose body is a single return statement.
|
|
|
|
**BAD:**
|
|
```dart
|
|
get width {
|
|
return right - left;
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
bool ready(num time) {
|
|
return minTime == null || minTime <= time;
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
containsValue(String value) {
|
|
return getValues().contains(value);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
get width => right - left;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
bool ready(num time) => minTime == null || minTime <= time;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
containsValue(String value) => getValues().contains(value);
|
|
```
|
|
preferFinalFields:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The private field #p0 could be 'final'."
|
|
correctionMessage: "Try making the field 'final'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a private field is only
|
|
assigned one time. The field can be initialized in multiple constructors
|
|
and still be flagged because only one of those constructors can ever run.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the field `_f` is only
|
|
assigned one time, in the field's initializer:
|
|
|
|
```dart
|
|
class C {
|
|
int [!_f = 1!];
|
|
|
|
int get f => _f;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Mark the field `final`:
|
|
|
|
```dart
|
|
class C {
|
|
final int _f = 1;
|
|
|
|
int get f => _f;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/design#prefer-making-fields-and-top-level-variables-final):
|
|
|
|
**DO** prefer declaring private fields as `final` if they are not reassigned
|
|
later in the library.
|
|
|
|
Declaring fields as `final` when possible is a good practice because it helps
|
|
avoid accidental reassignments and allows the compiler to do optimizations.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class BadImmutable {
|
|
var _label = 'hola mundo! BadImmutable'; // LINT
|
|
var label = 'hola mundo! BadImmutable'; // OK
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
class MultipleMutable {
|
|
var _label = 'hola mundo! GoodMutable', _offender = 'mumble mumble!'; // LINT
|
|
var _someOther; // LINT
|
|
|
|
MultipleMutable() : _someOther = 5;
|
|
|
|
MultipleMutable(this._someOther);
|
|
|
|
void changeLabel() {
|
|
_label= 'hello world! GoodMutable';
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class GoodImmutable {
|
|
final label = 'hola mundo! BadImmutable', bla = 5; // OK
|
|
final _label = 'hola mundo! BadImmutable', _bla = 5; // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class GoodMutable {
|
|
var _label = 'hola mundo! GoodMutable';
|
|
|
|
void changeLabel() {
|
|
_label = 'hello world! GoodMutable';
|
|
}
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
class AssignedInAllConstructors {
|
|
var _label; // LINT
|
|
AssignedInAllConstructors(this._label);
|
|
AssignedInAllConstructors.withDefault() : _label = 'Hello';
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class NotAssignedInAllConstructors {
|
|
var _label; // OK
|
|
NotAssignedInAllConstructors();
|
|
NotAssignedInAllConstructors.withDefault() : _label = 'Hello';
|
|
}
|
|
```
|
|
preferFinalInForEachPattern:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferFinalInForEach
|
|
problemMessage: "The pattern should be final."
|
|
correctionMessage: "Try making the pattern final."
|
|
state:
|
|
stable: "2.1"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the loop variable in a for-each
|
|
statement isn't marked as being `final`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the loop variable `e`
|
|
isn't marked as being `final`:
|
|
|
|
```dart
|
|
void f(List<int> l) {
|
|
for (var [!e!] in l) {
|
|
print(e);
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add the modifier `final` to the loop variable, removing the `var` if there
|
|
is one:
|
|
|
|
```dart
|
|
void f(List<int> l) {
|
|
for (final e in l) {
|
|
print(e);
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** prefer declaring for-each loop variables as final if they are not
|
|
reassigned later in the code.
|
|
|
|
Declaring for-each loop variables as final when possible is a good practice
|
|
because it helps avoid accidental reassignments and allows the compiler to do
|
|
optimizations.
|
|
|
|
**BAD:**
|
|
```dart
|
|
for (var element in elements) { // LINT
|
|
print('Element: $element');
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
for (final element in elements) {
|
|
print('Element: $element');
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
for (var element in elements) {
|
|
element = element + element;
|
|
print('Element: $element');
|
|
}
|
|
```
|
|
preferFinalInForEachVariable:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: preferFinalInForEach
|
|
problemMessage: "The variable '#p0' should be final."
|
|
correctionMessage: "Try making the variable final."
|
|
hasPublishedDocs: false
|
|
preferFinalLocals:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Local variables should be final."
|
|
correctionMessage: "Try making the variable final."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a local variable isn't marked
|
|
as being `final`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the variable `s` isn't
|
|
marked as being `final`:
|
|
|
|
```dart
|
|
int f(int i) {
|
|
[!var!] s = i + 1;
|
|
return s;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add the modifier `final` to the variable, removing the `var` if there is
|
|
one:
|
|
|
|
```dart
|
|
int f(int i) {
|
|
final s = i + 1;
|
|
return s;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** prefer declaring variables as final if they are not reassigned later in
|
|
the code.
|
|
|
|
Declaring variables as final when possible is a good practice because it helps
|
|
avoid accidental reassignments and allows the compiler to do optimizations.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void badMethod() {
|
|
var label = 'hola mundo! badMethod'; // LINT
|
|
print(label);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void goodMethod() {
|
|
final label = 'hola mundo! goodMethod';
|
|
print(label);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void mutableCase() {
|
|
var label = 'hola mundo! mutableCase';
|
|
print(label);
|
|
label = 'hello world';
|
|
print(label);
|
|
}
|
|
```
|
|
preferFinalParameters:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The parameter '#p0' should be final."
|
|
correctionMessage: "Try making the parameter final."
|
|
state:
|
|
stable: "2.14"
|
|
deprecated: "3.11"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
NOTE: This rule is removed in Dart 3.13.0; it is no longer functional.
|
|
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a parameter of a constructor,
|
|
method, function, or closure isn't marked as being `final`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the parameter `s`
|
|
isn't a `final` parameter:
|
|
|
|
```dart
|
|
// @dart=3.12
|
|
String f([!String s!]) => s;
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add the modifier `final` to the parameter:
|
|
|
|
```dart
|
|
// @dart=3.12
|
|
String f(final String s) => s;
|
|
```
|
|
deprecatedDetails: |-
|
|
**Note:** This lint rule was deprecated in Dart 3.11 and
|
|
is set to be removed in a future release of the Dart SDK.
|
|
Also note that the lint is not applied to code that is opted
|
|
in to the `primary-constructors` language feature where using
|
|
`final` for a parameter becomes a compile-time error.
|
|
If you want to ensure parameters aren't reassigned in function bodies,
|
|
consider enabling the
|
|
[`parameter_assignments`](https://dart.dev/lints/parameter_assignments)
|
|
lint rule instead.
|
|
|
|
**DO** prefer declaring parameters as final if they are not reassigned in
|
|
the function body.
|
|
|
|
Declaring parameters as final when possible is a good practice because it helps
|
|
avoid accidental reassignments.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void badParameter(String label) { // LINT
|
|
print(label);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void goodParameter(final String label) { // OK
|
|
print(label);
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void badExpression(int value) => print(value); // LINT
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void goodExpression(final int value) => print(value); // OK
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
[1, 4, 6, 8].forEach((value) => print(value + 2)); // LINT
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
[1, 4, 6, 8].forEach((final value) => print(value + 2)); // OK
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void mutableParameter(String label) { // OK
|
|
print(label);
|
|
label = 'Hello Linter!';
|
|
print(label);
|
|
}
|
|
```
|
|
preferForElementsToMapFromiterable:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'for' elements when building maps from iterables."
|
|
correctionMessage: "Try using a collection literal with a 'for' element."
|
|
state:
|
|
stable: "2.3"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when `Map.fromIterable` is used to
|
|
build a map that could be built using the `for` element.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because `fromIterable` is
|
|
being used to build a map that could be built using a `for` element:
|
|
|
|
```dart
|
|
void f(Iterable<String> data) {
|
|
[!Map<String, int>.fromIterable(
|
|
data,
|
|
key: (element) => element,
|
|
value: (element) => element.length,
|
|
)!];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use a `for` element to build the map:
|
|
|
|
```dart
|
|
void f(Iterable<String> data) {
|
|
<String, int>{
|
|
for (var element in data)
|
|
element: element.length
|
|
};
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
When building maps from iterables, it is preferable to use `for` elements.
|
|
|
|
Using 'for' elements brings several benefits including:
|
|
|
|
- Performance
|
|
- Flexibility
|
|
- Readability
|
|
- Improved type inference
|
|
- Improved interaction with null safety
|
|
|
|
|
|
**BAD:**
|
|
```dart
|
|
Map<String, WidgetBuilder>.fromIterable(
|
|
kAllGalleryDemos,
|
|
key: (demo) => '${demo.routeName}',
|
|
value: (demo) => demo.buildRoute,
|
|
);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
return {
|
|
for (var demo in kAllGalleryDemos)
|
|
'${demo.routeName}': demo.buildRoute,
|
|
};
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
// Map<int, Student> is not required, type is inferred automatically.
|
|
final pizzaRecipients = {
|
|
...studentLeaders,
|
|
for (var student in classG)
|
|
if (student.isPassing) student.id: student,
|
|
};
|
|
```
|
|
preferForeach:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'forEach' and a tear-off rather than a 'for' loop to apply a function to every element."
|
|
correctionMessage: "Try using 'forEach' and a tear-off rather than a 'for' loop."
|
|
state:
|
|
experimental: "2.0"
|
|
stable: "2.2"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `for` loop is used to operate
|
|
on every member of a collection and the method `forEach` could be used
|
|
instead.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because a `for` loop is being
|
|
used to invoke a single function for each key in `m`:
|
|
|
|
```dart
|
|
void f(Map<String, int> m) {
|
|
[!for (final key in m.keys) {
|
|
print(key);
|
|
}!]
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the for loop with an invocation of `forEach`:
|
|
|
|
```dart
|
|
void f(Map<String, int> m) {
|
|
m.keys.forEach(print);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use `forEach` if you are only going to apply a function or a method
|
|
to all the elements of an iterable.
|
|
|
|
Using `forEach` when you are only going to apply a function or method to all
|
|
elements of an iterable is a good practice because it makes your code more
|
|
terse.
|
|
|
|
**BAD:**
|
|
```dart
|
|
for (final key in map.keys.toList()) {
|
|
map.remove(key);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
map.keys.toList().forEach(map.remove);
|
|
```
|
|
|
|
**NOTE:** Replacing a for each statement with a forEach call may change the
|
|
behavior in the case where there are side-effects on the iterable itself.
|
|
```dart
|
|
for (final v in myList) {
|
|
foo().f(v); // This code invokes foo() many times.
|
|
}
|
|
|
|
myList.forEach(foo().f); // But this one invokes foo() just once.
|
|
```
|
|
preferFunctionDeclarationsOverVariables:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use a function declaration rather than a variable assignment to bind a function to a name."
|
|
correctionMessage: "Try rewriting the closure assignment as a function declaration."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a closure is assigned to a
|
|
local variable and the local variable is not re-assigned anywhere.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the local variable `f`
|
|
is initialized to be a closure and isn't assigned any other value:
|
|
|
|
```dart
|
|
void g() {
|
|
var [!f = (int i) => i * 2!];
|
|
f(1);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the local variable with a local function:
|
|
|
|
```dart
|
|
void g() {
|
|
int f(int i) => i * 2;
|
|
f(1);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/usage#do-use-a-function-declaration-to-bind-a-function-to-a-name):
|
|
|
|
**DO** use a function declaration to bind a function to a name.
|
|
|
|
As Dart allows local function declarations, it is a good practice to use them in
|
|
the place of function literals.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void main() {
|
|
var localFunction = () {
|
|
...
|
|
};
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void main() {
|
|
localFunction() {
|
|
...
|
|
}
|
|
}
|
|
```
|
|
preferGenericFunctionTypeAliases:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Use the generic function type syntax in 'typedef's."
|
|
correctionMessage: "Try using the generic function type syntax ('#p0')."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a typedef is written using the
|
|
older syntax for function type aliases in which the name being declared is
|
|
embedded in the function type.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because it uses the older
|
|
syntax:
|
|
|
|
```dart
|
|
typedef void [!F!]<T>();
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rewrite the typedef to use the newer syntax:
|
|
|
|
```dart
|
|
typedef F<T> = void Function();
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** generic function type aliases.
|
|
|
|
With the introduction of generic functions, function type aliases
|
|
(`typedef void F()`) couldn't express all of the possible kinds of
|
|
parameterization that users might want to express. Generic function type aliases
|
|
(`typedef F = void Function()`) fixed that issue.
|
|
|
|
For consistency and readability reasons, it's better to only use one syntax and
|
|
thus prefer generic function type aliases.
|
|
|
|
**BAD:**
|
|
```dart
|
|
typedef void F();
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
typedef F = void Function();
|
|
```
|
|
preferIfElementsToConditionalExpressions:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use an 'if' element to conditionally add elements."
|
|
correctionMessage: "Try using an 'if' element rather than a conditional expression."
|
|
state:
|
|
stable: "2.3"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
When building collections, it is preferable to use `if` elements rather than
|
|
conditionals.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var list = ['a', 'b', condition ? 'c' : null].where((e) => e != null).toList();
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var list = ['a', 'b', if (condition) 'c'];
|
|
```
|
|
preferIfNullOperators:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use the '??' operator rather than '?:' when testing for 'null'."
|
|
correctionMessage: "Try rewriting the code to use '??'."
|
|
state:
|
|
stable: "2.4"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a conditional expression (using
|
|
the `?:` operator) is used to select a different value when a local
|
|
variable is `null`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the variable `s` is
|
|
being compared to `null` so that a different value can be returned when
|
|
`s` is `null`:
|
|
|
|
```dart
|
|
String f(String? s) => [!s == null ? '' : s!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use the if-null operator instead:
|
|
|
|
```dart
|
|
String f(String? s) => s ?? '';
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** using `??` operators instead of `null` checks and conditional
|
|
expressions.
|
|
|
|
**BAD:**
|
|
```dart
|
|
v = a == null ? b : a;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
v = a ?? b;
|
|
```
|
|
preferInitializingFormals:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Use an initializing formal to assign a parameter to a field."
|
|
correctionMessage: "Try using an initialing formal ('this.#p0') to initialize the field."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a constructor parameter is used
|
|
to initialize a field without modification.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the parameter `c` is
|
|
only used to set the field `c`:
|
|
|
|
```dart
|
|
class C {
|
|
int c;
|
|
|
|
C(int c) : [!this.c = c!];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use an initializing formal parameter to initialize the field:
|
|
|
|
```dart
|
|
class C {
|
|
int c;
|
|
|
|
C(this.c);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use initializing formals when possible.
|
|
|
|
Using initializing formals when possible makes your code more terse.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Point {
|
|
num? x, y;
|
|
Point(num x, num y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Point {
|
|
num? x, y;
|
|
Point(num this.x, num this.y);
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Point {
|
|
num? x, y;
|
|
Point({num? x, num? y}) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Point {
|
|
num? x, y;
|
|
Point({required num this.x, required num this.y});
|
|
}
|
|
```
|
|
|
|
**NOTE:**
|
|
This rule will not generate a lint for named parameters unless the parameter
|
|
name and the field name are the same. The reason for this is that resolving
|
|
such a lint would require either renaming the field or renaming the parameter,
|
|
and both of those actions would potentially be a breaking change. For example,
|
|
the following will not generate a lint:
|
|
|
|
```dart
|
|
class Point {
|
|
bool? isEnabled;
|
|
Point({bool? enabled}) {
|
|
this.isEnabled = enabled; // OK
|
|
}
|
|
}
|
|
```
|
|
|
|
**NOTE:**
|
|
Also note that it is possible to enforce a type that is stricter than the
|
|
initialized field with an initializing formal parameter. In the following
|
|
example the unnamed `Bid` constructor requires a non-null `int` despite
|
|
`amount` being declared nullable (`int?`).
|
|
|
|
```dart
|
|
class Bid {
|
|
final int? amount;
|
|
Bid(int this.amount);
|
|
Bid.pass() : amount = null;
|
|
}
|
|
```
|
|
preferInlinedAddsMultiple:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferInlinedAdds
|
|
problemMessage: "The addition of multiple list items could be inlined."
|
|
correctionMessage: "Try adding the items to the list literal directly."
|
|
state:
|
|
stable: "2.3"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
deprecatedDetails: |-
|
|
Declare elements in list literals inline, rather than using `add` and
|
|
`addAll` methods where possible.
|
|
|
|
|
|
**BAD:**
|
|
```dart
|
|
var l = ['a']..add('b')..add('c');
|
|
var l2 = ['a']..addAll(['b', 'c']);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var l = ['a', 'b', 'c'];
|
|
var l2 = ['a', 'b', 'c'];
|
|
```
|
|
preferInlinedAddsSingle:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferInlinedAdds
|
|
problemMessage: "The addition of a list item could be inlined."
|
|
correctionMessage: "Try adding the item to the list literal directly."
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the methods `add` and `addAll`
|
|
are invoked on a list literal where the elements being added could be
|
|
included in the list literal.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `add` method is
|
|
being used to add `b`, when it could have been included directly in the
|
|
list literal:
|
|
|
|
```dart
|
|
List<String> f(String a, String b) {
|
|
return [a]..[!add!](b);
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the `addAll` method is
|
|
being used to add the elements of `b`, when it could have been included
|
|
directly in the list literal:
|
|
|
|
```dart
|
|
List<String> f(String a, List<String> b) {
|
|
return [a]..[!addAll!](b);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the `add` method is being used, then make the argument an element of
|
|
the list and remove the invocation:
|
|
|
|
```dart
|
|
List<String> f(String a, String b) {
|
|
return [a, b];
|
|
}
|
|
```
|
|
|
|
If the `addAll` method is being used, then use the spread operator on the
|
|
argument to add its elements to the list and remove the invocation:
|
|
|
|
```dart
|
|
List<String> f(String a, List<String> b) {
|
|
return [a, ...b];
|
|
}
|
|
```
|
|
preferIntLiterals:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of a 'double' literal."
|
|
correctionMessage: "Try using an 'int' literal."
|
|
state:
|
|
stable: "2.1"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `double` literal's value
|
|
has a fractional part of 0 (such as `8.0`)
|
|
and is used where the context type is `double`.
|
|
|
|
In these cases, you can use an `int` literal instead,
|
|
because it would evaluate to the same `double` value.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the literal `8.0`
|
|
is used to initialize a variable of type `double`,
|
|
but its value has a fractional part of `0`:
|
|
|
|
```dart
|
|
double d = [!8.0!];
|
|
```
|
|
|
|
The following code produces this diagnostic because the literal `7.1e2`
|
|
evaluates to `710.0`, which has a fractional part of `0`,
|
|
and is used to initialize a variable of type `double`:
|
|
|
|
```dart
|
|
double d = [!7.1e2!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the `double` literal with an equivalent `int` literal:
|
|
|
|
```dart
|
|
double d = 8;
|
|
```
|
|
|
|
```dart
|
|
double d = 710;
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use int literals rather than the corresponding double literal.
|
|
|
|
**BAD:**
|
|
```dart
|
|
const double myDouble = 8.0;
|
|
final anotherDouble = myDouble + 7.0e2;
|
|
main() {
|
|
someMethod(6.0);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
const double myDouble = 8;
|
|
final anotherDouble = myDouble + 700;
|
|
main() {
|
|
someMethod(6);
|
|
}
|
|
```
|
|
preferInterpolationToComposeStrings:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use interpolation to compose strings and values."
|
|
correctionMessage: "Try using string interpolation to build the composite string."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when string literals and computed
|
|
strings are being concatenated using the `+` operator, but string
|
|
interpolation would achieve the same result.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the String `s` is
|
|
concatenated with other strings using the `+` operator:
|
|
|
|
```dart
|
|
String f(String s) {
|
|
return [!'(' + s!] + ')';
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use string interpolation:
|
|
|
|
```dart
|
|
String f(List<String> l) {
|
|
return '(${l[0]}, ${l[1]})';
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** using interpolation to compose strings and values.
|
|
|
|
Using interpolation when composing strings and values is usually easier to write
|
|
and read than concatenation.
|
|
|
|
**BAD:**
|
|
```dart
|
|
'Hello, ' + person.name + ' from ' + person.city + '.';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
'Hello, ${person.name} from ${person.city}.'
|
|
```
|
|
preferIsEmptyAlwaysFalse:
|
|
# TODO(brianwilkerson): Should be warning rather than lint,
|
|
# as represents a bug rather than style preference.
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferIsEmpty
|
|
problemMessage: "The comparison is always 'false' because the length is always greater than or equal to 0."
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
preferIsEmptyAlwaysTrue:
|
|
# TODO(brianwilkerson): Should be warning rather than lint,
|
|
# as represents a bug rather than style preference.
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferIsEmpty
|
|
problemMessage: "The comparison is always 'true' because the length is always greater than or equal to 0."
|
|
hasPublishedDocs: false
|
|
preferIsEmptyUseIsEmpty:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferIsEmpty
|
|
problemMessage: "Use 'isEmpty' instead of 'length' to test whether the collection is empty."
|
|
correctionMessage: "Try rewriting the expression to use 'isEmpty'."
|
|
state:
|
|
stable: "2.0"
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the result of invoking either
|
|
`Iterable.length` or `Map.length` is compared for equality with zero
|
|
(`0`).
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the result of invoking
|
|
`length` is checked for equality with zero:
|
|
|
|
```dart
|
|
int f(Iterable<int> p) => [!p.length == 0!] ? 0 : p.first;
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the use of `length` with a use of either `isEmpty` or
|
|
`isNotEmpty`:
|
|
|
|
```dart
|
|
void f(Iterable<int> p) => p.isEmpty ? 0 : p.first;
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use `length` to see if a collection is empty.
|
|
|
|
The `Iterable` contract does not require that a collection know its length or be
|
|
able to provide it in constant time. Calling `length` just to see if the
|
|
collection contains anything can be painfully slow.
|
|
|
|
Instead, there are faster and more readable getters: `isEmpty` and
|
|
`isNotEmpty`. Use the one that doesn't require you to negate the result.
|
|
|
|
**BAD:**
|
|
```dart
|
|
if (lunchBox.length == 0) return 'so hungry...';
|
|
if (words.length != 0) return words.join(' ');
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (lunchBox.isEmpty) return 'so hungry...';
|
|
if (words.isNotEmpty) return words.join(' ');
|
|
```
|
|
preferIsEmptyUseIsNotEmpty:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferIsEmpty
|
|
problemMessage: "Use 'isNotEmpty' instead of 'length' to test whether the collection is empty."
|
|
correctionMessage: "Try rewriting the expression to use 'isNotEmpty'."
|
|
hasPublishedDocs: true
|
|
preferIsNotEmpty:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'isNotEmpty' rather than negating the result of 'isEmpty'."
|
|
correctionMessage: "Try rewriting the expression to use 'isNotEmpty'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the result of invoking
|
|
`Iterable.isEmpty` or `Map.isEmpty` is negated.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the result of invoking
|
|
`Iterable.isEmpty` is negated:
|
|
|
|
```dart
|
|
void f(Iterable<int> p) => [!!p.isEmpty!] ? p.first : 0;
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rewrite the code to use `isNotEmpty`:
|
|
|
|
```dart
|
|
void f(Iterable<int> p) => p.isNotEmpty ? p.first : 0;
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** `x.isNotEmpty` to `!x.isEmpty` for `Iterable` and `Map` instances.
|
|
|
|
When testing whether an iterable or map is empty, prefer `isNotEmpty` over
|
|
`!isEmpty` to improve code readability.
|
|
|
|
**BAD:**
|
|
```dart
|
|
if (!sources.isEmpty) {
|
|
process(sources);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (todo.isNotEmpty) {
|
|
sendResults(request, todo.isEmpty);
|
|
}
|
|
```
|
|
preferIsNotOperator:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use the 'is!' operator rather than negating the value of the 'is' operator."
|
|
correctionMessage: "Try rewriting the condition to use the 'is!' operator."
|
|
state:
|
|
stable: "2.7"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the prefix `!` operator is used
|
|
to negate the result of an `is` test.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the result of testing
|
|
to see whether `o` is a `String` is negated using the prefix `!` operator:
|
|
|
|
```dart
|
|
String f(Object o) {
|
|
if ([!!(o is String)!]) {
|
|
return o.toString();
|
|
}
|
|
return o;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use the `is!` operator instead:
|
|
|
|
```dart
|
|
String f(Object o) {
|
|
if (o is! String) {
|
|
return o.toString();
|
|
}
|
|
return o;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
When checking if an object is not of a specified type, it is preferable to use the 'is!' operator.
|
|
|
|
**BAD:**
|
|
```dart
|
|
if (!(foo is Foo)) {
|
|
...
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (foo is! Foo) {
|
|
...
|
|
}
|
|
```
|
|
preferIterableWheretype:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'whereType' to select elements of a given type."
|
|
correctionMessage: "Try rewriting the expression to use 'whereType'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the method `Iterable.where` is
|
|
being used to filter elements based on their type.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the method `where` is
|
|
being used to access only the strings within the iterable:
|
|
|
|
```dart
|
|
Iterable<Object> f(Iterable<Object> p) => p.[!where!]((e) => e is String);
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rewrite the code to use `whereType`:
|
|
|
|
```dart
|
|
Iterable<String> f(Iterable<Object> p) => p.whereType<String>();
|
|
```
|
|
|
|
This might also allow you to tighten the types in your code or remove
|
|
other type checks.
|
|
deprecatedDetails: |-
|
|
**PREFER** `iterable.whereType<T>()` over `iterable.where((e) => e is T)`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
iterable.where((e) => e is MyClass);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
iterable.whereType<MyClass>();
|
|
```
|
|
preferMixin:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Only mixins should be mixed in."
|
|
correctionMessage: "Try converting '#p0' to a mixin."
|
|
state:
|
|
stable: "2.1"
|
|
categories: [languageFeatureUsage, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Dart 2.1 introduced a new syntax for mixins that provides a safe way for a mixin
|
|
to invoke inherited members using `super`. The new style of mixins should always
|
|
be used for types that are to be mixed in. As a result, this lint will flag any
|
|
uses of a class in a `with` clause.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {}
|
|
class B extends Object with A {}
|
|
```
|
|
|
|
**OK:**
|
|
```dart
|
|
mixin M {}
|
|
class C with M {}
|
|
```
|
|
preferNullAwareMethodCalls:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use a null-aware invocation of the 'call' method rather than explicitly testing for 'null'."
|
|
correctionMessage: "Try using '?.call()' to invoke the function."
|
|
state:
|
|
stable: "2.14"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Instead of checking nullability of a function/method `f` before calling it,
|
|
you can use `f?.call()`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
if (f != null) f!();
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
f?.call();
|
|
```
|
|
preferNullAwareOperators:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use the null-aware operator '?.' rather than an explicit 'null' comparison."
|
|
correctionMessage: "Try using '?.'."
|
|
state:
|
|
stable: "2.2"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a comparison with `null` is
|
|
used to guard a member reference, and `null` is used as a result when the
|
|
guarded target is `null`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the invocation of
|
|
`length` is guarded by a `null` comparison even though the default value
|
|
is `null`:
|
|
|
|
```dart
|
|
int? f(List<int>? p) {
|
|
return [!p == null ? null : p.length!];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use a null-aware access operator instead:
|
|
|
|
```dart
|
|
int? f(List<int>? p) {
|
|
return p?.length;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** using `null`-aware operators instead of `null` checks in conditional
|
|
expressions.
|
|
|
|
**BAD:**
|
|
```dart
|
|
v = a == null ? null : a.b;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
v = a?.b;
|
|
```
|
|
preferRelativeImports:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use relative imports for files in the 'lib' directory."
|
|
correctionMessage: "Try converting the URI to a relative URI."
|
|
state:
|
|
stable: "2.6"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an `import` in a library inside
|
|
the `lib` directory uses a `package:` URI to refer to another library in
|
|
the same package.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because it uses a `package:`
|
|
URI when a relative URI could have been used:
|
|
|
|
```dart
|
|
import 'package:my_package/bar.dart';
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use a relative URI to import the library:
|
|
|
|
```dart
|
|
import 'bar.dart';
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** relative imports for files in `lib/`.
|
|
|
|
When mixing relative and absolute imports it's possible to create confusion
|
|
where the same member gets imported in two different ways. One way to avoid
|
|
that is to ensure you consistently use relative imports for files within the
|
|
`lib/` directory.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import 'package:my_package/bar.dart';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'bar.dart';
|
|
```
|
|
preferSingleQuotes:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of double quotes."
|
|
correctionMessage: "Try using single quotes unless the string contains single quotes."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a string literal uses double
|
|
quotes (`"`) when it could use single quotes (`'`) without needing extra
|
|
escapes and without hurting readability.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the string literal
|
|
uses double quotes but doesn't need to:
|
|
|
|
```dart
|
|
void f(String name) {
|
|
print([!"Hello $name"!]);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use single quotes in place of double quotes:
|
|
|
|
```dart
|
|
void f(String name) {
|
|
print('Hello $name');
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use single quotes where they wouldn't require additional escapes.
|
|
|
|
That means strings with an apostrophe may use double quotes so that the
|
|
apostrophe isn't escaped (note: we don't lint the other way around, ie, a single
|
|
quoted string with an escaped apostrophe is not flagged).
|
|
|
|
It's also rare, but possible, to have strings within string interpolations. In
|
|
this case, it's much more readable to use a double quote somewhere. So double
|
|
quotes are allowed either within, or containing, an interpolated string literal.
|
|
Arguably strings within string interpolations should be its own type of lint.
|
|
|
|
**BAD:**
|
|
```dart
|
|
useStrings(
|
|
"should be single quote",
|
|
r"should be single quote",
|
|
r"""should be single quotes""")
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
useStrings(
|
|
'should be single quote',
|
|
r'should be single quote',
|
|
r\'''should be single quotes\''',
|
|
"here's ok",
|
|
"nested \${a ? 'strings' : 'can'} be wrapped by a double quote",
|
|
'and nested \${a ? "strings" : "can be double quoted themselves"}');
|
|
```
|
|
preferSpreadCollections:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The addition of multiple elements could be inlined."
|
|
correctionMessage: "Try using the spread operator ('...') to inline the addition."
|
|
state:
|
|
stable: "2.3"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Use spread collections when possible.
|
|
|
|
Collection literals are excellent when you want to create a new collection out
|
|
of individual items. But, when existing items are already stored in another
|
|
collection, spread collection syntax leads to simpler code.
|
|
|
|
**BAD:**
|
|
|
|
```dart
|
|
Widget build(BuildContext context) {
|
|
return CupertinoPageScaffold(
|
|
child: ListView(
|
|
children: [
|
|
Tab2Header(),
|
|
]..addAll(buildTab2Conversation()),
|
|
),
|
|
);
|
|
}
|
|
```
|
|
|
|
```dart
|
|
var ints = [1, 2, 3];
|
|
print(['a']..addAll(ints.map((i) => i.toString()))..addAll(['c']));
|
|
```
|
|
|
|
```dart
|
|
var things;
|
|
var l = ['a']..addAll(things ?? const []);
|
|
```
|
|
|
|
|
|
**GOOD:**
|
|
|
|
```dart
|
|
Widget build(BuildContext context) {
|
|
return CupertinoPageScaffold(
|
|
child: ListView(
|
|
children: [
|
|
Tab2Header(),
|
|
...buildTab2Conversation(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
```
|
|
|
|
```dart
|
|
var ints = [1, 2, 3];
|
|
print(['a', ...ints.map((i) => i.toString()), 'c');
|
|
```
|
|
|
|
```dart
|
|
var things;
|
|
var l = ['a', ...?things];
|
|
```
|
|
preferTypingUninitializedVariablesForField:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferTypingUninitializedVariables
|
|
problemMessage: "An uninitialized field should have an explicit type annotation."
|
|
correctionMessage: "Try adding a type annotation."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne, unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a variable without an
|
|
initializer doesn't have an explicit type annotation.
|
|
|
|
Without either a type annotation or an initializer, a variable has the
|
|
type `dynamic`, which allows any value to be assigned to the variable,
|
|
often causing hard to identify bugs.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the variable `r`
|
|
doesn't have either a type annotation or an initializer:
|
|
|
|
```dart
|
|
Object f() {
|
|
var [!r!];
|
|
r = '';
|
|
return r;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the variable can be initialized, then add an initializer:
|
|
|
|
```dart
|
|
Object f() {
|
|
var r = '';
|
|
return r;
|
|
}
|
|
```
|
|
|
|
If the variable can't be initialized, then add an explicit type
|
|
annotation:
|
|
|
|
```dart
|
|
Object f() {
|
|
String r;
|
|
r = '';
|
|
return r;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** specifying a type annotation for uninitialized variables and fields.
|
|
|
|
Forgoing type annotations for uninitialized variables is a bad practice because
|
|
you may accidentally assign them to a type that you didn't originally intend to.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class BadClass {
|
|
static var bar; // LINT
|
|
var foo; // LINT
|
|
|
|
void method() {
|
|
var bar; // LINT
|
|
bar = 5;
|
|
print(bar);
|
|
}
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void aFunction() {
|
|
var bar; // LINT
|
|
bar = 5;
|
|
...
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class GoodClass {
|
|
static var bar = 7;
|
|
var foo = 42;
|
|
int baz; // OK
|
|
|
|
void method() {
|
|
int baz;
|
|
var bar = 5;
|
|
...
|
|
}
|
|
}
|
|
```
|
|
preferTypingUninitializedVariablesForLocalVariable:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: preferTypingUninitializedVariables
|
|
problemMessage: "An uninitialized variable should have an explicit type annotation."
|
|
correctionMessage: "Try adding a type annotation."
|
|
hasPublishedDocs: true
|
|
preferVoidToNull:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of the type 'Null'."
|
|
correctionMessage: "Try using 'void' instead."
|
|
state:
|
|
experimental: "2.1"
|
|
stable: "2.2"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when `Null` is used in a location
|
|
where `void` would be a valid choice.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the function `f` is
|
|
declared to return `null` (at some future time):
|
|
|
|
```dart
|
|
Future<[!Null!]> f() async {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the use of `Null` with a use of `void`:
|
|
|
|
```dart
|
|
Future<void> f() async {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use the type Null where void would work.
|
|
|
|
**BAD:**
|
|
```dart
|
|
Null f() {}
|
|
Future<Null> f() {}
|
|
Stream<Null> f() {}
|
|
f(Null x) {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f() {}
|
|
Future<void> f() {}
|
|
Stream<void> f() {}
|
|
f(void x) {}
|
|
```
|
|
|
|
Some exceptions include formulating special function types:
|
|
|
|
```dart
|
|
Null Function(Null, Null);
|
|
```
|
|
|
|
and for making empty literals which are safe to pass into read-only locations
|
|
for any type of map or list:
|
|
|
|
```dart
|
|
<Null>[];
|
|
<int, Null>{};
|
|
```
|
|
provideDeprecationMessage:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Missing a deprecation message."
|
|
correctionMessage: "Try using the constructor to provide a message ('@Deprecated(\"message\")')."
|
|
state:
|
|
stable: "2.2"
|
|
categories: [publicInterface]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `deprecated` annotation is
|
|
used instead of the `Deprecated` annotation.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the function `f` is
|
|
annotated with `deprecated`:
|
|
|
|
```dart
|
|
[!@deprecated!]
|
|
void f() {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Convert the code to use the longer form:
|
|
|
|
```dart
|
|
@Deprecated('Use g instead. Will be removed in 4.0.0.')
|
|
void f() {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** specify a deprecation message (with migration instructions and/or a
|
|
removal schedule) in the `Deprecated` constructor.
|
|
|
|
**BAD:**
|
|
```dart
|
|
@deprecated
|
|
void oldFunction(arg1, arg2) {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
@Deprecated("""
|
|
[oldFunction] is being deprecated in favor of [newFunction] (with slightly
|
|
different parameters; see [newFunction] for more information). [oldFunction]
|
|
will be removed on or after the 4.0.0 release.
|
|
""")
|
|
void oldFunction(arg1, arg2) {}
|
|
```
|
|
publicMemberApiDocs:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Missing documentation for a public member."
|
|
correctionMessage: "Try adding documentation for the member."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [publicInterface, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the declaration of part of the
|
|
public API of a package doesn't have a documentation comment.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the class `C` doesn't
|
|
have a documentation comment:
|
|
|
|
```dart
|
|
class [!C!] {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add a documentation comment.
|
|
|
|
```dart
|
|
/// Documentation comment.
|
|
class C {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** document all public members.
|
|
|
|
All non-overriding public members should be documented with `///` doc-style
|
|
comments.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Bad {
|
|
void meh() { }
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
/// A good thing.
|
|
abstract class Good {
|
|
/// Start doing your thing.
|
|
void start() => _start();
|
|
|
|
_start();
|
|
}
|
|
```
|
|
|
|
In case a public member overrides a member it is up to the declaring member
|
|
to provide documentation. For example, in the following, `Sub` needn't
|
|
document `init` (though it certainly may, if there's need).
|
|
|
|
**GOOD:**
|
|
```dart
|
|
/// Base of all things.
|
|
abstract class Base {
|
|
/// Initialize the base.
|
|
void init();
|
|
}
|
|
|
|
/// A sub base.
|
|
class Sub extends Base {
|
|
@override
|
|
void init() { ... }
|
|
}
|
|
```
|
|
|
|
Note that consistent with `dart doc`, an exception to the rule is made when
|
|
documented getters have corresponding undocumented setters. In this case the
|
|
setters inherit the docs from the getters.
|
|
recursiveGetters:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The getter '#p0' recursively returns itself."
|
|
correctionMessage: "Try changing the value being returned."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne, unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a getter invokes itself,
|
|
resulting in an infinite loop.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the getter `count`
|
|
invokes itself:
|
|
|
|
```dart
|
|
%ignore=unused_field
|
|
class C {
|
|
int _count = 0;
|
|
|
|
int get [!count!] => count;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Change the getter to not invoke itself:
|
|
|
|
```dart
|
|
class C {
|
|
int _count = 0;
|
|
|
|
int get count => _count;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** create recursive getters.
|
|
|
|
Recursive getters are getters which return themselves as a value. This is
|
|
usually a typo.
|
|
|
|
**BAD:**
|
|
```dart
|
|
int get field => field; // LINT
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
int get otherField {
|
|
return otherField; // LINT
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
int get field => _field;
|
|
```
|
|
removeDeprecationsInBreakingVersions:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Remove deprecated elements in breaking versions."
|
|
correctionMessage: "Try removing the deprecated element."
|
|
state:
|
|
stable: "3.10"
|
|
categories: [publicInterface]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic in packages that have a "breaking"
|
|
version number (`x.0.0` or `0.x.0`) for every declaration that has a
|
|
`@Deprecated` annotation.
|
|
|
|
#### Example
|
|
|
|
Given a package with a `pubspec.yaml` file containing:
|
|
|
|
```yaml
|
|
%uri="pubspec.yaml"
|
|
name: p
|
|
version: 2.0.0
|
|
environment:
|
|
sdk: ^3.9.0
|
|
```
|
|
|
|
The following code produces this diagnostic because the function `f` is
|
|
annotated with `@deprecated`:
|
|
|
|
```dart
|
|
@[!deprecated!]
|
|
void f() {}
|
|
|
|
void g() {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the declaration should be removed in the next release of the package,
|
|
then remove the declaration:
|
|
|
|
```dart
|
|
void g() {}
|
|
```
|
|
|
|
If you are not making a breaking change, then use a
|
|
minor or patch version increment for the package:
|
|
|
|
```yaml
|
|
%uri="pubspec.yaml"
|
|
name: p
|
|
version: 1.0.1
|
|
environment:
|
|
sdk: ^3.9.0
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** Remove deprecated elements in breaking version numbers.
|
|
Breaking version numbers are on the form `x.0.0` or `0.x.0`.
|
|
|
|
For example given a package with a `pubspec.yaml` file containing:
|
|
|
|
```yaml
|
|
name: p
|
|
version: 2.0.0
|
|
environment:
|
|
sdk: ^3.9.0
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
@deprecated
|
|
void f() {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
// (f is removed).
|
|
```
|
|
|
|
**GOOD:**
|
|
```yaml
|
|
name: p
|
|
version: 1.0.1
|
|
environment:
|
|
sdk: ^3.9.0
|
|
```
|
|
requireTrailingCommas:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Missing a required trailing comma."
|
|
correctionMessage: "Try adding a trailing comma."
|
|
state:
|
|
stable: "2.14"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**DO** use trailing commas for all multi-line parameter lists and argument
|
|
lists. A parameter list or argument list that fits on one line, including the
|
|
opening parenthesis and closing parenthesis, does not require a trailing comma.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void run() {
|
|
method('does not fit on one line',
|
|
'test test test test test test test test test test test');
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void run() {
|
|
method(
|
|
'does not fit on one line',
|
|
'test test test test test test test test test test test',
|
|
);
|
|
}
|
|
```
|
|
|
|
**EXCEPTION:** If the final argument in an argument list is positional (vs
|
|
named) and is either a function literal with curly braces, a map literal, a set
|
|
literal, or a list literal, then a trailing comma is not required.
|
|
This exception only applies if the final argument does not fit entirely on one
|
|
line.
|
|
|
|
**NOTE:** This lint rule assumes that code has been formatted with `dart format`
|
|
and may produce false positives on unformatted code.
|
|
securePubspecUrls:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The '#p0' protocol shouldn't be used because it isn't secure."
|
|
correctionMessage: "Try using a secure protocol, such as 'https'."
|
|
state:
|
|
stable: "2.16"
|
|
categories: [pub]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a URL in a `pubspec.yaml` file is
|
|
using a non-secure scheme, such as `http`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `pubspec.yaml` file
|
|
contains an `http` URL:
|
|
|
|
```yaml
|
|
dependencies:
|
|
example: any
|
|
repository: [!http://github.com/dart-lang/example!]
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Change the scheme of the URL to use a secure scheme, such as `https`:
|
|
|
|
```yaml
|
|
dependencies:
|
|
example: any
|
|
repository: https://github.com/dart-lang/example
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** Use secure urls in `pubspec.yaml`.
|
|
|
|
Use `https` instead of `http` or `git:`.
|
|
|
|
**BAD:**
|
|
```yaml
|
|
repository: http://github.com/dart-lang/example
|
|
```
|
|
|
|
```yaml
|
|
git:
|
|
url: git://github.com/dart-lang/example/example.git
|
|
```
|
|
|
|
**GOOD:**
|
|
```yaml
|
|
repository: https://github.com/dart-lang/example
|
|
```
|
|
simpleDirectivePaths:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use simple directive paths."
|
|
correctionMessage: "Try simplifying the directive path."
|
|
state:
|
|
stable: "3.12"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a directive uses a path that is
|
|
not simple (for example, a path containing redundant `./` or backtracking
|
|
`../` segments).
|
|
|
|
#### Example
|
|
|
|
Given a file named `bar.dart` in the `lib/src` directory:
|
|
|
|
```dart
|
|
%uri="lib/src/bar.dart"
|
|
class Bar {}
|
|
```
|
|
|
|
The following code produces this diagnostic because it uses an
|
|
unnecessarily complex relative URI:
|
|
|
|
```dart
|
|
import [!'./src/bar.dart'!];
|
|
|
|
void f(Bar b) {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use a minimal relative URI:
|
|
|
|
```dart
|
|
import 'src/bar.dart';
|
|
|
|
void f(Bar b) {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** simple directive paths.
|
|
|
|
Using simple directive paths (avoiding redundant `./` or backtracking `../`
|
|
segments) makes the code cleaner, more consistent, and easier to read. It
|
|
also reduces the risk of confusion when resolving paths relative to the
|
|
source file.
|
|
|
|
**BAD:**
|
|
```dart
|
|
import '../src/bar.dart';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
import 'bar.dart';
|
|
```
|
|
simplifyVariablePattern:
|
|
type: lint
|
|
parameters:
|
|
String memberName: The redundant member name.
|
|
String memberType: The type of the matched object. Whether a field, getter, or method.
|
|
problemMessage: "The #memberType identification '#memberName:' is redundant and can be removed."
|
|
correctionMessage: "Try removing the redundant #memberType identification."
|
|
state:
|
|
stable: "3.11"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a variable pattern includes a
|
|
redundant type member name.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the variable pattern
|
|
includes a redundant getter name:
|
|
|
|
```dart
|
|
void f(Object o) {
|
|
if (o case String([!length!]: var length)) {
|
|
print('string is $length characters long');
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the redundant member name:
|
|
|
|
```dart
|
|
void f(Object o) {
|
|
if (o case String(:var length)) {
|
|
print('string is $length characters long');
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** redundant member names in variable patterns.
|
|
|
|
When a variable pattern declares a variable with the same name as a member
|
|
of the matched type, the member name is redundant and can be omitted.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void f(Object o) {
|
|
if (o case String([!length!]: var length)) {
|
|
print('string is $length characters long');
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f(Object o) {
|
|
if (o case String(:var length)) {
|
|
print('string is $length characters long');
|
|
}
|
|
}
|
|
```
|
|
sizedBoxForWhitespace:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use a 'SizedBox' to add whitespace to a layout."
|
|
correctionMessage: "Try using a 'SizedBox' rather than a 'Container'."
|
|
state:
|
|
stable: "2.9"
|
|
categories: [flutter, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `Container` is created using
|
|
only the `height` and/or `width` arguments.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `Container` has
|
|
only the `width` argument:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget buildRow() {
|
|
return Row(
|
|
children: <Widget>[
|
|
const Text('...'),
|
|
[!Container!](
|
|
width: 4,
|
|
child: Text('...'),
|
|
),
|
|
const Expanded(
|
|
child: Text('...'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the `Container` with a `SizedBox` of the same dimensions:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget buildRow() {
|
|
return Row(
|
|
children: <Widget>[
|
|
Text('...'),
|
|
SizedBox(
|
|
width: 4,
|
|
child: Text('...'),
|
|
),
|
|
Expanded(
|
|
child: Text('...'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
Use `SizedBox` to add whitespace to a layout.
|
|
|
|
A `Container` is a heavier Widget than a `SizedBox`, and as bonus, `SizedBox`
|
|
has a `const` constructor.
|
|
|
|
**BAD:**
|
|
```dart
|
|
Widget buildRow() {
|
|
return Row(
|
|
children: <Widget>[
|
|
const MyLogo(),
|
|
Container(width: 4),
|
|
const Expanded(
|
|
child: Text('...'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Widget buildRow() {
|
|
return Row(
|
|
children: const <Widget>[
|
|
MyLogo(),
|
|
SizedBox(width: 4),
|
|
Expanded(
|
|
child: Text('...'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
```
|
|
sizedBoxShrinkExpand:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Use 'SizedBox.#p0' to avoid needing to specify the 'height' and 'width'."
|
|
correctionMessage: "Try using 'SizedBox.#p0' and removing the 'height' and 'width' arguments."
|
|
state:
|
|
stable: "2.16"
|
|
categories: [flutter, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `SizedBox` constructor
|
|
invocation specifies the values of both `height` and `width` as either
|
|
`0.0` or `double.infinity`.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because both the `height` and
|
|
`width` are `0.0`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget build() {
|
|
return [!SizedBox!](
|
|
height: 0.0,
|
|
width: 0.0,
|
|
child: const Text(''),
|
|
);
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because both the `height` and
|
|
`width` are `double.infinity`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget build() {
|
|
return [!SizedBox!](
|
|
height: double.infinity,
|
|
width: double.infinity,
|
|
child: const Text(''),
|
|
);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If both are `0.0`, then use `SizedBox.shrink`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget build() {
|
|
return SizedBox.shrink(
|
|
child: const Text(''),
|
|
);
|
|
}
|
|
```
|
|
|
|
If both are `double.infinity`, then use `SizedBox.expand`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget build() {
|
|
return SizedBox.expand(
|
|
child: const Text(''),
|
|
);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
Use `SizedBox.shrink(...)` and `SizedBox.expand(...)` constructors
|
|
appropriately.
|
|
|
|
Either the `SizedBox.shrink(...)` or `SizedBox.expand(...)` constructor should
|
|
be used instead of the more general `SizedBox(...)` constructor when one of the
|
|
named constructors capture the intent of the code more succinctly.
|
|
|
|
**Examples**
|
|
|
|
**BAD:**
|
|
```dart
|
|
Widget buildLogo() {
|
|
return SizedBox(
|
|
height: 0,
|
|
width: 0,
|
|
child: const MyLogo(),
|
|
);
|
|
}
|
|
```
|
|
|
|
```dart
|
|
Widget buildLogo() {
|
|
return SizedBox(
|
|
height: double.infinity,
|
|
width: double.infinity,
|
|
child: const MyLogo(),
|
|
);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Widget buildLogo() {
|
|
return SizedBox.shrink(
|
|
child: const MyLogo(),
|
|
);
|
|
}
|
|
```
|
|
|
|
```dart
|
|
Widget buildLogo() {
|
|
return SizedBox.expand(
|
|
child: const MyLogo(),
|
|
);
|
|
}
|
|
```
|
|
slashForDocComments:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use the end-of-line form ('///') for doc comments."
|
|
correctionMessage: "Try rewriting the comment to use '///'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a documentation comment uses
|
|
the block comment style (delimited by `/**` and `*/`).
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the documentation
|
|
comment for `f` uses a block comment style:
|
|
|
|
```dart
|
|
[!/**
|
|
* Example.
|
|
*/!]
|
|
void f() {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use an end-of-line comment style:
|
|
|
|
```dart
|
|
/// Example.
|
|
void f() {}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/documentation#do-use--doc-comments-to-document-members-and-types):
|
|
|
|
**DO** use `///` for documentation comments.
|
|
|
|
Although Dart supports two syntaxes of doc comments (`///` and `/**`), we
|
|
prefer using `///` for doc comments.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
/// Parses a set of option strings. For each option:
|
|
///
|
|
/// * If it is `null`, then it is ignored.
|
|
/// * If it is a string, then [validate] is called on it.
|
|
/// * If it is any other type, it is *not* validated.
|
|
void parse(List options) {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
Within a doc comment, you can use markdown for formatting.
|
|
sortChildPropertiesLast:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "The '#p0' argument should be last in widget constructor invocations."
|
|
correctionMessage: "Try moving the argument to the end of the argument list."
|
|
state:
|
|
stable: "2.4"
|
|
categories: [flutter, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the `child` or `children`
|
|
argument isn't the last argument in an invocation of a widget class'
|
|
constructor. An exception is made if all of the arguments after the
|
|
`child` or `children` argument are function expressions.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `child` argument
|
|
isn't the last argument in the invocation of the `Center` constructor:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget createWidget() {
|
|
return Center(
|
|
[!child: Text('...')!],
|
|
widthFactor: 0.5,
|
|
);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Move the `child` or `children` argument to be last:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget createWidget() {
|
|
return Center(
|
|
widthFactor: 0.5,
|
|
child: Text('...'),
|
|
);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
Sort child properties last in widget instance creations. This improves
|
|
readability and plays nicest with UI as Code visualization in IDEs with UI as
|
|
Code Guides in editors (such as IntelliJ) where Properties in the correct order
|
|
appear clearly associated with the constructor call and separated from the
|
|
children.
|
|
|
|
**BAD:**
|
|
```dart
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(widget.title),
|
|
),
|
|
body: Center(
|
|
child: Column(
|
|
children: <Widget>[
|
|
Text(
|
|
'You have pushed the button this many times:',
|
|
),
|
|
Text(
|
|
'$_counter',
|
|
style: Theme.of(context).textTheme.display1,
|
|
),
|
|
],
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
),
|
|
widthFactor: 0.5,
|
|
),
|
|
floatingActionButton: FloatingActionButton(
|
|
child: Icon(Icons.add),
|
|
onPressed: _incrementCounter,
|
|
tooltip: 'Increment',
|
|
),
|
|
);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(widget.title),
|
|
),
|
|
body: Center(
|
|
widthFactor: 0.5,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: <Widget>[
|
|
Text(
|
|
'You have pushed the button this many times:',
|
|
),
|
|
Text(
|
|
'$_counter',
|
|
style: Theme.of(context).textTheme.display1,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
floatingActionButton: FloatingActionButton(
|
|
onPressed: _incrementCounter,
|
|
tooltip: 'Increment',
|
|
child: Icon(Icons.add),
|
|
),
|
|
);
|
|
```
|
|
|
|
Exception: It's allowed to have parameter with a function expression after the
|
|
`child` property.
|
|
sortConstructorsFirst:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Constructor declarations should be before non-constructor declarations."
|
|
correctionMessage: "Try moving the constructor declaration before all other members."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a constructor declaration is
|
|
preceded by one or more non-constructor declarations.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the constructor for
|
|
`C` appears after the method `m`:
|
|
|
|
```dart
|
|
class C {
|
|
void m() {}
|
|
|
|
[!C!]();
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Move all of the constructor declarations before any other declarations:
|
|
|
|
```dart
|
|
class C {
|
|
C();
|
|
|
|
void m() {}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** sort constructor declarations before other members.
|
|
|
|
**BAD:**
|
|
```dart
|
|
abstract class Visitor {
|
|
double value;
|
|
visitSomething(Something s);
|
|
Visitor();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
abstract class Animation<T> {
|
|
const Animation(this.value);
|
|
double value;
|
|
void addListener(VoidCallback listener);
|
|
}
|
|
```
|
|
sortPubDependencies:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Dependencies not sorted alphabetically."
|
|
correctionMessage: "Try sorting the dependencies alphabetically (A to Z)."
|
|
state:
|
|
stable: "2.1"
|
|
categories: [pub]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the keys in a dependency map in
|
|
the `pubspec.yaml` file aren't sorted alphabetically. The dependency maps
|
|
that are checked are the `dependencies`, `dev_dependencies`, and
|
|
`dependency_overrides` maps.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the entries in the
|
|
`dependencies` map are not sorted:
|
|
|
|
```yaml
|
|
dependencies:
|
|
path: any
|
|
collection: any
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Sort the entries:
|
|
|
|
```yaml
|
|
dependencies:
|
|
collection: any
|
|
path: any
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** sort pub dependencies alphabetically (A to Z) in `pubspec.yaml`.
|
|
|
|
Sorting list of pub dependencies makes maintenance easier.
|
|
sortUnnamedConstructorsFirst:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Invalid location for the unnamed constructor."
|
|
correctionMessage: "Try moving the unnamed constructor before all other constructors."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an unnamed constructor appears
|
|
after a named constructor.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the unnamed
|
|
constructor is after the named constructor:
|
|
|
|
```dart
|
|
class C {
|
|
C.named();
|
|
|
|
[!C!]();
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Move the unnamed constructor before any other constructors:
|
|
|
|
```dart
|
|
class C {
|
|
C();
|
|
|
|
C.named();
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** sort unnamed constructor declarations first, before named ones.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class _PriorityItem {
|
|
factory _PriorityItem.forName(bool isStatic, String name, _MemberKind kind) => ...
|
|
_PriorityItem(this.isStatic, this.kind, this.isPrivate);
|
|
...
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
abstract class CancelableFuture<T> implements Future<T> {
|
|
factory CancelableFuture(computation()) => ...
|
|
factory CancelableFuture.delayed(Duration duration, [computation()]) => ...
|
|
...
|
|
}
|
|
```
|
|
specifyNonobviousLocalVariableTypes:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Specify the type of a local variable when the type is non-obvious."
|
|
correctionMessage: "Try adding a type annotation."
|
|
state:
|
|
experimental: "3.6"
|
|
stable: "3.11"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Do type annotate initialized local variables when
|
|
the type is [non-obvious](https://dart.dev/glossary/obviously-typed).
|
|
|
|
Type annotations on local variables can serve as a request for type inference,
|
|
documenting the expected outcome of the type inference step, and declaratively
|
|
allowing the compiler and analyzer to solve the possibly complex task of
|
|
finding type arguments and annotations in the initializing expression that
|
|
yield the desired result.
|
|
|
|
Type annotations on local variables can also inform readers about the type
|
|
of the initializing expression, which will allow them to proceed reading the
|
|
subsequent lines of code with known good information about the type of the
|
|
given variable (which may not be immediately evident by looking at the
|
|
initializing expression).
|
|
|
|
**BAD:**
|
|
```dart
|
|
List<List<Ingredient>> possibleDesserts(Set<Ingredient> pantry) {
|
|
var desserts = genericFunctionDeclaredFarAway(<num>[42], 'Something');
|
|
for (final recipe in cookbook) {
|
|
if (pantry.containsAll(recipe)) {
|
|
desserts.add(recipe);
|
|
}
|
|
}
|
|
|
|
return desserts;
|
|
}
|
|
|
|
const List<List<Ingredient>> cookbook = ...;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
List<List<Ingredient>> possibleDesserts(Set<Ingredient> pantry) {
|
|
List<List<Ingredient>> desserts = genericFunctionDeclaredFarAway(
|
|
<num>[42],
|
|
'Something',
|
|
);
|
|
for (final List<Ingredient> recipe in cookbook) {
|
|
if (pantry.containsAll(recipe)) {
|
|
desserts.add(recipe);
|
|
}
|
|
}
|
|
|
|
return desserts;
|
|
}
|
|
|
|
const List<List<Ingredient>> cookbook = ...;
|
|
```
|
|
specifyNonobviousPropertyTypes:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "A type annotation is needed because it isn't obvious."
|
|
correctionMessage: "Try adding a type annotation."
|
|
state:
|
|
experimental: "3.7"
|
|
stable: "3.11"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Do type annotate initialized top-level or static variables when
|
|
the type is [non-obvious](https://dart.dev/glossary/obviously-typed).
|
|
|
|
Type annotations on top-level or static variables can serve as a request for
|
|
type inference, documenting the expected outcome of the type inference step,
|
|
and declaratively allowing the compiler and analyzer to solve the possibly
|
|
complex task of finding type arguments and annotations in the initializing
|
|
expression that yield the desired result.
|
|
|
|
Type annotations on top-level or static variables can also inform readers about
|
|
the type of the initializing expression, which will allow them to proceed
|
|
reading the locations in code where this variable is used with known good
|
|
information about the type of the given variable (which may not be immediately
|
|
evident by looking at the initializing expression).
|
|
|
|
**BAD:**
|
|
```dart
|
|
final myTopLevelVariable =
|
|
genericFunctionWrittenByOtherFolks(with, args);
|
|
|
|
class A {
|
|
static var myStaticVariable =
|
|
myTopLevelVariable.update('foo', null);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
final Map<String, Widget?> myTopLevelVariable =
|
|
genericFunctionWrittenByOtherFolks(with, args);
|
|
|
|
class A {
|
|
static Map<String, Widget?> myStaticVariable =
|
|
myTopLevelVariable.update('foo', null);
|
|
}
|
|
```
|
|
strictTopLevelInferenceAddType:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: strictTopLevelInference
|
|
problemMessage: "Missing type annotation."
|
|
correctionMessage: "Try adding a type annotation."
|
|
state:
|
|
stable: "3.7"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Do type annotate top-level and class-like member declarations, where types
|
|
are not inferred from super-interfaces or initializers.
|
|
|
|
The lint warns about every omitted return type, parameter type, and
|
|
variable type of a top-level declaration or class-like-namespace-level
|
|
declaration (static or instance member or constructor declaration), which
|
|
is not given a type by inference, and which therefore defaults to dynamic.
|
|
|
|
The only omitted types that can be given a type by top-level inference,
|
|
are those of variable declarations with initializer expressions, and
|
|
return and parameter types of instance members that override a consistent
|
|
combined super-interface signature.
|
|
|
|
Setters do not need a return type, as it is always assumed to be `void`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var _zeroPointCache;
|
|
class Point {
|
|
get zero => ...;
|
|
final x, y;
|
|
Point(x, y) {}
|
|
closest(b, c) => distance(b) <= distance(c) ? b : c;
|
|
distance(other) => ...;
|
|
}
|
|
_sq(v) => v * v;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Point? _zeroPointCache;
|
|
class Point {
|
|
Point get zero => ...;
|
|
final int x, y;
|
|
Point(int x, int y) {}
|
|
closest(Point b, Point c) =>
|
|
distance(b) <= distance(c) ? b : c;
|
|
distance(Point other) => ...;
|
|
}
|
|
int _sq(int v) => v * v;
|
|
```
|
|
strictTopLevelInferenceReplaceKeyword:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: strictTopLevelInference
|
|
problemMessage: "Missing type annotation."
|
|
correctionMessage: "Try replacing '#p0' with a type annotation."
|
|
hasPublishedDocs: false
|
|
strictTopLevelInferenceSplitToTypes:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: strictTopLevelInference
|
|
problemMessage: "Missing type annotation."
|
|
correctionMessage: "Try splitting the declaration and specify the different type annotations."
|
|
hasPublishedDocs: false
|
|
superGoesLast:
|
|
type: lint
|
|
parameters: none
|
|
state:
|
|
stable: "2.0"
|
|
removed: "3.0"
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
NOTE: This rule is removed in Dart 3.0.0; it is no longer functional.
|
|
|
|
**DO** place the `super` call last in a constructor initialization list.
|
|
|
|
Field initializers are evaluated in the order that they appear in the
|
|
constructor initialization list. If you place a `super()` call in the middle of
|
|
an initializer list, the superclass's initializers will be evaluated right then
|
|
before evaluating the rest of the subclass's initializers.
|
|
|
|
What it doesn't mean is that the superclass's constructor body will be executed
|
|
then. That always happens after all initializers are run regardless of where
|
|
`super` appears. It's vanishingly rare that the order of initializers matters,
|
|
so the placement of `super` in the list almost never matters either.
|
|
|
|
Getting in the habit of placing it last improves consistency, visually
|
|
reinforces when the superclass's constructor body is run, and may help
|
|
performance.
|
|
|
|
**BAD:**
|
|
```dart
|
|
View(Style style, List children)
|
|
: super(style),
|
|
_children = children {
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
View(Style style, List children)
|
|
: _children = children,
|
|
super(style) {
|
|
```
|
|
switchOnType:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Avoid switch statements on a 'Type'."
|
|
correctionMessage: "Try using pattern matching on a variable instead."
|
|
state:
|
|
stable: "3.9"
|
|
categories: [unintentional, style, languageFeatureUsage, errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a switch statement or switch
|
|
expression is used on either the value of a `Type` or a `toString` call
|
|
on a `Type`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the switch statement
|
|
is used on a `Type`:
|
|
|
|
```dart
|
|
void f(Object o) {
|
|
switch ([!o.runtimeType!]) {
|
|
case const (int):
|
|
print('int');
|
|
case const (String):
|
|
print('String');
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use pattern matching on the variable instead:
|
|
|
|
```dart
|
|
void f(Object o) {
|
|
switch (o) {
|
|
case int():
|
|
print('int');
|
|
case String():
|
|
print('String');
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** using switch on `Type`.
|
|
|
|
Switching on `Type` is not type-safe and can lead to bugs if the
|
|
class hierarchy changes. Prefer to use pattern matching on the variable
|
|
instead.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void f(Object o) {
|
|
switch (o.runtimeType) {
|
|
case int:
|
|
print('int');
|
|
case String:
|
|
print('String');
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f(Object o) {
|
|
switch(o) {
|
|
case int():
|
|
print('int');
|
|
case String _:
|
|
print('String');
|
|
default:
|
|
print('other');
|
|
}
|
|
}
|
|
```
|
|
testTypesInEquals:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Missing type test for '#p0' in '=='."
|
|
correctionMessage: "Try testing the type of '#p0'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an override of the `==`
|
|
operator doesn't include a type test on the value of the parameter.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because `other` is not type
|
|
tested:
|
|
|
|
```dart
|
|
class C {
|
|
final int f;
|
|
|
|
C(this.f);
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
return ([!other as C!]).f == f;
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Perform an `is` test as part of computing the return value:
|
|
|
|
```dart
|
|
class C {
|
|
final int f;
|
|
|
|
C(this.f);
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
return other is C && other.f == f;
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** test type of argument in `operator ==(Object other)`.
|
|
|
|
Not testing the type might result in runtime type errors which will be
|
|
unexpected for consumers of your class.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Field {
|
|
}
|
|
|
|
class Bad {
|
|
final Field someField;
|
|
|
|
Bad(this.someField);
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
Bad otherBad = other as Bad; // LINT
|
|
bool areEqual = otherBad != null && otherBad.someField == someField;
|
|
return areEqual;
|
|
}
|
|
|
|
@override
|
|
int get hashCode {
|
|
return someField.hashCode;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Field {
|
|
}
|
|
|
|
class Good {
|
|
final Field someField;
|
|
|
|
Good(this.someField);
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (identical(this, other)) {
|
|
return true;
|
|
}
|
|
return other is Good &&
|
|
this.someField == other.someField;
|
|
}
|
|
|
|
@override
|
|
int get hashCode {
|
|
return someField.hashCode;
|
|
}
|
|
}
|
|
```
|
|
throwInFinally:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Use of '#p0' in 'finally' block."
|
|
correctionMessage: "Try moving the '#p0' outside the 'finally' block."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `throw` statement is found
|
|
inside a `finally` block.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because there is a `throw`
|
|
statement inside a `finally` block:
|
|
|
|
```dart
|
|
void f() {
|
|
try {
|
|
// ...
|
|
} catch (e) {
|
|
// ...
|
|
} finally {
|
|
[!throw 'error'!];
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rewrite the code so that the `throw` statement isn't inside a `finally`
|
|
block:
|
|
|
|
```dart
|
|
void f() {
|
|
try {
|
|
// ...
|
|
} catch (e) {
|
|
// ...
|
|
}
|
|
throw 'error';
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** throwing exceptions in `finally` blocks.
|
|
|
|
Throwing exceptions in `finally` blocks will inevitably cause unexpected
|
|
behavior that is hard to debug.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class BadThrow {
|
|
double nonCompliantMethod() {
|
|
try {
|
|
print('hello world! ${1 / 0}');
|
|
} catch (e) {
|
|
print(e);
|
|
} finally {
|
|
throw 'Find the hidden error :P'; // LINT
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Ok {
|
|
double compliantMethod() {
|
|
var i = 5;
|
|
try {
|
|
i = 1 / 0;
|
|
} catch (e) {
|
|
print(e); // OK
|
|
}
|
|
return i;
|
|
}
|
|
}
|
|
```
|
|
tightenTypeOfInitializingFormals:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use a type annotation rather than 'assert' to enforce non-nullability."
|
|
correctionMessage: "Try adding a type annotation and removing the 'assert'."
|
|
state:
|
|
stable: "2.12"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an `assert` is being used in
|
|
the initializer list of a constructor to ensure that only a non-`null`
|
|
value is being used to initialize a field.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because an `assert` is being
|
|
used to catch an error that could be caught by the type system:
|
|
|
|
```dart
|
|
class C {
|
|
final String? s;
|
|
|
|
C([!this.s!]) : assert(s != null);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the `assert` and add the non-nullable type before the initializing
|
|
formal:
|
|
|
|
```dart
|
|
class C {
|
|
final String? s;
|
|
|
|
C(String this.s);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
Tighten the type of an initializing formal if a non-null assert exists. This
|
|
allows the type system to catch problems rather than have them only be caught at
|
|
run-time.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
A.c1(this.p) : assert(p != null);
|
|
A.c2(this.p);
|
|
final String? p;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {
|
|
A.c1(String this.p);
|
|
A.c2(this.p);
|
|
final String? p;
|
|
}
|
|
|
|
class B {
|
|
String? b;
|
|
B(this.b);
|
|
}
|
|
|
|
class C extends B {
|
|
B(String super.b);
|
|
}
|
|
```
|
|
typeAnnotatePublicApis:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Missing type annotation on a public API."
|
|
correctionMessage: "Try adding a type annotation."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [effectiveDart, publicInterface]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the declaration of part of the
|
|
public API of a package doesn't have explicit type annotations.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the function `f`
|
|
doesn't have an explicit return type and the parameters `x` and `y` don't
|
|
have explicit types:
|
|
|
|
```dart
|
|
[!f!](x, y) => '';
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add type annotations to the API:
|
|
|
|
```dart
|
|
String f(int x, int y) => '';
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/design#do-type-annotate-fields-and-top-level-variables-if-the-type-isnt-obvious):
|
|
|
|
**PREFER** type annotating public APIs.
|
|
|
|
Type annotations are important documentation for how a library should be used.
|
|
Annotating the parameter and return types of public methods and functions helps
|
|
users understand what the API expects and what it provides.
|
|
|
|
Note that if a public API accepts a range of values that Dart's type system
|
|
cannot express, then it is acceptable to leave that untyped. In that case, the
|
|
implicit `dynamic` is the correct type for the API.
|
|
|
|
For code internal to a library (either private, or things like nested functions)
|
|
annotate where you feel it helps, but don't feel that you *must* provide them.
|
|
|
|
**BAD:**
|
|
```dart
|
|
install(id, destination) {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
Here, it's unclear what `id` is. A string? And what is `destination`? A string
|
|
or a `File` object? Is this method synchronous or asynchronous?
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Future<bool> install(PackageId id, String destination) {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
With types, all of this is clarified.
|
|
typeInitFormals:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't needlessly type annotate initializing formals."
|
|
correctionMessage: "Try removing the type."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an initializing formal
|
|
parameter (`this.x`) or a super parameter (`super.x`) has an explicit type
|
|
annotation that is the same as the field or overridden parameter.
|
|
|
|
If a constructor parameter is using `this.x` to initialize a field, then
|
|
the type of the parameter is implicitly the same type as the field. If a
|
|
constructor parameter is using `super.x` to forward to a super
|
|
constructor, then the type of the parameter is implicitly the same as the
|
|
super constructor parameter.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the parameter `this.c`
|
|
has an explicit type that is the same as the field `c`:
|
|
|
|
```dart
|
|
class C {
|
|
int c;
|
|
|
|
C([!int!] this.c);
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the parameter
|
|
`super.a` has an explicit type that is the same as the parameter `a` from
|
|
the superclass:
|
|
|
|
```dart
|
|
class A {
|
|
A(int a);
|
|
}
|
|
|
|
class B extends A {
|
|
B([!int!] super.a);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the type annotation from the parameter:
|
|
|
|
```dart
|
|
class C {
|
|
int c;
|
|
|
|
C(this.c);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/design#dont-type-annotate-initializing-formals):
|
|
|
|
**DON'T** type annotate initializing formals.
|
|
|
|
If a constructor parameter is using `this.x` to initialize a field, then the
|
|
type of the parameter is understood to be the same type as the field. If a
|
|
a constructor parameter is using `super.x` to forward to a super constructor,
|
|
then the type of the parameter is understood to be the same as the super
|
|
constructor parameter.
|
|
|
|
Type annotating an initializing formal with a different type than that of the
|
|
field is OK.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Point {
|
|
int x, y;
|
|
Point(int this.x, int this.y);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Point {
|
|
int x, y;
|
|
Point(this.x, this.y);
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
int a;
|
|
A(this.a);
|
|
}
|
|
|
|
class B extends A {
|
|
B(int super.a);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {
|
|
int a;
|
|
A(this.a);
|
|
}
|
|
|
|
class B extends A {
|
|
B(super.a);
|
|
}
|
|
```
|
|
typeLiteralInConstantPattern:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'TypeName _' instead of a type literal."
|
|
correctionMessage: "Replace with 'TypeName _'."
|
|
state:
|
|
stable: "3.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a type literal appears as a
|
|
pattern.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because a type literal is used
|
|
as a constant pattern:
|
|
|
|
```dart
|
|
void f(Object? x) {
|
|
if (x case [!num!]) {
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the type literal is intended to match an object of the given type, then
|
|
use either a variable pattern:
|
|
|
|
```dart
|
|
void f(Object? x) {
|
|
if (x case num _) {
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
Or an object pattern:
|
|
|
|
```dart
|
|
void f(Object? x) {
|
|
if (x case num()) {
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
If the type literal is intended to match the type literal, then write it
|
|
as a constant pattern:
|
|
|
|
```dart
|
|
void f(Object? x) {
|
|
if (x case const (num)) {
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
If you meant to test if the object has type `Foo`, instead write `Foo _`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void f(Object? x) {
|
|
if (x case num) {
|
|
print('int or double');
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f(Object? x) {
|
|
if (x case num _) {
|
|
print('int or double');
|
|
}
|
|
}
|
|
```
|
|
|
|
If you do mean to test that the matched value (which you expect to have the
|
|
type `Type`) is equal to the type literal `Foo`, then this lint can be
|
|
silenced using `const (Foo)`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void f(Object? x) {
|
|
if (x case int) {
|
|
print('int');
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f(Object? x) {
|
|
if (x case const (int)) {
|
|
print('int');
|
|
}
|
|
}
|
|
```
|
|
unawaitedFutures:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Missing an 'await' for the 'Future' computed by this expression."
|
|
correctionMessage: "Try adding an 'await' or wrapping the expression with 'unawaited'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic on an expression with a `Future`
|
|
type, only in a few specific cases:
|
|
|
|
* when the expression is itself a statement (like `f();`),
|
|
* when the expression is part of a cascade (like `C()..f()`),
|
|
* when the expression is a String interpolation (like `'${f()}'`).
|
|
|
|
The analyzer only produces this diagnostic on expressions inside an
|
|
`async` or `async*` function.
|
|
|
|
The two common corrections are to 'await' the expression, or to wrap the
|
|
expression in a call to `unawaited()`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the function `g`
|
|
returns a future, but the future isn't awaited:
|
|
|
|
```dart
|
|
Future<void> f() async {
|
|
[!g!]();
|
|
}
|
|
|
|
Future<int> g() => Future.value(0);
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the future needs to complete before the following code is executed,
|
|
then add an `await` before the invocation:
|
|
|
|
```dart
|
|
Future<void> f() async {
|
|
await g();
|
|
}
|
|
|
|
Future<int> g() => Future.value(0);
|
|
```
|
|
|
|
If the future doesn't need to complete before the following code is
|
|
executed, then wrap the `Future`-returning invocation in an invocation of
|
|
the `unawaited` function:
|
|
|
|
```dart
|
|
import 'dart:async';
|
|
|
|
Future<void> f() async {
|
|
unawaited(g());
|
|
}
|
|
|
|
Future<int> g() => Future.value(0);
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** await functions that return a `Future` inside of an async function body.
|
|
|
|
It's easy to forget await in async methods as naming conventions usually don't
|
|
tell us if a method is sync or async (except for some in `dart:io`).
|
|
|
|
When you really _do_ want to start a fire-and-forget `Future`, the recommended
|
|
way is to use `unawaited` from `dart:async`. The `// ignore` and
|
|
`// ignore_for_file` comments also work.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void main() async {
|
|
doSomething(); // Likely a bug.
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Future doSomething() => ...;
|
|
|
|
void main() async {
|
|
await doSomething();
|
|
|
|
unawaited(doSomething()); // Explicitly-ignored fire-and-forget.
|
|
}
|
|
```
|
|
unintendedHtmlInDocComment:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Angle brackets will be interpreted as HTML."
|
|
correctionMessage: "Try using backticks around the content with angle brackets, or try replacing `<` with `<` and `>` with `>`."
|
|
state:
|
|
stable: "3.5"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a documentation comment
|
|
contains angle bracketed text (`<...>`) that isn't one of the allowed
|
|
exceptions.
|
|
|
|
Such text is interpreted by markdown to be an HTML tag, which is rarely
|
|
what was intended.
|
|
|
|
See the [lint rule description](https://dart.dev/tools/linter-rules/unintended_html_in_doc_comment)
|
|
for the list of allowed exceptions.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the documentation
|
|
comment contains the text `<int>`, which isn't one of the allowed
|
|
exceptions:
|
|
|
|
```dart
|
|
/// Converts a List[!<int>!] to a comma-separated String.
|
|
String f(List<int> l) => '';
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the text was intended to be part of a code span, then add backticks
|
|
around the code:
|
|
|
|
```dart
|
|
/// Converts a `List<int>` to a comma-separated String.
|
|
String f(List<int> l) => '';
|
|
```
|
|
|
|
If the text was intended to be part of a link, then add square brackets
|
|
around the code:
|
|
|
|
```dart
|
|
/// Converts a [List<int>] to a comma-separated String.
|
|
String f(List<int> l) => '';
|
|
```
|
|
|
|
If the text was intended to be printed as-is, including the angle
|
|
brackets, then add backslash escapes before the angle brackets:
|
|
|
|
```dart
|
|
/// Converts a List\<int\> to a comma-separated String.
|
|
String f(List<int> l) => '';
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use angle-bracketed text, `<…>`, in a doc comment unless you want to
|
|
write an HTML tag or link.
|
|
|
|
Markdown allows HTML tags as part of the Markdown code, so you can write, for
|
|
example, `T<sub>1</sub>`. Markdown does not restrict the allowed tags, it just
|
|
includes the tags verbatim in the output.
|
|
|
|
Dartdoc only allows some known and valid HTML tags, and will omit any disallowed
|
|
HTML tag from the output. See the list of allowed tags and directives below.
|
|
Your doc comment should not contain any HTML tags that are not on this list.
|
|
|
|
Markdown also allows you to write an "auto-link" to an URL as for example
|
|
`<https://example.com/page.html>`, delimited only by `<...>`. Such a link is
|
|
allowed by Dartdoc as well.
|
|
A `<...>` delimited text is an auto-link if it is a valid absolute URL, starting
|
|
with a scheme of at least two characters followed by a colon, like
|
|
`<mailto:mr_example@example.com>`.
|
|
|
|
Any other other occurrence of `<word...>` or `</word...>` is likely a mistake
|
|
and this lint will warn about it.
|
|
If something looks like an HTML tag, meaning it starts with `<` or `</`
|
|
and then a letter, and it has a later matching `>`, then it's considered an
|
|
invalid HTML tag unless it is an auto-link, or it starts with an *allowed*
|
|
HTML tag.
|
|
|
|
Such a mistake can, for example, happen if writing Dart code with type arguments
|
|
outside of a code span, for example `The type List<int> is ...`, where `<int>`
|
|
looks like an HTML tag. Missing the end quote of a code span can have the same
|
|
effect: ``The type `List<int> is ...`` will also treat `<int>` as an HTML tag.
|
|
|
|
Allows the following HTML directives: HTML comments, `<!-- text -->`, processing
|
|
instructions, `<?...?>`, CDATA-sections, and `<[CDATA...]>`.
|
|
Allows DartDoc links like `[List<int>]` which are not after a `]` or before a
|
|
`[` or `(`, and allows the following recognized HTML tags:
|
|
`a`, `abbr`, `address`, `area`, `article`, `aside`, `audio`, `b`,
|
|
`bdi`, `bdo`, `blockquote`, `br`, `button`, `canvas`, `caption`,
|
|
`cite`, `code`, `col`, `colgroup`, `data`, `datalist`, `dd`, `del`,
|
|
`dfn`, `div`, `dl`, `dt`, `em`, `fieldset`, `figcaption`, `figure`,
|
|
`footer`, `form`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `header`, `hr`,
|
|
`i`, `iframe`, `img`, `input`, `ins`, `kbd`, `keygen`, `label`,
|
|
`legend`, `li`, `link`, `main`, `map`, `mark`, `meta`, `meter`, `nav`,
|
|
`noscript`, `object`, `ol`, `optgroup`, `option`, `output`, `p`,
|
|
`param`, `pre`, `progress`, `q`, `s`, `samp`, `script`, `section`,
|
|
`select`, `small`, `source`, `span`, `strong`, `style`, `sub`, `sup`,
|
|
`table`, `tbody`, `td`, `template`, `textarea`, `tfoot`, `th`, `thead`,
|
|
`time`, `title`, `tr`, `track`, `u`, `ul`, `var`, `video` and `wbr`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
/// The type List<int>.
|
|
/// <assignment> -> <variable> = <expression>
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
/// The type `List<int>`.
|
|
/// The type [List<int>]
|
|
/// `<assignment> -> <variable> = <expression>`
|
|
/// \<assignment\> -> \<variable\> = \<expression\>`
|
|
/// <https://example.com/example>
|
|
```
|
|
unnecessaryAsync:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Don't make a function 'async' if it doesn't use 'await'."
|
|
correctionMessage: "Try removing the 'async' modifier."
|
|
state:
|
|
experimental: "3.7"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a function, method, or
|
|
closure is marked `async` but doesn't contain any `await` expressions
|
|
or `await for` statements. The `async` modifier is unnecessary in these
|
|
cases and can be removed without changing the behavior of the code.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the function `f` is
|
|
marked `async` but doesn't use `await`:
|
|
|
|
```dart
|
|
void f() [!async!] {
|
|
print('done');
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the `async` modifier:
|
|
|
|
```dart
|
|
void f() {
|
|
print('done');
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
Functions that don't do `await` don't have to be `async`.
|
|
|
|
Usually such functions also don't have to return a `Future`, which allows
|
|
an invoker to avoid `await` in its code, etc. Synchronous code in
|
|
general runs faster, and is easier to reason about.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void f() async {
|
|
// await Future.delayed(const Duration(seconds: 2));
|
|
print(0);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void f() {
|
|
// await Future.delayed(const Duration(seconds: 2));
|
|
print(0);
|
|
}
|
|
```
|
|
unnecessaryAwaitInReturn:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary 'await'."
|
|
correctionMessage: "Try removing the 'await'."
|
|
state:
|
|
stable: "2.1"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an `async` function or method
|
|
returns an `await` expression and the static type of the
|
|
awaited `Future` is a [subtype][] of the declared return type.
|
|
|
|
The `await` is unnecessary because returning a `Future` from an
|
|
`async` function already produces a `Future` that
|
|
completes with the same result.
|
|
|
|
The analyzer doesn't report this diagnostic for `await` expressions
|
|
inside a `try` block because removing the `await` changes how
|
|
errors from the awaited `Future` are handled.
|
|
|
|
[subtype]: https://dart.dev/resources/glossary#subtype
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the function `f`
|
|
has a return type of `Future<int>` and returns an awaited `Future`
|
|
whose static type is also `Future<int>`:
|
|
|
|
```dart
|
|
Future<int> f(Future<int> future) async {
|
|
return [!await!] future;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the unnecessary `await`:
|
|
|
|
```dart
|
|
Future<int> f(Future<int> future) async {
|
|
return future;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
Avoid returning an awaited expression when the expression type is assignable to
|
|
the function's return type.
|
|
|
|
|
|
**BAD:**
|
|
```dart
|
|
Future<int> future;
|
|
Future<int> f1() async => await future;
|
|
Future<int> f2() async {
|
|
return await future;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Future<int> future;
|
|
Future<int> f1() => future;
|
|
Future<int> f2() {
|
|
return future;
|
|
}
|
|
```
|
|
unnecessaryBraceInStringInterps:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary braces in a string interpolation."
|
|
correctionMessage: "Try removing the braces."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a string interpolation with
|
|
braces is used to interpolate a simple identifier and isn't followed by
|
|
alphanumeric text.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the interpolation
|
|
element `${s}` uses braces when they are not necessary:
|
|
|
|
```dart
|
|
String f(String s) {
|
|
return '"[!${s}!]"';
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the unnecessary braces:
|
|
|
|
```dart
|
|
String f(String s) {
|
|
return '"$s"';
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** using braces in interpolation when not needed.
|
|
|
|
If you're just interpolating a simple identifier, and it's not immediately
|
|
followed by more alphanumeric text, the `{}` can and should be omitted.
|
|
|
|
**BAD:**
|
|
```dart
|
|
print("Hi, ${name}!");
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
print("Hi, $name!");
|
|
```
|
|
unnecessaryBreaks:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary 'break' statement."
|
|
correctionMessage: "Try removing the 'break'."
|
|
state:
|
|
stable: "3.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `break` statement appears
|
|
at the end of a non-empty switch case or default clause. In Dart 3.0
|
|
and later, switch cases don't fall through to the next case, so a
|
|
`break` at the end of a non-empty case body is redundant.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `break` is at
|
|
the end of a non-empty `case` body:
|
|
|
|
```dart
|
|
void f(int x) {
|
|
switch (x) {
|
|
case 1:
|
|
print('one');
|
|
[!break;!]
|
|
case 2:
|
|
print('two');
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the `break` statement:
|
|
|
|
```dart
|
|
void f(int x) {
|
|
switch (x) {
|
|
case 1:
|
|
print('one');
|
|
case 2:
|
|
print('two');
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
Only use a `break` in a non-empty switch case statement if you need to break
|
|
before the end of the case body. Dart does not support fallthrough execution
|
|
for non-empty cases, so `break`s at the end of non-empty switch case statements
|
|
are unnecessary.
|
|
|
|
**BAD:**
|
|
```dart
|
|
switch (1) {
|
|
case 1:
|
|
print("one");
|
|
break;
|
|
case 2:
|
|
print("two");
|
|
break;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
switch (1) {
|
|
case 1:
|
|
print("one");
|
|
case 2:
|
|
print("two");
|
|
}
|
|
```
|
|
|
|
```dart
|
|
switch (1) {
|
|
case 1:
|
|
case 2:
|
|
print("one or two");
|
|
}
|
|
```
|
|
|
|
```dart
|
|
switch (1) {
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
print("just two");
|
|
}
|
|
```
|
|
|
|
NOTE: This lint only reports unnecessary breaks in libraries with a
|
|
[language version](https://dart.dev/resources/language/evolution#language-versioning)
|
|
of 3.0 or greater. Explicit breaks are still required in Dart 2.19 and below.
|
|
unnecessaryConst:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary 'const' keyword."
|
|
correctionMessage: "Try removing the keyword."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the keyword `const` is used in
|
|
a [constant context][]. The keyword isn't required because it's implied.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the keyword `const` in
|
|
the list literal isn't needed:
|
|
|
|
```dart
|
|
const l = [!const!] <int>[];
|
|
```
|
|
|
|
The list is implicitly `const` because of the keyword `const` on the
|
|
variable declaration.
|
|
|
|
#### Common fixes
|
|
|
|
Remove the unnecessary keyword:
|
|
|
|
```dart
|
|
const l = <int>[];
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** repeating `const` keyword in a `const` context.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A { const A(); }
|
|
m(){
|
|
const a = const A();
|
|
final b = const [const A()];
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A { const A(); }
|
|
m(){
|
|
const a = A();
|
|
final b = const [A()];
|
|
}
|
|
```
|
|
unnecessaryConstInEnumConstructor:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary 'const' keyword in an enum constructor."
|
|
correctionMessage: "Try removing the keyword."
|
|
state:
|
|
experimental: "3.13"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the keyword `const` is used in
|
|
a generative enum constructor. Generative enum constructors are implicitly
|
|
`const`.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the keyword `const` in
|
|
the enum's primary constructor isn't needed:
|
|
|
|
```dart
|
|
%experiments=primary-constructors
|
|
enum [!const!] E(final int i) {
|
|
a(1), b(2);
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the keyword `const` in
|
|
the enum's secondary constructor isn't needed:
|
|
|
|
```dart
|
|
%experiments=primary-constructors
|
|
enum E {
|
|
a(1), b(2);
|
|
|
|
[!const!] E(this.i);
|
|
|
|
final int i;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the unnecessary keyword:
|
|
|
|
```dart
|
|
%experiments=primary-constructors
|
|
enum E(final int i) {
|
|
a(1), b(2);
|
|
}
|
|
```
|
|
|
|
```dart
|
|
%experiments=primary-constructors
|
|
enum E {
|
|
a(1), b(2);
|
|
|
|
E(this.i);
|
|
|
|
final int i;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
Don't use an explicit `const` in a generative enum constructor. Generative
|
|
enum constructors are implicitly `const`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
enum const E(final int i) {
|
|
a(1), b(2);
|
|
}
|
|
```
|
|
|
|
```dart
|
|
enum E {
|
|
a(1), b(2);
|
|
|
|
const E(this.i);
|
|
|
|
final int i;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
enum E(final int i) {
|
|
a(1), b(2);
|
|
}
|
|
```
|
|
|
|
```dart
|
|
enum E {
|
|
a(1), b(2);
|
|
|
|
E(this.i);
|
|
|
|
final int i;
|
|
}
|
|
```
|
|
unnecessaryConstructorName:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary '.new' constructor name."
|
|
correctionMessage: "Try removing the '.new'."
|
|
state:
|
|
stable: "2.15"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a reference to an unnamed
|
|
constructor uses `.new`. The only place where `.new` is required is in a
|
|
constructor tear-off.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because `.new` is being used
|
|
to refer to the unnamed constructor where it isn't required:
|
|
|
|
```dart
|
|
var o = Object.[!new!]();
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the unnecessary `.new`:
|
|
|
|
```dart
|
|
var o = Object();
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** using the default unnamed Constructor over `.new`.
|
|
|
|
Given a class `C`, the named unnamed constructor `C.new` refers to the same
|
|
constructor as the unnamed `C`. As such it adds nothing but visual noise to
|
|
invocations and should be avoided (unless being used to identify a constructor
|
|
tear-off).
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
A.new(); // LINT
|
|
}
|
|
|
|
var a = A.new(); // LINT
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {
|
|
A.ok();
|
|
}
|
|
|
|
var a = A();
|
|
var aa = A.ok();
|
|
var makeA = A.new;
|
|
```
|
|
unnecessaryFinalWithType:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: unnecessaryFinal
|
|
problemMessage: "Local variables should not be marked as 'final'."
|
|
correctionMessage: "Remove the 'final'."
|
|
state:
|
|
stable: "2.7"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a local variable is marked as
|
|
being `final`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the local variable `c`
|
|
is marked as being `final`:
|
|
|
|
```dart
|
|
void f(int a, int b) {
|
|
[!final!] c = a + b;
|
|
print(c);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the variable doesn't have a type annotation, then replace the `final`
|
|
with `var`:
|
|
|
|
```dart
|
|
void f(int a, int b) {
|
|
var c = a + b;
|
|
print(c);
|
|
}
|
|
```
|
|
|
|
If the variable has a type annotation, then remove the `final`
|
|
modifier:
|
|
|
|
```dart
|
|
void f(int a, int b) {
|
|
int c = a + b;
|
|
print(c);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
Use `var`, not `final`, when declaring local variables.
|
|
|
|
Per [Effective Dart](https://dart.dev/effective-dart/usage#do-follow-a-consistent-rule-for-var-and-final-on-local-variables),
|
|
there are two styles in wide use. This rule enforces the `var` style.
|
|
For the alternative style that prefers `final`, enable `prefer_final_locals`
|
|
and `prefer_final_in_for_each` instead.
|
|
|
|
For fields, `final` is always recommended; see the rule `prefer_final_fields`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void badMethod() {
|
|
final label = 'Final or var?';
|
|
for (final char in ['v', 'a', 'r']) {
|
|
print(char);
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void goodMethod() {
|
|
var label = 'Final or var?';
|
|
for (var char in ['v', 'a', 'r']) {
|
|
print(char);
|
|
}
|
|
}
|
|
```
|
|
unnecessaryFinalWithoutType:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: unnecessaryFinal
|
|
problemMessage: "Local variables should not be marked as 'final'."
|
|
correctionMessage: "Replace 'final' with 'var'."
|
|
hasPublishedDocs: false
|
|
unnecessaryGettersSetters:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of getter and setter to wrap a field."
|
|
correctionMessage: "Try removing the getter and setter and renaming the field."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a getter and setter pair
|
|
returns and sets the value of a field without any additional processing.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the getter/setter pair
|
|
named `c` only expose the field named `_c`:
|
|
|
|
```dart
|
|
class C {
|
|
int? _c;
|
|
|
|
int? get [!c!] => _c;
|
|
|
|
set c(int? v) => _c = v;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Make the field public and remove the getter and setter:
|
|
|
|
```dart
|
|
class C {
|
|
int? c;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/usage#dont-wrap-a-field-in-a-getter-and-setter-unnecessarily):
|
|
|
|
**AVOID** wrapping fields in getters and setters just to be "safe".
|
|
|
|
In Java and C#, it's common to hide all fields behind getters and setters (or
|
|
properties in C#), even if the implementation just forwards to the field. That
|
|
way, if you ever need to do more work in those members, you can do it without needing
|
|
to touch the callsites. This is because calling a getter method is different
|
|
than accessing a field in Java, and accessing a property isn't binary-compatible
|
|
with accessing a raw field in C#.
|
|
|
|
Dart doesn't have this limitation. Fields and getters/setters are completely
|
|
indistinguishable. You can expose a field in a class and later wrap it in a
|
|
getter and setter without having to touch any code that uses that field.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Box {
|
|
var _contents;
|
|
get contents => _contents;
|
|
set contents(value) {
|
|
_contents = value;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Box {
|
|
var contents;
|
|
}
|
|
```
|
|
unnecessaryIgnore:
|
|
type: lint
|
|
parameters:
|
|
String name: The diagnostic name.
|
|
sharedName: unnecessaryIgnore
|
|
problemMessage: "The diagnostic '#name' isn't produced at this location so it doesn't need to be ignored."
|
|
correctionMessage: "Try removing the ignore comment."
|
|
state:
|
|
experimental: "3.8"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an ignore is specified to
|
|
ignore a diagnostic that isn't produced.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the
|
|
`unused_local_variable` diagnostic isn't reported at the ignored location:
|
|
|
|
```dart
|
|
// ignore: [!unused_local_variable!]
|
|
void f() {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the ignore comment:
|
|
|
|
```dart
|
|
void f() {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** specify an ignore for a diagnostic that is not produced.
|
|
unnecessaryIgnoreFile:
|
|
type: lint
|
|
parameters:
|
|
String name: The diagnostic name.
|
|
sharedName: unnecessaryIgnore
|
|
problemMessage: "The diagnostic '#name' isn't produced in this file so it doesn't need to be ignored."
|
|
correctionMessage: "Try removing the ignore comment."
|
|
hasPublishedDocs: false
|
|
unnecessaryIgnoreName:
|
|
type: lint
|
|
parameters:
|
|
String name: The diagnostic name.
|
|
sharedName: unnecessaryIgnore
|
|
problemMessage: "The diagnostic '#name' isn't produced at this location so it doesn't need to be ignored."
|
|
correctionMessage: "Try removing the name from the list."
|
|
hasPublishedDocs: false
|
|
unnecessaryIgnoreNameFile:
|
|
type: lint
|
|
parameters:
|
|
String name: The diagnostic name.
|
|
sharedName: unnecessaryIgnore
|
|
problemMessage: "The diagnostic '#name' isn't produced in this file so it doesn't need to be ignored."
|
|
correctionMessage: "Try removing the name from the list."
|
|
hasPublishedDocs: false
|
|
unnecessaryLambdas:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Closure should be a tearoff."
|
|
correctionMessage: "Try using a tearoff rather than a closure."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a closure (lambda) could be
|
|
replaced by a tear-off.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the closure passed to
|
|
`forEach` contains only an invocation of the function `print` with the
|
|
parameter of the closure:
|
|
|
|
```dart
|
|
void f(List<String> strings) {
|
|
strings.forEach([!(string) {
|
|
print(string);
|
|
}!]);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the closure with a tear-off of the function or method being
|
|
invoked with the closure:
|
|
|
|
```dart
|
|
void f(List<String> strings) {
|
|
strings.forEach(print);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** create a lambda when a tear-off will do.
|
|
|
|
**BAD:**
|
|
```dart
|
|
names.forEach((name) {
|
|
print(name);
|
|
});
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
names.forEach(print);
|
|
```
|
|
unnecessaryLate:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary 'late' modifier."
|
|
correctionMessage: "Try removing the 'late'."
|
|
state:
|
|
stable: "2.16"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a top-level variable or static
|
|
field with an initializer is marked as `late`. Top-level variables and
|
|
static fields are implicitly late, so they don't need to be explicitly
|
|
marked.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the static field `c`
|
|
has the modifier `late` even though it has an initializer:
|
|
|
|
```dart
|
|
class C {
|
|
static [!late!] String c = '';
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the keyword `late`:
|
|
|
|
```dart
|
|
class C {
|
|
static String c = '';
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** not specify the `late` modifier for top-level and static variables
|
|
when the declaration contains an initializer.
|
|
|
|
Top-level and static variables with initializers are already evaluated lazily
|
|
as if they are marked `late`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
late String badTopLevel = '';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
String goodTopLevel = '';
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
class BadExample {
|
|
static late String badStatic = '';
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class GoodExample {
|
|
late String goodStatic;
|
|
}
|
|
```
|
|
unnecessaryLibraryDirective:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Library directives without comments or annotations should be avoided."
|
|
correctionMessage: "Try deleting the library directive."
|
|
state:
|
|
stable: "2.19"
|
|
categories: [brevity]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `library` directive
|
|
doesn't have a documentation comment or any annotations.
|
|
|
|
If no documentation comment or annotation precedes the library directive,
|
|
then either it's unnecessary or the documentation comment and/or
|
|
annotations are misplaced and should be moved.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the `library`
|
|
directive doesn't have a documentation comment or annotation:
|
|
|
|
```dart
|
|
[!library;!]
|
|
|
|
class C {}
|
|
```
|
|
|
|
The following code produces this diagnostic because the library's
|
|
documentation comment is below the `library` directive:
|
|
|
|
```dart
|
|
[!library;!]
|
|
|
|
/// This library provides the [C] class.
|
|
|
|
/// The [C] class.
|
|
class C {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If you don't want to add a documentation comment or annotation
|
|
to the library, then remove the `library` directive:
|
|
|
|
```dart
|
|
class C {}
|
|
```
|
|
|
|
If you intend to add a documentation comment or annotation,
|
|
then add or move it to the `library` directive:
|
|
|
|
```dart
|
|
/// This library provides the [C] class.
|
|
library;
|
|
|
|
/// The [C] class.
|
|
class C {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use library directives if you want to document a library and/or annotate
|
|
a library.
|
|
|
|
**BAD:**
|
|
```dart
|
|
library;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
/// This library does important things
|
|
library;
|
|
```
|
|
|
|
```dart
|
|
@TestOn('js')
|
|
library;
|
|
```
|
|
|
|
NOTE: Due to limitations with this lint, libraries with parts will not be
|
|
flagged for unnecessary library directives.
|
|
unnecessaryLibraryName:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Library names are not necessary."
|
|
correctionMessage: "Remove the library name."
|
|
state:
|
|
stable: "3.4"
|
|
categories: [brevity, languageFeatureUsage, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `library` directive specifies
|
|
a name.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `library`
|
|
directive includes a name:
|
|
|
|
```dart
|
|
library [!some.name!];
|
|
|
|
class C {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the name from the `library` directive:
|
|
|
|
```dart
|
|
library;
|
|
|
|
class C {}
|
|
```
|
|
|
|
If the library has any parts, then any `part of` declarations that use
|
|
the library name should be updated to use the URI of the library instead.
|
|
deprecatedDetails: |-
|
|
**DON'T** have a library name in a `library` declaration.
|
|
|
|
Library names are not necessary.
|
|
|
|
A library does not need a library declaration, but one can be added to attach
|
|
library documentation and library metadata to. A declaration of `library;` is
|
|
sufficient for those uses.
|
|
|
|
The only *use* of a library name is for a `part` file to refer back to its
|
|
owning library, but part files should prefer to use a string URI to refer back
|
|
to the library file, not a library name.
|
|
|
|
If a library name is added to a library declaration, it introduces the risk of
|
|
name *conflicts*. It's a compile-time error if two libraries in the same program
|
|
have the same library name. To avoid that, library names tend to be long,
|
|
including the package name and path, just to avoid accidental name clashes. That
|
|
makes such library names hard to read, and not even useful as documentation.
|
|
|
|
**BAD:**
|
|
```dart
|
|
/// This library has a long name.
|
|
library magnificator.src.helper.bananas;
|
|
```
|
|
|
|
```dart
|
|
library utils; // Not as verbose, but risks conflicts.
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
/// This library is awesome.
|
|
library;
|
|
|
|
part "apart.dart"; // contains: `part of "good_library.dart";`
|
|
```
|
|
unnecessaryNew:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary 'new' keyword."
|
|
correctionMessage: "Try removing the 'new' keyword."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, languageFeatureUsage, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the keyword `new` is used to
|
|
invoke a constructor.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the keyword `new` is
|
|
used to invoke the unnamed constructor from `Object`:
|
|
|
|
```dart
|
|
var o = [!new!] Object();
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the keyword `new`:
|
|
|
|
```dart
|
|
var o = Object();
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** new keyword to create instances.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A { A(); }
|
|
m(){
|
|
final a = new A();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A { A(); }
|
|
m(){
|
|
final a = A();
|
|
}
|
|
```
|
|
unnecessaryNullAwareAssignments:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary assignment of 'null'."
|
|
correctionMessage: "Try removing the assignment."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the right-hand side of a
|
|
null-aware assignment is the `null` literal.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the null aware
|
|
operator is being used to assign `null` to `s` when `s` is already `null`:
|
|
|
|
```dart
|
|
void f(String? s) {
|
|
[!s ??= null!];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If a non-null value should be assigned to the left-hand operand, then
|
|
change the right-hand side:
|
|
|
|
```dart
|
|
void f(String? s) {
|
|
s ??= '';
|
|
}
|
|
```
|
|
|
|
If there is no non-null value to assign to the left-hand operand, then
|
|
remove the assignment:
|
|
|
|
```dart
|
|
void f(String? s) {
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** `null` in `null`-aware assignment.
|
|
|
|
Using `null` on the right-hand side of a `null`-aware assignment effectively
|
|
makes the assignment redundant.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var x;
|
|
x ??= null;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var x;
|
|
x ??= 1;
|
|
```
|
|
unnecessaryNullAwareOperatorOnExtensionOnNullable:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of a null-aware operator to invoke an extension method on a nullable type."
|
|
correctionMessage: "Try removing the '?'."
|
|
state:
|
|
stable: "2.18"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a null-aware operator is used
|
|
to invoke an extension method on an extension whose type is nullable.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the extension method
|
|
`m` is invoked using `?.` when it doesn't need to be:
|
|
|
|
```dart
|
|
extension E on int? {
|
|
int m() => 1;
|
|
}
|
|
|
|
int? f(int? i) => i[!?.!]m();
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If it isn't a requirement not invoke the method when the receiver is
|
|
`null`, then remove the question mark from the invocation:
|
|
|
|
```dart
|
|
extension E on int? {
|
|
int m() => 1;
|
|
}
|
|
|
|
int? f(int? i) => i.m();
|
|
```
|
|
deprecatedDetails: |-
|
|
Avoid null aware operators for members defined in an extension on a nullable
|
|
type.
|
|
|
|
**BAD:**
|
|
|
|
```dart
|
|
extension E on int? {
|
|
int m() => 1;
|
|
}
|
|
f(int? i) => i?.m();
|
|
```
|
|
|
|
**GOOD:**
|
|
|
|
```dart
|
|
extension E on int? {
|
|
int m() => 1;
|
|
}
|
|
f(int? i) => i.m();
|
|
```
|
|
unnecessaryNullChecks:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of a null check ('!')."
|
|
correctionMessage: "Try removing the null check."
|
|
state:
|
|
experimental: "2.12"
|
|
stable: "3.13"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a null check operator (`!`) is
|
|
used in a context where a nullable value is acceptable.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because a null check is being
|
|
used even though `null` is a valid value to return:
|
|
|
|
```dart
|
|
int? f(int? i) {
|
|
return i[!!!];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the null check operator:
|
|
|
|
```dart
|
|
int? f(int? i) {
|
|
return i;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** apply a `null` check where a nullable value is accepted.
|
|
|
|
**BAD:**
|
|
```dart
|
|
f(int? i) {}
|
|
m() {
|
|
int? j;
|
|
f(j!);
|
|
}
|
|
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
f(int? i) {}
|
|
m() {
|
|
int? j;
|
|
f(j);
|
|
}
|
|
```
|
|
unnecessaryNullInIfNullOperators:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of '??' with 'null'."
|
|
correctionMessage: "Try removing the '??' operator and the 'null' operand."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the right operand of the `??`
|
|
operator is the literal `null`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the right-hand operand
|
|
of the `??` operator is `null`:
|
|
|
|
```dart
|
|
String? f(String? s) => s ?? [!null!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If a non-null value should be used for the right-hand operand, then
|
|
change the right-hand side:
|
|
|
|
```dart
|
|
String f(String? s) => s ?? '';
|
|
```
|
|
|
|
If there is no non-null value to use for the right-hand operand, then
|
|
remove the operator and the right-hand operand:
|
|
|
|
```dart
|
|
String? f(String? s) => s;
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** using `null` as an operand in `??` operators.
|
|
|
|
Using `null` in an `if null` operator is redundant, regardless of which side
|
|
`null` is used on.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var x = a ?? null;
|
|
var y = null ?? 1;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var x = a ?? 1;
|
|
```
|
|
unnecessaryNullableForFinalVariableDeclarations:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Type could be non-nullable."
|
|
correctionMessage: "Try changing the type to be non-nullable."
|
|
state:
|
|
stable: "2.10"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a final field or variable has a
|
|
nullable type but is initialized to a non-nullable value.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the final variable `i`
|
|
has a nullable type (`int?`), but can never be `null`:
|
|
|
|
```dart
|
|
final int? [!i!] = 1;
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Make the type non-nullable:
|
|
|
|
```dart
|
|
final int i = 1;
|
|
```
|
|
deprecatedDetails: |-
|
|
Use a non-nullable type for a final variable initialized with a non-nullable
|
|
value.
|
|
|
|
**BAD:**
|
|
```dart
|
|
final int? i = 1;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
final int i = 1;
|
|
```
|
|
unnecessaryOverrides:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary override."
|
|
correctionMessage: "Try adding behavior in the overriding member or removing the override."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an instance member overrides an
|
|
inherited member but only invokes the overridden member with exactly the
|
|
same arguments.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the method `D.m`
|
|
doesn't do anything other than invoke the overridden method:
|
|
|
|
```dart
|
|
class C {
|
|
int m(int x) => x;
|
|
}
|
|
|
|
class D extends C {
|
|
@override
|
|
int [!m!](int x) => super.m(x);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the method should do something more than what the overridden method
|
|
does, then implement the missing functionality:
|
|
|
|
```dart
|
|
class C {
|
|
int m(int x) => x;
|
|
}
|
|
|
|
class D extends C {
|
|
@override
|
|
int m(int x) => super.m(x) + 1;
|
|
}
|
|
```
|
|
|
|
If the overridden method should be modified by changing the return type or
|
|
one or more of the parameter types, making one of the parameters
|
|
`covariant`, having a documentation comment, or by having additional
|
|
annotations, then update the code:
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
class C {
|
|
int m(int x) => x;
|
|
}
|
|
|
|
class D extends C {
|
|
@mustCallSuper
|
|
@override
|
|
int m(int x) => super.m(x);
|
|
}
|
|
```
|
|
|
|
If the overriding method doesn't change or enhance the semantics of the
|
|
code, then remove it:
|
|
|
|
```dart
|
|
class C {
|
|
int m(int x) => x;
|
|
}
|
|
|
|
class D extends C {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** override a method to do a super method invocation with same parameters.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A extends B {
|
|
@override
|
|
void foo() {
|
|
super.foo();
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A extends B {
|
|
@override
|
|
void foo() {
|
|
doSomethingElse();
|
|
}
|
|
}
|
|
```
|
|
|
|
It's valid to override a member in the following cases:
|
|
|
|
* if a type (return type or a parameter type) is not the exactly the same as the
|
|
super member,
|
|
* if the `covariant` keyword is added to one of the parameters,
|
|
* if documentation comments are present on the member,
|
|
* if the member has annotations other than `@override`,
|
|
* if the member is not annotated with `@protected`, and the super member is.
|
|
|
|
`noSuchMethod` is a special method and is not checked by this rule.
|
|
unnecessaryParenthesis:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of parentheses."
|
|
correctionMessage: "Try removing the parentheses."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when parentheses are used where they
|
|
do not affect the semantics of the code.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the parentheses around
|
|
the binary expression are not necessary:
|
|
|
|
```dart
|
|
int f(int a, int b) => [!(a + b)!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the unnecessary parentheses:
|
|
|
|
```dart
|
|
int f(int a, int b) => a + b;
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** using parentheses when not needed.
|
|
|
|
**BAD:**
|
|
```dart
|
|
a = (b);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
a = b;
|
|
```
|
|
|
|
Parentheses are considered unnecessary if they do not change the meaning of the
|
|
code and they do not improve the readability of the code. The goal is not to
|
|
force all developers to maintain the expression precedence table in their heads,
|
|
which is why the second condition is included. Examples of this condition
|
|
include:
|
|
|
|
* cascade expressions - it is sometimes not clear what the target of a cascade
|
|
expression is, especially with assignments, or nested cascades. For example,
|
|
the expression `a.b = (c..d)`.
|
|
* expressions with whitespace between tokens - it can look very strange to see
|
|
an expression like `!await foo` which is valid and equivalent to
|
|
`!(await foo)`.
|
|
* logical expressions - parentheses can improve the readability of the implicit
|
|
grouping defined by precedence. For example, the expression
|
|
`(a && b) || c && d`.
|
|
unnecessaryPrimaryConstructorBody:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary primary constructor body."
|
|
correctionMessage: "Try removing the body."
|
|
state:
|
|
experimental: "3.13"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Don't include an empty primary constructor body.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class C() {
|
|
this;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class C();
|
|
```
|
|
unnecessaryRawStrings:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of a raw string."
|
|
correctionMessage: "Try using a normal string."
|
|
state:
|
|
stable: "2.8"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a string literal is marked as
|
|
being raw (is prefixed with an `r`), but making the string raw doesn't
|
|
change the value of the string.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the string literal
|
|
will have the same value without the `r` as it does with the `r`:
|
|
|
|
```dart
|
|
var s = [!r'abc'!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the `r` in front of the string literal:
|
|
|
|
```dart
|
|
var s = 'abc';
|
|
```
|
|
deprecatedDetails: |-
|
|
Use raw string only when needed.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var s1 = r'a';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var s1 = 'a';
|
|
var s2 = r'$a';
|
|
var s3 = r'\a';
|
|
```
|
|
unnecessaryStatements:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary statement."
|
|
correctionMessage: "Try completing the statement or breaking it up."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when an expression statement has no
|
|
clear effect.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the addition of the
|
|
returned values from the two invocations has no clear effect:
|
|
|
|
```dart
|
|
void f(int Function() first, int Function() second) {
|
|
[!first() + second()!];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the expression doesn't need to be computed, then remove it:
|
|
|
|
```dart
|
|
void f(int Function() first, int Function() second) {
|
|
}
|
|
```
|
|
|
|
If the value of the expression is needed, then make use of it, possibly
|
|
assigning it to a local variable first:
|
|
|
|
```dart
|
|
void f(int Function() first, int Function() second) {
|
|
print(first() + second());
|
|
}
|
|
```
|
|
|
|
If portions of the expression need to be executed, then remove the
|
|
unnecessary portions:
|
|
|
|
```dart
|
|
void f(int Function() first, int Function() second) {
|
|
first();
|
|
second();
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** using unnecessary statements.
|
|
|
|
Statements which have no clear effect are usually unnecessary, or should be
|
|
broken up.
|
|
|
|
For example,
|
|
|
|
**BAD:**
|
|
```dart
|
|
myvar;
|
|
list.clear;
|
|
1 + 2;
|
|
methodOne() + methodTwo();
|
|
foo ? bar : baz;
|
|
```
|
|
|
|
Though the added methods have a clear effect, the addition itself does not
|
|
unless there is some magical overload of the + operator.
|
|
|
|
Usually code like this indicates an incomplete thought, and is a bug.
|
|
|
|
**GOOD:**
|
|
```dart
|
|
some.method();
|
|
const SomeClass();
|
|
methodOne();
|
|
methodTwo();
|
|
foo ? bar() : baz();
|
|
return myvar;
|
|
```
|
|
unnecessaryStringEscapes:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary escape in string literal."
|
|
correctionMessage: "Remove the '\\' escape."
|
|
state:
|
|
stable: "2.8"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when characters in a string are
|
|
escaped when escaping them is unnecessary.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because single quotes don't
|
|
need to be escaped inside strings delimited by double quotes:
|
|
|
|
```dart
|
|
var s = "Don[!\!]'t use a backslash here.";
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the unnecessary backslashes:
|
|
|
|
```dart
|
|
var s = "Don't use a backslash here.";
|
|
```
|
|
deprecatedDetails: |-
|
|
Remove unnecessary backslashes in strings.
|
|
|
|
**BAD:**
|
|
```dart
|
|
'this string contains 2 \"double quotes\" ';
|
|
"this string contains 2 \'single quotes\' ";
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
'this string contains 2 "double quotes" ';
|
|
"this string contains 2 'single quotes' ";
|
|
```
|
|
unnecessaryStringInterpolations:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of string interpolation."
|
|
correctionMessage: "Try replacing the string literal with the variable name."
|
|
state:
|
|
stable: "2.8"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a string literal contains a
|
|
single interpolation of a `String`-valued variable and no other
|
|
characters.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the string literal
|
|
contains a single interpolation and doesn't contain any character outside
|
|
the interpolation:
|
|
|
|
```dart
|
|
String f(String s) => [!'$s'!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the string literal with the content of the interpolation:
|
|
|
|
```dart
|
|
String f(String s) => s;
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use string interpolation if there's only a string expression in it.
|
|
|
|
**BAD:**
|
|
```dart
|
|
String message;
|
|
String o = '$message';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
String message;
|
|
String o = message;
|
|
```
|
|
unnecessaryThis:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary 'this.' qualifier."
|
|
correctionMessage: "Try removing 'this.'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the keyword `this` is used to
|
|
access a member that isn't shadowed.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the use of `this` to
|
|
access the field `_f` isn't necessary:
|
|
|
|
```dart
|
|
class C {
|
|
int _f = 2;
|
|
|
|
int get f => [!this!]._f;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the `this.`:
|
|
|
|
```dart
|
|
class C {
|
|
int _f = 2;
|
|
|
|
int get f => _f;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/usage#dont-use-this-when-not-needed-to-avoid-shadowing):
|
|
|
|
**DON'T** use `this` when not needed to avoid shadowing.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Box {
|
|
int value;
|
|
void update(int newValue) {
|
|
this.value = newValue;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Box {
|
|
int value;
|
|
void update(int newValue) {
|
|
value = newValue;
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Box {
|
|
int value;
|
|
void update(int value) {
|
|
this.value = value;
|
|
}
|
|
}
|
|
```
|
|
unnecessaryToListInSpreads:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of 'toList' in a spread."
|
|
correctionMessage: "Try removing the invocation of 'toList'."
|
|
state:
|
|
stable: "2.18"
|
|
categories: [brevity]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when `toList` is used to convert an
|
|
`Iterable` to a `List` just before a spread operator is applied to the
|
|
list. The spread operator can be applied to any `Iterable`, so the
|
|
conversion isn't necessary.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because `toList` is invoked on
|
|
the result of `map`, which is an `Iterable` that the spread operator could
|
|
be applied to directly:
|
|
|
|
```dart
|
|
List<String> toLowercase(List<String> strings) {
|
|
return [
|
|
...strings.map((String s) => s.toLowerCase()).[!toList!](),
|
|
];
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the invocation of `toList`:
|
|
|
|
```dart
|
|
List<String> toLowercase(List<String> strings) {
|
|
return [
|
|
...strings.map((String s) => s.toLowerCase()),
|
|
];
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
Unnecessary `toList()` in spreads.
|
|
|
|
**BAD:**
|
|
```dart
|
|
children: <Widget>[
|
|
...['foo', 'bar', 'baz'].map((String s) => Text(s)).toList(),
|
|
]
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
children: <Widget>[
|
|
...['foo', 'bar', 'baz'].map((String s) => Text(s)),
|
|
]
|
|
```
|
|
unnecessaryTypeNameInConstructor:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary type name in a constructor."
|
|
correctionMessage: "Try removing the type name."
|
|
state:
|
|
stable: "3.13"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a secondary constructor
|
|
declaration includes a type name.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the generative
|
|
constructor declaration includes the type name:
|
|
|
|
```dart
|
|
%experiments=primary-constructors
|
|
class C {
|
|
[!C!]();
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the factory
|
|
constructor declaration includes the type name:
|
|
|
|
```dart
|
|
%experiments=primary-constructors
|
|
class C {
|
|
factory [!C!]() => C._();
|
|
|
|
new _() {}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the type name from the constructor. If the constructor is named,
|
|
also remove the period before the name:
|
|
|
|
```dart
|
|
%experiments=primary-constructors
|
|
class C {
|
|
new ();
|
|
}
|
|
```
|
|
|
|
or
|
|
|
|
```dart
|
|
%experiments=primary-constructors
|
|
class C {
|
|
factory () => C._();
|
|
|
|
new _() {}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
Don't include the type name in a constructor declaration. It isn't
|
|
necessary, and the code is shorter and cleaner without it.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class C {
|
|
C();
|
|
C.name();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class C {
|
|
new ();
|
|
new name();
|
|
}
|
|
```
|
|
unnecessaryUnawaited:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of 'unawaited'."
|
|
correctionMessage: "Try removing the use of 'unawaited', as the unawaited element is annotated with '@awaitNotRequired'."
|
|
state:
|
|
stable: "3.9"
|
|
categories: [brevity, unintentional, unusedCode]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when `unawaited` is used to mark a
|
|
call to a function, method, or operator, or a reference to a field,
|
|
getter, or top-level variable as safely not being awaited, but the called
|
|
member is also annotated with `@awaitNotRequired`. This annotation itself
|
|
signals that wrapping with `unawaited` is unnecessary at any call site.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because `unawaited` is invoked
|
|
on a call to a function that's annotated with `@awaitNotRequired`:
|
|
|
|
```dart
|
|
import 'dart:async';
|
|
import 'package:meta/meta.dart';
|
|
|
|
@awaitNotRequired
|
|
Future<bool> log(String message) async => true;
|
|
|
|
void f() {
|
|
[!unawaited!](log('Message.'));
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Remove the invocation of `unawaited`:
|
|
|
|
```dart
|
|
import 'package:meta/meta.dart';
|
|
|
|
@awaitNotRequired
|
|
Future<bool> log(String message) async => true;
|
|
|
|
void f() {
|
|
log('Message.');
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
A call to a function, method, or operator, or a reference to a field,
|
|
getter, or top-level variable which is annotated with `@awaitNotRequired`
|
|
does not need to be wrapped in a call to `unawaited()`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
@awaitNotRequired
|
|
Future<LogMessage> log(String message) { ... }
|
|
|
|
void f() {
|
|
unawaited(log('Message.'));
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
@awaitNotRequired
|
|
Future<LogMessage> log(String message) { ... }
|
|
|
|
void f() {
|
|
log('Message.');
|
|
}
|
|
```
|
|
unnecessaryUnderscores:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Unnecessary use of multiple underscores."
|
|
correctionMessage: "Try using '_'."
|
|
state:
|
|
experimental: "3.7"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### 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 _) { }
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** using multiple underscores when a single wildcard will do.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void function(int __) { }
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void function(int _) { }
|
|
```
|
|
|
|
|
|
unreachableFromMain:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Unreachable member '#p0' in an executable library."
|
|
correctionMessage: "Try referencing the member or removing it."
|
|
state:
|
|
stable: "2.19"
|
|
categories: [unusedCode]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Any member declared in an executable library should be used directly inside that
|
|
library. An executable library is a library that contains a `main` top-level
|
|
function or that contains a top-level function annotated with
|
|
`@pragma('vm:entry-point')`). Executable libraries are not usually imported
|
|
and it's better to avoid defining unused members.
|
|
|
|
This rule assumes that an executable library isn't imported by other libraries
|
|
except to execute its `main` function.
|
|
|
|
**BAD:**
|
|
|
|
```dart
|
|
main() {}
|
|
void f() {}
|
|
```
|
|
|
|
**GOOD:**
|
|
|
|
```dart
|
|
main() {
|
|
f();
|
|
}
|
|
void f() {}
|
|
```
|
|
unrelatedTypeEqualityChecksInExpression:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
sharedName: unrelatedTypeEqualityChecks
|
|
problemMessage: "The type of the right operand ('#p0') isn't a subtype or a supertype of the left operand ('#p1')."
|
|
correctionMessage: "Try changing one or both of the operands."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when two objects are being compared
|
|
and neither of the static types of the two objects is a subtype of the
|
|
other.
|
|
|
|
Such a comparison will usually return `false` and might not reflect the
|
|
programmer's intent.
|
|
|
|
There can be false positives. For example, a class named `Point` might
|
|
have subclasses named `CartesianPoint` and `PolarPoint`, neither of which
|
|
is a subtype of the other, but it might still be appropriate to test the
|
|
equality of instances.
|
|
|
|
As a concrete case, the classes `Int64` and `Int32` from `package:fixnum`
|
|
allow comparing instances to an `int` provided the `int` is on the
|
|
right-hand side. This case is specifically allowed by the diagnostic, but
|
|
other such cases are not.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the string `s` is
|
|
being compared to the integer `1`:
|
|
|
|
```dart
|
|
bool f(String s) {
|
|
return s [!==!] 1;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace one of the operands with something compatible with the other
|
|
operand:
|
|
|
|
```dart
|
|
bool f(String s) {
|
|
return s.length == 1;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** Compare references of unrelated types for equality.
|
|
|
|
Comparing references of a type where neither is a subtype of the other most
|
|
likely will return `false` and might not reflect programmer's intent.
|
|
|
|
`Int64` and `Int32` from `package:fixnum` allow comparing to `int` provided
|
|
the `int` is on the right hand side. The lint allows this as a special case.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction() {
|
|
var x = '1';
|
|
if (x == 1) print('someFunction'); // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction1() {
|
|
String x = '1';
|
|
if (x == 1) print('someFunction1'); // LINT
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
void someFunction13(DerivedClass2 instance) {
|
|
var other = DerivedClass3();
|
|
|
|
if (other == instance) print('someFunction13'); // LINT
|
|
}
|
|
|
|
class ClassBase {}
|
|
|
|
class DerivedClass1 extends ClassBase {}
|
|
|
|
abstract class Mixin {}
|
|
|
|
class DerivedClass2 extends ClassBase with Mixin {}
|
|
|
|
class DerivedClass3 extends ClassBase implements Mixin {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction2() {
|
|
var x = '1';
|
|
var y = '2';
|
|
if (x == y) print(someFunction2); // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction3() {
|
|
for (var i = 0; i < 10; i++) {
|
|
if (i == 0) print(someFunction3); // OK
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction4() {
|
|
var x = '1';
|
|
if (x == null) print(someFunction4); // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction7() {
|
|
List someList;
|
|
|
|
if (someList.length == 0) print('someFunction7'); // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction8(ClassBase instance) {
|
|
DerivedClass1 other;
|
|
|
|
if (other == instance) print('someFunction8'); // OK
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction10(unknown) {
|
|
var what = unknown - 1;
|
|
for (var index = 0; index < unknown; index++) {
|
|
if (what == index) print('someFunction10'); // OK
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void someFunction11(Mixin instance) {
|
|
var other = DerivedClass2();
|
|
|
|
if (other == instance) print('someFunction11'); // OK
|
|
if (other != instance) print('!someFunction11'); // OK
|
|
}
|
|
|
|
class ClassBase {}
|
|
|
|
abstract class Mixin {}
|
|
|
|
class DerivedClass2 extends ClassBase with Mixin {}
|
|
```
|
|
unrelatedTypeEqualityChecksInPattern:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
Object p1: undocumented
|
|
sharedName: unrelatedTypeEqualityChecks
|
|
problemMessage: "The type of the operand ('#p0') isn't a subtype or a supertype of the value being matched ('#p1')."
|
|
correctionMessage: "Try changing one or both of the operands."
|
|
hasPublishedDocs: true
|
|
unsafeHtmlAttribute:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: unsafeHtml
|
|
problemMessage: "Assigning to the attribute '#p0' is unsafe."
|
|
correctionMessage: "Try finding a different way to implement the page."
|
|
state:
|
|
stable: "2.4"
|
|
removed: "3.7"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**NOTE:** This lint is deprecated and will be removed in a future release.
|
|
Remove all inclusions of this lint from your analysis options.
|
|
|
|
**AVOID**
|
|
|
|
* assigning directly to the `href` field of an AnchorElement
|
|
* assigning directly to the `src` field of an EmbedElement, IFrameElement, or
|
|
ScriptElement
|
|
* assigning directly to the `srcdoc` field of an IFrameElement
|
|
* calling the `createFragment` method of Element
|
|
* calling the `open` method of Window
|
|
* calling the `setInnerHtml` method of Element
|
|
* calling the `Element.html` constructor
|
|
* calling the `DocumentFragment.html` constructor
|
|
|
|
|
|
**BAD:**
|
|
```dart
|
|
var script = ScriptElement()..src = 'foo.js';
|
|
```
|
|
|
|
This rule has been removed.
|
|
unsafeHtmlConstructor:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: unsafeHtml
|
|
problemMessage: "Invoking the constructor '#p0' is unsafe."
|
|
correctionMessage: "Try finding a different way to implement the page."
|
|
hasPublishedDocs: false
|
|
unsafeHtmlMethod:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: unsafeHtml
|
|
problemMessage: "Invoking the method '#p0' is unsafe."
|
|
correctionMessage: "Try finding a different way to implement the page."
|
|
hasPublishedDocs: false
|
|
unsafeVariance:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "This type is unsafe: a type parameter occurs in a non-covariant position."
|
|
correctionMessage: "Try using a more general type that doesn't contain any type parameters in such a position."
|
|
state:
|
|
experimental: "3.7"
|
|
categories: [errorProne]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
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
|
|
|
|
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<X> {
|
|
bool Function([!X!]) f;
|
|
C(this.f);
|
|
}
|
|
```
|
|
|
|
This is unsafe: If `c` has static type `C<num>` and run-time type `C<int>`
|
|
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
|
|
|
|
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:
|
|
|
|
```dart
|
|
class C<X> {
|
|
// NB: Ensure manually that `_f` is only accessed on `this`.
|
|
// ignore: unsafe_variance
|
|
bool Function(X) _f;
|
|
|
|
C(this._f);
|
|
|
|
// We can write a forwarding method to allow clients to call `_f`.
|
|
bool f(X x) => _f(x);
|
|
}
|
|
```
|
|
|
|
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.
|
|
|
|
```dart
|
|
class C<X> {
|
|
bool Function(Never) f;
|
|
C(this.f);
|
|
}
|
|
```
|
|
|
|
If `c` has static type `C<num>` 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.
|
|
|
|
You can also eliminate the unsafe variance by using a much more general
|
|
type like `Function`, which is essentially the type `dynamic` for
|
|
functions.
|
|
|
|
```dart
|
|
class C<X> {
|
|
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.
|
|
deprecatedDetails: |-
|
|
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<X> {...}`, 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.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class C<X> {
|
|
final bool Function([!X!]) fun; // LINT
|
|
C(this.fun);
|
|
}
|
|
|
|
void main() {
|
|
C<num> c = C<int>((i) => i.isEven);
|
|
c.fun(10); // Throws.
|
|
}
|
|
```
|
|
|
|
The problem is that `X` occurs as a parameter type in the type
|
|
of `fun`.
|
|
|
|
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<X> {
|
|
// ignore: unsafe_variance
|
|
final bool Function(X) _fun;
|
|
bool fun(X x) => _fun(x);
|
|
C(this._fun);
|
|
}
|
|
|
|
void main() {
|
|
C<num> c = C<int>((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> = X Function(X);
|
|
typedef C<X> = _C<X, Inv<X>>;
|
|
|
|
class _C<X, Invariance extends Inv<X>> {
|
|
// ignore: unsafe_variance
|
|
final bool Function(X) fun; // Safe!
|
|
_C(this.fun);
|
|
}
|
|
|
|
void main() {
|
|
C<int> c = C<int>((i) => i.isEven);
|
|
c.fun(10); // Succeeds.
|
|
}
|
|
```
|
|
|
|
With this approach, `C<int>` is not a subtype of `C<num>`, 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<X> {
|
|
final bool Function(Never) fun;
|
|
C(this.fun);
|
|
}
|
|
|
|
void main() {
|
|
C<num> c = C<int>((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.
|
|
}
|
|
```
|
|
useBuildContextSynchronouslyAsyncUse:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: useBuildContextSynchronously
|
|
problemMessage: "Don't use 'BuildContext's across async gaps."
|
|
correctionMessage: "Try rewriting the code to not use the 'BuildContext', or guard the use with a 'mounted' check."
|
|
state:
|
|
experimental: "2.13"
|
|
stable: "3.2"
|
|
categories: [errorProne, flutter]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
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
|
|
crashes. Asynchronous gaps implicitly store a `BuildContext`, making them
|
|
easy to overlook for diagnosis.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the `context` is
|
|
passed to a constructor after the `await`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
class MyWidget extends Widget {
|
|
void onButtonTapped(BuildContext context) async {
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
Navigator.of([!context!]).pop();
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If you can remove the asynchronous gap, do so:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
class MyWidget extends Widget {
|
|
void onButtonTapped(BuildContext context) {
|
|
Navigator.of(context).pop();
|
|
}
|
|
}
|
|
```
|
|
|
|
If you can't remove the asynchronous gap, then use `mounted` to guard the
|
|
use of the `context`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
class MyWidget extends Widget {
|
|
void onButtonTapped(BuildContext context) async {
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
if (context.mounted) {
|
|
Navigator.of(context).pop();
|
|
}
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** use `BuildContext` across asynchronous gaps.
|
|
|
|
Storing `BuildContext` for later usage can easily lead to difficult-to-diagnose
|
|
crashes. Asynchronous gaps are implicitly storing `BuildContext` and are some of
|
|
the easiest to overlook when writing code.
|
|
|
|
When a `BuildContext` is used, a `mounted` property must be checked after an
|
|
asynchronous gap, depending on how the `BuildContext` is accessed:
|
|
|
|
* When using a `State`'s `context` property, the `State`'s `mounted` property
|
|
must be checked.
|
|
* For other `BuildContext` instances (like a local variable or function
|
|
argument), the `BuildContext`'s `mounted` property must be checked.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void onButtonTapped(BuildContext context) async {
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
Navigator.of(context).pop();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void onButtonTapped(BuildContext context) {
|
|
Navigator.of(context).pop();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void onButtonTapped(BuildContext context) async {
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
|
|
if (!context.mounted) return;
|
|
Navigator.of(context).pop();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
abstract class MyState extends State<MyWidget> {
|
|
void foo() async {
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
if (!mounted) return; // Checks `this.mounted`, not `context.mounted`.
|
|
Navigator.of(context).pop();
|
|
}
|
|
}
|
|
```
|
|
useBuildContextSynchronouslyWrongMounted:
|
|
type: lint
|
|
parameters: none
|
|
sharedName: useBuildContextSynchronously
|
|
problemMessage: "Don't use 'BuildContext's across async gaps, guarded by an unrelated 'mounted' check."
|
|
correctionMessage: "Guard a 'State.context' use with a 'mounted' check on the State, and other BuildContext use with a 'mounted' check on the BuildContext."
|
|
hasPublishedDocs: true
|
|
useColoredBox:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use a 'ColoredBox' rather than a 'Container' with only a 'Color'."
|
|
correctionMessage: "Try replacing the 'Container' with a 'ColoredBox'."
|
|
state:
|
|
stable: "2.17"
|
|
categories: [flutter, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `Container` is created that
|
|
only sets the color.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the only attribute of
|
|
the container that is set is the `color`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget build() {
|
|
return [!Container!](
|
|
color: Colors.red,
|
|
child: const Text('hello'),
|
|
);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the `Container` with a `ColoredBox`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget build() {
|
|
return ColoredBox(
|
|
color: Colors.red,
|
|
child: const Text('hello'),
|
|
);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use `ColoredBox` when `Container` has only a `Color`.
|
|
|
|
A `Container` is a heavier Widget than a `ColoredBox`, and as bonus,
|
|
`ColoredBox` has a `const` constructor.
|
|
|
|
**BAD:**
|
|
```dart
|
|
Widget buildArea() {
|
|
return Container(
|
|
color: Colors.blue,
|
|
child: const Text('hello'),
|
|
);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Widget buildArea() {
|
|
return const ColoredBox(
|
|
color: Colors.blue,
|
|
child: Text('hello'),
|
|
);
|
|
}
|
|
```
|
|
useDeclaringParameters:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use a declaring parameter."
|
|
correctionMessage: "Try using a declaring parameter."
|
|
state:
|
|
experimental: "3.13"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Use a declaring parameter everywhere it's valid to do so.
|
|
useDecoratedBox:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'DecoratedBox' rather than a 'Container' with only a 'Decoration'."
|
|
correctionMessage: "Try replacing the 'Container' with a 'DecoratedBox'."
|
|
state:
|
|
stable: "2.16"
|
|
categories: [flutter, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `Container` is created that
|
|
only sets the decoration.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the only attribute of
|
|
the container that is set is the `decoration`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget buildArea() {
|
|
return [!Container!](
|
|
decoration: const BoxDecoration(
|
|
color: Colors.red,
|
|
borderRadius: BorderRadius.all(
|
|
Radius.circular(5),
|
|
),
|
|
),
|
|
child: const Text('...'),
|
|
);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the `Container` with a `DecoratedBox`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Widget buildArea() {
|
|
return DecoratedBox(
|
|
decoration: const BoxDecoration(
|
|
color: Colors.red,
|
|
borderRadius: BorderRadius.all(
|
|
Radius.circular(5),
|
|
),
|
|
),
|
|
child: const Text('...'),
|
|
);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use `DecoratedBox` when `Container` has only a `Decoration`.
|
|
|
|
A `Container` is a heavier Widget than a `DecoratedBox`, and as bonus,
|
|
`DecoratedBox` has a `const` constructor.
|
|
|
|
**BAD:**
|
|
```dart
|
|
Widget buildArea() {
|
|
return Container(
|
|
decoration: const BoxDecoration(
|
|
color: Colors.blue,
|
|
borderRadius: BorderRadius.all(
|
|
Radius.circular(5),
|
|
),
|
|
),
|
|
child: const Text('...'),
|
|
);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Widget buildArea() {
|
|
return const DecoratedBox(
|
|
decoration: BoxDecoration(
|
|
color: Colors.blue,
|
|
borderRadius: BorderRadius.all(
|
|
Radius.circular(5),
|
|
),
|
|
),
|
|
child: Text('...'),
|
|
);
|
|
}
|
|
```
|
|
useEnums:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Class should be an enum."
|
|
correctionMessage: "Try using an enum rather than a class."
|
|
state:
|
|
stable: "2.17"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Classes that look like enumerations should be declared as `enum`s.
|
|
|
|
**DO** use enums where appropriate.
|
|
|
|
Candidates for enums are classes that:
|
|
|
|
* are concrete,
|
|
* are private or have only private generative constructors,
|
|
* have two or more static const fields with the same type as the class,
|
|
* have generative constructors that are only invoked at the top-level of the
|
|
initialization expression of these static fields,
|
|
* do not define `hashCode`, `==`, `values` or `index`,
|
|
* do not extend any class other than `Object`, and
|
|
* have no subclasses declared in the defining library.
|
|
|
|
To learn more about creating and using these enums, check out
|
|
[Declaring enhanced enums](https://dart.dev/language/enums#declaring-enhanced-enums).
|
|
|
|
**BAD:**
|
|
```dart
|
|
class LogPriority {
|
|
static const error = LogPriority._(1, 'Error');
|
|
static const warning = LogPriority._(2, 'Warning');
|
|
static const log = LogPriority._unknown('Log');
|
|
|
|
final String prefix;
|
|
final int priority;
|
|
const LogPriority._(this.priority, this.prefix);
|
|
const LogPriority._unknown(String prefix) : this._(-1, prefix);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
enum LogPriority {
|
|
error(1, 'Error'),
|
|
warning(2, 'Warning'),
|
|
log.unknown('Log');
|
|
|
|
final String prefix;
|
|
final int priority;
|
|
const LogPriority(this.priority, this.prefix);
|
|
const LogPriority.unknown(String prefix) : this(-1, prefix);
|
|
}
|
|
```
|
|
useFullHexValuesForFlutterColors:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Instances of 'Color' should be created using an 8-digit hexadecimal integer (such as '0xFFFFFFFF')."
|
|
state:
|
|
stable: "2.2"
|
|
categories: [flutter, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the argument to the constructor
|
|
of the `Color` class is a literal integer that isn't represented as an
|
|
8-digit hexadecimal integer.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the argument (`1`)
|
|
isn't represented as an 8-digit hexadecimal integer:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Color c = Color([!1!]);
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Convert the representation to be an 8-digit hexadecimal integer:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
Color c = Color(0x00000001);
|
|
```
|
|
deprecatedDetails: |-
|
|
**PREFER** an 8-digit hexadecimal integer (for example, 0xFFFFFFFF) to
|
|
instantiate a Color. Colors have four 8-bit channels, which adds up to 32 bits,
|
|
so Colors are described using a 32-bit integer.
|
|
|
|
**BAD:**
|
|
```dart
|
|
Color(1);
|
|
Color(0x000001);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Color(0x00000001);
|
|
```
|
|
useFunctionTypeSyntaxForParameters:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Use the generic function type syntax to declare the parameter '#p0'."
|
|
correctionMessage: "Try using the generic function type syntax."
|
|
state:
|
|
stable: "2.1"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the older style function-valued
|
|
parameter syntax is used.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the function-valued
|
|
parameter `f` is declared using an older style syntax:
|
|
|
|
```dart
|
|
void g([!bool f(String s)!]) {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use the generic function type syntax to declare the parameter:
|
|
|
|
```dart
|
|
void g(bool Function(String) f) {}
|
|
```
|
|
deprecatedDetails: |-
|
|
Use generic function type syntax for parameters.
|
|
|
|
**BAD:**
|
|
```dart
|
|
Iterable<T> where(bool predicate(T element)) {}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Iterable<T> where(bool Function(T) predicate) {}
|
|
```
|
|
useIfNullToConvertNullsToBools:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use an if-null operator to convert a 'null' to a 'bool'."
|
|
correctionMessage: "Try using an if-null operator."
|
|
state:
|
|
stable: "2.13"
|
|
deprecated: "3.11"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a nullable `bool`-valued
|
|
expression is compared (using `==` or `!=`) to a boolean literal.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the nullable boolean
|
|
variable `b` is compared to `true`:
|
|
|
|
```dart
|
|
void f(bool? b) {
|
|
if ([!b == true!]) {
|
|
// Treats `null` as `false`.
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Rewrite the condition to use `??` instead:
|
|
|
|
```dart
|
|
void f(bool? b) {
|
|
if (b ?? false) {
|
|
// Treats `null` as `false`.
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/usage#prefer-using--to-convert-null-to-a-boolean-value):
|
|
|
|
Use `??` operators to convert `null`s to `bool`s.
|
|
|
|
**BAD:**
|
|
```dart
|
|
if (nullableBool == true) {
|
|
}
|
|
if (nullableBool != false) {
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
if (nullableBool ?? false) {
|
|
}
|
|
if (nullableBool ?? true) {
|
|
}
|
|
```
|
|
useIsEvenRatherThanModulo:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Use '#p0' rather than '% 2'."
|
|
correctionMessage: "Try using '#p0'."
|
|
state:
|
|
stable: "2.9"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
**PREFER** the use of intValue.isOdd/isEven to check for evenness.
|
|
|
|
**BAD:**
|
|
```dart
|
|
bool isEven = 1 % 2 == 0;
|
|
bool isOdd = 13 % 2 == 1;
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
bool isEven = 1.isEven;
|
|
bool isOdd = 13.isOdd;
|
|
```
|
|
useKeyInWidgetConstructors:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Constructors for public widgets should have a named 'key' parameter."
|
|
correctionMessage: "Try adding a named parameter to the constructor."
|
|
state:
|
|
stable: "2.8"
|
|
categories: [flutter, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a constructor in a subclass of
|
|
`Widget` that isn't private to its library doesn't have a parameter named
|
|
`key`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the constructor for
|
|
the class `MyWidget` doesn't have a parameter named `key`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
class MyWidget extends StatelessWidget {
|
|
[!MyWidget!]({required int height});
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the default
|
|
constructor for the class `MyWidget` doesn't have a parameter named `key`:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
class [!MyWidget!] extends StatelessWidget {}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Add a parameter named `key` to the constructor, explicitly declaring the
|
|
constructor if necessary:
|
|
|
|
```dart
|
|
import 'package:flutter/material.dart';
|
|
|
|
class MyWidget extends StatelessWidget {
|
|
MyWidget({super.key, required int height});
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use key in widget constructors.
|
|
|
|
It's a good practice to expose the ability to provide a key when creating public
|
|
widgets.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class MyPublicWidget extends StatelessWidget {
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class MyPublicWidget extends StatelessWidget {
|
|
MyPublicWidget({super.key});
|
|
}
|
|
```
|
|
useLateForPrivateFieldsAndVariables:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'late' for private members with a non-nullable type."
|
|
correctionMessage: "Try making adding the modifier 'late'."
|
|
state:
|
|
experimental: "2.10"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a private field or variable is
|
|
marked as being nullable, but every reference assumes that the variable is
|
|
never `null`.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the private top-level
|
|
variable `_i` is nullable, but every reference assumes that it will not be
|
|
`null`:
|
|
|
|
```dart
|
|
void f() {
|
|
_i!.abs();
|
|
}
|
|
|
|
int? [!_i!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Mark the variable or field as being both non-nullable and `late` to
|
|
indicate that it will always be assigned a non-null:
|
|
|
|
```dart
|
|
void f() {
|
|
_i.abs();
|
|
}
|
|
|
|
late int _i;
|
|
```
|
|
deprecatedDetails: |-
|
|
Use `late` for private members with non-nullable types that are always expected
|
|
to be non-null. Thus it's clear that the field is not expected to be `null`
|
|
and it avoids null checks.
|
|
|
|
**BAD:**
|
|
```dart
|
|
int? _i;
|
|
m() {
|
|
_i!.abs();
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
late int _i;
|
|
m() {
|
|
_i.abs();
|
|
}
|
|
```
|
|
|
|
**OK:**
|
|
```dart
|
|
int? _i;
|
|
m() {
|
|
_i?.abs();
|
|
_i = null;
|
|
}
|
|
```
|
|
useNamedConstants:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
problemMessage: "Use the constant '#p0' rather than a constructor returning the same object."
|
|
correctionMessage: "Try using '#p0'."
|
|
state:
|
|
stable: "2.13"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a constant is created with the
|
|
same value as a known `const` variable.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because there is a known
|
|
`const` field (`Duration.zero`) whose value is the same as what the
|
|
constructor invocation will evaluate to:
|
|
|
|
```dart
|
|
Duration d = [!const Duration(seconds: 0)!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the constructor invocation with a reference to the known `const`
|
|
variable:
|
|
|
|
```dart
|
|
Duration d = Duration.zero;
|
|
```
|
|
deprecatedDetails: |-
|
|
Where possible, use already defined const values.
|
|
|
|
**BAD:**
|
|
```dart
|
|
const Duration(seconds: 0);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
Duration.zero;
|
|
```
|
|
useNullAwareElements:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use the null-aware marker '?' rather than a null check via an 'if'."
|
|
correctionMessage: "Try using '?'."
|
|
state:
|
|
stable: "3.8"
|
|
categories: [brevity, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a null check is used instead
|
|
of a null-aware marker inside of a collection literal.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because a null check is used
|
|
to decide whether `x` should be inserted into the list, while the
|
|
null-aware marker '?' would be less brittle and less verbose.
|
|
|
|
```dart
|
|
f(int? x) => [[!if!] (x != null) x];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Replace the null-check with the null-aware marker '?':
|
|
|
|
```dart
|
|
f(int? x) => [?x];
|
|
```
|
|
deprecatedDetails: |-
|
|
Where possible, use null-aware elements in collection literals.
|
|
|
|
**BAD:**
|
|
```dart
|
|
f(String? key) => {if (key != null) key: "value"};
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
f(String? key) => {?key: "value"};
|
|
```
|
|
usePrimaryConstructors:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use a primary constructor."
|
|
correctionMessage: "Try using a primary constructor."
|
|
state:
|
|
experimental: "3.13"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Use a primary constructor everywhere it's valid to do so.
|
|
useRawStrings:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use a raw string to avoid using escapes."
|
|
correctionMessage: "Try making the string a raw string and removing the escapes."
|
|
state:
|
|
stable: "2.8"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a string literal containing
|
|
escapes, and no interpolations, could be marked as being raw in order to
|
|
avoid the need for the escapes.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the string contains
|
|
escaped characters that wouldn't need to be escaped if the string is
|
|
made a raw string:
|
|
|
|
```dart
|
|
var s = [!'A string with only \\ and \$'!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Mark the string as being raw and remove the unnecessary backslashes:
|
|
|
|
```dart
|
|
var s = r'A string with only \ and $';
|
|
```
|
|
deprecatedDetails: |-
|
|
A raw string can be used to avoid escaping only backslashes and dollars.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var s = 'A string with only \\ and \$';
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var s = r'A string with only \ and $';
|
|
```
|
|
useRethrowWhenPossible:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use 'rethrow' to rethrow a caught exception."
|
|
correctionMessage: "Try replacing the 'throw' with a 'rethrow'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [brevity, effectiveDart]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a caught exception is thrown
|
|
using a `throw` expression rather than a `rethrow` statement.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the caught exception
|
|
`e` is thrown using a `throw` expression:
|
|
|
|
```dart
|
|
void f() {
|
|
try {
|
|
// ...
|
|
} catch (e) {
|
|
[!throw e!];
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use `rethrow` instead of `throw`:
|
|
|
|
```dart
|
|
void f() {
|
|
try {
|
|
// ...
|
|
} catch (e) {
|
|
rethrow;
|
|
}
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/usage#do-use-rethrow-to-rethrow-a-caught-exception):
|
|
|
|
**DO** use rethrow to rethrow a caught exception.
|
|
|
|
As Dart provides rethrow as a feature, it should be used to improve terseness
|
|
and readability.
|
|
|
|
**BAD:**
|
|
```dart
|
|
try {
|
|
somethingRisky();
|
|
} catch(e) {
|
|
if (!canHandle(e)) throw e;
|
|
handle(e);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
try {
|
|
somethingRisky();
|
|
} catch(e) {
|
|
if (!canHandle(e)) rethrow;
|
|
handle(e);
|
|
}
|
|
```
|
|
useSettersToChangeProperties:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The method is used to change a property."
|
|
correctionMessage: "Try converting the method to a setter."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a method is used to set the
|
|
value of a field, or a function is used to set the value of a top-level
|
|
variable, and nothing else.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the method `setF` is
|
|
used to set the value of the field `_f` and does no other work:
|
|
|
|
```dart
|
|
%ignore=unused_field
|
|
class C {
|
|
int _f = 0;
|
|
|
|
void [!setF!](int value) => _f = value;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Convert the method to a setter:
|
|
|
|
```dart
|
|
%ignore=unused_field
|
|
class C {
|
|
int _f = 0;
|
|
|
|
set f(int value) => _f = value;
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use a setter for operations that conceptually change a property.
|
|
|
|
**BAD:**
|
|
```dart
|
|
rectangle.setWidth(3);
|
|
button.setVisible(false);
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
rectangle.width = 3;
|
|
button.visible = false;
|
|
```
|
|
useStringBuffers:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use a string buffer rather than '+' to compose strings."
|
|
correctionMessage: "Try writing the parts of a string to a string buffer."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [nonPerformant]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when values are concatenated to a
|
|
string inside a loop without using a `StringBuffer` to do the
|
|
concatenation.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the string `result` is
|
|
computed by repeated concatenation within the `for` loop:
|
|
|
|
```dart
|
|
String f() {
|
|
var result = '';
|
|
for (int i = 0; i < 10; i++) {
|
|
[!result += 'a'!];
|
|
}
|
|
return result;
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use a `StringBuffer` to compute the result:
|
|
|
|
```dart
|
|
String f() {
|
|
var buffer = StringBuffer();
|
|
for (int i = 0; i < 10; i++) {
|
|
buffer.write('a');
|
|
}
|
|
return buffer.toString();
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use string buffers to compose strings.
|
|
|
|
In most cases, using a string buffer is preferred for composing strings due to
|
|
its improved performance.
|
|
|
|
**BAD:**
|
|
```dart
|
|
String foo() {
|
|
final buffer = '';
|
|
for (int i = 0; i < 10; i++) {
|
|
buffer += 'a'; // LINT
|
|
}
|
|
return buffer;
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
String foo() {
|
|
final buffer = StringBuffer();
|
|
for (int i = 0; i < 10; i++) {
|
|
buffer.write('a');
|
|
}
|
|
return buffer.toString();
|
|
}
|
|
```
|
|
useStringInPartOfDirectives:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "The part-of directive uses a library name."
|
|
correctionMessage: "Try converting the directive to use the URI of the library."
|
|
state:
|
|
stable: "2.19"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a `part of` directive uses a
|
|
library name to refer to the library that the part is a part of.
|
|
|
|
#### Example
|
|
|
|
Given a file named `lib.dart` that contains the following:
|
|
|
|
```dart
|
|
%uri="lib/lib.dart"
|
|
library lib;
|
|
|
|
part 'test.dart';
|
|
```
|
|
|
|
The following code produces this diagnostic because the `part of`
|
|
directive uses the name of the library rather than the URI of the library
|
|
it's part of:
|
|
|
|
```dart
|
|
[!part of lib;!]
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use a URI to reference the library:
|
|
|
|
```dart
|
|
part of 'lib.dart';
|
|
```
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/usage#do-use-strings-in-part-of-directives):
|
|
|
|
**DO** use strings in `part of` directives.
|
|
|
|
**BAD:**
|
|
|
|
```dart
|
|
part of my_library;
|
|
```
|
|
|
|
**GOOD:**
|
|
|
|
```dart
|
|
part of '../../my_library.dart';
|
|
```
|
|
useSuperParametersMultiple:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: useSuperParameters
|
|
problemMessage: "Parameters '#p0' could be super parameters."
|
|
correctionMessage: "Trying converting '#p0' to super parameters."
|
|
state:
|
|
experimental: "2.17"
|
|
categories: [brevity]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a parameter to a constructor is
|
|
passed to a super constructor without being referenced or modified and a
|
|
`super` parameter isn't used.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the parameters of the
|
|
constructor for `B` are only used as arguments to the super constructor:
|
|
|
|
```dart
|
|
class A {
|
|
A({int? x, int? y});
|
|
}
|
|
class B extends A {
|
|
[!B!]({int? x, int? y}) : super(x: x, y: y);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use a `super` parameter to pass the arguments:
|
|
|
|
```dart
|
|
class A {
|
|
A({int? x, int? y});
|
|
}
|
|
class B extends A {
|
|
B({super.x, super.y});
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
"Forwarding constructor"s, that do nothing except forward parameters to their
|
|
superclass constructors should take advantage of super-initializer parameters
|
|
rather than repeating the names of parameters when passing them to the
|
|
superclass constructors. This makes the code more concise and easier to read
|
|
and maintain.
|
|
|
|
**DO** use super-initializer parameters where possible.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A {
|
|
A({int? x, int? y});
|
|
}
|
|
class B extends A {
|
|
B({int? x, int? y}) : super(x: x, y: y);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class A {
|
|
A({int? x, int? y});
|
|
}
|
|
class B extends A {
|
|
B({super.x, super.y});
|
|
}
|
|
```
|
|
useSuperParametersSingle:
|
|
type: lint
|
|
parameters:
|
|
Object p0: undocumented
|
|
sharedName: useSuperParameters
|
|
problemMessage: "Parameter '#p0' could be a super parameter."
|
|
correctionMessage: "Trying converting '#p0' to a super parameter."
|
|
hasPublishedDocs: true
|
|
useTestThrowsMatchers:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use the 'throwsA' matcher instead of using 'fail' when there is no exception thrown."
|
|
correctionMessage: "Try removing the try-catch and using 'throwsA' to expect an exception."
|
|
state:
|
|
stable: "2.14"
|
|
categories: [style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
Use the `throwsA` matcher instead of try-catch with `fail()`.
|
|
|
|
**BAD:**
|
|
|
|
```dart
|
|
// sync code
|
|
try {
|
|
someSyncFunctionThatThrows();
|
|
fail('expected Error');
|
|
} on Error catch (error) {
|
|
expect(error.message, contains('some message'));
|
|
}
|
|
|
|
// async code
|
|
try {
|
|
await someAsyncFunctionThatThrows();
|
|
fail('expected Error');
|
|
} on Error catch (error) {
|
|
expect(error.message, contains('some message'));
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
// sync code
|
|
expect(
|
|
() => someSyncFunctionThatThrows(),
|
|
throwsA(isA<Error>().having((Error error) => error.message, 'message', contains('some message'))),
|
|
);
|
|
|
|
// async code
|
|
await expectLater(
|
|
() => someAsyncFunctionThatThrows(),
|
|
throwsA(isA<Error>().having((Error error) => error.message, 'message', contains('some message'))),
|
|
);
|
|
```
|
|
useToAndAsIfApplicable:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Start the name of the method with 'to' or 'as'."
|
|
correctionMessage: "Try renaming the method to use either 'to' or 'as'."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [effectiveDart, style]
|
|
hasPublishedDocs: false
|
|
deprecatedDetails: |-
|
|
From [Effective Dart](https://dart.dev/effective-dart/design#prefer-naming-a-method-to___-if-it-copies-the-objects-state-to-a-new-object):
|
|
|
|
**PREFER** naming a method `to___()` if it copies the object's state to a new
|
|
object.
|
|
|
|
**PREFER** naming a method `as___()` if it returns a different representation
|
|
backed by the original object.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class Bar {
|
|
Foo myMethod() {
|
|
return Foo.from(this);
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Bar {
|
|
Foo toFoo() {
|
|
return Foo.from(this);
|
|
}
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
class Bar {
|
|
Foo asFoo() {
|
|
return Foo.from(this);
|
|
}
|
|
}
|
|
```
|
|
useTruncatingDivision:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Use truncating division."
|
|
correctionMessage: "Try using truncating division, '~/', instead of regular division ('/') followed by 'toInt()'."
|
|
state:
|
|
stable: "3.6"
|
|
categories: [languageFeatureUsage]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the result of dividing two
|
|
numbers is converted to an integer using `toInt`.
|
|
|
|
Dart has a built-in integer division operator that is both more efficient
|
|
and more concise.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the result of dividing
|
|
`x` and `y` is converted to an integer using `toInt`:
|
|
|
|
```dart
|
|
int divide(int x, int y) => [!(x / y).toInt()!];
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Use the integer division operator (`~/`):
|
|
|
|
```dart
|
|
int divide(int x, int y) => x ~/ y;
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use truncating division, '~/', instead of regular division ('/') followed
|
|
by 'toInt()'.
|
|
|
|
Dart features a "truncating division" operator which is the same operation as
|
|
division followed by truncation, but which is more concise and expressive, and
|
|
may be more performant on some platforms, for certain inputs.
|
|
|
|
**BAD:**
|
|
```dart
|
|
var x = (2 / 3).toInt();
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
var x = 2 ~/ 3;
|
|
```
|
|
validRegexps:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Invalid regular expression syntax."
|
|
correctionMessage: "Try correcting the regular expression."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [unintentional]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when the string passed to the
|
|
default constructor of the class `RegExp` doesn't contain a valid regular
|
|
expression.
|
|
|
|
A regular expression created with invalid syntax will throw a
|
|
`FormatException` at runtime.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the regular expression
|
|
isn't valid:
|
|
|
|
```dart
|
|
var r = RegExp([!r'('!]);
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
Fix the regular expression:
|
|
|
|
```dart
|
|
var r = RegExp(r'\(');
|
|
```
|
|
deprecatedDetails: |-
|
|
**DO** use valid regular expression syntax when creating regular expression
|
|
instances.
|
|
|
|
Regular expressions created with invalid syntax will throw a `FormatException`
|
|
at runtime so should be avoided.
|
|
|
|
**BAD:**
|
|
```dart
|
|
print(RegExp(r'(').hasMatch('foo()'));
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
print(RegExp(r'\(').hasMatch('foo()'));
|
|
```
|
|
varWithNoTypeAnnotation:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Avoid declaring parameters with `var` and no type annotation."
|
|
correctionMessage: "Try removing the keyword 'var' or replacing `var` with a type annotation."
|
|
state:
|
|
experimental: "3.12"
|
|
categories: [languageFeatureUsage]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
NOTE: This rule is removed in Dart 3.13.0; it is no longer functional.
|
|
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a parameter is
|
|
declared using the keyword `var` without a type annotation.
|
|
|
|
This pattern should be avoided as it likely will
|
|
be a compile-time error in a future version of Dart.
|
|
Beyond this, using `var` without an explicit type results in
|
|
the parameter having a type of `dynamic`,
|
|
disabling static type checking for that parameter.
|
|
|
|
#### Examples
|
|
|
|
The following code produces this diagnostic because the
|
|
parameter `x` is declared with `var`:
|
|
|
|
```dart
|
|
// @dart=3.12
|
|
void f([!var!] x) {
|
|
print(x);
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the
|
|
initializing formal parameter `x` is declared with `var`:
|
|
|
|
```dart
|
|
// @dart=3.12
|
|
class C {
|
|
int x;
|
|
|
|
C([!var!] this.x);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If you know the desired type of the parameter,
|
|
then replace `var` with a type annotation:
|
|
|
|
```dart
|
|
void f(String x) {
|
|
print(x);
|
|
}
|
|
```
|
|
|
|
If you don't know or don't want any specific type for the parameter,
|
|
then replace `var` with a type of `Object?` or `dynamic` to
|
|
make the lack of any more specific type explicit:
|
|
|
|
```dart
|
|
void f(Object? x) {
|
|
print(x);
|
|
}
|
|
```
|
|
deprecatedDetails: |-
|
|
**AVOID** declaring parameters with `var` and no type annotation.
|
|
|
|
Declaring formal parameters using `var` and no type annotation will be a
|
|
compile-time error when primary constructors are enabled. This lint flags
|
|
those parameters to ease migration.
|
|
|
|
**BAD:**
|
|
```dart
|
|
void func(var x) { // LINT
|
|
print(x);
|
|
}
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
void func(String x) { // OK
|
|
print(x);
|
|
}
|
|
```
|
|
|
|
**BAD:**
|
|
```dart
|
|
[1, 4, 6, 8].forEach((var value) => print(value + 2)); // LINT
|
|
```
|
|
|
|
**GOOD:**
|
|
```dart
|
|
[1, 4, 6, 8].forEach((value) => print(value + 2)); // OK
|
|
```
|
|
visitRegisteredNodes:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Declare 'visit' methods for all registered node types."
|
|
correctionMessage: "Try declaring a 'visit' method for all registered node types."
|
|
hasPublishedDocs: true
|
|
state:
|
|
internal: "3.10"
|
|
categories: [errorProne]
|
|
deprecatedDetails: "For internal use only."
|
|
voidChecks:
|
|
type: lint
|
|
parameters: none
|
|
problemMessage: "Assignment to a variable of type 'void'."
|
|
correctionMessage: "Try removing the assignment or changing the type of the variable."
|
|
state:
|
|
stable: "2.0"
|
|
categories: [style]
|
|
hasPublishedDocs: true
|
|
documentation: |-
|
|
#### Description
|
|
|
|
The analyzer produces this diagnostic when a value is assigned to a
|
|
variable of type `void`.
|
|
|
|
It isn't possible to access the value of such a variable, so the
|
|
assignment has no value.
|
|
|
|
#### Example
|
|
|
|
The following code produces this diagnostic because the field `value` has
|
|
the type `void`, but a value is being assigned to it:
|
|
|
|
```dart
|
|
class A<T> {
|
|
T? value;
|
|
}
|
|
|
|
void f(A<void> a) {
|
|
[!a.value = 1!];
|
|
}
|
|
```
|
|
|
|
The following code produces this diagnostic because the type of the
|
|
parameter `p` in the method `m` is `void`, but a value is being assigned
|
|
to it in the invocation:
|
|
|
|
```dart
|
|
class A<T> {
|
|
void m(T p) { }
|
|
}
|
|
|
|
void f(A<void> a) {
|
|
a.m([!1!]);
|
|
}
|
|
```
|
|
|
|
#### Common fixes
|
|
|
|
If the type of the variable is incorrect, then change the type of the
|
|
variable:
|
|
|
|
```dart
|
|
class A<T> {
|
|
T? value;
|
|
}
|
|
|
|
void f(A<int> a) {
|
|
a.value = 1;
|
|
}
|
|
```
|
|
|
|
If the type of the variable is correct, then remove the assignment:
|
|
|
|
```dart
|
|
class A<T> {
|
|
T? value;
|
|
}
|
|
|
|
void f(A<void> a) {}
|
|
```
|
|
deprecatedDetails: |-
|
|
**DON'T** assign to `void`.
|
|
|
|
**BAD:**
|
|
```dart
|
|
class A<T> {
|
|
T value;
|
|
void test(T arg) { }
|
|
}
|
|
|
|
void main() {
|
|
A<void> a = A<void>();
|
|
a.value = 1; // LINT
|
|
a.test(1); // LINT
|
|
}
|
|
```
|