Skip to content

Commit 60a81b0

Browse files
bwilkersoncommit-bot@chromium.org
authored andcommitted
Add hints to find equal elements in literal sets and maps
Change-Id: Ib930b49b338d7cb088b17909991934b16c4f1538 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/138602 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent 93ff953 commit 60a81b0

11 files changed

+157
-8
lines changed

pkg/analyzer/lib/error/error.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,8 @@ const List<ErrorCode> errorCodeValues = [
367367
HintCode.DUPLICATE_IMPORT,
368368
HintCode.DUPLICATE_HIDDEN_NAME,
369369
HintCode.DUPLICATE_SHOWN_NAME,
370+
HintCode.EQUAL_ELEMENTS_IN_SET,
371+
HintCode.EQUAL_KEYS_IN_MAP,
370372
HintCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE,
371373
HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE,
372374
HintCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION,

pkg/analyzer/lib/src/dart/error/hint_codes.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,21 @@ class HintCode extends AnalyzerErrorCode {
370370
correction: "Try removing the repeated name from the list of shown "
371371
"members.");
372372

373+
/**
374+
* No parameters.
375+
*/
376+
static const HintCode EQUAL_ELEMENTS_IN_SET = HintCode(
377+
'EQUAL_ELEMENTS_IN_SET',
378+
"Two elements in a set literal shouldn't be equal.",
379+
correction: "Change or remove the duplicate element.");
380+
381+
/**
382+
* No parameters.
383+
*/
384+
static const HintCode EQUAL_KEYS_IN_MAP = HintCode(
385+
'EQUAL_KEYS_IN_MAP', "Two keys in a map literal shouldn't be equal.",
386+
correction: "Change or remove the duplicate key.");
387+
373388
/**
374389
* It is a bad practice for a source file in a package "lib" directory
375390
* hierarchy to traverse outside that directory hierarchy. For example, a

pkg/analyzer/lib/src/error/best_practices_verifier.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,12 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
565565
super.visitRedirectingConstructorInvocation(node);
566566
}
567567

568+
@override
569+
void visitSetOrMapLiteral(SetOrMapLiteral node) {
570+
_checkForDuplications(node);
571+
super.visitSetOrMapLiteral(node);
572+
}
573+
568574
@override
569575
void visitSimpleIdentifier(SimpleIdentifier node) {
570576
_checkForDeprecatedMemberUseAtIdentifier(node);
@@ -824,6 +830,31 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
824830
return false;
825831
}
826832

833+
/// Generate hints related to duplicate elements (keys) in sets (maps).
834+
void _checkForDuplications(SetOrMapLiteral node) {
835+
// This only checks for top-level elements. If, for, and spread elements
836+
// that contribute duplicate values are not detected.
837+
if (node.isConst) {
838+
// This case is covered by the ErrorVerifier.
839+
return;
840+
}
841+
final expressions = node.isSet
842+
? node.elements.whereType<Expression>()
843+
: node.elements.whereType<MapLiteralEntry>().map((entry) => entry.key);
844+
final alreadySeen = <DartObject>{};
845+
for (final expression in expressions) {
846+
final constEvaluation = _linterContext.evaluateConstant(expression);
847+
if (constEvaluation.errors.isEmpty) {
848+
if (!alreadySeen.add(constEvaluation.value)) {
849+
var errorCode = node.isSet
850+
? HintCode.EQUAL_ELEMENTS_IN_SET
851+
: HintCode.EQUAL_KEYS_IN_MAP;
852+
_errorReporter.reportErrorForNode(errorCode, expression);
853+
}
854+
}
855+
}
856+
}
857+
827858
/// Checks whether [node] violates the rules of [immutable].
828859
///
829860
/// If [node] is marked with [immutable] or inherits from a class or mixin

pkg/analyzer/lib/src/error/codes.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1654,7 +1654,8 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
16541654
// returned by an iterator.
16551655
static const CompileTimeErrorCode EQUAL_ELEMENTS_IN_CONST_SET =
16561656
CompileTimeErrorCode('EQUAL_ELEMENTS_IN_CONST_SET',
1657-
"Two values in a constant set can't be equal.",
1657+
"Two elements in a constant set literal can't be equal.",
1658+
correction: "Change or remove the duplicate element.",
16581659
hasPublishedDocs: true);
16591660

16601661
/**

pkg/analyzer/test/src/diagnostics/equal_elements_in_const_set_test.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,12 @@ var c = const {1, ...{1}};
177177
}
178178

179179
test_nonConst_entry() async {
180-
await assertNoErrorsInCode('''
180+
// No error, but there is a hint.
181+
await assertErrorsInCode('''
181182
var c = {1, 2, 1};
182-
''');
183+
''', [
184+
error(HintCode.EQUAL_ELEMENTS_IN_SET, 15, 1),
185+
]);
183186
}
184187
}
185188

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/src/dart/error/hint_codes.dart';
6+
import 'package:test_reflective_loader/test_reflective_loader.dart';
7+
8+
import '../dart/resolution/driver_resolution.dart';
9+
10+
main() {
11+
defineReflectiveSuite(() {
12+
defineReflectiveTests(EqualElementsInSetTest);
13+
});
14+
}
15+
16+
@reflectiveTest
17+
class EqualElementsInSetTest extends DriverResolutionTest {
18+
test_constant_constant() async {
19+
await assertErrorsInCode('''
20+
const a = 1;
21+
const b = 1;
22+
var s = {a, b};
23+
''', [
24+
error(HintCode.EQUAL_ELEMENTS_IN_SET, 38, 1),
25+
]);
26+
}
27+
28+
test_literal_constant() async {
29+
await assertErrorsInCode('''
30+
const one = 1;
31+
var s = {1, one};
32+
''', [
33+
error(HintCode.EQUAL_ELEMENTS_IN_SET, 27, 3),
34+
]);
35+
}
36+
37+
test_literal_literal() async {
38+
await assertErrorsInCode('''
39+
var s = {1, 1};
40+
''', [
41+
error(HintCode.EQUAL_ELEMENTS_IN_SET, 12, 1),
42+
]);
43+
}
44+
}

pkg/analyzer/test/src/diagnostics/equal_keys_in_const_map_test.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,12 @@ var c = const {1: null, ...{1: null}};
158158
}
159159

160160
test_nonConst_entry() async {
161-
await assertNoErrorsInCode('''
161+
// No error, but there is a hint.
162+
await assertErrorsInCode('''
162163
var c = {1: null, 2: null, 1: null};
163-
''');
164+
''', [
165+
error(HintCode.EQUAL_KEYS_IN_MAP, 27, 1),
166+
]);
164167
}
165168
}
166169

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/src/dart/error/hint_codes.dart';
6+
import 'package:test_reflective_loader/test_reflective_loader.dart';
7+
8+
import '../dart/resolution/driver_resolution.dart';
9+
10+
main() {
11+
defineReflectiveSuite(() {
12+
defineReflectiveTests(EqualKeysInMapTest);
13+
});
14+
}
15+
16+
@reflectiveTest
17+
class EqualKeysInMapTest extends DriverResolutionTest {
18+
test_constant_constant() async {
19+
await assertErrorsInCode('''
20+
const a = 1;
21+
const b = 1;
22+
var s = {a: 2, b: 3};
23+
''', [
24+
error(HintCode.EQUAL_KEYS_IN_MAP, 41, 1),
25+
]);
26+
}
27+
28+
test_literal_constant() async {
29+
await assertErrorsInCode('''
30+
const one = 1;
31+
var s = {1: 2, one: 3};
32+
''', [
33+
error(HintCode.EQUAL_KEYS_IN_MAP, 30, 3),
34+
]);
35+
}
36+
37+
test_literal_literal() async {
38+
await assertErrorsInCode('''
39+
var s = {1: 2, 1: 3};
40+
''', [
41+
error(HintCode.EQUAL_KEYS_IN_MAP, 15, 1),
42+
]);
43+
}
44+
}

pkg/analyzer/test/src/diagnostics/recursive_compile_time_constant_test.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ const int y = x;
4747
''',
4848
);
4949
// No errors, because the cycle is not in this source.
50-
await assertNoErrorsInCode(r'''
50+
await assertErrorsInCode(r'''
5151
import 'constants.dart';
5252
final z = {x: 0, y: 1};
53-
''');
53+
''', [
54+
error(HintCode.EQUAL_KEYS_IN_MAP, 42, 1),
55+
]);
5456
}
5557

5658
test_initializer_after_toplevel_var() async {

pkg/analyzer/test/src/diagnostics/test_all.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ import 'duplicate_named_argument_test.dart' as duplicate_named_argument;
8282
import 'duplicate_part_test.dart' as duplicate_part;
8383
import 'duplicate_shown_name_test.dart' as duplicate_shown_name;
8484
import 'equal_elements_in_const_set_test.dart' as equal_elements_in_const_set;
85+
import 'equal_elements_in_set_test.dart' as equal_elements_in_set;
8586
import 'equal_keys_in_const_map_test.dart' as equal_keys_in_const_map;
87+
import 'equal_keys_in_map_test.dart' as equal_keys_in_map;
8688
import 'expected_one_list_type_arguments_test.dart'
8789
as expected_one_list_type_arguments;
8890
import 'expected_one_set_type_arguments_test.dart'
@@ -557,7 +559,9 @@ main() {
557559
duplicate_part.main();
558560
duplicate_shown_name.main();
559561
equal_elements_in_const_set.main();
562+
equal_elements_in_set.main();
560563
equal_keys_in_const_map.main();
564+
equal_keys_in_map.main();
561565
expected_one_list_type_arguments.main();
562566
expected_one_set_type_arguments.main();
563567
expected_two_map_type_arguments.main();

pkg/analyzer/tool/diagnostics/diagnostics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1399,7 +1399,7 @@ class C {
13991399

14001400
### equal_elements_in_const_set
14011401

1402-
_Two values in a constant set can't be equal._
1402+
_Two elements in a constant set literal can't be equal._
14031403

14041404
#### Description
14051405

0 commit comments

Comments
 (0)