What’s new in DCM 1.22.0
Today we’re excited to announce the release of DCM 1.22.0!
This release includes 15 new rules, DCM Teams Console improvements, ignore comment support for pubspec rules and the first version of our public roadmap.
In the next release we'll update the recommended preset to include new rules. If you use this preset, please be ready to address new warnings.
Let's go through the highlights of this release! (And you can find the full list of changes in our changelog)
Introducing Our Public Roadmap
We are excited to share the first version of our public roadmap.
It provides an overview of what is planned for the upcoming months (without going into too much detail and without describing smaller features like what rules will be added).
If it's something the community finds useful, we'll put more effort into improving it.
Let us know what you think!
Introducing Our New Video Series
In August we announced our new YouTube series: 🎉 Rules of the Week! 🎉.
Join us as we discuss various bugs and style issues in your Flutter code, share tips & show how DCM can help avoid them. 💻✨
DCM Teams Console Updates
Multiple Email Logins
We are excited to announce that multiple login emails for the same account are now supported!
If you want your account to have multiple login emails, please reach out to our support.
New Modal to Reset CI/CD Key
Admin panel now supports resetting CI/CD keys. To reset your key, use the "Generate new CI key" button and then click "Generate new key".
Ignore Comments for Pubspec Rules
With this release you can now ignore pubspec rules with # ignore:
and # ignore_for_file:
ignore comments!
name: some_package
dependencies:
some_dependency: ^1.0.0 # ignore: prefer-pinned-version-syntax
Code actions to add ignores are currently not available, so you need to add the ignore comments manually.
Rule updates
"avoid-unrelated-type-casts" and "avoid-unnecessary-type-casts" Update
avoid-unrelated-type-casts
and avoid-unnecessary-type-casts
now also support unrelated or unnecessary .cast
invocations.
For example,
void main() {
<int>[].cast<String>();
final List<String?> myList = ['', null];
final newList = myList.cast<List<String>>().toList();
}
here, .cast<String>()
and .cast<List<String>>()
are unrelated to the type of the target collection and will throw at runtime.
New rules
Common
avoid-unnecessary-extends. Suggests removing unnecessary extends
clauses that match default values for classes and type parameters.
In Dart, all classes by default extend Object
and all type parameters extend Object?
. Removing those extends clauses will not affect your code.
For example,
class Base {}
class Subclass extends Object {}
class Another extends Base {}
class WithTypeParam<T extends Object?, B extends Object> {}
here, Subclass
explicitly extends Object
and since it's the default behavior, this extend clause can be removed. Same for the T
parameter.
avoid-assignments-as-conditions. Warns when an assignment is used inside a condition.
Assignments inside conditions can not only make readability worse, but also be a sign of a bug (then =
is used instead of ==
).
For example,
void fn(List<String> values) {
bool? flag;
if (flag ??= values.isEmpty) {}
if (flag = values.isEmpty) {}
}
here, to address the rule's warnings for both assignments, it's recommended to move them out of the condition and use flag
instead.
avoid-type-casts. Warns about any usages of the as
operator.
Incorrect type casts can throw an exception at runtime (which is usually undesired and not expected).
For example,
void fn() {
dynamic a = 3;
final s = a as String;
int b = 2;
final s = b as String;
if (b case String() as Object) {}
}
here, a as String
and b as String
will throw at runtime. To avoid this, it's recommended to use type checks (is
operator) instead.
void fn(List<String> values) {
dynamic a = 3;
if (a is String) {
...
}
}
avoid-unused-assignment. Warns when an assignment is not used in the subsequent statements.
For example,
void fn1() {
var v = 'used';
doSomething(v);
v = 'unused';
}
void fn2(bool condition) {
var v = 'used';
if (condition) {
v = 'unused';
return;
}
doSomething(v);
}
here, both assignments to v
after the declaration are actually not used and can be simply removed.
avoid-unnecessary-overrides. Warns when a parent declaration is overridden by a declaration without an implementation.
For example,
abstract class B {
String get field;
void foo();
}
abstract class A extends B {
String get field;
Future<void> foo();
}
mixin M on B {
String get field;
}
here, field
and foo
overrides add no implementation or additional logic and therefore can be removed without changing how the code behaves.
avoid-duplicate-constant-values. Warns when a class or enum declaration has several constants with the same primitive values.
For example,
enum MyEnum {
a('hi'),
b('hi'),
c('another');
final String value;
const MyEnum(this.value);
}
class RuleType {
final String value;
const RuleType._(this.value);
static const common = RuleType._('common');
static const flutter = RuleType._('common');
}
here, the string values passed to b
and flutter
constant match a
and common
(respectively). Having different constant values with the same primitive value is usually undesired and is a sign of a bug.
avoid-unnecessary-enum-arguments. Warns when a enum constant has an unnecessary empty argument list.
For example,
enum MyEnum {
alpha.named(),
beta.named(),
gama();
final String value;
const MyEnum.named() : value = '2';
const MyEnum() : value = '3';
}
here, parentheses in gama()
have no use and can be removed
enum MyEnum {
alpha.named(),
beta.named(),
gama;
final String value;
const MyEnum.named() : value = '2';
const MyEnum() : value = '3';
}
prefer-contains. Suggests using .contains
instead of .indexOf
when checking for the presence of an element.
For example,
void fn() {
final list = [1, 2, 3];
if (list.indexOf(1) == 2) {}
if (list.indexOf(1) == -1) {}
if (list.indexOf(1) != -1) {}
}
here, both == -1
and != -1
can be replaced with a contains
instead.
avoid-unnecessary-constructor. Suggests removing an unnecessary empty constructor for enum and class declarations.
In Dart, classes and enums always have an implicit default constructor and declaring it explicitly is usually unnecessary.
For example,
class SomeClass {
final values = <int>{};
SomeClass();
}
enum MyEnum {
alpha,
beta,
gama;
const MyEnum();
}
here, SomeClass();
and const MyEnum();
can be simply removed.
avoid-implicitly-nullable-extension-types. Warns when an extension type declaration does not have the implements
clause.
Not adding the implements
clause (when applicable) implicitly makes the extension type nullable even when the representation type is not.
For example,
extension type A(Map json) {
B get b => json['b'] as B;
}
extension type B(Map json) {}
void main() {
var a = A({'b': {}, 'c': 'hello'});
a.b!;
}
here, since B
has no implements
clause, it's considered allowing both nullable and non-nullable values (even though Map
is not marked as nullable). This leads to the analyzer not showing a warning for a.b!
(for the !
being unnecessary) which is usually undesirable.
To avoid this, extension types with a non-nullable representation type should have the implements
clause.
avoid-getter-prefix. Warns when a getter name starts from a banned prefix.
For example,
dart_code_metrics:
...
rules:
...
- avoid-getter-prefix:
prefix: '^get'
abstract class ModalRoute {
bool get getBarrierDismissible;
}
class Some implements ModalRoute {
bool get getBarrierDismissible => false;
}
here, getBarrierDismissible
will be highlighted because it starts with the banned prefix.
avoid-assigning-to-static-field. Warns when an instance method assigns to a static field.
For example,
class Some {
static int value = 1;
void work() {
value = 2;
}
}
should be rewritten to
class Some {
static int value = 1;
static void work() {
value = 2;
}
}
avoid-unnecessary-enum-prefix. Suggests removing unnecessary enum prefixes.
For example,
enum MyEnum {
alpha(),
beta(),
gama();
const MyEnum();
int someFunction() => switch (this) {
MyEnum.alpha => 0,
beta => 1,
MyEnum.gama => 2,
};
}
here, since MyEnum.alpha
is used inside the MyEnum
declaration, the MyEnum
prefix can be omitted.
enum MyEnum {
alpha(),
beta(),
gama();
const MyEnum();
int someFunction() => switch (this) {
alpha => 0,
beta => 1,
gama => 2,
};
}
avoid-non-final-exception-class-fields. Warns when an exception class declaration has non-final fields.
Mutating exception class fields is usually undesired and can lead to unexpected behavior.
For example,
class CaughtException implements Exception {
final Object exception;
final String? message;
StackTrace stackTrace;
CaughtException(Object exception, StackTrace stackTrace)
: this.withMessage(null, exception, stackTrace);
CaughtException.withMessage(this.message, this.exception, this.stackTrace);
}
should be rewritten to
class CaughtException implements Exception {
final Object exception;
final String? message;
final StackTrace stackTrace;
CaughtException(Object exception, StackTrace stackTrace)
: this.withMessage(null, exception, stackTrace);
CaughtException.withMessage(this.message, this.exception, this.stackTrace);
}
Pub
prefer-pinned-version-syntax. Warns when a dependency version is not specified as the exact version (e.g. 1.2.3).
Using exact version can help you avoid unexpected breaking changes in patch versions and have more control over which package version is used.
For example,
name: some_package
description: ...
version: 1.0.0
dependencies:
some_dependency: ^1.0.0
should be rewritten to
name: some_package
description: ...
version: 1.0.0
dependencies:
some_dependency: 1.0.0
What’s next
To learn more about upcoming features, check out our public roadmap.
And to learn more about upcoming videos and "Rules of the Week" content, subscribe to our Youtube Channel.
Sharing your feedback
If there is something you miss from DCM right now, want us to make something better, or have any general feedback - join our Discord server!