mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +00:00
Report duplicated field names in record literals
Change-Id: Ifc5c3a06c7f489b00421f5b1af47df52615694d5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256200 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
parent
65b4621a09
commit
2b27d3257b
|
@ -354,6 +354,8 @@ CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_NAME:
|
|||
status: needsEvaluation
|
||||
CompileTimeErrorCode.DUPLICATE_DEFINITION:
|
||||
status: needsEvaluation
|
||||
CompileTimeErrorCode.DUPLICATE_FIELD_NAME:
|
||||
status: needsEvaluation
|
||||
CompileTimeErrorCode.DUPLICATE_FIELD_FORMAL_PARAMETER:
|
||||
status: needsEvaluation
|
||||
CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT:
|
||||
|
|
|
@ -159,6 +159,7 @@ const List<ErrorCode> errorCodeValues = [
|
|||
CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_DEFAULT,
|
||||
CompileTimeErrorCode.DUPLICATE_CONSTRUCTOR_NAME,
|
||||
CompileTimeErrorCode.DUPLICATE_DEFINITION,
|
||||
CompileTimeErrorCode.DUPLICATE_FIELD_NAME,
|
||||
CompileTimeErrorCode.DUPLICATE_FIELD_FORMAL_PARAMETER,
|
||||
CompileTimeErrorCode.DUPLICATE_NAMED_ARGUMENT,
|
||||
CompileTimeErrorCode.DUPLICATE_PART,
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/error/listener.dart';
|
||||
import 'package:analyzer/src/dart/ast/ast.dart';
|
||||
import 'package:analyzer/src/diagnostic/diagnostic_factory.dart';
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
|
||||
/// Helper for resolving [RecordLiteral]s.
|
||||
class RecordLiteralResolver {
|
||||
final ResolverVisitor _resolver;
|
||||
|
||||
RecordLiteralResolver({
|
||||
required ResolverVisitor resolver,
|
||||
}) : _resolver = resolver;
|
||||
|
||||
ErrorReporter get errorReporter => _resolver.errorReporter;
|
||||
|
||||
/// Report any named fields in the record literal [node] that use a previously
|
||||
/// defined name.
|
||||
void reportDuplicateFieldDefinitions(RecordLiteralImpl node) {
|
||||
var fields = node.fields;
|
||||
var usedNames = <String, NamedExpression>{};
|
||||
for (var field in fields) {
|
||||
if (field is NamedExpression) {
|
||||
var nameNode = field.name.label;
|
||||
var name = nameNode.name;
|
||||
var previousField = usedNames[name];
|
||||
if (previousField != null) {
|
||||
errorReporter.reportError(DiagnosticFactory()
|
||||
.duplicateFieldDefinition(
|
||||
errorReporter.source, field, previousField));
|
||||
} else {
|
||||
usedNames[name] = field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resolve(RecordLiteralImpl node, {required DartType? contextType}) {
|
||||
// TODO(brianwilkerson) Move resolution from the `visitRecordLiteral`
|
||||
// methods of `ResolverVisitor` and `StaticTypeAnalyzer` to this class.
|
||||
}
|
||||
}
|
|
@ -61,6 +61,29 @@ class DiagnosticFactory {
|
|||
);
|
||||
}
|
||||
|
||||
/// Return a diagnostic indicating that [duplicateField] reuses a name
|
||||
/// already used by [originalField].
|
||||
AnalysisError duplicateFieldDefinition(Source source,
|
||||
NamedExpression duplicateField, NamedExpression originalField) {
|
||||
var duplicateNode = duplicateField.name.label;
|
||||
var duplicateName = duplicateNode.name;
|
||||
return AnalysisError(
|
||||
source,
|
||||
duplicateNode.offset,
|
||||
duplicateNode.length,
|
||||
CompileTimeErrorCode.DUPLICATE_FIELD_NAME,
|
||||
[duplicateName],
|
||||
[
|
||||
DiagnosticMessageImpl(
|
||||
filePath: source.fullName,
|
||||
length: duplicateName.length,
|
||||
message: 'The first ',
|
||||
offset: originalField.name.label.offset,
|
||||
url: source.uri.toString()),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Return a diagnostic indicating that the [duplicateElement] (in a constant
|
||||
/// set) is a duplicate of the [originalElement].
|
||||
AnalysisError equalElementsInConstSet(
|
||||
|
|
|
@ -1130,6 +1130,12 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
|
|||
hasPublishedDocs: true,
|
||||
);
|
||||
|
||||
static const CompileTimeErrorCode DUPLICATE_FIELD_NAME = CompileTimeErrorCode(
|
||||
'DUPLICATE_FIELD_NAME',
|
||||
"The field name '{0}' is already used.",
|
||||
correctionMessage: "Try renaming the field.",
|
||||
);
|
||||
|
||||
/// Parameters:
|
||||
/// 0: the name of the parameter that was duplicated
|
||||
static const CompileTimeErrorCode DUPLICATE_NAMED_ARGUMENT =
|
||||
|
|
|
@ -50,6 +50,7 @@ import 'package:analyzer/src/dart/resolver/postfix_expression_resolver.dart';
|
|||
import 'package:analyzer/src/dart/resolver/prefix_expression_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/prefixed_identifier_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/property_element_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/record_literal_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/scope.dart';
|
||||
import 'package:analyzer/src/dart/resolver/simple_identifier_resolver.dart';
|
||||
import 'package:analyzer/src/dart/resolver/this_lookup.dart';
|
||||
|
@ -248,6 +249,9 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
|||
late final PropertyElementResolver _propertyElementResolver =
|
||||
PropertyElementResolver(this);
|
||||
|
||||
late final RecordLiteralResolver _recordLiteralResolver =
|
||||
RecordLiteralResolver(resolver: this);
|
||||
|
||||
late final AnnotationResolver _annotationResolver = AnnotationResolver(this);
|
||||
|
||||
final bool genericMetadataIsEnabled;
|
||||
|
@ -2215,6 +2219,7 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
|
|||
}
|
||||
}
|
||||
typeAnalyzer.visitRecordLiteral(node, contextType: contextType);
|
||||
_recordLiteralResolver.reportDuplicateFieldDefinitions(node);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -3361,6 +3361,9 @@ CompileTimeErrorCode:
|
|||
int x = 0;
|
||||
int y = 1;
|
||||
```
|
||||
DUPLICATE_FIELD_NAME:
|
||||
problemMessage: The field name '{0}' is already used.
|
||||
correctionMessage: Try renaming the field.
|
||||
DUPLICATE_FIELD_FORMAL_PARAMETER:
|
||||
problemMessage: "The field '{0}' can't be initialized by multiple parameters in the same constructor."
|
||||
correctionMessage: Try removing one of the parameters, or using different fields.
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analyzer/src/error/codes.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/context_collection_resolution.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(DuplicateFieldNameTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class DuplicateFieldNameTest extends PubPackageResolutionTest {
|
||||
test_duplicated() async {
|
||||
await assertErrorsInCode(r'''
|
||||
var r = (a: 1, a: 2);
|
||||
''', [
|
||||
error(CompileTimeErrorCode.DUPLICATE_FIELD_NAME, 15, 1,
|
||||
contextMessages: [message('/home/test/lib/test.dart', 9, 1)]),
|
||||
]);
|
||||
}
|
||||
|
||||
test_notDuplicated() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
var r = (a: 1, b: 2);
|
||||
''');
|
||||
}
|
||||
}
|
|
@ -153,6 +153,7 @@ import 'duplicate_constructor_name_test.dart' as duplicate_constructor_name;
|
|||
import 'duplicate_definition_test.dart' as duplicate_definition;
|
||||
import 'duplicate_field_formal_parameter_test.dart'
|
||||
as duplicate_field_formal_parameter;
|
||||
import 'duplicate_field_name_test.dart' as duplicate_field_name;
|
||||
import 'duplicate_hidden_name_test.dart' as duplicate_hidden_name;
|
||||
import 'duplicate_ignore_test.dart' as duplicate_ignore;
|
||||
import 'duplicate_import_test.dart' as duplicate_import;
|
||||
|
@ -904,6 +905,7 @@ main() {
|
|||
duplicate_constructor_default.main();
|
||||
duplicate_constructor_name.main();
|
||||
duplicate_definition.main();
|
||||
duplicate_field_name.main();
|
||||
duplicate_field_formal_parameter.main();
|
||||
duplicate_hidden_name.main();
|
||||
duplicate_ignore.main();
|
||||
|
|
Loading…
Reference in a new issue