What’s new in DCM 1.16.0
Today we’re excited to announce the release of DCM 1.16.0!
This release includes 2 new commands (one for the advanced analysis of parameters and another one for the project structure analysis), 5 new rules, UX improvements and more. 🚀
Let's go through the highlights of this release! (And you can find the full list of changes in our changelog)
New command for advanced parameters analysis
First, let's start with the new "check-parameters" command, which will help you analyze the parameters of functions, methods, and constructors.
Previously, with "dcm check-unnecessary-nullable" you could only find unnecessary nullable parameters (parameters that are marked as nullable, but that never receive a nullable value or null
).
Now, with the new command, you can find not only that type of parameters, but also:
- optional parameters that are never passed
- optional parameters that always receive a value and therefore can be made non-optional (or
required
) - parameters that receive the same constant value in all invocations (such parameters can be removed and their constant value can be used directly)
- parameters that have a too broad type (for example, if a
List<Object>
parameter always receivesList<String>
arguments) - parameters that have unused
@visibleForTesting
annotations - parameters that have unused default values
- parameters that are always part of another parameter
Here is an example of the CLI output:
This command is intended to eventually replace the "check-unnecessary-nullable" command once we have fixed all the false positives and adjusted the configuration.
Until then, both commands remain available.
New command for structure analysis
Another command included into this release is designed to help you with analyzing how files in your project import other files and packages (and later we also plan to introduce a check that will help you avoid unwanted imports, cycles and never forget to add a specific file).
This command also supports custom grouping (using regular expressions) to help you group your folders (and the files within them) into modules and analyze how modules depend on each other.
This command uses dot
format as the output format, so you can use any other tool that helps visualize this format (including online playgrounds).
We intend to support more output formats later.
Improved UX for commands that take options for subcommands
We also continue to improve existing commands!
With this release, commands that accept option for various subcommands ("dcm run" and "dcm fix") have an updated help section to help you better understand which option is used for which subcommand.
Additionally, "dcm run" now exits with a usage exception when an option for a specific subcommand is passed without an option for that subcommand.
For example, if you pass the --show-similarity
flag without the --analyze-widgets
flag:
Configurable metric values for "Analyze Widgets"
Moving on to "dcm analyze-widgets". We're excited to announce that the previous limitation for this command has been resolved! The command now pick up configured metrics from the corresponding analysis_options.yaml
files:
And if we set the cyclomatic-complexity
to be 2
(instead of 20
) and the maximum-nesting-level
to be 1
(instead of 5
), here is how the output changes:
Unused code improvements
This release includes a fix for a newly discovered false positive with several fields with the same name declared both in the base class and mixin.
For example,
mixin TestMixin {
bool get isEmpty => true;
}
abstract class Empty {
bool get isEmpty;
}
class Data with TestMixin implements Empty {}
void test() {
print(Data().isEmpty);
}
here, isEmpty
is declared twice, but prior to this release, only the one declared in the mixin was correctly marked as used.
Now, both isEmpty
declarations are correctly marked as used!
Rule updates
New config option for "arguments-ordering"
With this release, the "arguments-ordering" has a new config option that replaces the old one. Now, you can fully configure the list of arguments that you want to keep last (not just child
and children
).
New rules
Flutter
prefer-for-loop-in-children. Suggests using for-loop in arguments that accept a list of widgets.
For example,
Column(
children: exampleList.map((example) => const Text(example)).toList(),
);
Column(
children: [
...exampleList.map((example) => const Text(example)).toList(),
],
);
Column(
children: List.generate(
exampleList.length,
(index) => Text(exampleList[index]),
),
);
Column(
children: exampleList.fold([], (list, example) {
list.add(Text(example));
return list;
}),
);
for all the cases above the rule will suggest using the for-loop collection element
Column(
children: [
for (final example in exampleList) Text(example),
],
);
Flutter Hooks
avoid-unnecessary-hook-widgets. Warns when a hook widget or HookBuilder
does not use hooks.
Hooks widgets that do not use hooks can be safely converted to regular Flutter widgets.
For example,
class MyWidget extends HookWidget {
const MyWidget();
Widget build(BuildContext context) {
return Container();
}
}
final hookBuilder = HookBuilder(
builder: (context) {
return Container();
},
);
here, both MyWidget
and hookBuilder
do not use any hooks and the rule will highlight them both.
Common
missing-use-result-annotation. Warns when a method or function declaration is missing the @useResult
annotation.
@useResult annotation is used by the Dart analyzer to indicate that the value obtained by invoking a declaration should be used.
For example,
Either<Left, Right> someFunction() => ...;
here, Either
is expected to be always used and to ensure that, the declaration can be marked with the @useResult
annotation.
avoid-conditions-with-boolean-literals. Warns then a binary expression has a boolean constant that either makes the resulting value always the same or does not affect it.
Sometimes due to a refactoring, a condition can have a constant boolean value (true
or false
) used directly or via a variable that either does not affect the condition at all or makes the resulting value always the same.
For example,
final someSet = {1, 2, 3};
void fn() {
final value = someSet.contains(1) && false;
}
bool get someGetter => someSet.contains(1) || false;
here, the first condition will always be false
despite the result of someSet.contains(1)
. And in the second one || false
does not affect the result at all.
no-magic-string. Warns when string literals are used outside of named constants or variables.
This rule excludes some commonly used cases where a string constant is not expected (for example, regular expressions, methods of the String
class, annotations, default values, errors, exceptions and few more).
What’s next
Admin panel for managing seats. We are actively working an admin panel to simplify managing seats and activations. Stay tuned for more updates!
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.
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.
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!