blob: 9f7c5e8f592ebf64beeaaf26987936847175a097 [file] [edit]
// Copyright (c) 2026, 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:_fe_analyzer_shared/src/parser/formal_parameter_kind.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/type_environment.dart';
import '../api_prototype/experimental_flags.dart';
import '../base/messages.dart';
import '../base/uri_offset.dart';
import '../builder/formal_parameter_builder.dart';
import '../codes/diagnostic.dart' as diag;
/// Helper class for handling augmentation of parameters.
///
/// It groups corresponding parameters from different fragment to allow for
/// checking and distributing properties, such as default values, across
/// parameters.
class AugmentedParameters {
final List<_Parameter> _parameters = [];
final List<_PositionalParameter> _positionalParameters = [];
final Map<String, _NamedParameter> _namedParameters = {};
/// Registers [formals] as the declared parameter for a fragment.
///
/// [isImplementation] is `true` if the fragment is considered the
/// implementation.
void registerParameters(
List<FormalParameterBuilder>? formals, {
required bool isImplementation,
}) {
if (formals != null) {
int index = 0;
for (FormalParameterBuilder formal in formals) {
switch (formal.kind) {
case FormalParameterKind.optionalPositional:
case FormalParameterKind.requiredPositional:
if (index < _positionalParameters.length) {
_positionalParameters[index].addFormalParameterBuilder(
formal,
isImplementation: isImplementation,
);
} else {
_PositionalParameter parameter = new _PositionalParameter(
index: index,
builder: formal,
);
_positionalParameters.add(parameter);
_parameters.add(parameter);
}
index++;
case FormalParameterKind.optionalNamed:
case FormalParameterKind.requiredNamed:
String name = formal.name;
if (formal.isDuplicate) {
// Don't match duplicate named parameter with other parameter to
// avoid cascading failure due to default values and types across
// parameters with the same name.
_parameters.add(new _NamedParameter(name: name, builder: formal));
} else {
_NamedParameter? namedParameter = _namedParameters[name];
if (namedParameter != null) {
namedParameter.addFormalParameterBuilder(
formal,
isImplementation: isImplementation,
);
} else {
namedParameter = new _NamedParameter(
name: name,
builder: formal,
);
_namedParameters[name] = namedParameter;
_parameters.add(namedParameter);
}
}
}
}
}
}
void processOutlineExpressions(
ProblemReporting problemReporting,
TypeEnvironment typeEnvironment, {
required bool isAbstract,
required bool isExternal,
required LibraryFeatures libraryFeatures,
}) {
for (_Parameter parameter in _parameters) {
parameter.processOutlineExpressions(
problemReporting: problemReporting,
typeEnvironment: typeEnvironment,
isAbstract: isAbstract,
isExternal: isExternal,
libraryFeatures: libraryFeatures,
);
}
}
}
abstract class _Parameter {
final List<FormalParameterBuilder> _builders = [];
FormalParameterBuilder _implementation;
new({required FormalParameterBuilder builder}) : _implementation = builder {
_builders.add(builder);
}
void addFormalParameterBuilder(
FormalParameterBuilder builder, {
required bool isImplementation,
}) {
_builders.add(builder);
if (isImplementation) {
_implementation = builder;
}
}
void processOutlineExpressions({
required ProblemReporting problemReporting,
required TypeEnvironment typeEnvironment,
required bool isAbstract,
required bool isExternal,
required LibraryFeatures libraryFeatures,
}) {
FormalParameterBuilder introductoryFormal = _builders.first;
if (introductoryFormal.isOptional) {
FormalParameterBuilder? formalWithDefaultValue;
for (int i = 0; i < _builders.length; i++) {
FormalParameterBuilder formal = _builders[i];
if (formal.hasDeclaredDefaultValue) {
if (formalWithDefaultValue != null &&
// TODO(johnniwinther): Report this on patches.
libraryFeatures.augmentations.isEnabled) {
problemReporting.addProblem2(
diag.defaultValueAlreadySpecifiedInAugmentationChain,
formal.uriOffset,
context: [
diag.defaultValueAlreadySpecifiedInAugmentationChainCause
.withLocation2(formalWithDefaultValue.uriOffset),
],
);
}
formalWithDefaultValue = formal;
}
}
// TODO(johnniwinther): Check that the parameter types match.
if (!(isAbstract || isExternal) &&
_implementation.variable.type.isPotentiallyNonNullable &&
formalWithDefaultValue == null) {
problemReporting.addProblem2(
diag.optionalNonNullableWithoutInitializerError.withArguments(
parameterName: _implementation.name,
parameterType: _implementation.variable.type,
),
_implementation.uriOffset,
);
for (int i = 0; i < _builders.length; i++) {
_builders[i].variable.hasErroneousDefaultValue = true;
}
}
if (formalWithDefaultValue != null) {
Expression? defaultValue =
formalWithDefaultValue.variable.inferredDefaultValue;
if (defaultValue != null) {
for (FormalParameterBuilder builder in _builders) {
if (builder != formalWithDefaultValue) {
builder.variable.setInferredDefaultValue(defaultValue);
}
}
}
}
}
}
}
class _PositionalParameter({required final int index, required super.builder})
extends _Parameter {}
class _NamedParameter({required final String name, required super.builder})
extends _Parameter;