| # 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. |
| |
| # 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. |
| |
| LintCode: |
| always_declare_return_types_of_functions: |
| sharedName: always_declare_return_types |
| problemMessage: "The function '{0}' should have a return type but doesn't." |
| correctionMessage: "Try adding a return type to the function." |
| addedIn: "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); |
| ``` |
| always_declare_return_types_of_methods: |
| sharedName: always_declare_return_types |
| problemMessage: "The method '{0}' should have a return type but doesn't." |
| correctionMessage: "Try adding a return type to the method." |
| hasPublishedDocs: true |
| always_put_control_body_on_new_line: |
| problemMessage: "Statement should be on a separate line." |
| correctionMessage: "Try moving the statement to a new line." |
| addedIn: "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. |
| always_put_required_named_parameters_first: |
| problemMessage: "Required named parameters should be before optional named parameters." |
| correctionMessage: "Try moving the required named parameter to be before any optional named parameters." |
| addedIn: "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}) ; |
| ``` |
| always_require_non_null_named_parameters: |
| addedIn: "2.0" |
| removedIn: "3.3" |
| 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. |
| always_specify_types_add_type: |
| sharedName: always_specify_types |
| problemMessage: "Missing type annotation." |
| correctionMessage: "Try adding a type annotation." |
| addedIn: "2.0" |
| categories: [style] |
| hasPublishedDocs: false |
| 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 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! |
| } |
| ``` |
| always_specify_types_replace_keyword: |
| sharedName: always_specify_types |
| problemMessage: "Missing type annotation." |
| correctionMessage: "Try replacing '{0}' with '{1}'." |
| hasPublishedDocs: false |
| always_specify_types_specify_type: |
| sharedName: always_specify_types |
| problemMessage: "Missing type annotation." |
| correctionMessage: "Try specifying the type '{0}'." |
| hasPublishedDocs: false |
| always_specify_types_split_to_types: |
| sharedName: always_specify_types |
| problemMessage: "Missing type annotation." |
| correctionMessage: "Try splitting the declaration and specify the different type annotations." |
| hasPublishedDocs: false |
| always_use_package_imports: |
| problemMessage: "Use 'package:' imports for files in the 'lib' directory." |
| correctionMessage: "Try converting the URI to a 'package:' URI." |
| addedIn: "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 |
| import [!'a.dart'!]; |
| ``` |
| |
| #### Common fixes |
| |
| Use a package import: |
| |
| ```dart |
| 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'; |
| ... |
| ``` |
| annotate_overrides: |
| problemMessage: "The member '{0}' overrides an inherited member but isn't annotated with '@override'." |
| correctionMessage: "Try adding the '@override' annotation." |
| addedIn: "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() {} |
| } |
| ``` |
| annotate_redeclares: |
| problemMessage: "The member '{0}' is redeclaring but isn't annotated with '@redeclare'." |
| correctionMessage: "Try adding the '@redeclare' annotation." |
| addedIn: "3.2" |
| categories: [style] |
| hasPublishedDocs: false |
| 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() { |
| ... |
| } |
| } |
| ``` |
| avoid_annotating_with_dynamic: |
| problemMessage: "Unnecessary 'dynamic' type annotation." |
| correctionMessage: "Try removing the type 'dynamic'." |
| addedIn: "2.0" |
| categories: [brevity, style] |
| hasPublishedDocs: false |
| 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; |
| } |
| ``` |
| avoid_as: |
| addedIn: "2.0" |
| removedIn: "3.0" |
| 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; |
| ``` |
| avoid_bool_literals_in_conditional_expressions: |
| problemMessage: "Conditional expressions with a 'bool' literal can be simplified." |
| correctionMessage: "Try rewriting the expression to use either '&&' or '||'." |
| addedIn: "2.0" |
| categories: [brevity] |
| hasPublishedDocs: 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 |
| ``` |
| avoid_catches_without_on_clauses: |
| problemMessage: "Catch clause should use 'on' to specify the type of exception being caught." |
| correctionMessage: "Try adding an 'on' clause before the 'catch'." |
| addedIn: "2.0" |
| categories: [effectiveDart, style] |
| hasPublishedDocs: false |
| 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." |
| avoid_catching_errors_class: |
| sharedName: avoid_catching_errors |
| problemMessage: "The type 'Error' should not be caught." |
| correctionMessage: "Try removing the catch or catching an 'Exception' instead." |
| addedIn: "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); |
| } |
| ``` |
| avoid_catching_errors_subclass: |
| sharedName: avoid_catching_errors |
| problemMessage: "The type '{0}' should not be caught because it is a subclass of 'Error'." |
| correctionMessage: "Try removing the catch or catching an 'Exception' instead." |
| hasPublishedDocs: false |
| avoid_classes_with_only_static_members: |
| problemMessage: "Classes should define instance members." |
| correctionMessage: "Try adding instance behavior or moving the members out of the class." |
| addedIn: "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'; |
| ``` |
| avoid_double_and_int_checks: |
| problemMessage: "Explicit check for double or int." |
| correctionMessage: "Try removing the check." |
| addedIn: "2.0" |
| categories: [errorProne, web] |
| hasPublishedDocs: false |
| 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 { |
| ... |
| } |
| } |
| ``` |
| avoid_dynamic_calls: |
| problemMessage: "Method invocation or property access on a 'dynamic' target." |
| correctionMessage: "Try giving the target a type." |
| addedIn: "2.12" |
| categories: [binarySize, errorProne] |
| hasPublishedDocs: false |
| 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(); |
| } |
| ``` |
| avoid_empty_else: |
| problemMessage: "Empty statements are not allowed in an 'else' clause." |
| correctionMessage: "Try removing the empty statement or removing the else clause." |
| addedIn: "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'); |
| ``` |
| avoid_equals_and_hash_code_on_mutable_classes: |
| problemMessage: "The method '{0}' should not be overridden in classes not annotated with '@immutable'." |
| correctionMessage: "Try removing the override or annotating the class with '@immutable'." |
| addedIn: "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; |
| } |
| ``` |
| avoid_escaping_inner_quotes: |
| problemMessage: "Unnecessary escape of '{0}'." |
| correctionMessage: "Try changing the outer quotes to '{1}'." |
| addedIn: "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"; |
| ``` |
| avoid_field_initializers_in_const_classes: |
| problemMessage: "Fields in 'const' classes should not have initializers." |
| correctionMessage: "Try converting the field to a getter or initialize the field in the constructors." |
| addedIn: "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(); |
| } |
| ``` |
| avoid_final_parameters: |
| problemMessage: "Parameters should not be marked as 'final'." |
| correctionMessage: "Try removing the keyword 'final'." |
| addedIn: "2.16" |
| categories: [style] |
| hasPublishedDocs: false |
| 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 |
| ``` |
| avoid_function_literals_in_foreach_calls: |
| problemMessage: "Function literals shouldn't be passed to 'forEach'." |
| correctionMessage: "Try using a 'for' loop." |
| addedIn: "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) { |
| ... |
| } |
| ``` |
| avoid_futureor_void: |
| problemMessage: "Don't use the type 'FutureOr<void>'." |
| correctionMessage: "Try using 'Future<void>?' or 'void'." |
| addedIn: "3.6-wip" |
| 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 may be changed |
| or removed. Feedback on its behavior is welcome! The main issue is here: |
| https://github.com/dart-lang/linter/issues/4622 |
| avoid_implementing_value_types: |
| problemMessage: "Classes that override '==' should not be implemented." |
| correctionMessage: "Try removing the class from the 'implements' clause." |
| addedIn: "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); |
| }); |
| } |
| ``` |
| avoid_init_to_null: |
| problemMessage: "Redundant initialization to 'null'." |
| correctionMessage: "Try removing the initializer." |
| addedIn: "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; |
| } |
| ``` |
| avoid_js_rounded_ints: |
| problemMessage: "Integer literal can't be represented exactly when compiled to JavaScript." |
| correctionMessage: "Try using a 'BigInt' to represent the value." |
| addedIn: "2.0" |
| categories: [errorProne, web] |
| hasPublishedDocs: false |
| 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'); |
| ``` |
| avoid_multiple_declarations_per_line: |
| problemMessage: "Multiple variables declared on a single line." |
| correctionMessage: "Try splitting the variable declarations into multiple lines." |
| addedIn: "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; |
| ``` |
| avoid_null_checks_in_equality_operators: |
| problemMessage: "Unnecessary null comparison in implementation of '=='." |
| correctionMessage: "Try removing the comparison." |
| addedIn: "2.0" |
| categories: [style] |
| hasPublishedDocs: false |
| deprecatedDetails: |- |
| **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; |
| } |
| ``` |
| avoid_positional_boolean_parameters: |
| problemMessage: "'bool' parameters should be named parameters." |
| correctionMessage: "Try converting the parameter to a named parameter." |
| addedIn: "2.0" |
| categories: [effectiveDart, style] |
| hasPublishedDocs: 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); |
| ``` |
| avoid_print: |
| problemMessage: "Don't invoke 'print' in production code." |
| correctionMessage: "Try using a logging framework." |
| addedIn: "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'); |
| } |
| ... |
| } |
| ``` |
| avoid_private_typedef_functions: |
| problemMessage: "The typedef is unnecessary because it is only used in one place." |
| correctionMessage: "Try inlining the type or using it in other places." |
| addedIn: "2.0" |
| categories: [style] |
| hasPublishedDocs: false |
| deprecatedDetails: |- |
| **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); |
| ``` |
| avoid_redundant_argument_values: |
| problemMessage: "The value of the argument is redundant because it matches the default value." |
| correctionMessage: "Try removing the argument." |
| addedIn: "2.8" |
| categories: [brevity, style] |
| hasPublishedDocs: false |
| 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(); |
| } |
| ``` |
| avoid_relative_lib_imports: |
| 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." |
| addedIn: "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'; |
| |
| ... |
| ``` |
| avoid_renaming_method_parameters: |
| problemMessage: "The parameter name '{0}' doesn't match the name '{1}' in the overridden method." |
| correctionMessage: "Try changing the name to '{1}'." |
| addedIn: "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); |
| } |
| ``` |
| avoid_return_types_on_setters: |
| problemMessage: "Unnecessary return type on a setter." |
| correctionMessage: "Try removing the return type." |
| addedIn: "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); |
| ``` |
| avoid_returning_null: |
| addedIn: "2.0" |
| removedIn: "3.3" |
| 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; |
| ``` |
| avoid_returning_null_for_future: |
| addedIn: "2.1" |
| removedIn: "3.3" |
| 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. |
| avoid_returning_null_for_void_from_function: |
| sharedName: avoid_returning_null_for_void |
| problemMessage: "Don't return 'null' from a function with a return type of 'void'." |
| correctionMessage: "Try removing the 'null'." |
| addedIn: "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; |
| } |
| ``` |
| avoid_returning_null_for_void_from_method: |
| sharedName: avoid_returning_null_for_void |
| problemMessage: "Don't return 'null' from a method with a return type of 'void'." |
| correctionMessage: "Try removing the 'null'." |
| hasPublishedDocs: true |
| avoid_returning_this: |
| problemMessage: "Don't return 'this' from a method." |
| correctionMessage: "Try changing the return type to 'void' and removing the return." |
| addedIn: "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'); |
| ``` |
| avoid_setters_without_getters: |
| problemMessage: "Setter has no corresponding getter." |
| correctionMessage: "Try adding a corresponding getter or removing the setter." |
| addedIn: "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; |
| } |
| } |
| ``` |
| avoid_shadowing_type_parameters: |
| problemMessage: "The type parameter '{0}' shadows a type parameter from the enclosing {1}." |
| correctionMessage: "Try renaming one of the type parameters." |
| addedIn: "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>() {} |
| } |
| ``` |
| avoid_single_cascade_in_expression_statements: |
| problemMessage: "Unnecessary cascade expression." |
| correctionMessage: "Try using the operator '{0}'." |
| addedIn: "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(); |
| ``` |
| avoid_slow_async_io: |
| problemMessage: "Use of an async 'dart:io' method." |
| correctionMessage: "Try using the synchronous version of the method." |
| addedIn: "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 |
| } |
| ``` |
| avoid_type_to_string: |
| problemMessage: "Using 'toString' on a 'Type' is not safe in production code." |
| correctionMessage: "Try a normal type check or compare the 'runtimeType' directly." |
| addedIn: "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); |
| } |
| ``` |
| avoid_types_as_parameter_names: |
| problemMessage: "The parameter name '{0}' matches a visible type name." |
| correctionMessage: "Try adding a name for the parameter or changing the parameter name to not match an existing type." |
| addedIn: "2.0" |
| 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. |
| |
| #### 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)); |
| ``` |
| avoid_types_on_closure_parameters: |
| problemMessage: "Unnecessary type annotation on a function expression parameter." |
| correctionMessage: "Try removing the type annotation." |
| addedIn: "2.0" |
| categories: [style] |
| hasPublishedDocs: false |
| 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); |
| ``` |
| avoid_unnecessary_containers: |
| problemMessage: "Unnecessary instance of 'Container'." |
| correctionMessage: "Try removing the 'Container' (but not its children) from the widget tree." |
| addedIn: "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('...'), |
| ), |
| ], |
| ); |
| } |
| ``` |
| avoid_unstable_final_fields: |
| addedIn: "3.3" |
| removedIn: "3.3" |
| deprecatedDetails: |- |
| This rule has been removed. |
| avoid_unused_constructor_parameters: |
| problemMessage: "The parameter '{0}' is not used in the constructor." |
| correctionMessage: "Try using the parameter or removing it." |
| addedIn: "2.0" |
| # TODO(srawlins): This isn't even just about unintentional syntax; unused |
| # parameters can represent code bloat. |
| categories: [unintentional] |
| hasPublishedDocs: false |
| 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; |
| } |
| } |
| ``` |
| avoid_void_async: |
| problemMessage: "An 'async' function should have a 'Future' return type when it doesn't return a value." |
| correctionMessage: "Try changing the return type." |
| addedIn: "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(); |
| } |
| ``` |
| avoid_web_libraries_in_flutter: |
| problemMessage: "Don't use web-only libraries outside Flutter web plugins." |
| correctionMessage: "Try finding a different library for your needs." |
| addedIn: "2.6" |
| 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 |
| 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 |
| 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. |
| await_only_futures: |
| problemMessage: "Uses 'await' on an instance of '{0}', which is not a subtype of 'Future'." |
| correctionMessage: "Try removing the 'await' or changing the expression." |
| addedIn: "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); |
| } |
| ``` |
| camel_case_extensions: |
| problemMessage: "The extension name '{0}' isn't an UpperCamelCase identifier." |
| correctionMessage: "Try changing the name to follow the UpperCamelCase style." |
| addedIn: "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> { |
| // ... |
| } |
| ``` |
| camel_case_types: |
| problemMessage: "The type name '{0}' isn't an UpperCamelCase identifier." |
| correctionMessage: "Try changing the name to follow the UpperCamelCase style." |
| addedIn: "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); |
| ``` |
| cancel_subscriptions: |
| problemMessage: "Uncancelled instance of 'StreamSubscription'." |
| correctionMessage: "Try invoking 'cancel' in the function in which the 'StreamSubscription' was created." |
| addedIn: "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/linter/issues/317) |
| for more information. |
| cascade_invocations: |
| problemMessage: "Unnecessary duplication of receiver." |
| correctionMessage: "Try using a cascade to avoid the duplication." |
| addedIn: "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(); |
| ``` |
| cast_nullable_to_non_nullable: |
| 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." |
| addedIn: "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!; |
| ``` |
| close_sinks: |
| problemMessage: "Unclosed instance of 'Sink'." |
| correctionMessage: "Try invoking 'close' in the function in which the 'Sink' was created." |
| addedIn: "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 [linter#1381](https://github.com/dart-lang/linter/issues/1381) |
| for more information. |
| collection_methods_unrelated_type: |
| problemMessage: "The argument type '{0}' isn't related to '{1}'." |
| correctionMessage: "Try changing the argument or element type to match." |
| addedIn: "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 |
| } |
| ``` |
| combinators_ordering: |
| problemMessage: "Sort combinator names alphabetically." |
| correctionMessage: "Try sorting the combinator names alphabetically." |
| addedIn: "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; |
| ``` |
| comment_references: |
| problemMessage: "The referenced name isn't visible in scope." |
| correctionMessage: "Try adding an import for the referenced name." |
| addedIn: "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 |
| [linter#1142](https://github.com/dart-lang/linter/issues/1142) for more |
| information. |
| conditional_uri_does_not_exist: |
| problemMessage: "The target of the conditional URI '{0}' doesn't exist." |
| correctionMessage: "Try creating the file referenced by the URI, or try using a URI for a file that does exist." |
| addedIn: "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'; |
| ``` |
| constant_identifier_names: |
| problemMessage: "The constant name '{0}' isn't a lowerCamelCase identifier." |
| correctionMessage: "Try changing the name to follow the lowerCamelCase style." |
| addedIn: "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(); |
| } |
| ``` |
| control_flow_in_finally: |
| problemMessage: "Use of '{0}' in a 'finally' clause." |
| correctionMessage: "Try restructuring the code." |
| addedIn: "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 |
| int f() { |
| try { |
| return 1; |
| } catch (e) { |
| print(e); |
| } finally { |
| [!return 0;!] |
| } |
| } |
| ``` |
| |
| #### Common fixes |
| |
| If the statement isn't needed, then remove the statement, and remove the |
| `finally` clause if the block is empty: |
| |
| ```dart |
| int f() { |
| try { |
| return 1; |
| } catch (e) { |
| print(e); |
| } |
| } |
| ``` |
| |
| If the statement is needed, then move the statement outside the `finally` |
| block: |
| |
| ```dart |
| int f() { |
| try { |
| return 1; |
| } catch (e) { |
| print(e); |
| } |
| return 0; |
| } |
| ``` |
| 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; |
| } |
| } |
| ``` |
| curly_braces_in_flow_control_structures: |
| problemMessage: "Statements in {0} should be enclosed in a block." |
| correctionMessage: "Try wrapping the statement in a block." |
| addedIn: "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; |
| } |
| ``` |
| dangling_library_doc_comments: |
| problemMessage: "Dangling library doc comment." |
| correctionMessage: "Add a 'library' directive after the library comment." |
| addedIn: "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. |
| depend_on_referenced_packages: |
| problemMessage: "The imported package '{0}' isn't a dependency of the importing package." |
| correctionMessage: "Try adding a dependency for '{0}' in the 'pubspec.yaml' file." |
| addedIn: "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 |
| 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 |
| ``` |
| deprecated_consistency_constructor: |
| sharedName: deprecated_consistency |
| problemMessage: "Constructors in a deprecated class should be deprecated." |
| correctionMessage: "Try marking the constructor as deprecated." |
| addedIn: "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}); |
| } |
| ``` |
| deprecated_consistency_field: |
| sharedName: deprecated_consistency |
| problemMessage: "Fields that are initialized by a deprecated parameter should be deprecated." |
| correctionMessage: "Try marking the field as deprecated." |
| hasPublishedDocs: false |
| deprecated_consistency_parameter: |
| sharedName: deprecated_consistency |
| problemMessage: "Parameters that initialize a deprecated field should be deprecated." |
| correctionMessage: "Try marking the parameter as deprecated." |
| hasPublishedDocs: false |
| deprecated_member_use_from_same_package_with_message: |
| sharedName: deprecated_member_use_from_same_package |
| problemMessage: "'{0}' is deprecated and shouldn't be used. {1}" |
| correctionMessage: "Try replacing the use of the deprecated member with the replacement, if a replacement is specified." |
| addedIn: "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; |
| } |
| ``` |
| deprecated_member_use_from_same_package_without_message: |
| sharedName: deprecated_member_use_from_same_package |
| problemMessage: "'{0}' 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 |
| diagnostic_describe_all_properties: |
| problemMessage: "The public property isn't described by either 'debugFillProperties' or 'debugDescribeChildren'." |
| correctionMessage: "Try describing the property." |
| addedIn: "2.3" |
| categories: [errorProne, flutter] |
| hasPublishedDocs: false |
| 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)); |
| } |
| } |
| ``` |
| directives_ordering_alphabetical: |
| sharedName: directives_ordering |
| problemMessage: "Sort directive sections alphabetically." |
| correctionMessage: "Try sorting the directives." |
| addedIn: "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 |
| ``` |
| directives_ordering_dart: |
| sharedName: directives_ordering |
| problemMessage: "Place 'dart:' {0}s before other {0}s." |
| correctionMessage: "Try sorting the directives." |
| hasPublishedDocs: false |
| directives_ordering_exports: |
| sharedName: directives_ordering |
| problemMessage: "Specify exports in a separate section after all imports." |
| correctionMessage: "Try sorting the directives." |
| hasPublishedDocs: false |
| directives_ordering_package_before_relative: |
| sharedName: directives_ordering |
| problemMessage: "Place 'package:' {0}s before relative {0}s." |
| correctionMessage: "Try sorting the directives." |
| hasPublishedDocs: false |
| discarded_futures: |
| problemMessage: "Asynchronous function invoked in a non-'async' function." |
| correctionMessage: "Try converting the enclosing function to be 'async' and then 'await' the future." |
| addedIn: "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 {} |
| ``` |
| do_not_use_environment: |
| problemMessage: "Invalid use of an environment declaration." |
| correctionMessage: "Try removing the environment declaration usage." |
| addedIn: "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; |
| ``` |
| document_ignores: |
| problemMessage: "Missing documentation explaining why the diagnostic is ignored." |
| correctionMessage: "Try adding a comment immediately above the ignore comment." |
| addedIn: "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; |
| ``` |
| empty_catches: |
| problemMessage: "Empty catch block." |
| correctionMessage: "Try adding statements to the block, adding a comment to the block, or removing the 'catch' clause." |
| addedIn: "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); |
| } |
| ``` |
| empty_constructor_bodies: |
| problemMessage: "Empty constructor bodies should be written using a ';' rather than '{}'." |
| correctionMessage: "Try replacing the constructor body with ';'." |
| addedIn: "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); |
| } |
| ``` |
| empty_statements: |
| problemMessage: "Unnecessary empty statement." |
| correctionMessage: "Try removing the empty statement or restructuring the code." |
| addedIn: "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(); |
| ``` |
| enable_null_safety: |
| addedIn: "2.19" |
| removedIn: "3.0" |
| 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() { |
| } |
| ``` |
| eol_at_end_of_file: |
| problemMessage: "Missing a newline at the end of the file." |
| correctionMessage: "Try adding a newline at the end of the file." |
| addedIn: "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 |
| ``` |
| erase_dart_type_extension_types: |
| problemMessage: "Unsafe use of 'DartType' in an 'is' check." |
| correctionMessage: "Ensure DartType extension types are erased by using a helper method." |
| addedIn: "3.3" |
| categories: [errorProne] |
| hasPublishedDocs: false |
| todo: |- |
| TODO(nshahan): Update. |
| deprecatedDetails: |- |
| Experimental WIP lint to help ensure `DartType` accesses are safe in the dev_compiler. |
| |
| **For internal use only.** |
| exhaustive_cases: |
| problemMessage: "Missing case clauses for some constants in '{0}'." |
| correctionMessage: "Try adding case clauses for the missing constants." |
| addedIn: "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; |
| } |
| } |
| ``` |
| file_names: |
| problemMessage: "The file name '{0}' isn't a lower_case_with_underscores identifier." |
| correctionMessage: "Try changing the name to follow the lower_case_with_underscores style." |
| addedIn: "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 lowercas
|