Original address: medium.com/wriketechcl…

Incendial.medium.com/

Published: April 28, 2021

Dart Code Metrics is a static Code analysis tool that allows you to collect Code Metrics and provide additional rules for the profiler. This tool helps developers monitor the quality of their code and improve it. In this article, we want to share the capabilities of Dart Code Metrics with the community. This tool helped us Wrike, and we hope it helps you too.

It can be launched from the command line, connected to the Dart analysis server as a plug-in, or as a library. Starting from the command line, you can easily integrate the tool into your CI/CD flow, and you can get results in console, HTML, JSON, CodeClimate, or GitHub. Connecting to the analysis server as a plug-in allows you to receive real-time feedback directly from the IDE.

Why did we develop such a tool? Wrike has about 2.5 million lines of code written in Dart. Codebase like this comes at a cost of maintenance — so how do you know when to refactor your code, and where to start?

The Dart SDK distributes a profiler with a built-in Interter and a set of rules. In the official pub, you can also find recommended rule sets: Pedantic, effective_dart, and so on. This helps us avoid mistakes and maintain the codebase as suggested by the language authors. However, we didn’t have enough code to analyze the data, and that’s where it all started.

Measure of the

After studying materials on evaluating and designing program code, we realized that the first step was to implement the collection of software metrics from the code.

The analyzer now collects the following metrics.

  • Cyclic complexity
  • Number of lines of executable code
  • Number of lines of code
  • Number of parameters
  • Number of methods
  • The biggest nested
  • The weight of the class

We set a basic threshold for each metric, then suggested refactoring the code. Also, thresholds can easily be predefined and passed to tools when called from the console or through analysis_options.yaml during the analyzer’s plug-in configuration. You can read more about this here.

In the next step, the tool relies on data from several metrics to find antipatterns in the code base. Only two anti-patterns are currently implemented — long methods and long parameter lists — but we will extend this list in the future.

While metrics like number of parameters or number of methods are easy to understand, what about cycle complexity?

The cyclic complexity of a piece of code is the number of linearly independent paths in the code. For example, if the source code does not contain any branch points or loops, it has a complexity of 1 because there is only one path in the code. If we have an if statement in our code that contains a simple condition, we have two routes through our code: if statements are true or false.

Many separate paths in the function body can affect readability and maintenance of the code. So in this case, it’s best to split the function.

The tool provides various reporting formats to visualize metrics. We will discuss this later in the “Reports” section.

screening

Initially, the tool only collected metrics, but later we added hints.

Other ecosystems have useful rules, such as checking for unused parameters, class member ordering, and so on. These rules don’t exist in the built-in Dart SDK prompter, so we made our own.

Why did we decide to add Linting in a separate package instead of doing PR in a built-in profiler? Because we decided to implement extended configuration capabilities. For example, we can configure the exact sequential list of class members that should be checked.

List of current rules.

The general rule

  • Avoid-unused-parameters
  • binary-expression-operand-order
  • Double text format
  • Members of the order
  • Member sorting – extension
  • Returns the preceding newline character
  • No Boolean – literal – compare
  • No empty block
  • Not equal to parameter
  • Not equal to — otherwise
  • Unmagical number
  • No object declaration
  • Favoring conditional expressions
  • The trailing comma is preferred

For international language libraries

  • Tend to the intl – name
  • Provide the correct intL-args

Dart Angular

  • Avoid reserving white space -false
  • component-annotation-arguments-ordering
  • Prefer to use on-push-cd-strategy

You can find the latest list in the documentation.

Style rules are not the only important consideration; We also want to highlight some potential errors, such as no-equal-then-else, no-equal-arguments, etc.

Our rules are based in part on issues we encounter during the review process. Ideally, we want these questions to be automatically overwritten so we can focus on the important work. Another part of our rule was developed in the process of studying the rules of other tools. (Hats off to PVS-Studio, TSLint and ESLint for inspiration!)

Let’s take a closer look at some of these rules.

Avoid unused parameters. Check for unused arguments to a function or method. An unused parameter can indicate that it is no longer needed during refactoring, or that a variable is somewhere in the body of a function (or method) other than the parameter. In the first case, this rule helps remove unused code; The second case indicates the possibility of an error.

Here is a simple example.

String method(StringValue) = > "";Copy the code

The value argument is not used, and the parser will display “parameter not used”.

Since Dart allows you to inherit from any class and requires the argument to be included in a subclass, you can rename it _ in the base class and the parser will skip it.

For instance

String method(String_) = > "";Copy the code

Favoring trailing commas. Check trailing commas for parameters, parameters, enumerations, and collections as long as they span more than one line.

For example.

void firstFunction (String firstArgument, String secondArgument, String thirdArgument) {
  ...
}
Copy the code

For such a function, the rule suggests putting a comma at the end so that, once formatted, it becomes.

void firstFunction(
  String firstArgument,
  String secondArgument,
  String thirdArgument,
) {
  ...
}
Copy the code

If the argument is initially placed on a single line, the parser does not consider this an error.

void secondFunction(String arg1, String arg2, String arg3) {
  ...
}
Copy the code

You can also specify a break-on parameter for a rule that performs additional checks on a specified number of elements. For example, the above example is considered correct if there is no break-on. But if you set the rule to break-on: 2, the parser will display an error for this function and suggest adding a comma.

They don’t have the same parameters. Checks whether the same argument is passed more than once when instantiating a class or calling a method/function.

Suppose there is some user class and a separate function to create the user.

class User {
  final String firstName;
  final String lastName;
  
  const User(this.firstName, this.lastName);
}
User createUser(String lastName) {
  String firstName = getFirstName();
  return User(
    firstName,
    firstName,
  );
}
Copy the code

The fields of both User classes are strings, and it’s easy to miss passing the same variable at creation time. In this case, the rule will show that the variable firstName was passed more than once, which could be an error.

Member sorting extension. This rule checks the order of class members. This rule accepts an extended suffix because we already have a member collation, but it’s not that flexible.

This rule accepts a sort configuration with a fairly flexible template. Not only does it allow you to specify the type of a class member (field, method, constructor), but it also allows you to specify keywords such as late, const, final, or, for example, Nullable.

The configuration could look like this.

  • Public – late – Final – fields
  • Private – late – final – field
  • public-nullable-fields
  • A private, zeroable field
  • Named structure
  • Factory structure
  • getter
  • setters
  • Public static methods
  • Private static method
  • Protected methods
  • And so on.

Or it could be reduced to

  • field
  • methods
  • setters
  • Accessors (or accessors-setters if you don’t need to separate them).
  • The constructor

The ability to read more about rules in the documentation.

In addition, the rule may need to be sorted alphabetically. To do this, you need to pass alphabetize: true in its configuration.

The report

To visualize all collected metrics and prompt results, the tool conveniently provides reports in the following format.

  • The console
  • HTML
  • JSON
  • CodeClimate
  • GitHub

The Console format is the default and can be moved to the Console tool via the — Reporter flag.

When running the command, for example

$ dart pub run dart_code_metrics:metrics lib
# or for a Flutter package
$ flutter pub run dart_code_metrics:metrics lib
Copy the code

When executing this command on the Dart Code Metrics base, we receive the following report on the console.

If you want to select a different type of report (such as HTML), you need to run the following command.

$ dart pub run dart_code_metrics:metrics lib --reporter=html
# or for a Flutter package
$ flutter pub run dart_code_metrics:metrics lib --reporter=html
Copy the code

The report will be generated in the Metrics folder. You can also pass the generated folder using the –output-directory or -o flags.

In the report, you can view each file individually.

To see the details of metrics, hover over the icon to the left of the code.

If you use the GitHub workflow and want immediate reports in the PR you create, you need to add a new step to the pipeline.

jobs: your_job_name: ... steps: ... - Name: Run Code Metrics Run: flutter pub Run dart_code_metrics: Metrics -- Reporter =github libCopy the code

This will get you the report in this format.

Detailed information on all types of metrics and configuration methods can be found in the documentation.

The installation

If you want to try this package out for yourself, here’s a short guide to connecting it as a plug-in for the Dart Analyzer.

Step 1: Install the package as a development dependency.

$ dart pub add --dev dart_code_metrics
# or for a Flutter package
$ flutter pub add --dev dart_code_metrics
Copy the code

Alternatively, manually add the package to pubspec.yaml.

Important: If your software package has not been migrated to NULL Safety, use version 2.4.1.

dev_dependencies:
  dart_code_metrics: ^ 3.0.0
Copy the code

Run the command to install the dependencies.

$ dart pub get
# or for a Flutter package
$ flutter pub get
Copy the code

Step 2: Add the configuration in analysis_options.yaml.

analyzer:
  plugins:
    - dart_code_metrics
 
dart_code_metrics:
  anti-patterns:
    - long-method
    - long-parameter-list
  metrics:
    cyclomatic-complexity: 20
    lines-of-executable-code: 50
    number-of-parameters: 4
    maximum-nesting-level: 5
  metrics-exclude:
    - test/**
  rules:
    - newline-before-return
    - no-boolean-literal-compare
    - no-empty-block
    - prefer-trailing-comma
    - prefer-conditional-expressions
    - no-equal-then-else
Copy the code

This configuration specifies antipatterns for long methods and long parameter lists, metrics and their thresholds, and rules. The full list of available metrics can be found here, and the list of rules here.

Step 3: Reload the IDE and let the analyzer detect the plug-in.

If you want to use this package as a CLI, check out the documentation here. If you want to use it as a library, see the documentation here.

conclusion

Given the popularity of Flutter, we are considering what metrics and rules can help developers use the framework. We are open to suggestions; If you have ideas for rules or metrics that could be useful to Dart or Flutter developers, feel free to write to us or report the problem via GitHub. We’re excited to make developers’ lives better.

Our plans for the future can be tracked at any time in the issues list or projects in the common project library, where you can also leave feedback on the tool.


Translation via www.DeepL.com/Translator (free version)