What’s new in DCM 1.19.0
Today we’re excited to announce the release of DCM 1.19.0!
This release includes 9 new rules (4 for Riverpod), proper support for codegen packages by the check-dependencies
command, new summary output to sdterr for various reporters, new visualization of the statically resolved widget trees for analyze-widgets
, new fix type for removing unused files (moved from check-unused-files
), documentation updates and other DX improvements! 🚀
Let's go through the highlights of this release! (And you can find the full list of changes in our changelog)
Documentation updates
As we continually work to improve our documentation, we released several notable changes in June to improve the overall user experience.
First, we published a new structure (and introduced categories for commands and metrics):
Next, we completely reworked the "Getting Started" page and split it into several pages (while also keeping the "Quick Start" guide):
And last but not least, we added new sections for Introduction to DCM, DCM Teams Console docs and new guides for GitLab and Bitbucket CI/CD integrations.
If you have any feedback regarding the new docs, please consider posting it on our Discord!
Deprecation of the "Performance" severity
The initial idea behind the "Performance" severity was to specify which rules directly impact performance. But it didn't really work.
First of all, a violation can be both a warning/error and a performance issue (or, for example, a readability issue). And secondly, most of the modern IDEs support only 3 colors for showing diagnostics (which are usually used for error, warning and style issue types).
So in this release, we're moving away from the "Performance" severity for the rules and will later introduce categories for rules and metrics to help you quickly identify which ones affect performance, readability, maintainability and so on.
And all rules that has the "Performance" severity are now warnings.
General command improvements
No congratulate flag changes
To avoid confusion with integrating DCM in CI/CD, the --no-congratulate
flag is now disabled by default for all reporters used in CI/CD (e.g. GitLab or JSON).
Explicitly passing --congratulate
or --no-congratulate
will override this behavior (which can be useful for debugging purposes).
Summary report example
JSON, Checkstyle, GitLab and Codeclimate reporters now output a report summary to stderr, which is designed to help you better understand why a command failed when you pipe the regular output to a file.
New hint for missing dependencies
Given that DCM relies on installed dependencies (and does not run dart pub get
or flutter pub get
), in this release all commands now show an error if the dependencies are not installed.
Analyze Widgets improvements
We've significantly improved the resolution of the statically available widget tree, including complex cases with switch statements, method invocations, external properties and so on.
Plus, the HTML report now also includes the resolved tree (as a mermaid diagram) to help you quickly identify potential areas for improvement:
We also reworked how the --report-all
CLI option works with reporters and now it applies to all reporters (not just the Console reporter).
Calculate Metrics improvements
As with "analyze-widgets", the --report-all
CLI option now works for all reporters (not just the Console reporter) and the JSON reporter now only reports metric violations by default.
In addition, this release also includes two HTML report improvements: new tooltips and corrected column sorting.
Given that the metrics table only has abbreviations for the metric names (so that each column takes up less space), it was sometimes difficult to understand which metric was which.
Now you can simply hover over the column headers to see the full name of each metric:
Regarding the sorting issue, the "Directory" and "File" columns now have the same internal id, which ensures sort consistency when selecting one of these columns:
Check Dependencies improvements
In this release, we significantly improved how the code generation packages are handled by the check-dependencies
command.
First, any code generation package that is listed in the main dependencies will be reported as used in the wrong dependencies section.
dependencies:
mobx_codegen: 2.6.1 # LINT
Next, if a code generation package has a corresponding regular package (e.g. mobx
for mobx_codegen
or riverpod
for riverpod_generator
), the code generation package will not be reported. The command will also show a hint to double-check that the code generation packages are still in use.
And last but not least, if the command finds configuration for flutter_gen
, flutter_launcher_icons
or flutter_native_splash
, they are also marked as used.
Check Unused Files improvements
Prior to this release, only the test
directory was supported by the "used only in tests" mode of the "check-unused-files" command.
Now, it also correctly treats integration_test
and test_driver
as test folders.
In the next releases we will also add support for integration_test
and test_driver
folders for other commands and rules that rely on the test
folder.
Rule updates
"banned-usage" improvements
Rule's configuration now supports a new option paths
(which takes a list of regular expressions) to help with applying particular config entries to specific paths.
For example,
dart_code_metrics:
...
rules:
...
- banned-usage:
entries:
- ident: strangeName
description: The name is too strange
paths:
- .*_model.dart
will only highlight the usages of strangeName
in files that are matched by .*_model.dart
.
This option works only for the ident
entries.
"banned-dependencies" improvements
With this release the rule supports a new config entry called banned-transitive
to help you detect packages that come with a dependency you want to avoid.
If the unwanted dependency comes directly from one of the installed packages, that package will be highlighted in the pubspec.yaml
.
But if that dependency comes from the deeper nested dependency, the rule will highlight just the dependencies
entry in the pubspec.yaml
.
This new option can help with migrating from deprecated packages (e.g. package:js
).
"move-variable-closer-to-its-usage" improvements
New mode for this rule (currently behind the ignore-after-if
config option, enabled by default) is designed to highlight variables that are used only after the next if statement and therefore can be moved after it.
For example,
void fn(bool condition) {
final value = 1; // No lint when `ignore-after-if` is true
if (condition) {
...
return;
}
print(value);
}
once the ignore-after-if
is set to false
, the rule will highlight value
since it's not used inside the if (condition)
block and can be moved after it.
New rules
Common
handle-throwing-invocations. Warns when an invocation's declaration is annotated with @Throws()
but the potential exception is not handled.
This rule is designed to help you avoid uncaught exceptions in your code making it easier to see which declarations can throw an exception or error and which exceptions/errors can be thrown.
For example,
({IndexError})
void someFunction() {
...
}
here, someFunction
is explicitly annotated as throwing IndexError
and therefore any invocation of someFunction
should handle that error.
The rule also works for more complex cases, for example:
try {
someFunction();
} on Exception catch (_) {}
void fn(() void Function() callback) {
callback();
}
IndexError
is actually not a subtype of Exception
and the invocation of someFunction
will be highlighted by the rule.
Same for the the invocation of the callback
parameter that is also annotated with @Throws()
.
prefer-correct-throws. Warns when a declaration is missing or has an unnecessary @Throws()
annotation.
Another rule to complement the new annotation, but this one to help to always keep the annotation up to date.
For example,
()
void fn() {
print('hi');
}
void withThrow() {
throw Error();
}
here, the first declaration does not throw an exception or call any other function that does. So the annotation is unnecessary and should be removed.
And the last declaration actually throws an exception, but does not have the annotation. The rule will highlight that as well.
prefer-extracting-function-callbacks. Warns when an inline callback is passed as an argument to a function or method invocation.
For example,
void fn() {
listen(onPressed: () {
// Some
// Huge
// Callback
});
}
here, the rule will trigger if the passed callback exceeds the configured length.
Flutter
avoid-incorrect-image-opacity. Warns when an Image
widget is wrapped into an Opacity
widget.
Passing a value for the opacity
parameter is much more efficient than using the Opacity
widget.
For example,
Opacity(
opacity: 1,
child: Image.asset('...'),
);
should be rewritten to
Image.asset(
'...',
opacity: const AlwaysStoppedAnimation(0.5),
);
Riverpod
use-ref-read-synchronously. Warns when ref.read
is called past an await point (also known as asynchronous gap).
For example,
class HomeConsumerState extends ConsumerState<HomeConsumerStatefulWidget> {
Widget build(context) {
return FooWidget(
onChange: (value) async {
ref.read<WithAsync>();
await fetch();
ref.read<WithAsync>();
},
);
}
}
here, the second ref.read
call can happen when the context is no longer available.
To avoid that, check for mounted
after the async gap:
class HomeConsumerState extends ConsumerState<HomeConsumerStatefulWidget> {
Widget build(context) {
return FooWidget(
onChange: (value) async {
ref.read<WithAsync>();
await fetch();
if (mounted) {
ref.read<WithAsync>();
}
},
);
}
}
avoid-calling-notifier-members-inside-build. Warns when a Notifier
(or AsyncNotifier
) member is called inside the build
method.
For example,
class HomeConsumerState extends ConsumerState<HomeConsumerStatefulWidget> {
Widget build(BuildContext context) {
final counter = ref.watch<Counter>(provider.notifier);
counter.increment();
}
}
here, mutating the notifier's state should be avoided.
avoid-notifier-constructors. Warns when a Notifier
(or AsyncNotifier
) has a non-empty constructor.
For example,
class Counter extends Notifier<int> {
var state = 0;
Counter() {
state = 1;
}
int build() {
return state;
}
void increment() {
state++;
}
}
should be rewritten to
class Counter extends Notifier<int> {
var state = 0;
int build() {
state = 1;
return state;
}
void increment() {
state++;
}
}
dispose-provided-instances. Warns when an instance with a dispose
method created inside a provider does not have the dispose method called in ref.onDispose
.
Not disposing such instances may lead to memory leaks and should be avoided.
For example,
Provider.autoDispose((ref) {
final instance = DisposableService();
return instance;
});
class DisposableService {
void dispose() {}
}
here, the dispose
method of the DisposableService
never gets called.
So the example should be rewritten to
Provider.autoDispose((ref) {
final instance = DisposableService();
ref.onDispose(instance.dispose);
return instance;
});
class DisposableService {
void dispose() {}
}
GetX
proper-getx-super-calls. Checks that super calls in the onStart
, onInit
, onClose
and onDelete
methods are called in the correct order.
For example,
class VideoViewerController extends GetxController {
void onInit() {
// some code
super.onInit();
}
void onStart() {
// some code
super.onStart();
}
void onClose() {
super.onClose();
// some code
}
void onDelete() {
super.onDelete();
// some code
}
}
should be rewritten to
class VideoViewerController extends GetxController {
void onInit() {
super.onInit();
// some code
}
void onStart() {
super.onStart();
// some code
}
void onClose() {
// some code
super.onClose();
}
void onDelete() {
// some code
super.onDelete();
}
}
What’s next
Baseline for all commands. To further simplify DCM integration into existing projects, we want to expand baseline support from just "dcm analyze" to all commands that can produce analysis issues ("dcm check-unused-code", "dcm check-unused-files" and other).
Support for all commands in DCM GitHub action. One of the ideas behind "dcm run" was to integrate it into DCM GitHub action so that it's not limited to just "dcm analyze". Expanding the number of supported commands will make the action even 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!