What’s new in DCM 1.9.0
Today we’re excited to announce the release of DCM 1.9.0!
New "Recommended" presets, 13 new rules and many more improved, dedicated GitHub Action to run DCM checks 🚀.
Let’s go on a quick journey together to explore all updates!
New "Recommended" presets
Considering the number of rules and metrics DCM provides, it might be hard to pick the ones you want to start with.
To address this problem this release adds two new presets: recommended
and metrics_recommended
.
The first one provides the core set of rules with a main focus on highlighting potential errors. There are also some other rules to simplify code and just a few to enforce style. We are not intended to update this preset regularly (aside from the all
preset which gets updated with every release).
And the metrics_recommended
preset provides all metrics with the default recommended values to simplify enabling them.
DCM GitHub Action
To simplify setting up DCM on GitHub, you can now use two dedicated GitHub actions: setup-dcm
and dcm-action
.
setup-dcm
setup-dcm
installs and sets up DCM for use in GitHub Actions.
Usage example
name: DCM
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Dart and Flutter
uses: subosito/flutter-action@v2
- name: Install dependencies
run: flutter pub get
- name: Install DCM
uses: CQLabs/setup-dcm@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Run DCM
run: dcm analyze --ci-key="${{ secrets.DCM_CI_KEY }}" --email="${{ secrets.DCM_EMAIL }}" lib
Inputs
The action takes the following inputs:
github_token
: Used to get the latest DCM version from GitHub releases (required).version
: Which DCM version to setup:- A specific DCM version (ex.
1.6.0
) - or
latest
(default)
- A specific DCM version (ex.
Outputs
The action produces the following output:
dcm-version
: The version of the DCM executable that was installed.
dcm-action
dcm-action
runs DCM checks in GitHub Actions. It can also add a comment with dcm checks status to your Pull Requests.
Usage example (combined with setup-dcm)
name: DCM
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Dart and Flutter
uses: subosito/flutter-action@v2
- name: Install dependencies
run: flutter pub get
- name: Install DCM
uses: CQLabs/setup-dcm@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Run DCM
uses: CQLabs/dcm-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
ci_key: ${{ secrets.DCM_CI_KEY }}
email: ${{ secrets.DCM_EMAIL }}
folders: lib
Inputs
The action takes the following inputs:
Name | Required | Description | Default |
---|---|---|---|
github_token | ☑️ | Required to post a report on GitHub. Note: the secret GITHUB_TOKEN is already provided by GitHub and you don't have to set it up yourself. | |
github_pat | Required if you had private GitHub repository in the package dependencies | Personal access token must access to repo and read:user scopes | |
ci_key | ☑️ | License key to run on CI server. | |
☑️ | Email used to purchase the license. | ||
folders | List of folders and files (separated by commas) to scan. | [lib ] | |
pull_request_comment | Publish report overview directly into your pull request. | true | |
package_name | Package name to differentiate the report. Set when running several DCM runs at once. | ||
fatal_performance | Treat performance level issues as fatal. | true | |
fatal_style | Treat style level issues as fatal. | true | |
fatal_warnings | Treat warning level issues as fatal. | true |
Outputs
Action console
PR summary
PR comment
New rules
Fake Async
avoid-async-callback-in-fake-async. Warns when an async callback is passed to FakeAsync
.
FakeAsync
implementation does not await an async callback and the test will pass even if it should not.
For example,
void main() {
FakeAsync().run((fakeAsync) async {
...
});
fakeAsync((fakeAsync) async {
...
});
}
should be rewritten to
void main() {
FakeAsync().run((fakeAsync) {});
fakeAsync((fakeAsync) {});
}
Common
move-variable-closer-to-its-usage. Warns when a variable is declared in the outer block, but used only in the inner one.
Declaring variables too early can result in additional memory being allocated and potentially heavy calculations being performed.
For example,
void withIf(bool condition) {
final value = 1;
if (condition) {
print(value);
}
}
should be rewritten to
void withIf(bool condition) {
if (condition) {
final value = 1;
print(value);
}
}
avoid-passing-self-as-argument. Warns when an object is used as an argument to its own method.
For example,
final list = [1, 2, 3];
final another = [4, 5, 6];
list.addAll(list);
should be rewritten to
final list = [1, 2, 3];
final another = [4, 5, 6];
list.addAll(another);
avoid-passing-default-values. Warns when an invocation has an argument that matches the parameter's default value.
Explicitly passing a value that is equal to the default value can be considered redundant, but if you depend on external API, you might want to keep explicitly passed default values in case this API changes. Consider that before enabling this rule.
For example,
class WithString {
final String value;
const WithString({this.value = 'some'});
}
void main() {
WithString(value: 'some');
}
should be rewritten to
class WithString {
final String value;
const WithString({this.value = 'some'});
}
void main() {
WithString();
// OR
WithString(value: 'another');
}
prefer-getter-over-method. Suggests to convert a method that has no parameters and side-effects to a getter.
For example,
extension StringExtension on String {
String useCorrectEllipsis() {
return replaceAll('', '\u200B');
}
String removeSpaces() => replaceAll(' ', '');
String removeHyphens() => replaceAll('-', '');
}
here, all these methods do not have any parameters, side-effects and other limitations for them to be converted to getters.
avoid-referencing-discarded-variables. Warns when a variable with the name that has only underscores (ex. _
, __
, etc.) is referenced.
For example,
void main() {
final _ = 'some value';
print(_);
}
should be rewritten to
void main() {
final value = 'some value';
print(value);
}
void main() {
final _ = 'some value';
// no other code that references _
}
avoid-unconditional-break. Warns when a break
, continue
, return
or throw
are used unconditionally in a for loop.
For example,
void main() {
final list = <int>[];
for (final item in list) {
continue;
}
for (final item in list) {
break;
}
for (final item in list) {
return;
}
for (final item in list) {
throw '';
}
}
here, all loops will get only one iteration. It's rarely a desired outcome and usually is an indicator of a mistake.
avoid-weak-cryptographic-algorithms. Warns when a weak cryptographic algorithm (ex. md5 or sha1) is used.
Although there are still some valid cases to use those algorithms, it's best to avoid them if you need to secure the data. Consider using more advanced algorithms instead.
For example,
import 'dart:convert';
import 'package:crypto/crypto.dart';
void main() {
final key = utf8.encode('password1234');
final hmacSha1 = Hmac(sha1, key);
}
here, it's preferable to use sha256 in order to avoid any vulnerabilities sha1 has.
avoid-identical-exception-handling-blocks. Warns when a try / catch has multiple catch blocks with the same body.
For example,
void main() {
try {
print('hello');
} on StateError catch (error) {
print(error);
} on Exception catch (error) {
print(error);
}
}
should be rewritten to
void main() {
try {
print('hello');
} on StateError catch (error) {
print('Got StateError $error');
} on Exception catch (error) {
print('Got Exception $error');
}
}
avoid-recursive-calls. Warns when a function calls itself recursively.
Dart does not support tail call optimization, so when working with a recursion there is always a chance to hit a StackOverflow error. Consider rewriting recursion to a stack instead.
avoid-missing-interpolation. Warns when a string is equal to a variable name that is available in the current scope but is not wrapped into an interpolation.
For example,
void main() {
final someClass = SomeClass('hello');
print('someClass');
print('value: $someClass.value');
print('isNotEmpty: $someClass.value.isNotEmpty');
print('$function(value)');
}
String function(String value) {
return 'result value';
}
class SomeClass {
final String value;
const SomeClass(this.value);
}
here, the rule will highlight not only someClass
being printed instead of the someClass.toString()
, but also that $someClass.value
is an incomplete interpolation and misses {}
.
avoid-unnecessary-if. Warns when a return statement inside an if block is equal to the return statement after it.
For example,
final value = 1;
int function() {
if (value == 2) {
return 1;
}
return 1;
}
here, both returns are equal which makes the if statement redundant.
parameters-ordering. Ensures consistent alphabetical order of parameters by their names.
For example,
void named({String b, String a}) {}
here, the rule will trigger since a
should come first.
What’s next
Improving the "unused code in the IDE" feature. There are plenty areas of improvements (for example, this feature only supports the "monorepo" mode for now, can be a bit annoying when you just created a new class and so on). That's why we are releasing it as "Beta" (and disabled by default) and will work on improving it in the upcoming releases.
New commands. Reworking metric HTML reports unblocked other commands that need an HTML report as well. We hope to add them in the upcoming release to make DCM cover even more use-cases.
Move performance improvements. We received a several reports about DCM performance issues on very large codebase. This is a serious problems that we also want to address. There is still some room to make DCM better.
Sharing your feedback
If there is something you miss from DCM right now or want us to make something better or have any general feedback - join our Discord server!