What’s new in DCM 1.14.0
Today we’re excited to announce the release of DCM 1.14.0!
This release includes a new command to find duplicate functions, methods and constructors, new built-in preset to avoid installing an additional package, 4 new rules, and more 🚀.
The recommended preset now has new rules from 1.13.0, so you may get new warnings once you update the dart-code-metrics-presets
package dependency.
Now let's go through the highlights of this release 🚀.
Notable website updates
Features page
To simplify DCM features discoverability, the website now has a dedicated page with available features. This change also resulted in the removal of the "Core Concepts" docs page.
If you are still considering whether to use DCM or not, we hope this page helps you with that decision.
Ensuring DCM IDE extension is installed by your entire team
VS Code and IntelliJ extension docs now include a new section to help you ensure that your entire team has the DCM extension installed.
VS Code users can configure the "Workspace recommended extensions".
{
"recommendations": ["dcmdev.dcm-vscode-extension"]
}
And IntelliJ users can set up the list of "Required plugins".
Enabling inline warnings
Another extension docs update includes a new section on how to make IDE diagnostics appear as text at the end of the line.
For VS Code, consider installing the Error Lens extension.
For IntelliJ, the Inspection Lens plugin gives the same functionality.
Make sure the installed third-party plugin is not malicious. We are not liable for any damage caused by third-party plugins.
New command "dcm check-code-duplication"
We are excited to introduce a new command to help you find duplicate functions, methods and constructors This command performs a structural check and is resistant to insignificant code changes.
For example,
void doWork() {
functionWithParameters(firstNamed: ..., secondNamed: ...);
someMethod(...);
}
void doAnotherWork() {
functionWithParameters(secondNamed: ..., firstNamed: ...);
shared.someMethod(...);
}
Even though these two functions have different names, different named arguments order and call the same function with a different import prefix, all these differences do not change the fact that this code is a duplication.
And this command will report these functions as such.
Here is an example of the CLI output:
Configuration
There are several ways you can configure this command.
First, you can enable code comparison on a per-package level. To do so, just pass the --per-package
CLI flag.
Using the --exclude-overrides
flag, you can exclude methods with the @override
annotation from the command output.
And last but not least, you can configure the minimum number of statements via --statements-threshold
(default is 3).
Note that the --statements-threshold
does not affect expression bodies and single return statements.
If you have any feedback regarding the new command, please consider posting it on our Discord.
This is the first iteration of the code duplication detection in DCM. We plan to introduce per-statement duplication detection later.
Presets update
To address feedback about the additional complexity with presets (related to installing an additional package), this release includes a new built-in preset called "recommended".
This preset matches the "recommended" preset in dart_code_metrics_presets
, but is included into the tool.
Here is how you can enable it:
dart_code_metrics:
extends:
- recommended
One downside of this approach is that this preset is updated along with the executable, so you could potentially get new warnings after updating.
Keep this in mind if you decide to migrate to the built-in preset.
Trial license changes
The introduction of the built-in preset also made it possible to disable the "recommended rules by default" behavior for the trial license, since enabling rules now requires less effort.
Therefore, if you are still in the trial period and rely on this feature, you will need to migrate to the recommended preset described above.
Additionally, "dcm analyze" now suggests enabling rules for external packages that are added to your project and that DCM supports (for example, bloc) if they are not already enabled. We hope this helps with discovering applicable rules.
Reworked excludes for all commands
Prior to this release, if a package was excluded in the root analysis_options file (in the analyzer exclude
section), all command would still analyze it and show output for it. But the IDE integration was not showing any issues for that package (as it should).
With this release we are brining this behavior towards the one the IDE has.
This means that if you open your project from the root and have a subpackage excluded in the root analysis_options, the commands won't show any output for this package unless you open its root directory (the one with the pubspec.yaml) and call the command from there.
Baseline improvements
Unified path separator
With this release, baseline now uses /
as path separator for all platforms to avoid the problem with baseline generated on Windows not working on other platforms.
If your team generated baseline on the Windows device, you'll need to regenerate it after updating to 1.14.0.
New mode for IDE integration
Another baseline change allows you to show issues from baseline in your IDE, but keep them ignored when DCM is called from CLI or on CI/CD.
Both VS Code and IntelliJ extensions now have a new config option called Disable Baseline
to enable this mode.
Preview improvements
Preview command now has a new option to exclude issues that are already in baseline to give you better perspective on the number of violation in your projects.
You can hide these issues with the --no-baseline-violations
CLI flag.
Rule updates
Among other rule changes, there are two that we would like to take a closer look at.
First, the member-ordering rule got a new config option field-getter-setter
that helps you group together fields, getters and setters with the same name.
This group has higher priority then individual groups (-fields, -getters or -setters) and only applies to class members if there are at least 2 of 3 declarations with the same name (e.g. a field AND a getter).
Additionally, if your class has multiple field-getter-setter groups, the field-getter-setter
config will require to sort fields, getters and setter by their name (based on the order of the first declaration in the group).
For example,
class Correct {
final String someField;
const Correct(this.someField, this._forGetter, this._another);
final String _forGetter;
final String _another;
String get forGetter => _forGetter;
String get another => _another;
}
here, even though the field-getter-setter
is the last, but the rule will still trigger, unless you group fields and getters by their name:
class Correct {
final String someField;
const Correct(this.someField, this._forGetter, this._another);
final String _forGetter;
String get forGetter => _forGetter;
final String _another;
String get another => _another;
}
Another notable change is that five existing rules (match-class-name-pattern
, avoid-banned-annotations
, banned-usage
, avoid-banned-imports
and avoid-banned-types
) got a new config option called severity
which allows you to override the main severity of the rule for each configuration entry separately.
For example,
dart_code_metrics:
rules:
- avoid-banned-imports:
severity: perf
entries:
- paths: ['some/folder/.*\.dart', 'another/folder/.*\.dart']
deny: ['package:flutter/material.dart']
message: 'Do not import Flutter Material Design library, we should not depend on it!'
severity: error
will mark all issues matching the config entry as errors.
New rules
Common
avoid-empty-test-groups. Warns when a test group does not have any test cases.
Test groups with no test cases can be accidentally introduced, for example during refactoring or when writing nested groups.
For example,
group('some group', () {
group('inner group', () {
setUp({
...
});
});
});
here, the inner group contains only the setUp
invocation and no test cases.
avoid-contradictory-expressions. Warns when several conditions contradict each other.
The idea behind this rule is to help you quickly spot contradicting expressions in conditions which are usually a bug.
For example,
void fn() {
final num = 1;
final anotherNum = 2;
if (anotherNum == 3 && anotherNum == 4) {}
if (anotherNum < 4 && anotherNum > 4) {}
if (anotherNum == 2 && anotherNum != 2) {}
if (anotherNum == num && anotherNum != num) {}
}
here, all four if statements contain contradicting expressions that will never be evaluated to true
.
avoid-excessive-expressions. Warns when a condition has excessive expressions.
For example,
void fn() {
final num = 1;
final anotherNum = 2;
if (anotherNum == 3 && anotherNum != 4) {}
if (num != null && num is int) {}
if (num == null || num is! int) {}
if (5 < anotherNum && anotherNum > 4) {}
}
here, anotherNum == 3
also means that the value is not equal 4. Therefore, the second condition is excessive and can be safely removed.
Next, num is int
implicitly checks for the num
value not to equal null
which makes the first condition excessive. Same applies to num == null || num is! int
.
And last but not least, anotherNum > 4
includes the > 5
range making the first condition excessive.
avoid-not-encodable-in-to-json. Warns when a toJson
method references a non-encodable object.
Passing results of such toJson
methods to jsonEncode
will result in a runtime exception. If you for some reason write toJson
methods manually, this rule is for you.
For example,
class SomeClass {
final String value;
const SomeClass(this.value);
}
class WithJson {
Map<String, Object?> toJson(Set<String> values) => {
'key': values,
'function': () => null,
'instance': SomeClass(''),
};
}
here, all three keys have non-encodable values.
To make this map encodable, it can be rewritten to:
class SomeClass {
final String value;
const SomeClass(this.value);
Map<String, Object?> toJson() => {'some': 'class'};
}
class WithJson {
Map<String, Object?> toJson(Set<String> values) => {
'key': values.toList(),
'instance': SomeClass(''),
};
}
What’s next
Documentation improvements. We plan to improve the overall state of the documentation, including a rewrite of the Getting Started section, introducing several user guides, as well as updating the example rules and adding more information about the problem behind each rule. We hope these changes will help us better explain DCM's features and help you get started with the product faster.
New command to analyze project structure. This command will help you visualize and analyze how different files / folders / packages depend on each other. Such analysis can help you create better boundaries between different modules of your apps and make the code more maintainable. On top of that, the command will allow validation of the project structure including cases when a file is expected to be present, but it as actually missing.
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!