Check Code Duplication
Reports duplicate functions, methods, constructors and test cases.
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.
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.
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 formatduplicationResults
- an array of objectssummary
- 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 issuesissues
- an array of issues detected in the target file
{
"path": "lib/src/some/file.dart",
"issues": [
...
],
}
The issue object fields are
id
- issue idmessage
- the message associated with the issuelocation
- the location associated with the issueeffortInMinutes
- an estimated effort to fix the issue (in minutes)declarationType
- duplication declaration typedeclarationName
- duplication declaration nameduplications
- 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 typedeclarationName
- duplication entry declaration namelocation
- the location associated with the entryrelativePath
- path, relative to the root directoryrelativeToFirstDeclarationPath
- 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 entitystartLine
- the start line of the entityendColumn
- the end column of the entityendLine
- the end line of the entitystartOffset
- 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 titlevalue
- 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 formatcodeDuplications
- 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 fileissues
- an array of issues detected in the target file
{
"path": "lib/src/some/file.dart",
"issues": [
...
],
}
The issue object fields are
declarationType
- duplication declaration typedeclarationName
- duplication declaration nameduplications
- an array of duplication entries for this declarationoffset
- a zero-based offset of the class member location in the sourceline
- a zero-based line of the class member location in the sourcecolumn
- 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 typedeclarationName
- duplication entry declaration namerelativePath
- path, relative to the root directoryrelativeToFirstDeclarationPath
- path, relative to the main code duplication declarationoffset
- a zero-based offset of the class member location in the sourceline
- a zero-based line of the class member location in the sourcecolumn
- 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>
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
}
}
]
}
]
}