blob: 58882f24442b3f2d7ef3f8c5c0d1a35e7e9ed8be [file] [log] [blame] [edit]
// Copyright (c) 2025, 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:kernel/ast.dart' hide Combinator, MapLiteralEntry;
import 'package:kernel/class_hierarchy.dart'
show ClassHierarchyBase, ClassHierarchyMembers;
import 'package:kernel/src/bounds_checks.dart'
show
TypeArgumentIssue,
findTypeArgumentIssues,
findTypeArgumentIssuesForInvocation,
getGenericTypeName,
hasGenericFunctionTypeAsTypeArgument;
import 'package:kernel/type_algebra.dart';
import 'package:kernel/type_environment.dart' show TypeEnvironment;
import '../api_prototype/experimental_flags.dart';
import '../base/compiler_context.dart';
import '../base/messages.dart';
import '../base/uri_offset.dart';
import '../builder/formal_parameter_builder.dart';
import '../kernel/internal_ast.dart';
import 'source_library_builder.dart';
extension CheckHelper on ProblemReporting {
InvalidExpression buildProblem({
required CompilerContext compilerContext,
required Message message,
required Uri fileUri,
required int fileOffset,
required int length,
List<LocatedMessage>? context,
bool errorHasBeenReported = false,
Expression? expression,
}) {
if (!errorHasBeenReported) {
addProblem(
message,
fileOffset,
length,
fileUri,
wasHandled: true,
context: context,
);
}
String text = compilerContext
.format(
message.withLocation(fileUri, fileOffset, length),
CfeSeverity.error,
)
.plain;
return new InvalidExpression(text, expression)..fileOffset = fileOffset;
}
Expression buildProblemWithContextFromMember({
required CompilerContext compilerContext,
required String name,
required Member member,
required LocatedMessage message,
required Uri fileUri,
}) {
List<LocatedMessage>? context;
Location? location = member.location;
if (location != null) {
Uri uri = location.file;
int offset = member.fileOffset;
Message contextMessage;
int length = noLength;
if (member is Constructor && member.isSynthetic) {
offset = member.enclosingClass.fileOffset;
contextMessage = codeCandidateFoundIsDefaultConstructor
.withArgumentsOld(member.enclosingClass.name);
} else {
if (member is Constructor) {
if (member.name.text == '') {
length = member.enclosingClass.name.length;
} else {
// Assume no spaces around the dot. Not perfect, but probably the
// best we can do with the information available.
length = member.enclosingClass.name.length + 1 + name.length;
}
} else {
length = name.length;
}
contextMessage = codeCandidateFound;
}
context = [contextMessage.withLocation(uri, offset, length)];
}
return buildProblem(
compilerContext: compilerContext,
message: message.messageObject,
fileUri: fileUri,
fileOffset: message.charOffset,
length: message.length,
context: context,
);
}
InvalidExpression buildProblemFromLocatedMessage({
required CompilerContext compilerContext,
required LocatedMessage message,
}) {
return buildProblem(
compilerContext: compilerContext,
message: message.messageObject,
fileUri: message.uri!,
fileOffset: message.charOffset,
length: message.length,
);
}
LocatedMessage? checkArgumentsForFunction({
required FunctionNode function,
required TypeArguments? explicitTypeArguments,
required ActualArguments arguments,
required int fileOffset,
required Uri fileUri,
required List<TypeParameter> typeParameters,
Extension? extension,
}) {
int typeParameterCount = typeParameters.length;
int requiredParameterCount = function.requiredParameterCount;
int positionalParameterCount = function.positionalParameters.length;
int positionalArgumentsCount = arguments.positionalCount;
if (extension != null) {
// Extension member invocations have additional synthetic parameter for
// `this`.
--requiredParameterCount;
--positionalParameterCount;
typeParameterCount -= extension.typeParameters.length;
}
if (positionalArgumentsCount < requiredParameterCount) {
return codeTooFewArguments
.withArgumentsOld(requiredParameterCount, positionalArgumentsCount)
.withLocation(fileUri, arguments.fileOffset, noLength);
}
if (positionalArgumentsCount > positionalParameterCount) {
return codeTooManyArguments
.withArgumentsOld(positionalParameterCount, positionalArgumentsCount)
.withLocation(fileUri, arguments.fileOffset, noLength);
}
Set<String> argumentNames = {};
if (arguments.namedCount > 0) {
Set<String?> parameterNames = new Set.of(
function.namedParameters.map((a) => a.name),
);
for (Argument argument in arguments.argumentList) {
switch (argument) {
case NamedArgument():
NamedExpression namedExpression = argument.namedExpression;
String name = namedExpression.name;
argumentNames.add(name);
if (!parameterNames.contains(name)) {
return codeNoSuchNamedParameter
.withArgumentsOld(name)
.withLocation(
fileUri,
namedExpression.fileOffset,
name.length,
);
}
case PositionalArgument():
break;
}
}
}
if (function.namedParameters.isNotEmpty) {
for (int i = 0; i < function.namedParameters.length; i++) {
VariableDeclaration parameter = function.namedParameters[i];
if (parameter.isRequired && !argumentNames.contains(parameter.name)) {
return codeValueForRequiredParameterNotProvidedError
.withArgumentsOld(parameter.name!)
.withLocation(fileUri, arguments.fileOffset, noLength);
}
}
}
if (explicitTypeArguments != null) {
if (typeParameterCount != explicitTypeArguments.types.length) {
// A wrong (non-zero) amount of type arguments given. That's an error.
// TODO(jensj): Position should be on type arguments instead.
return codeTypeArgumentMismatch
.withArgumentsOld(typeParameterCount)
.withLocation(fileUri, fileOffset, noLength);
}
}
return null;
}
LocatedMessage? checkArgumentsForType({
required FunctionType function,
required TypeArguments? explicitTypeArguments,
required ActualArguments arguments,
required Uri fileUri,
required int fileOffset,
}) {
int requiredPositionalParameterCountToReport =
function.requiredParameterCount;
int positionalParameterCountToReport = function.positionalParameters.length;
int positionalArgumentCountToReport = arguments.positionalCount;
if (positionalArgumentCountToReport < function.requiredParameterCount) {
return codeTooFewArguments
.withArgumentsOld(
requiredPositionalParameterCountToReport,
positionalArgumentCountToReport,
)
.withLocation(fileUri, arguments.fileOffset, noLength);
}
if (positionalArgumentCountToReport >
function.positionalParameters.length) {
return codeTooManyArguments
.withArgumentsOld(
positionalParameterCountToReport,
positionalArgumentCountToReport,
)
.withLocation(fileUri, arguments.fileOffset, noLength);
}
Set<String> argumentNames = {};
if (arguments.namedCount > 0) {
Set<String> names = new Set.of(
function.namedParameters.map((a) => a.name),
);
for (Argument argument in arguments.argumentList) {
switch (argument) {
case NamedArgument():
NamedExpression namedExpression = argument.namedExpression;
String name = namedExpression.name;
argumentNames.add(name);
if (!names.contains(name)) {
return codeNoSuchNamedParameter
.withArgumentsOld(name)
.withLocation(
fileUri,
namedExpression.fileOffset,
name.length,
);
}
case PositionalArgument():
break;
}
}
}
if (function.namedParameters.isNotEmpty) {
for (int i = 0; i < function.namedParameters.length; i++) {
NamedType parameter = function.namedParameters[i];
if (parameter.isRequired && !argumentNames.contains(parameter.name)) {
return codeValueForRequiredParameterNotProvidedError
.withArgumentsOld(parameter.name)
.withLocation(fileUri, arguments.fileOffset, noLength);
}
}
}
List<StructuralParameter> typeParameters = function.typeParameters;
if (explicitTypeArguments != null &&
typeParameters.length != explicitTypeArguments.types.length) {
// A wrong (non-zero) amount of type arguments given. That's an error.
// TODO(jensj): Position should be on type arguments instead.
return codeTypeArgumentMismatch
.withArgumentsOld(typeParameters.length)
.withLocation(fileUri, fileOffset, noLength);
}
return null;
}
void checkAsyncReturnType({
required SourceLibraryBuilder libraryBuilder,
required TypeEnvironment typeEnvironment,
required AsyncMarker asyncModifier,
required DartType returnType,
required Uri fileUri,
required int fileOffset,
required int length,
}) {
// For async, async*, and sync* functions with declared return types, we
// need to determine whether those types are valid.
// We use the same trick in each case below. For example to decide whether
// Future<T> <: [returnType] for every T, we rely on Future<Bot> and
// transitivity of the subtyping relation because Future<Bot> <: Future<T>
// for every T.
// We use [problem == null] to signal success.
Message? problem;
switch (asyncModifier) {
case AsyncMarker.Async:
DartType futureBottomType = libraryBuilder.loader.futureOfBottom;
if (!typeEnvironment.isSubtypeOf(futureBottomType, returnType)) {
problem = codeIllegalAsyncReturnType;
}
break;
case AsyncMarker.AsyncStar:
DartType streamBottomType = libraryBuilder.loader.streamOfBottom;
if (returnType is VoidType) {
problem = codeIllegalAsyncGeneratorVoidReturnType;
} else if (!typeEnvironment.isSubtypeOf(streamBottomType, returnType)) {
problem = codeIllegalAsyncGeneratorReturnType;
}
break;
case AsyncMarker.SyncStar:
DartType iterableBottomType = libraryBuilder.loader.iterableOfBottom;
if (returnType is VoidType) {
problem = codeIllegalSyncGeneratorVoidReturnType;
} else if (!typeEnvironment.isSubtypeOf(
iterableBottomType,
returnType,
)) {
problem = codeIllegalSyncGeneratorReturnType;
}
break;
case AsyncMarker.Sync:
break; // skip
}
if (problem != null) {
// TODO(hillerstrom): once types get annotated with location
// information, we can improve the quality of the error message by
// using the offset of [returnType] (and the length of its name).
addProblem(problem, fileOffset, length, fileUri);
}
}
void checkBoundsInConstructorInvocation({
required LibraryFeatures libraryFeatures,
required Constructor constructor,
required List<DartType> explicitOrInferredTypeArguments,
required TypeEnvironment typeEnvironment,
required Uri fileUri,
required int fileOffset,
required bool hasInferredTypeArguments,
}) {
if (explicitOrInferredTypeArguments.isEmpty) return;
Class klass = constructor.enclosingClass;
DartType constructedType = new InterfaceType(
klass,
klass.enclosingLibrary.nonNullable,
explicitOrInferredTypeArguments,
);
checkBoundsInType(
libraryFeatures: libraryFeatures,
type: constructedType,
typeEnvironment: typeEnvironment,
fileUri: fileUri,
fileOffset: fileOffset,
hasInferredTypeArguments: hasInferredTypeArguments,
allowSuperBounded: false,
);
}
void checkBoundsInFactoryInvocation({
required LibraryFeatures libraryFeatures,
required Procedure factory,
required List<DartType> explicitOrInferredTypeArguments,
required TypeEnvironment typeEnvironment,
required Uri fileUri,
required int fileOffset,
required bool hasInferredTypeArguments,
}) {
if (explicitOrInferredTypeArguments.isEmpty) {
return;
}
assert(factory.isFactory || factory.isExtensionTypeMember);
DartType constructedType = Substitution.fromPairs(
factory.function.typeParameters,
explicitOrInferredTypeArguments,
).substituteType(factory.function.returnType);
checkBoundsInType(
libraryFeatures: libraryFeatures,
type: constructedType,
typeEnvironment: typeEnvironment,
fileUri: fileUri,
fileOffset: fileOffset,
hasInferredTypeArguments: hasInferredTypeArguments,
allowSuperBounded: false,
);
}
void checkBoundsInFunctionInvocation({
required ProblemReportingHelper problemReportingHelper,
required LibraryFeatures libraryFeatures,
required TypeEnvironment typeEnvironment,
required FunctionType functionType,
required String? localName,
required List<DartType> explicitOrInferredTypeArguments,
required ActualArguments arguments,
required Uri fileUri,
required int fileOffset,
required bool hasInferredTypeArguments,
}) {
if (explicitOrInferredTypeArguments.isEmpty) {
return;
}
if (functionType.typeParameters.length !=
explicitOrInferredTypeArguments.length) {
// Coverage-ignore-block(suite): Not run.
assert(
problemReportingHelper.assertProblemReportedElsewhere(
"SourceLibraryBuilder.checkBoundsInFunctionInvocation: "
"the numbers of type parameters and type arguments don't match.",
expectedPhase: CompilationPhaseForProblemReporting.outline,
),
);
return;
}
final DartType bottomType = const NeverType.nonNullable();
List<TypeArgumentIssue> issues = findTypeArgumentIssuesForInvocation(
getFreshTypeParametersFromStructuralParameters(
functionType.typeParameters,
).freshTypeParameters,
explicitOrInferredTypeArguments,
typeEnvironment,
bottomType,
areGenericArgumentsAllowed: libraryFeatures.genericMetadata.isEnabled,
);
_reportTypeArgumentIssues(
issues,
fileUri,
fileOffset,
inferred: hasInferredTypeArguments,
// TODO(johnniwinther): Special-case messaging on function type
// invocation to avoid reference to 'call' and use the function type
// instead.
targetName: localName ?? 'call',
);
}
void checkBoundsInInstantiation({
required ProblemReportingHelper problemReportingHelper,
required LibraryFeatures libraryFeatures,
required TypeEnvironment typeEnvironment,
required FunctionType functionType,
required List<DartType> explicitOrInferredTypeArguments,
required Uri fileUri,
required int fileOffset,
required bool hasInferredTypeArguments,
}) {
if (explicitOrInferredTypeArguments.isEmpty) {
return;
}
if (functionType.typeParameters.length !=
explicitOrInferredTypeArguments.length) {
// Coverage-ignore-block(suite): Not run.
assert(
problemReportingHelper.assertProblemReportedElsewhere(
"SourceLibraryBuilder.checkBoundsInInstantiation: "
"the numbers of type parameters and type arguments don't match.",
expectedPhase: CompilationPhaseForProblemReporting.outline,
),
);
return;
}
final DartType bottomType = const NeverType.nonNullable();
List<TypeArgumentIssue> issues = findTypeArgumentIssuesForInvocation(
getFreshTypeParametersFromStructuralParameters(
functionType.typeParameters,
).freshTypeParameters,
explicitOrInferredTypeArguments,
typeEnvironment,
bottomType,
areGenericArgumentsAllowed: libraryFeatures.genericMetadata.isEnabled,
);
_reportTypeArgumentIssues(
issues,
fileUri,
fileOffset,
targetReceiver: functionType,
inferred: hasInferredTypeArguments,
);
}
void checkBoundsInMethodInvocation({
required ProblemReportingHelper problemReportingHelper,
required LibraryFeatures libraryFeatures,
required DartType receiverType,
required TypeEnvironment typeEnvironment,
required ClassHierarchyBase classHierarchy,
required ClassHierarchyMembers membersHierarchy,
required Name name,
required Member? interfaceTarget,
required List<DartType> explicitOrInferredTypeArguments,
required ActualArguments arguments,
required Uri fileUri,
required int fileOffset,
required bool hasInferredTypeArguments,
}) {
if (explicitOrInferredTypeArguments.isEmpty) {
return;
}
Class klass;
List<DartType> receiverTypeArguments;
Map<TypeParameter, DartType> substitutionMap = <TypeParameter, DartType>{};
if (receiverType is InterfaceType) {
klass = receiverType.classNode;
receiverTypeArguments = receiverType.typeArguments;
for (int i = 0; i < receiverTypeArguments.length; ++i) {
substitutionMap[klass.typeParameters[i]] = receiverTypeArguments[i];
}
} else {
return;
}
// TODO(cstefantsova): Find a better way than relying on [interfaceTarget].
Member? method =
membersHierarchy.getDispatchTarget(klass, name) ?? interfaceTarget;
if (method == null || method is! Procedure) {
return;
}
if (klass != method.enclosingClass) {
Supertype parent = classHierarchy.getClassAsInstanceOf(
klass,
method.enclosingClass!,
)!;
klass = method.enclosingClass!;
receiverTypeArguments = parent.typeArguments;
Map<TypeParameter, DartType> instanceSubstitutionMap = substitutionMap;
substitutionMap = <TypeParameter, DartType>{};
for (int i = 0; i < receiverTypeArguments.length; ++i) {
substitutionMap[klass.typeParameters[i]] = substitute(
receiverTypeArguments[i],
instanceSubstitutionMap,
);
}
}
List<TypeParameter> methodParameters = method.function.typeParameters;
if (methodParameters.length != explicitOrInferredTypeArguments.length) {
// Coverage-ignore-block(suite): Not run.
assert(
problemReportingHelper.assertProblemReportedElsewhere(
"SourceLibraryBuilder.checkBoundsInMethodInvocation: "
"the numbers of type parameters and type arguments don't match.",
expectedPhase: CompilationPhaseForProblemReporting.outline,
),
);
return;
}
List<TypeParameter> methodTypeParametersOfInstantiated =
getFreshTypeParameters(methodParameters).freshTypeParameters;
for (TypeParameter typeParameter in methodTypeParametersOfInstantiated) {
typeParameter.bound = substitute(typeParameter.bound, substitutionMap);
typeParameter.defaultType = substitute(
typeParameter.defaultType,
substitutionMap,
);
}
final DartType bottomType = const NeverType.nonNullable();
List<TypeArgumentIssue> issues = findTypeArgumentIssuesForInvocation(
methodTypeParametersOfInstantiated,
explicitOrInferredTypeArguments,
typeEnvironment,
bottomType,
areGenericArgumentsAllowed: libraryFeatures.genericMetadata.isEnabled,
);
_reportTypeArgumentIssues(
issues,
fileUri,
fileOffset,
inferred: hasInferredTypeArguments,
targetReceiver: receiverType,
targetName: name.text,
);
}
void checkBoundsInStaticInvocation({
required ProblemReportingHelper problemReportingHelper,
required LibraryFeatures libraryFeatures,
required String targetName,
required TypeEnvironment typeEnvironment,
required Uri fileUri,
required List<TypeParameter> typeParameters,
required List<DartType> explicitOrInferredTypeArguments,
required bool hasInferredTypeArguments,
required int fileOffset,
}) {
if (explicitOrInferredTypeArguments.isEmpty) return;
if (typeParameters.length != explicitOrInferredTypeArguments.length) {
// Coverage-ignore-block(suite): Not run.
assert(
problemReportingHelper.assertProblemReportedElsewhere(
"SourceLibraryBuilder.checkBoundsInStaticInvocation: "
"the numbers of type parameters and type arguments don't match.",
expectedPhase: CompilationPhaseForProblemReporting.outline,
),
);
return;
}
final DartType bottomType = const NeverType.nonNullable();
List<TypeArgumentIssue> issues = findTypeArgumentIssuesForInvocation(
typeParameters,
explicitOrInferredTypeArguments,
typeEnvironment,
bottomType,
areGenericArgumentsAllowed: libraryFeatures.genericMetadata.isEnabled,
);
if (issues.isNotEmpty) {
_reportTypeArgumentIssues(
issues,
fileUri,
fileOffset,
inferred: hasInferredTypeArguments,
targetName: targetName,
);
}
}
void checkBoundsInType({
required LibraryFeatures libraryFeatures,
required DartType type,
required TypeEnvironment typeEnvironment,
required Uri fileUri,
required int fileOffset,
required bool hasInferredTypeArguments,
bool allowSuperBounded = true,
}) {
List<TypeArgumentIssue> issues = findTypeArgumentIssues(
type,
typeEnvironment,
allowSuperBounded: allowSuperBounded,
areGenericArgumentsAllowed: libraryFeatures.genericMetadata.isEnabled,
);
_reportTypeArgumentIssues(
issues,
fileUri,
fileOffset,
inferred: hasInferredTypeArguments,
);
}
/// Reports an error if [type] contains is a generic function type used as
/// a type argument through its alias.
///
/// For instance
///
/// typedef A = B<void Function<T>(T)>;
///
/// here `A` doesn't use a generic function as type argument directly, but
/// its unaliased value `B<void Function<T>(T)>` does.
///
/// This is used for reporting generic function types used as a type argument,
/// which was disallowed before the 'generic-metadata' feature was enabled.
void checkGenericFunctionTypeAsTypeArgumentThroughTypedef({
required LibraryFeatures libraryFeatures,
required TypedefType type,
required Uri fileUri,
required int fileOffset,
}) {
assert(!libraryFeatures.genericMetadata.isEnabled);
if (!hasGenericFunctionTypeAsTypeArgument(type)) {
DartType unaliased = type.unalias;
if (hasGenericFunctionTypeAsTypeArgument(unaliased)) {
addProblem(
codeGenericFunctionTypeAsTypeArgumentThroughTypedef.withArgumentsOld(
unaliased,
type,
),
fileOffset,
noLength,
fileUri,
);
}
}
}
void checkGetterSetterTypes({
required LibraryFeatures libraryFeatures,
required TypeEnvironment typeEnvironment,
required DartType getterType,
required String getterName,
required UriOffsetLength getterUriOffset,
required DartType setterType,
required String setterName,
required UriOffsetLength setterUriOffset,
}) {
if (libraryFeatures.getterSetterError.isEnabled ||
getterType is InvalidType ||
setterType is InvalidType) {
// Don't report a problem because the it isn't considered a problem in the
// current Dart version or because something else is wrong that has
// already been reported.
} else {
bool isValid = typeEnvironment.isSubtypeOf(getterType, setterType);
if (!isValid) {
addProblem2(
codeInvalidGetterSetterType.withArgumentsOld(
getterType,
getterName,
setterType,
setterName,
),
getterUriOffset,
context: [
codeInvalidGetterSetterTypeSetterContext
.withArgumentsOld(setterName)
.withLocation2(setterUriOffset),
],
);
}
}
}
/// Checks that non-nullable optional parameters have a default value.
void checkInitializersInFormals({
required List<FormalParameterBuilder>? formals,
required TypeEnvironment typeEnvironment,
required bool isAbstract,
required bool isExternal,
}) {
if (formals != null && !(isAbstract || isExternal)) {
for (int i = 0; i < formals.length; i++) {
FormalParameterBuilder formal = formals[i];
bool isOptionalPositional =
formal.isOptionalPositional && formal.isPositional;
bool isOptionalNamed = !formal.isRequiredNamed && formal.isNamed;
bool isOptional = isOptionalPositional || isOptionalNamed;
if (isOptional &&
formal.variable!.type.isPotentiallyNonNullable &&
!formal.hasDeclaredInitializer) {
addProblem(
codeOptionalNonNullableWithoutInitializerError.withArgumentsOld(
formal.name,
formal.variable!.type,
),
formal.fileOffset,
formal.name.length,
formal.fileUri,
);
formal.variable?.isErroneouslyInitialized = true;
}
}
}
}
Expression? checkStaticArguments({
required CompilerContext compilerContext,
required Member target,
required TypeArguments? explicitTypeArguments,
required ActualArguments arguments,
required int fileOffset,
required Uri fileUri,
}) {
List<TypeParameter> typeParameters = target.function!.typeParameters;
if (target is Constructor) {
assert(!target.enclosingClass.isAbstract);
typeParameters = target.enclosingClass.typeParameters;
}
LocatedMessage? argMessage = checkArgumentsForFunction(
function: target.function!,
explicitTypeArguments: explicitTypeArguments,
arguments: arguments,
fileOffset: fileOffset,
fileUri: fileUri,
typeParameters: typeParameters,
);
if (argMessage != null) {
return buildProblemWithContextFromMember(
compilerContext: compilerContext,
name: target.name.text,
member: target,
message: argMessage,
fileUri: fileUri,
);
}
return null;
}
void checkTypesInField({
required TypeEnvironment typeEnvironment,
required bool isInstanceMember,
required bool isLate,
required bool isExternal,
required bool hasInitializer,
required DartType fieldType,
required String name,
required int nameLength,
required int nameOffset,
required Uri fileUri,
}) {
// Check that the field has an initializer if its type is potentially
// non-nullable.
// Only static and top-level fields are checked here. Instance fields are
// checked elsewhere.
if (!isInstanceMember &&
!isLate &&
!isExternal &&
fieldType is! InvalidType &&
fieldType.isPotentiallyNonNullable &&
!hasInitializer) {
addProblem(
codeFieldNonNullableWithoutInitializerError.withArgumentsOld(
name,
fieldType,
),
nameOffset,
nameLength,
fileUri,
);
}
}
void reportTypeArgumentIssue({
required Message message,
required Uri fileUri,
required int fileOffset,
TypeParameter? typeParameter,
DartType? superBoundedAttempt,
DartType? superBoundedAttemptInverted,
}) {
List<LocatedMessage>? context;
// Skip reporting location for function-type type parameters as it's a
// limitation of Kernel.
if (typeParameter != null &&
typeParameter.fileOffset != -1 &&
typeParameter.location?.file != null) {
// It looks like when parameters come from augmentation libraries, they
// don't have a reportable location.
(context ??= <LocatedMessage>[]).add(
codeIncorrectTypeArgumentVariable.withLocation(
typeParameter.location!.file,
typeParameter.fileOffset,
noLength,
),
);
}
if (superBoundedAttemptInverted != null && superBoundedAttempt != null) {
// Coverage-ignore-block(suite): Not run.
(context ??= <LocatedMessage>[]).add(
codeSuperBoundedHint
.withArgumentsOld(superBoundedAttempt, superBoundedAttemptInverted)
.withLocation(fileUri, fileOffset, noLength),
);
}
addProblem(message, fileOffset, noLength, fileUri, context: context);
}
Expression wrapInLocatedProblem({
required CompilerContext compilerContext,
required Expression expression,
required LocatedMessage message,
List<LocatedMessage>? context,
bool errorHasBeenReported = false,
bool includeExpression = true,
}) {
// TODO(askesc): Produce explicit error expression wrapping the original.
// See [issue 29717](https://github.com/dart-lang/sdk/issues/29717)
int offset = expression.fileOffset;
if (offset == -1) {
offset = message.charOffset;
}
return buildProblem(
compilerContext: compilerContext,
message: message.messageObject,
fileUri: message.uri!,
fileOffset: message.charOffset,
length: message.length,
context: context,
expression: includeExpression ? expression : null,
errorHasBeenReported: errorHasBeenReported,
);
}
Expression wrapInProblem({
required CompilerContext compilerContext,
required Expression expression,
required Message message,
required Uri fileUri,
required int fileOffset,
required int length,
List<LocatedMessage>? context,
bool? errorHasBeenReported,
bool includeExpression = true,
}) {
CfeSeverity severity = message.code.severity;
if (severity == CfeSeverity.error) {
return wrapInLocatedProblem(
compilerContext: compilerContext,
expression: expression,
message: message.withLocation(fileUri, fileOffset, length),
context: context,
errorHasBeenReported:
errorHasBeenReported ?? expression is InvalidExpression,
includeExpression: includeExpression,
);
} else {
// Coverage-ignore-block(suite): Not run.
if (expression is! InvalidExpression) {
addProblem(message, fileOffset, length, fileUri, context: context);
}
return expression;
}
}
void _reportTypeArgumentIssueForStructuralParameter(
Message message,
Uri fileUri,
int fileOffset, {
TypeParameter? typeParameter,
DartType? superBoundedAttempt,
DartType? superBoundedAttemptInverted,
}) {
List<LocatedMessage>? context;
// Skip reporting location for function-type type parameters as it's a
// limitation of Kernel.
if (typeParameter != null && typeParameter.location != null) {
// It looks like when parameters come from augmentation libraries, they
// don't have a reportable location.
(context ??= <LocatedMessage>[]).add(
codeIncorrectTypeArgumentVariable.withLocation(
typeParameter.location!.file,
typeParameter.fileOffset,
noLength,
),
);
}
if (superBoundedAttemptInverted != null && superBoundedAttempt != null) {
(context ??= // Coverage-ignore(suite): Not run.
<LocatedMessage>[])
.add(
codeSuperBoundedHint
.withArgumentsOld(
superBoundedAttempt,
superBoundedAttemptInverted,
)
.withLocation(fileUri, fileOffset, noLength),
);
}
addProblem(message, fileOffset, noLength, fileUri, context: context);
}
void _reportTypeArgumentIssues(
List<TypeArgumentIssue> issues,
Uri fileUri,
int offset, {
required bool inferred,
DartType? targetReceiver,
String? targetName,
}) {
for (int i = 0; i < issues.length; i++) {
TypeArgumentIssue issue = issues[i];
DartType argument = issue.argument;
TypeParameter? typeParameter = issue.typeParameter;
Message message;
if (issue.isGenericTypeAsArgumentIssue) {
if (inferred) {
message = codeGenericFunctionTypeInferredAsActualTypeArgument
.withArgumentsOld(argument);
} else {
message = codeGenericFunctionTypeUsedAsActualTypeArgument;
}
typeParameter = null;
} else {
if (issue.enclosingType == null && targetReceiver != null) {
if (targetName != null) {
if (inferred) {
message = codeIncorrectTypeArgumentQualifiedInferred
.withArgumentsOld(
argument,
typeParameter.bound,
typeParameter.name!,
targetReceiver,
targetName,
);
} else {
message = codeIncorrectTypeArgumentQualified.withArgumentsOld(
argument,
typeParameter.bound,
typeParameter.name!,
targetReceiver,
targetName,
);
}
} else {
if (inferred) {
message = codeIncorrectTypeArgumentInstantiationInferred
.withArgumentsOld(
argument,
typeParameter.bound,
typeParameter.name!,
targetReceiver,
);
} else {
message = codeIncorrectTypeArgumentInstantiation.withArgumentsOld(
argument,
typeParameter.bound,
typeParameter.name!,
targetReceiver,
);
}
}
} else {
String enclosingName = issue.enclosingType == null
? targetName!
: getGenericTypeName(issue.enclosingType!);
if (inferred) {
message = codeIncorrectTypeArgumentInferred.withArgumentsOld(
argument,
typeParameter.bound,
typeParameter.name!,
enclosingName,
);
} else {
message = codeIncorrectTypeArgument.withArgumentsOld(
argument,
typeParameter.bound,
typeParameter.name!,
enclosingName,
);
}
}
}
// Don't show the hint about an attempted super-bounded type if the issue
// with the argument is that it's generic.
_reportTypeArgumentIssueForStructuralParameter(
message,
fileUri,
offset,
typeParameter: typeParameter,
superBoundedAttempt: issue.isGenericTypeAsArgumentIssue
? null
: issue.enclosingType,
superBoundedAttemptInverted: issue.isGenericTypeAsArgumentIssue
? null
: issue.invertedType,
);
}
}
}