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:
Brian Wilkerson 2022-08-24 16:33:49 +00:00 committed by Commit Bot
parent 65b4621a09
commit 2b27d3257b
9 changed files with 121 additions and 0 deletions

View file

@ -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:

View file

@ -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,

View file

@ -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.
}
}

View file

@ -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(

View file

@ -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 =

View file

@ -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

View file

@ -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.

View file

@ -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);
''');
}
}

View file

@ -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();