blob: 38a57aee4c0d4462ada0d6f7afa7b92daa50d08a [file] [log] [blame]
// Copyright (c) 2016, 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.
library fasta.named_type_builder;
import 'package:kernel/ast.dart' show DartType, Supertype;
import '../fasta_codes.dart'
show
Message,
Template,
noLength,
templateMissingExplicitTypeArguments,
messageNotATypeContext,
LocatedMessage,
templateNotAType,
templateTypeArgumentMismatch,
templateTypeArgumentsOnTypeVariable,
templateTypeNotFound;
import '../messages.dart'
show noLength, templateSupertypeIsIllegal, templateSupertypeIsTypeVariable;
import '../problems.dart' show unhandled;
import '../severity.dart' show Severity;
import 'builder.dart'
show
Builder,
Identifier,
LibraryBuilder,
NullabilityBuilder,
PrefixBuilder,
QualifiedName,
Scope,
TypeBuilder,
TypeDeclarationBuilder,
TypeVariableBuilder,
flattenName;
import '../kernel/kernel_builder.dart'
show
ClassBuilder,
InvalidTypeBuilder,
LibraryBuilder,
TypeBuilder,
TypeDeclarationBuilder,
TypeVariableBuilder,
flattenName;
class NamedTypeBuilder extends TypeBuilder {
final Object name;
List<TypeBuilder> arguments;
final NullabilityBuilder nullabilityBuilder;
@override
TypeDeclarationBuilder declaration;
NamedTypeBuilder(this.name, this.nullabilityBuilder, this.arguments);
NamedTypeBuilder.fromTypeDeclarationBuilder(
this.declaration, this.nullabilityBuilder,
[this.arguments])
: this.name = declaration.name;
@override
void bind(TypeDeclarationBuilder declaration) {
this.declaration = declaration?.origin;
}
@override
void resolveIn(
Scope scope, int charOffset, Uri fileUri, LibraryBuilder library) {
if (declaration != null) return;
final Object name = this.name;
Builder member;
if (name is QualifiedName) {
Object qualifier = name.qualifier;
String prefixName = flattenName(qualifier, charOffset, fileUri);
Builder prefix = scope.lookup(prefixName, charOffset, fileUri);
if (prefix is PrefixBuilder) {
member = prefix.lookup(name.name, name.charOffset, fileUri);
}
} else if (name is String) {
member = scope.lookup(name, charOffset, fileUri);
} else {
unhandled("${name.runtimeType}", "resolveIn", charOffset, fileUri);
}
if (member is TypeVariableBuilder) {
declaration = member.origin;
if (arguments != null) {
String typeName;
int typeNameOffset;
if (name is Identifier) {
typeName = name.name;
typeNameOffset = name.charOffset;
} else {
typeName = name;
typeNameOffset = charOffset;
}
Message message =
templateTypeArgumentsOnTypeVariable.withArguments(typeName);
library.addProblem(message, typeNameOffset, typeName.length, fileUri);
declaration = buildInvalidType(
message.withLocation(fileUri, typeNameOffset, typeName.length));
}
return;
} else if (member is TypeDeclarationBuilder) {
declaration = member.origin;
if (!declaration.isExtension) {
if (arguments == null && declaration.typeVariablesCount != 0) {
String typeName;
int typeNameOffset;
if (name is Identifier) {
typeName = name.name;
typeNameOffset = name.charOffset;
} else {
typeName = name;
typeNameOffset = charOffset;
}
library.addProblem(
templateMissingExplicitTypeArguments
.withArguments(declaration.typeVariablesCount),
typeNameOffset,
typeName.length,
fileUri);
}
return;
}
}
Template<Message Function(String name)> template =
member == null ? templateTypeNotFound : templateNotAType;
String flatName = flattenName(name, charOffset, fileUri);
List<LocatedMessage> context;
if (member != null) {
context = <LocatedMessage>[
messageNotATypeContext.withLocation(member.fileUri, member.charOffset,
name is Identifier ? name.name.length : "$name".length)
];
}
int length =
name is Identifier ? name.endCharOffset - charOffset : flatName.length;
Message message = template.withArguments(flatName);
library.addProblem(message, charOffset, length, fileUri, context: context);
declaration = buildInvalidType(
message.withLocation(fileUri, charOffset, length),
context: context);
}
@override
void check(LibraryBuilder library, int charOffset, Uri fileUri) {
if (arguments != null &&
arguments.length != declaration.typeVariablesCount) {
Message message = templateTypeArgumentMismatch
.withArguments(declaration.typeVariablesCount);
library.addProblem(message, charOffset, noLength, fileUri);
declaration =
buildInvalidType(message.withLocation(fileUri, charOffset, noLength));
}
}
@override
void normalize(int charOffset, Uri fileUri) {
if (arguments != null &&
arguments.length != declaration.typeVariablesCount) {
// [arguments] will be normalized later if they are null.
arguments = null;
}
}
String get debugName => "NamedTypeBuilder";
StringBuffer printOn(StringBuffer buffer) {
buffer.write(name);
if (arguments?.isEmpty ?? true) return buffer;
buffer.write("<");
bool first = true;
for (TypeBuilder t in arguments) {
if (!first) buffer.write(", ");
first = false;
t.printOn(buffer);
}
buffer.write(">");
nullabilityBuilder.writeNullabilityOn(buffer);
return buffer;
}
InvalidTypeBuilder buildInvalidType(LocatedMessage message,
{List<LocatedMessage> context}) {
// TODO(ahe): Consider if it makes sense to pass a QualifiedName to
// InvalidTypeBuilder?
return new InvalidTypeBuilder(
flattenName(name, message.charOffset, message.uri), message,
context: context);
}
Supertype handleInvalidSupertype(
LibraryBuilder library, int charOffset, Uri fileUri) {
Template<Message Function(String name)> template =
declaration.isTypeVariable
? templateSupertypeIsTypeVariable
: templateSupertypeIsIllegal;
library.addProblem(
template.withArguments(flattenName(name, charOffset, fileUri)),
charOffset,
noLength,
fileUri);
return null;
}
DartType build(LibraryBuilder library) {
assert(declaration != null, "Declaration has not been resolved on $this.");
return declaration.buildType(library, nullabilityBuilder, arguments);
}
Supertype buildSupertype(
LibraryBuilder library, int charOffset, Uri fileUri) {
TypeDeclarationBuilder declaration = this.declaration;
if (declaration is ClassBuilder) {
return declaration.buildSupertype(library, arguments);
} else if (declaration is InvalidTypeBuilder) {
library.addProblem(
declaration.message.messageObject,
declaration.message.charOffset,
declaration.message.length,
declaration.message.uri,
severity: Severity.error);
return null;
} else {
return handleInvalidSupertype(library, charOffset, fileUri);
}
}
Supertype buildMixedInType(
LibraryBuilder library, int charOffset, Uri fileUri) {
TypeDeclarationBuilder declaration = this.declaration;
if (declaration is ClassBuilder) {
return declaration.buildMixedInType(library, arguments);
} else if (declaration is InvalidTypeBuilder) {
library.addProblem(
declaration.message.messageObject,
declaration.message.charOffset,
declaration.message.length,
declaration.message.uri,
severity: Severity.error);
return null;
} else {
return handleInvalidSupertype(library, charOffset, fileUri);
}
}
TypeBuilder subst(Map<TypeVariableBuilder, TypeBuilder> substitution) {
TypeBuilder result = substitution[declaration];
if (result != null) {
assert(declaration is TypeVariableBuilder);
return result;
} else if (arguments != null) {
List<TypeBuilder> arguments;
int i = 0;
for (TypeBuilder argument in this.arguments) {
TypeBuilder type = argument.subst(substitution);
if (type != argument) {
arguments ??= this.arguments.toList();
arguments[i] = type;
}
i++;
}
if (arguments != null) {
NamedTypeBuilder result =
new NamedTypeBuilder(name, nullabilityBuilder, arguments);
if (declaration != null) {
result.bind(declaration);
} else {
throw new UnsupportedError("Unbound type in substitution: $result.");
}
return result;
}
}
return this;
}
NamedTypeBuilder clone(List<TypeBuilder> newTypes) {
List<TypeBuilder> clonedArguments;
if (arguments != null) {
clonedArguments = new List<TypeBuilder>(arguments.length);
for (int i = 0; i < clonedArguments.length; i++) {
clonedArguments[i] = arguments[i].clone(newTypes);
}
}
NamedTypeBuilder newType =
new NamedTypeBuilder(name, nullabilityBuilder, clonedArguments);
newTypes.add(newType);
return newType;
}
}