rspec/rules/S7113/dart/rule.adoc

139 lines
4.3 KiB
Plaintext
Raw Normal View History

Classes marked with the https://api.flutter.dev/flutter/meta/immutable-constant.html[`@immutable` annotation] should only have https://dart.dev/language/constructors#constant-constructors[`const` constructors].
== Why is this an issue?
The `meta.dart` package defines an `@immutable` annotation that can be used to mark classes that are https://en.wikipedia.org/wiki/Immutable_object[immutable], meaning that all their instance variables, directly defined or inherited, have to be https://dart.dev/language/variables#final-and-const[`final`], that is, once the instance is created, it cannot be modified.
However, like any other annotation, the `@immutable` annotation only provides information to the developer using the class and to developer tools such as the analyzer package, and it does not enforce any constraint.
Therefore, there is nothing that prevents a developer from creating a mutable class and marking it as `@immutable`, as in the following example:
[source,dart]
----
import 'package:meta/meta.dart';
@immutable
class Point {
int x;
int y;
Point(this.x, this.y);
}
void main() {
var p = Point(2, 3);
p.x = 4;
print(p.x); // Will print 4
}
----
This is a problem because it can lead to confusion and bugs, as developers might expect the class to be immutable and not realize that it is actually not.
The best way to prevent this from happening is to make sure that classes marked with the `@immutable` annotation only have https://dart.dev/language/constructors#constant-constructors[`const` constructors]. This will in turn ensure that all instance variables are `final`, hence the instance is truly immutable.
[source,dart]
----
import 'package:meta/meta.dart';
@immutable
class Point {
final int x; // Final required by the const constructor
final int y; // Final required by the const constructor
const Point(this.x, this.y);
}
void main() {
const p = Point(2, 3);
// p.x = 4; // This will now be a compile-time error
print(p.x);
}
----
=== What is the potential impact?
Even if the `@immutable` class is truly immutable, missing the `const` constraint on its constructors will lead to subpar performance, as the Dart compiler will not create compile-time constants in a non-constant context, and will raise a compile-time error in a const context.
[source,dart]
----
import 'package:meta/meta.dart';
@immutable
class Point {
final int x;
final int y;
Point(this.x, this.y); // Missing const
}
void main() {
const p = Point(2, 3); // Compile-time error
const list = [Point(2, 3)]; // Compile-time error
var list = const [Point(2, 3)]; // Compile-time error
}
----
== How to fix it
Add the constant constraint to each of the class's constructors, by adding the `const` keyword before the constructor's name.
=== Code examples
==== Noncompliant code example
[source,dart,diff-id=1,diff-type=noncompliant]
----
import 'package:meta/meta.dart';
@immutable
class Point {
final int x;
final int y;
Point(this.x, this.y); // Non compliant
}
----
==== Compliant solution
[source,dart,diff-id=1,diff-type=compliant]
----
import 'package:meta/meta.dart';
@immutable
class Point {
final int x;
final int y;
const Point(this.x, this.y);
}
----
== Resources
=== Documentation
* Dart Docs - https://dart.dev/tools/linter-rules/prefer_const_constructors_in_immutables[Dart Linter rule - prefer_const_constructors_in_immutables]
* Dart Docs - https://dart.dev/language/variables#final-and-const[Language - Final and const]
* Dart Docs - https://dart.dev/language/constructors#constant-constructors[Language - Constant constructors]
* Flutter API Reference - https://api.flutter.dev/flutter/meta/immutable-constant.html[meta.dart - immutable top-level constant]
* Wikipedia - https://en.wikipedia.org/wiki/Immutable_object[Immutable object]
=== Related rules
* S7112 - Const constructors should be invoked with const
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
Constructors in '@immutable' classes should be declared as 'const'.
=== Highlighting
The name identifier of the constructor: e.g. `Point` in `Point(this.x, this.y);`.
In the case of an `extension type`, it's the name identifier of the `extension type` itself: e.g. `ExtensionTypeName` in `extension type ExtensionTypeName(int i)`.
endif::env-github,rspecator-view[]