Skip to content
This repository was archived by the owner on Jul 16, 2023. It is now read-only.

feat: add avoid-double-slash-imports rule #1095

Merged
merged 1 commit into from
Dec 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

* fix: remove recursive traversal for [`ban-name`](https://dartcodemetrics.dev/docs/rules/common/ban-name) rule.
* feat: add static code diagnostic [`avoid-double-slash-imports`](https://dartcodemetrics.dev/docs/rules/common/avoid-double-slash-imports).
* feat: add static code diagnostic [`prefer-using-list-view`](https://dartcodemetrics.dev/docs/rules/flutter/prefer-using-list-view).

## 5.1.0
Expand Down
2 changes: 2 additions & 0 deletions lib/src/analyzers/lint_analyzer/rules/rules_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'rules_list/avoid_banned_imports/avoid_banned_imports_rule.dart';
import 'rules_list/avoid_border_all/avoid_border_all_rule.dart';
import 'rules_list/avoid_cascade_after_if_null/avoid_cascade_after_if_null_rule.dart';
import 'rules_list/avoid_collection_methods_with_unrelated_types/avoid_collection_methods_with_unrelated_types_rule.dart';
import 'rules_list/avoid_double_slash_imports/avoid_double_slash_imports_rule.dart';
import 'rules_list/avoid_duplicate_exports/avoid_duplicate_exports_rule.dart';
import 'rules_list/avoid_dynamic/avoid_dynamic_rule.dart';
import 'rules_list/avoid_expanded_as_spacer/avoid_expanded_as_spacer_rule.dart';
Expand Down Expand Up @@ -77,6 +78,7 @@ final _implementedRules = <String, Rule Function(Map<String, Object>)>{
AvoidCascadeAfterIfNullRule.ruleId: AvoidCascadeAfterIfNullRule.new,
AvoidCollectionMethodsWithUnrelatedTypesRule.ruleId:
AvoidCollectionMethodsWithUnrelatedTypesRule.new,
AvoidDoubleSlashImportsRule.ruleId: AvoidDoubleSlashImportsRule.new,
AvoidDuplicateExportsRule.ruleId: AvoidDuplicateExportsRule.new,
AvoidDynamicRule.ruleId: AvoidDynamicRule.new,
AvoidGlobalStateRule.ruleId: AvoidGlobalStateRule.new,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// ignore_for_file: public_member_api_docs

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';

import '../../../../../utils/node_utils.dart';
import '../../../lint_utils.dart';
import '../../../models/internal_resolved_unit_result.dart';
import '../../../models/issue.dart';
import '../../../models/replacement.dart';
import '../../../models/severity.dart';
import '../../models/common_rule.dart';
import '../../rule_utils.dart';

part 'visitor.dart';

class AvoidDoubleSlashImportsRule extends CommonRule {
static const String ruleId = 'avoid-double-slash-imports';

static const _warning = 'Avoid double slash import/export directives.';
static const _correctionMessage = 'Remove double slash.';

AvoidDoubleSlashImportsRule([Map<String, Object> config = const {}])
: super(
id: ruleId,
severity: readSeverity(config, Severity.warning),
excludes: readExcludes(config),
includes: readIncludes(config),
);

@override
Iterable<Issue> check(InternalResolvedUnitResult source) {
final visitor = _Visitor();

source.unit.visitChildren(visitor);

return visitor.nodes
.map(
(node) => createIssue(
rule: this,
location: nodeLocation(node: node, source: source),
message: _warning,
replacement: _createReplacement(node),
),
)
.toList(growable: false);
}

Replacement _createReplacement(UriBasedDirective directive) {
final updatedDirective =
directive.toString().replaceAll('//', '/').replaceAll(r'\\', r'\');

return Replacement(
comment: _correctionMessage,
replacement: updatedDirective,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
part of 'avoid_double_slash_imports_rule.dart';

class _Visitor extends GeneralizingAstVisitor<void> {
final _nodes = <UriBasedDirective>[];

Iterable<UriBasedDirective> get nodes => _nodes;

_Visitor();

@override
void visitUriBasedDirective(UriBasedDirective node) {
final uri = node.uri.stringValue;
if (uri == null) {
return;
}

if (uri.contains('//') || (uri.contains(r'\\') && !uri.startsWith(r'\\'))) {
_nodes.add(node);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/models/severity.dart';
import 'package:dart_code_metrics/src/analyzers/lint_analyzer/rules/rules_list/avoid_double_slash_imports/avoid_double_slash_imports_rule.dart';
import 'package:test/test.dart';

import '../../../../../helpers/rule_test_helper.dart';

const _examplePath = 'avoid_double_slash_imports/examples/example.dart';

void main() {
group('AvoidDoubleSlashImportsRule', () {
test('initialization', () async {
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
final issues = AvoidDoubleSlashImportsRule().check(unit);

RuleTestHelper.verifyInitialization(
issues: issues,
ruleId: 'avoid-double-slash-imports',
severity: Severity.warning,
);
});

test('reports about found issues', () async {
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
final issues = AvoidDoubleSlashImportsRule().check(unit);

RuleTestHelper.verifyIssues(
issues: issues,
startLines: [1, 3, 5, 7],
startColumns: [1, 1, 1, 1],
locationTexts: [
"import 'package:test//material.dart';",
"import '../../..//rule_utils_test.dart';",
"export 'package:mocktail//good_file.dart';",
"part '..//empty.dart';",
],
messages: [
'Avoid double slash import/export directives.',
'Avoid double slash import/export directives.',
'Avoid double slash import/export directives.',
'Avoid double slash import/export directives.',
],
replacements: [
"import 'package:test/material.dart';",
"import '../../../rule_utils_test.dart';",
"export 'package:mocktail/good_file.dart';",
"part '../empty.dart';",
],
replacementComments: [
'Remove double slash.',
'Remove double slash.',
'Remove double slash.',
'Remove double slash.',
],
);
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:test//material.dart'; // LINT
import 'package:mocktail/good_file.dart';
import '../../..//rule_utils_test.dart'; // LINT

export 'package:mocktail//good_file.dart'; // LINT

part '..//empty.dart'; // LINT
37 changes: 37 additions & 0 deletions website/docs/rules/common/avoid-double-slash-imports.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import RuleDetails from '@site/src/components/RuleDetails';

<RuleDetails version="5.2.0" severity="warning" hasFix />

Warns when an import/export directive contains a double slash.

Double slash in the URI is considered valid, but under some circumstances the programm won't run.

See:

- <https://github.com/dart-lang/sdk/issues/36337>

### Example {#example}

**❌ Bad:**

```dart
import 'package:test//material.dart'; // LINT
import 'package:mocktail/good_file.dart';
import '../../..//rule_utils_test.dart'; // LINT

export 'package:mocktail//good_file.dart'; // LINT

part '..//empty.dart'; // LINT
```

**✅ Good:**

```dart
import 'package:test/material.dart';
import 'package:mocktail/good_file.dart';
import '../../../rule_utils_test.dart';

export 'package:mocktail/good_file.dart';

part '../empty.dart';
```
21 changes: 16 additions & 5 deletions website/docs/rules/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ Rules are grouped by category to help you understand their purpose. Each rule ha
hasConfig
hasFix
>
Enforces named argument order in function and constructor invocations
to be the same as corresponding named parameter declaration order.
Enforces named argument order in function and constructor invocations to be
the same as corresponding named parameter declaration order.
</RuleEntry>

<RuleEntry
Expand Down Expand Up @@ -58,6 +58,16 @@ Rules are grouped by category to help you understand their purpose. Each rule ha
of integers using a string key.
</RuleEntry>

<RuleEntry
name="avoid-double-slash-imports"
type="common"
severity="warning"
version="5.2.0"
hasFix
>
Warns when an import/export directive contains a double slash.
</RuleEntry>

<RuleEntry
name="avoid-duplicate-exports"
type="common"
Expand Down Expand Up @@ -487,8 +497,8 @@ Rules are grouped by category to help you understand their purpose. Each rule ha
version="5.1.0"
hasConfig
>
Suggests to use static class member instead of global constants, variables
and functions.
Suggests to use static class member instead of global constants, variables and
functions.
</RuleEntry>

<RuleEntry
Expand Down Expand Up @@ -654,7 +664,8 @@ Rules are grouped by category to help you understand their purpose. Each rule ha
severity="performance"
version="5.2.0"
>
Warns when a <code>Column</code> widget with only <code>children</code> parameter is wrapped in a <code>SingleChildScrollView</code> widget.
Warns when a <code>Column</code> widget with only <code>children</code>{' '}
parameter is wrapped in a <code>SingleChildScrollView</code> widget.
</RuleEntry>

## Intl specific {#intl-specific}
Expand Down