mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 04:06:59 +00:00
Resolve values of duplicate named arguments.
Change-Id: I3a84db8e7c7e29f730ec0687aee4ed0a62b84f94 Reviewed-on: https://dart-review.googlesource.com/66280 Reviewed-by: Paul Berry <paulberry@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
e2dd823912
commit
1fe0518d3b
|
@ -357,12 +357,6 @@ class CompileTimeErrorCodeTest_Kernel extends CompileTimeErrorCodeTest_Driver {
|
|||
return super.test_duplicateDefinition_inPart();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
test_duplicateNamedArgument() async {
|
||||
await super.test_duplicateNamedArgument();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
@FastaProblem('https://github.com/dart-lang/sdk/issues/30959')
|
||||
|
|
|
@ -150,20 +150,6 @@ class NonErrorResolverTest_Kernel extends NonErrorResolverTest_Driver {
|
|||
return super.test_constConstructorWithNonConstSuper_unresolved();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
test_fieldFormalParameter_genericFunctionTyped() {
|
||||
// Expected 0 errors of type ParserErrorCode.EXPECTED_TOKEN, found 1 (88)
|
||||
return super.test_fieldFormalParameter_genericFunctionTyped();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
test_fieldFormalParameter_genericFunctionTyped_named() {
|
||||
// Expected 0 errors of type ParserErrorCode.EXPECTED_TOKEN, found 1 (89)
|
||||
return super.test_fieldFormalParameter_genericFunctionTyped_named();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
@potentialAnalyzerProblem
|
||||
|
|
|
@ -157,12 +157,6 @@ class StaticTypeWarningCodeTest_Kernel
|
|||
await super.test_returnOfInvalidType_async_future_int_mismatches_int();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
test_returnOfInvalidType_void() async {
|
||||
await super.test_returnOfInvalidType_void();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
test_typeArgumentNotMatchingBounds_classTypeAlias() async {
|
||||
|
@ -359,6 +353,12 @@ class StaticTypeWarningCodeTest_Kernel
|
|||
await super.test_undefinedSuperMethod();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
test_undefinedSuperOperator_indexSetter() async {
|
||||
await super.test_undefinedSuperOperator_indexSetter();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
test_wrongNumberOfTypeArguments_classAlias() async {
|
||||
|
|
|
@ -74,6 +74,14 @@ class AnalysisDriverResolutionTest extends BaseAnalysisDriverTest {
|
|||
expect(actual, isNull);
|
||||
}
|
||||
|
||||
void assertIdentifierTopGetRef(SimpleIdentifier ref, String name) {
|
||||
var getter = findElement.topGet(name);
|
||||
assertElement(ref, getter);
|
||||
|
||||
var type = getter.returnType.toString();
|
||||
assertType(ref, type);
|
||||
}
|
||||
|
||||
void assertInvokeType(InvocationExpression expression, String expected) {
|
||||
DartType actual = expression.staticInvokeType;
|
||||
expect(actual?.toString(), expected);
|
||||
|
@ -86,10 +94,9 @@ class AnalysisDriverResolutionTest extends BaseAnalysisDriverTest {
|
|||
expect(actual.baseElement, same(expectedBase));
|
||||
}
|
||||
|
||||
void assertTopGetRef(String search, String name, String type) {
|
||||
void assertTopGetRef(String search, String name) {
|
||||
var ref = findNode.simple(search);
|
||||
assertElement(ref, findElement.topGet(name));
|
||||
assertType(ref, type);
|
||||
assertIdentifierTopGetRef(ref, name);
|
||||
}
|
||||
|
||||
void assertType(Expression expression, String expected) {
|
||||
|
@ -3309,9 +3316,9 @@ main() {
|
|||
var parenthesized = findNode.parenthesized('(a + b)');
|
||||
assertType(parenthesized, 'int');
|
||||
|
||||
assertTopGetRef('a + b', 'a', 'int');
|
||||
assertTopGetRef('b)', 'b', 'int');
|
||||
assertTopGetRef('c;', 'c', 'double');
|
||||
assertTopGetRef('a + b', 'a');
|
||||
assertTopGetRef('b)', 'b');
|
||||
assertTopGetRef('c;', 'c');
|
||||
|
||||
var assignment = findNode.assignment('= c');
|
||||
if (useCFE) {
|
||||
|
@ -3815,6 +3822,62 @@ main(C c) {
|
|||
assertType(aRef, 'int');
|
||||
}
|
||||
|
||||
test_invalid_invocation_arguments_named_duplicate2() async {
|
||||
addTestFile(r'''
|
||||
void f({p}) {}
|
||||
int a, b;
|
||||
main() {
|
||||
f(p: a, p: b);
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
expect(result.errors, isNotEmpty);
|
||||
var f = findElement.function('f');
|
||||
|
||||
var invocation = findNode.methodInvocation('f(p: a');
|
||||
assertElement(invocation.methodName, f);
|
||||
assertType(invocation.methodName, '({p: dynamic}) → void');
|
||||
assertType(invocation, 'void');
|
||||
|
||||
NamedExpression arg0 = invocation.argumentList.arguments[0];
|
||||
assertElement(arg0.name.label, f.parameters[0]);
|
||||
assertIdentifierTopGetRef(arg0.expression, 'a');
|
||||
|
||||
NamedExpression arg1 = invocation.argumentList.arguments[1];
|
||||
assertElement(arg1.name.label, f.parameters[0]);
|
||||
assertIdentifierTopGetRef(arg1.expression, 'b');
|
||||
}
|
||||
|
||||
test_invalid_invocation_arguments_named_duplicate3() async {
|
||||
addTestFile(r'''
|
||||
void f({p}) {}
|
||||
int a, b, c;
|
||||
main() {
|
||||
f(p: a, p: b, p: c);
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
expect(result.errors, isNotEmpty);
|
||||
var f = findElement.function('f');
|
||||
|
||||
var invocation = findNode.methodInvocation('f(p: a');
|
||||
assertElement(invocation.methodName, f);
|
||||
assertType(invocation.methodName, '({p: dynamic}) → void');
|
||||
assertType(invocation, 'void');
|
||||
|
||||
NamedExpression arg0 = invocation.argumentList.arguments[0];
|
||||
assertElement(arg0.name.label, f.parameters[0]);
|
||||
assertIdentifierTopGetRef(arg0.expression, 'a');
|
||||
|
||||
NamedExpression arg1 = invocation.argumentList.arguments[1];
|
||||
assertElement(arg1.name.label, f.parameters[0]);
|
||||
assertIdentifierTopGetRef(arg1.expression, 'b');
|
||||
|
||||
NamedExpression arg2 = invocation.argumentList.arguments[2];
|
||||
assertElement(arg2.name.label, f.parameters[0]);
|
||||
assertIdentifierTopGetRef(arg2.expression, 'c');
|
||||
}
|
||||
|
||||
test_invalid_invocation_arguments_static_method() async {
|
||||
addTestFile(r'''
|
||||
class C {
|
||||
|
|
|
@ -2053,6 +2053,27 @@ Message _withArgumentsDuplicatedNamePreviouslyUsedCause(String name) {
|
|||
message: """Previous use of '${name}'.""", arguments: {'name': name});
|
||||
}
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Template<Message Function(String name)> templateDuplicatedNamedArgument =
|
||||
const Template<Message Function(String name)>(
|
||||
messageTemplate: r"""Duplicated named argument '#name'.""",
|
||||
withArguments: _withArgumentsDuplicatedNamedArgument);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Code<Message Function(String name)> codeDuplicatedNamedArgument =
|
||||
const Code<Message Function(String name)>(
|
||||
"DuplicatedNamedArgument", templateDuplicatedNamedArgument,
|
||||
analyzerCode: "DUPLICATE_NAMED_ARGUMENT",
|
||||
dart2jsCode: "*fatal*",
|
||||
severity: Severity.error);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
Message _withArgumentsDuplicatedNamedArgument(String name) {
|
||||
return new Message(codeDuplicatedNamedArgument,
|
||||
message: """Duplicated named argument '${name}'.""",
|
||||
arguments: {'name': name});
|
||||
}
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Template<Message Function(String name)> templateDuplicatedParameterName =
|
||||
const Template<Message Function(String name)>(
|
||||
|
|
|
@ -1027,35 +1027,6 @@ abstract class BodyBuilder extends ScopeListener<JumpTarget>
|
|||
arguments.getRange(0, firstNamedArgumentIndex));
|
||||
List<NamedExpression> named = new List<NamedExpression>.from(
|
||||
arguments.getRange(firstNamedArgumentIndex, arguments.length));
|
||||
if (named.length == 2) {
|
||||
if (named[0].name == named[1].name) {
|
||||
named = <NamedExpression>[
|
||||
new NamedExpression(
|
||||
named[1].name,
|
||||
deprecated_buildCompileTimeError(
|
||||
"Duplicated named argument '${named[1].name}'.",
|
||||
named[1].fileOffset))
|
||||
];
|
||||
}
|
||||
} else if (named.length > 2) {
|
||||
Map<String, NamedExpression> seenNames = <String, NamedExpression>{};
|
||||
bool hasProblem = false;
|
||||
for (NamedExpression expression in named) {
|
||||
if (seenNames.containsKey(expression.name)) {
|
||||
hasProblem = true;
|
||||
NamedExpression prevNamedExpression = seenNames[expression.name];
|
||||
prevNamedExpression.value = deprecated_buildCompileTimeError(
|
||||
"Duplicated named argument '${expression.name}'.",
|
||||
expression.fileOffset)
|
||||
..parent = prevNamedExpression;
|
||||
} else {
|
||||
seenNames[expression.name] = expression;
|
||||
}
|
||||
}
|
||||
if (hasProblem) {
|
||||
named = new List<NamedExpression>.from(seenNames.values);
|
||||
}
|
||||
}
|
||||
push(forest.arguments(positional, beginToken, named: named));
|
||||
} else {
|
||||
// TODO(kmillikin): Find a way to avoid allocating a second list in the
|
||||
|
@ -2873,7 +2844,7 @@ abstract class BodyBuilder extends ScopeListener<JumpTarget>
|
|||
Set<String> names =
|
||||
new Set.from(function.namedParameters.map((a) => a.name));
|
||||
for (NamedExpression argument in named) {
|
||||
if (!names.remove(argument.name)) {
|
||||
if (!names.contains(argument.name)) {
|
||||
return fasta.templateNoSuchNamedParameter
|
||||
.withArguments(argument.name)
|
||||
.withLocation(uri, argument.fileOffset, argument.name.length);
|
||||
|
@ -2916,7 +2887,7 @@ abstract class BodyBuilder extends ScopeListener<JumpTarget>
|
|||
Set<String> names =
|
||||
new Set.from(function.namedParameters.map((a) => a.name));
|
||||
for (NamedExpression argument in named) {
|
||||
if (!names.remove(argument.name)) {
|
||||
if (!names.contains(argument.name)) {
|
||||
return fasta.templateNoSuchNamedParameter
|
||||
.withArguments(argument.name)
|
||||
.withLocation(uri, argument.fileOffset, argument.name.length);
|
||||
|
|
|
@ -77,6 +77,7 @@ import '../fasta_codes.dart'
|
|||
messageVoidExpression,
|
||||
noLength,
|
||||
templateArgumentTypeNotAssignable,
|
||||
templateDuplicatedNamedArgument,
|
||||
templateImplicitCallOfNonMethod,
|
||||
templateInvalidAssignment,
|
||||
templateInvalidCastFunctionExpr,
|
||||
|
@ -1374,6 +1375,48 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
|||
receiverType, expressionType);
|
||||
}
|
||||
});
|
||||
|
||||
// Check for and remove duplicated named arguments.
|
||||
var named = arguments.named;
|
||||
if (named.length == 2) {
|
||||
if (named[0].name == named[1].name) {
|
||||
var name = named[1].name;
|
||||
var error = helper.buildCompileTimeError(
|
||||
templateDuplicatedNamedArgument.withArguments(name),
|
||||
named[1].fileOffset,
|
||||
name.length);
|
||||
arguments.named = [new kernel.NamedExpression(named[1].name, error)];
|
||||
formalTypes.removeLast();
|
||||
actualTypes.removeLast();
|
||||
}
|
||||
} else if (named.length > 2) {
|
||||
var seenNames = <String, kernel.NamedExpression>{};
|
||||
var hasProblem = false;
|
||||
var namedTypeIndex = arguments.positional.length;
|
||||
var uniqueNamed = <kernel.NamedExpression>[];
|
||||
for (var expression in named) {
|
||||
var name = expression.name;
|
||||
if (seenNames.containsKey(name)) {
|
||||
hasProblem = true;
|
||||
var prevNamedExpression = seenNames[name];
|
||||
prevNamedExpression.value = helper.buildCompileTimeError(
|
||||
templateDuplicatedNamedArgument.withArguments(name),
|
||||
expression.fileOffset,
|
||||
name.length)
|
||||
..parent = prevNamedExpression;
|
||||
formalTypes.removeAt(namedTypeIndex);
|
||||
actualTypes.removeAt(namedTypeIndex);
|
||||
} else {
|
||||
seenNames[name] = expression;
|
||||
uniqueNamed.add(expression);
|
||||
namedTypeIndex++;
|
||||
}
|
||||
}
|
||||
if (hasProblem) {
|
||||
arguments.named = uniqueNamed;
|
||||
}
|
||||
}
|
||||
|
||||
if (inferenceNeeded) {
|
||||
typeSchemaEnvironment.inferGenericFunctionOrType(
|
||||
returnType,
|
||||
|
|
|
@ -108,6 +108,7 @@ DuplicatedExportInType/analyzerCode: Fail
|
|||
DuplicatedExportInType/example: Fail
|
||||
DuplicatedModifier/script1: Fail
|
||||
DuplicatedName/example: Fail
|
||||
DuplicatedNamedArgument/example: Fail
|
||||
DuplicatedParameterName/example: Fail
|
||||
Encoding/analyzerCode: Fail
|
||||
EnumConstantSameNameAsEnclosing/example: Fail
|
||||
|
|
|
@ -2049,6 +2049,12 @@ DuplicatedNamePreviouslyUsedCause:
|
|||
analyzerCode: REFERENCED_BEFORE_DECLARATION
|
||||
severity: CONTEXT
|
||||
|
||||
DuplicatedNamedArgument:
|
||||
template: "Duplicated named argument '#name'."
|
||||
severity: ERROR
|
||||
analyzerCode: DUPLICATE_NAMED_ARGUMENT
|
||||
dart2jsCode: "*fatal*"
|
||||
|
||||
DuplicatedParameterName:
|
||||
template: "Duplicated parameter name '#name'."
|
||||
severity: ERROR
|
||||
|
|
|
@ -11,6 +11,6 @@ class C extends core::Object {
|
|||
static method test() → void {
|
||||
self::C::m(a: invalid-expression "pkg/front_end/testcases/duplicated_named_args_3.dart:13:19: Error: Duplicated named argument 'a'.
|
||||
C.m(a: 1, a: 2, a: 3);
|
||||
^" as{TypeError} core::int);
|
||||
^");
|
||||
}
|
||||
static method main() → dynamic {}
|
||||
|
|
|
@ -11,6 +11,6 @@ class C extends core::Object {
|
|||
static method test() → void {
|
||||
self::C::m(a: invalid-expression "pkg/front_end/testcases/duplicated_named_args_3.dart:13:19: Error: Duplicated named argument 'a'.
|
||||
C.m(a: 1, a: 2, a: 3);
|
||||
^" as{TypeError} core::int);
|
||||
^");
|
||||
}
|
||||
static method main() → dynamic {}
|
||||
|
|
|
@ -2613,7 +2613,7 @@ class Arguments extends TreeNode {
|
|||
final List<DartType> types;
|
||||
@coqsingle
|
||||
final List<Expression> positional;
|
||||
final List<NamedExpression> named;
|
||||
List<NamedExpression> named;
|
||||
|
||||
Arguments(this.positional,
|
||||
{List<DartType> types, List<NamedExpression> named})
|
||||
|
|
Loading…
Reference in a new issue