Skip to main content

What’s new in DCM for Teams 1.1.0

· 8 min read

Cover

Today we’re excited to announce the release of DCM for Teams 1.1.0! In this version we improved the unused code check (now it can check for separate fields and methods), added two new commands: dcm check-exports-completeness and dcm fix, ten new rules and small improvements like highlighting the unused // ignore comments and reanalysing all the files after the config is changed.

Let’s go on a quick journey together to explore all the new features!

Unused code check improvements

Previously DCM allowed to check only for top-level unused declarations (like classes, function, mixins, etc.), but now the check-unused-code command can also find unused fields, properties and methods allowing you to have a more accurate picture of the unused code you have.

Here are several examples of the results, produced by this command:

Removing (or at least being aware of) the unused code you have is important, because sometimes this code indicates that there is a bug in your system. But even if the code is unused due to a refactoring, keeping it might introduce unwanted technical debt.

New command — Check Exports Completeness

If you have a package with a public API and this API contains several complex classes, it might be easy to forget to export some parts of this API for the external users. That’s why DCM now provides a new command check-exports-completeness that reports all declarations that are part of the public API, but are not exported.

Here is an example of this command output:

Example

The command also shows the path by which the declaration was required. This might be useful if you want to understand why this declaration is expected to exported.

New command — Fix

DCM now provides a long-awaited command to fix issues reported by dcm analyze. To apply the fixes, simply call dcm fix <target directory>.

This command is implemented differently from the dart fix, allowing to apply complex code changes without conflicts. For example, it allows you to move a class member, rename it and replace its type all at once.

Here is the list of rules that are supported by dcm fix:

  • avoid-collapsible-if
  • avoid-redundant-async
  • avoid-redundant-async-on-load
  • avoid-unnecessary-nullable-return-type
  • list-all-equatable-fields
  • member-ordering
  • prefer-trailing-comma

In the upcoming releases we’ll add all the other rules that support quick fixes to dcm fix, so the experience from the IDE and the CLI will become similar.

Note that this command is currently in beta and it can occasionally remove some of your inline comments. If you experience this behavior, please write to us.

New rules

Ten new rules are included into this release:

avoid-collapsible-if. Warns when multiple nested If statements can be merged into one. Reducing the nesting level can help improve the code readability in general.

For example:

if (value1 == 1 && value2 == 3) {
if (value2 == 2) {
if (value1 == 4) {
return;
}
}
}

First two If statements have only one another nested If statement, which allows safely to transform them all into one single If.

avoid-explicit-type-declaration. Warns when a variable or a class field is declared with an explicit type what can be omitted. Can be useful to reduce the line length of the variable or field declaration.

For example:

class SomeClass {
static const String val = '123';

final Map<String, String> initedWithType = <String, String>{};

final Set<String> elements = {};
}

All these field declarations can have their types omitted, which transforms the code to:

class SomeClass {
static const val = '123';

final initedWithType = <String, String>{};

final elements = <String>{};
}

avoid-incomplete-copy-with. Warns when a copyWith method does not include all the parameters, provided by the default constructor. This rule might be especially useful if you write copyWith manually since it’s pretty easy to forget to update the method if the constructor changes.

For example:

class Person {
const Person({
required this.name,
required this.surname,
});

final String name;
final String surname;

Person copyWith({String? name}) {
return Person(
name: name ?? this.name,
surname: surname,
);
}
}

This copyWith declaration will produce an error, since it should also have a parameter for surname — String? surname.

avoid-nested-switches. Warns when a switch case body has another switch statement. Nested switches are difficult to understand because you can easily confuse the cases of an inner switch as belonging to an outer statement.

For example:

void func() {
final str = 'someString';

switch (str) {
case 'string':
{
switch (str) {
case 'another-one':
break;
}

break;
}

case 'anotherString':
{
break;
}
}
}

avoid-redundant-else. Checks for else blocks that can be removed without changing semantics.

For example:

class SomeClass {
String someString = '123';

void withInstance() {
if (someString == '0') {
print("Nothing to do");
return;
} else {
print("Moving on...");
}
}
}

Removing the else block won’t change the semantics, because the then block has a return statement at the end. So the code can be transformed to:

class SomeClass {
String someString = '123';

void withInstance() {
if (someString == '0') {
print("Nothing to do");
return;
}

print("Moving on...");
}
}

Which also improves overall readability.

avoid-self-assignment. Warns when a variable or a class instance field / property is assigned to itself. Such assignments are usually an indicator of an error.

For example:

class SomeClass {
String someString = '123';

void update(String newValue) {
someString = someString;
}
}

The rule will trigger , because instead of assigning someString to itself, newValue should be assigned to it.

avoid-self-compare. Warns when a comparison has both sides exactly the same. Such comparisons are usually an indicator of an error.

For example:

void main() {
final val = '1';

if (val == val) {
return;
}

if (val.length == val.length) {
return;
}

if (val > val) {
return;
}

if (val < val) {
return;
}
}

avoid-similar-names. Checks for names within the scope that are very similar and thus confusing.

For example:

void main() {
final value = '1';
final value2 = '2';
}

If both variables are frequently used within the block, it might become too easy to get confused by them.

no-equal-switch-case. Warns when a switch has cases with equal bodies. Instead of having several equal bodies it’s a good practice to group cases that need the same body.

For example:

void main() {
final str = '123';

switch (str) {
case '1':
{
print('hello world');
break;
}

case '2':
{
print('same');
break;
}

case '3':
{
print('same');
break;
}
}
}

Cases two and three have the same body which might be a mistake or can be refactored into:

void main() {
final str = '123';

switch (str) {
case '1':
{
print('hello world');
break;
}

case '2':
case '3':
{
print('same');
break;
}
}
}

no-equal-conditions. Warns when an If statement has duplicate conditions. Duplicating a condition automatically leads to dead code. Usually, this is due to a copy/paste error. At best, it’s simply dead code and at worst, it’s a bug that is likely to induce further bugs as the code is maintained, and obviously it could lead to unexpected behavior.

For example:

void main() {
final value = 1;

if (value == 1) {
return value;
} else if (value == 2) {
return value;
} else if (value == 1) {
return value;
}
}

What’s next

Bringing the unused code check to the next level. What if the unused code check was available not only as a command, but was also integrated into the IDE providing real-time info on the unused parts? Let’s find out how this will change the overall experience.

Reworking metrics and reporting. Metrics is one of the first features we implemented and it’s time to rethink and rework some of its parts. Waited long for an ability to ignore a separate metric violation? Want to see the metrics in real-time? Want to have more usable reports? In the future releases all these issues will be addressed.

Improving dcm fix. Currently the command supports only a limited amount of rules, but in the future releases we’ll add all the rules that support quick-fixes and provide more options like “dry-run” or the ability to pass additional lint rules to the command.

Baseline support. Some feedback we received was about adding DCM to an existing codebase and that this process is not easy. We want to address this problem by introducing support for baseline when you will be able to ignore existing code or have a specific config that automatically ignores all existing issues allowing you to partially enable rules for the new code.

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.