Skip to main content
Pro+

Check Code Duplication

Reports duplicate functions, methods, constructors and test cases.

info

The duplicate detection algorithm is resistant to some insignificant code changes (for example, a method with renamed parameters or variables is still considered a duplicate) as it performs a structural check rather than an exact match check.

To execute the command, run:

$ dcm check-code-duplication lib # or dcm cd lib

Full command description:

Usage: dcm check-code-duplication [arguments] <directories>
-h, --help Print this usage information.


--per-package Compare code for duplications only within one package.
--exclude-overrides Exclude methods marked with @override.
--statements-threshold Minimum number of statements inside a declaration block.
(defaults to "3")


-r, --reporter="console" (--output-format) Analysis output format.
[console (default), json, codeclimate, gitlab, checkstyle, sonar]
-a, --absolute-path Show absolute paths in console reporter output.
--output-to="path/to/file" Path to the file with the analysis output.


-c, --print-config Print resolved config.


--root-folder="./" Root folder.
(defaults to the current directory)
-s, --sdk-path="directory-path" Dart SDK directory path.
If the project has a `.fvm/flutter_sdk` symlink, it will be used if the SDK is not found.
-e, --exclude="{**/*.g.dart,**/*.freezed.dart}" Files to exclude (in Glob syntax).
(defaults to "{**/*.g.dart,**/*.freezed.dart}")


--no-congratulate Don't show output even when there are no issues.


--verbose Show verbose logs.


--ci-key The license key to run on CI server. Can be provided via DCM_CI_KEY env variable.
--email The email used to purchase the license. Can be provided via DCM_EMAIL env variable.


--no-analytics Disable sending anonymous usage statistics.


--[no-]fatal-found Treat duplicate code as fatal.
(defaults to on)

Suppressing the Command

To suppress issues produced by the command, use the ignore: code-duplication comment. To suppress the command for an entire file, add the ignore_for_file: code-duplication comment to the beginning of a file.

Calculating Duplication on Per-package Level

To compare code for duplication only within one package, pass the --per-package CLI flag.

Use this mode if you want to avoid unifying code between several packages.

Excluding Overrides

To exclude methods marked with the "@override" annotation, pass the --exclude-overrides CLI flag.

Configuring the Minimum Number of Statements

By default, the command checks for declarations with at least 3 statements and declarations with an expression body (e.g. (String param) => someMethod();).

To configure the minimum number of statements, pass the --statements-threshold CLI option.

info

Blocks with a single return statement and expression bodies are handled differently. This configuration does not affect them.

Detecting Duplicate Test Cases

To detect duplicate test cases, run the command against your tests directory (for example, dcm check-code-duplication test).

The command will then analyze all test invocations (for example, test(...), testWidget(...), and every other invocations that is marked with @isTest under the hood) and include duplicate cases into the output.

Output Example

Console (default)

Use --reporter=console to get output in console format.

Console

JSON

Use --reporter=json to get output as a single JSON object containing metadata and the list of duplication issues.

Format specification

The root object fields are

  • formatVersion - an integer representing the format version (will be incremented each time the serialization format changes)
  • timestamp - a creation time of the report in YYYY-MM-DD HH:MM:SS format
  • duplicationResults - an array of objects
  • summary - an array of objects
{
"formatVersion": 10,
"timestamp": "2021-04-11 14:44:42",
"duplicationResults": [
{
...
},
{
...
},
{
...
}
],
"summary": [
{
...
},
{
...
}
]
}

The result object fields are

  • path - the relative path to the file with issues
  • issues - an array of issues detected in the target file
{
"path": "lib/src/some/file.dart",
"issues": [
...
],
}

The issue object fields are

  • id - issue id
  • message - the message associated with the issue
  • location - the location associated with the issue
  • effortInMinutes - an estimated effort to fix the issue (in minutes)
  • declarationType - duplication declaration type
  • declarationName - duplication declaration name
  • duplications - an array of duplication entries for this declaration
{
"id": "duplication-issue",
"message": "This method has 1 duplicate declaration",
"location": {
...
},
"effortInMinutes": 20,
"declarationName": "build",
"declarationType": "method",
"duplications": [
...
]
}

The duplication object fields are

  • declarationType - duplication entry declaration type
  • declarationName - duplication entry declaration name
  • location - the location associated with the entry
  • relativePath - path, relative to the root directory
  • relativeToFirstDeclarationPath - path, relative to the main code duplication declaration
{
"declarationType": "method",
"declarationName": "someMethod",
"location": {
...
},
"relativePath": "lib/src/some/another_file.dart",
"relativeToFirstDeclarationPath": "./another_file.dart",
}

The location object fields are

  • startColumn - the start column of the entity
  • startLine - the start line of the entity
  • endColumn - the end column of the entity
  • endLine - the end line of the entity
  • startOffset - the offset of the entity
{
"endColumn": 2,
"endLine": 90,
"startColumn": 1,
"startLine": 48,
"startOffset": 1638
}

The summary-record object fields are

  • title - a message with the summary entry title
  • value - the actual value of the entry
{
"title": "Total code duplication",
"value": 1
}
Old format specification (prior to DCM 1.26.0)

The root object fields are

  • formatVersion - an integer representing the format version (will be incremented each time the serialization format changes)
  • timestamp - a creation time of the report in YYYY-MM-DD HH:MM:SS format
  • codeDuplications - an array of code duplication issues
{
"formatVersion": 2,
"timestamp": "2021-04-11 14:44:42",
"codeDuplications": [
{
...
},
{
...
},
{
...
}
]
}

The codeDuplication object fields are

  • path - a relative path of the unused file
  • issues - an array of issues detected in the target file
{
"path": "lib/src/some/file.dart",
"issues": [
...
],
}

The issue object fields are

  • declarationType - duplication declaration type
  • declarationName - duplication declaration name
  • duplications - an array of duplication entries for this declaration
  • offset - a zero-based offset of the class member location in the source
  • line - a zero-based line of the class member location in the source
  • column - a zero-based column of class member the location in the source
{
"declarationType": "function",
"declarationName": "myFunction",
"duplications": [
...
],
"offset": 156,
"line": 7,
"column": 1
}

The duplication object fields are

  • declarationType - duplication entry declaration type
  • declarationName - duplication entry declaration name
  • relativePath - path, relative to the root directory
  • relativeToFirstDeclarationPath - path, relative to the main code duplication declaration
  • offset - a zero-based offset of the class member location in the source
  • line - a zero-based line of the class member location in the source
  • column - a zero-based column of class member the location in the source
{
"declarationType": "method",
"declarationName": "someMethod",
"relativePath": "lib/src/some/another_file.dart",
"relativeToFirstDeclarationPath": "./another_file.dart",
"offset": 156,
"line": 7,
"column": 1
}

GitLab

Use --reporter=gitlab to get output in a GitLab-compatible format. To learn how to integrate DCM with GitLab, refer to this guide.

Code Climate

Use --reporter=codeclimate to get output in Code Climate format.

Output example
{"type":"issue","check_name":"duplication-issue","description":"This method has 1 duplicate declaration","categories":["Duplication"],"location":{"path":"lib/src/sheets/macos_sheet.dart","positions":{"begin":{"column":3,"line":168},"end":{"column":4,"line":179}}},"severity":"major","fingerprint":"58e573c82a01a5be2ee4d8b8360b8978"}
{"type":"issue","check_name":"duplication-issue","description":"This method is a duplicate of buildPage (located at lib/src/sheets/macos_sheet.dart).","categories":["Duplication"],"location":{"path":"lib/src/dialogs/macos_alert_dialog.dart","positions":{"begin":{"column":3,"line":310},"end":{"column":4,"line":321}}},"severity":"major","fingerprint":"f2278858ccdb8268971fa038fa4b9ca8"}

Checkstyle

Use --reporter=checkstyle to get output in Checkstyle format.

Output example
<?xml version="1.0"?>
<checkstyle version="10.0">
<file name="example.dart">
<error line="5" column="3" severity="warning" message="This function has 2 duplicate declarations" source="duplication-issue"/>
</file>
<file name="relative_root.dart">
<error line="5" column="3" severity="warning" message="This method is a duplicate of doWork (located at example.dart). Target declaration has 1 other duplicate." source="duplication-issue"/>
<error line="5" column="3" severity="warning" message="This function is a duplicate of doWork (located at example.dart). Target declaration has 1 other duplicate." source="duplication-issue"/>
</file>
</checkstyle>
note

Checkstyle format is supported by Bitbucket. To learn how to integrate DCM with Bitbucket, refer to this guide.

Sonar

Use --reporter=sonar to get output in SonarQube's generic format for external issues.

Output example
{
"rules": [
{
"cleanCodeAttribute": "CLEAR",
"description": "To learn more, visit the documentation https://dcm.dev/docs/cli/code-quality-checks/code-duplication/",
"engineId": "dcm",
"id": "duplication-issue",
"impacts": [
{
"severity": "MEDIUM",
"softwareQuality": "MAINTAINABILITY"
}
],
"name": "duplication-issue"
}
],
"issues": [
{
"effortMinutes": 20,
"primaryLocation": {
"filePath": "lib/src/unused_code_widget.dart",
"message": "This method has 1 duplicate declaration",
"textRange": {
"endColumn": 4,
"endLine": 34,
"startColumn": 3,
"startLine": 31
}
},
"ruleId": "duplication-issue",
"secondaryLocations": [
{
"filePath": "lib/src/unused_widget.dart",
"message": "This method is a duplicate of build (located at lib/src/unused_code_widget.dart).",
"textRange": {
"endColumn": 4,
"endLine": 17,
"startColumn": 3,
"startLine": 14
}
}
]
}
]
}