What’s new in DCM for Teams 1.3.0
New month, new DCM release! Today we’re excited to announce the release of DCM for Teams 1.3.0! In this version we completely reworked metrics (plus added new ones), added long promised baseline support, added support for applying dcm fix
on save, new rules and more!
Let’s go on a quick journey together to explore all the new features!
Completely reworked metrics
Metrics got a significant update!
Now, instead of calculating metrics via analyze
command, they have their own command calculate-metrics
. This separation allows to get better developer experience, as metrics and rules are completely different features that were coupled together, resulting in incorrect expectations (such as displaying metrics in GitHub reports and rule issues in HTML).
Metrics now support ignore comments. You can use the same syntax as for ignoring rules // ignore:
and // ignore_for_file:
. But you can also ignore all possible metrics within the file via // ignore: type=metric
.
HTML report also got an update. Previously, all metrics were hardcoded into the HTML report and calculated despite being disabled. New implementation allows to calculate and display only enabled metrics, providing more info like min
, max
, average
and sum
values. It's also possible to sort columns!
New metrics
This release includes eight new metrics.
Number of Imports (NOI). Helps measure the number of imports in a file. Too many imports indicate a high complexity or entities that are responsible for too many things.
Coupling Between Object Classes (CBO). Measures the number of classes to which a class is coupled. Two classes are coupled when methods declared in one class use methods or instance variables defined by the other class. The uses relationship can go either way: both uses and used-by relationships are taken into account, but only once.
High CBO is undesirable. Excessive coupling between object classes is detrimental to modular design and prevents reuse. The more independent a class is, the easier it is to reuse it in another application. In order to improve modularity and promote encapsulation, inter-object class couples should be kept to a minimum. The larger the number of couples, the higher the sensitivity to changes in other parts of the design, and therefore maintenance is more difficult. A high coupling has been found to indicate fault-proneness.
Depth of Inheritance Tree (DIT). Measures the maximum inheritance path from the class to the root class. The deeper a class is in the hierarchy, the more methods and variables it is likely to inherit, making it more complex. Deep trees as such indicate greater design complexity. Inheritance is a tool to manage complexity, really, not to not increase it. As a positive factor, deep trees promote reuse because of method inheritance.
Response for a Class (RFC). Measures the number of methods in the class plus number of remote methods directly called by methods of the class. The response set of a class is a set of methods that can potentially be executed in response to a message received by an object of that class. Since RFC specifically includes methods called from outside the class, it is also a measure of the potential communication between the class and other classes.
Tight Class Cohesion (TCC). Measures how well the methods of a class are related to each other. A cohesive class performs one function. A non-cohesive class performs two or more unrelated functions. A non-cohesive class may need to be restructured into two or more smaller classes.
Weighted Methods Per Class (WMC). Measures the sum of the complexity of all class methods.
Number of Used Widgets (NUW). Measures the number of widgets used inside a build
method.
Widgets Nesting Level (WNL). Measures the depths of widgets used inside a build
method. Deeply nested widget trees are read and maintain.
Baseline support
If adopting DCM on large projects was a problem for you due to need to address all issues at once, now DCM provides a way to generate a baseline which allows you to gradually adopt DCM and fix existing issues only when the code is changed.
DCM generates a JSON file with all existing issues and this file is used both by IDE and CLI. If any generated issue is found this file, it won't be reported.
Here is an example of generated file:
{
"date": "2023-04-06 11:21:12.433111Z",
"paths": {
"lib/src/fixes/models/fix_producer.dart": {
"prefer-match-file-name": [
"2dca28e191c53faa330a518d0af4ffc5"
]
},
"lib/src/fixes/correct_source_visitor.dart": {
"no-empty-block": [
"99914b932bd37a50b983c5e7c90ae93b",
"99914b932bd37a50b983c5e7c90ae93b"
],
"prefer-correct-identifier-length": [
"865c0c0b4ab0e063e5caa3387c1a8741",
"865c0c0b4ab0e063e5caa3387c1a8741"
]
},
}
}
As you can see, it contains file paths and issues for each file are grouped by their id. This format was chosen in order to help you speed up fixing existing issues. You can manually remove a specific file or set of issues to make them appear in IDE / CLI output.
You can find more info about baseline here.
Existing command improvements
All commands now have an alias allowing you to type even less. For example, dcm analyze lib
becomes dcm a lib
and dcm check-unnecessary-nullable lib
becomes just dcm un lib
.
You can find the list of all commands and aliases here.
Analyze
Since metrics are now calculated by a separate command, analyze
docs was updated to reflect these changes. Note, that this command now doesn't have an HTML reporter.
It also got a small update and now outputs a summary:
Scanned folders: 239
Scanned files: 596
perf issues: 47, style issues: 18, warning issues: 4
Unused files
Before, unused files commands treated cyclic usage as a valid usage (it's when a file is referenced by another file which in its turn is referenced by the first one).
Now, if there is only one level of cyclic usage and the files are not used by other file, they both will be marked as unused.
Unnecessary nullable
Unnecessary nullable now correctly handles redirecting constructors, tear-offs and external members.
Fix
In this release we fixed a bunch of false-positives with formatting and the fix
command now runs only on files that have issues, when launched via CLI.
You can now also run dcm fix
on an opened file in the IDE (even on save). Read more about this below in IDE updates.
We're also working on making more rules supported by dcm fix
. The command now supports two more rules: prefer-correct-edge-insets
and prefer-immediate-return
, bringing the number of rules that have quick fixes, but are not supported by the fix command to 6 (and we hope to support them all in the upcoming release).
New rules
Common
prefer-explicit-parameter-names. Warns when parameter names in function types are omitted. Parameter names are used by IDEs code completion instead of default names like p0 and p1. Therefore, providing them can improve the overall DX.
For example,
class LayoutBuilder {
LayoutBuilder({this.builder});
final Widget Function(BuildContext, ConstraintType) builder;
}
if you use the IDE suggestion, the builder will receive the following callback (p0, p1) => {}
. But if both parameters have a name, their names will be used instead.
prefer-correct-switch-length. Warns when a switch has too few or too many cases. This rule can be useful to manage when to use switches instead of if statements.
avoid-equal-expressions. Warns when both sides of a binary expression are the same. If you work with complex if statements, this rule can help avoid tricky errors when both sides of a binary expression becomes equal due to refactoring.
For example,
// LINT
if (_isOneShifting() && _isOneShifting()) {
return;
}
// LINT
if (num == anotherNum && num == anotherNum) {
return;
}
issues like that might be hard to notice.
avoid-declaring-call-method. Warns when a class has a call method. Dart has a concept of callable classes, meaning that you can invoke a class instance (if a class has a call method) like if you'd invoke a regular function / method. While this feature might be convenient for some use-cases, this behavior is implicit and might lead to unexpected results or overcomplicated code.
For example,
class WannabeFunction {
String call(String a, String b, String c) => '$a $b $c!'; // LINT
}
var wf = WannabeFunction();
var out = wf('Hi', 'there,', 'gang');
can be replaced to a regular method:
class WannabeFunction {
String calculate(String a, String b, String c) => '$a $b $c!';
}
var wf = WannabeFunction();
var out = wf.calculate('Hi', 'there,', 'gang');
prefer-bytes-builder. If you work with large byte lists, this rule is must. It warns when an expensive byte list operation is used. Using lists concatenation, addAll or spread operator can significantly affect the performance. It's recommended to use a BytesBuilder instead. Just take a look at the benchmarks.
prefer-early-return. Warns when an if statement can be transformed to an early return. Early returns allow to reduce the code nesting level, improving the overall readability.
banned-usage. This is an improved version of ban-name
that allows to ban instance members.
For example,
final list = [1, 2, 3];
list.sort(); // LINT
if you want to avoid list sort
method and instead use sorted
, you can configure this rule to:
dart_code_metrics:
...
rules:
...
- banned-usage:
entries:
- type: List
entries:
- ident: sort
description: Use "sorted" instead
and it will trigger only for sort
invocations on the list instances.
avoid-missed-calls. Warns when a method that should be invoked is passed as tear-off. Helps to detect tricky bugs when a method is not called, but actually should be.
Flutter
dispose-fields. Warns when a widget state field is not disposed in dispose method. Not disposing fields that have a dispose method might lead to memory leaks and should be avoided.
For example,
class SomeDisposable implements Disposable {
void dispose() {}
}
class _ShinyWidgetState extends State<ShinyWidget> {
final _someDisposable = SomeDisposable();
final _anotherDisposable = SomeDisposable(); // LINT
void dispose() {
_someDisposable.dispose();
super.dispose();
}
}
here the field _anotherDisposable
should be disposed too.
avoid-empty-setstate. Warns when a setState callback is empty.
prefer-widget-private-members. If you prefer your widgets not to expose any extra public members (fields, methods, etc.) this rule will detect violations of this approach.
Performance improvements
We're continuing improving the overall memory / CPU usage and in this release a lot of underlying analyzer code, that is not used by DCM was removed in order to achieve that. You should see a 20% memory consumption reduction after updating to 1.3.0 and have your codebase analyzed several times.
You might also want to clear the cache by deleting .dartServer/dart-code-metrics-teams/
to speed up this process. Note, that after you clear the cache first 1-2 runs will consume more resources, than usual.
VS Code extension updates
VS Code now supports applying dcm fix
on save. In order to enable this feature, add the following config to the settings.json
file:
{
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.dcm.fixAll": true
}
}
If you don't want all fixable DCM issues be fixed on save, you can use a shortcut ⌘ + Shift + S
or call the "DCM: Fix All Auto-fixable Problems" command manually in command palette.
IntelliJ plugin updates
IntelliJ plugin now supports applying dcm fix
as well. You can use a shortcut ⌘ + Shift + S
or call the "DCM: Fix All Auto-fixable Problems" command manually in command palette.
It also got support for: code actions and configurable rule icons in the analysis_options
file, an assist to extract a class to the new file.
You can now easily apply quick fixes via DCM Problems View. The same shortcut as in the IDE editor allows you to quickly open the fix menu an choose a fix.
What’s next
Reworking metrics and reporting. There are still some work that needs to be done to polish new metrics implementation. We also want to add ability to view metric reports directly in the IDE.
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 codebases. This is a serious problems that we also want to address. There is still some room to make DCM better.
New rules and metrics. As usual, we're working on implementing new ideas about rules and metrics.
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 - there are several ways to share: you can join our Discord server or send us feedback directly from our website.