blob: c2ef70fe0c5e8cc8324a6bdf63311c2d1b9db4c0 [file] [log] [blame]
// Copyright (c) 2024, 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 'arguments.dart';
import 'expressions.dart';
import 'references.dart';
import 'scope.dart';
import 'type_annotations.dart';
import 'util.dart';
/// A [Proto] represents a parsed substructure before it can be determined
/// whether is is an expression, a type annotation, or something else.
///
/// For instance when parsing `Foo` we cannot determine what it means before
/// we have parsed what comes after `Foo`. If it is followed by `.bar` it could
/// be the class name prefix of a static field access `Foo.bar` and if it is
/// followed by `,` then it could be a type literal `Foo` as an expression.
sealed class Proto {
/// Creates the [Proto] corresponding to this [Proto] followed by [send].
///
/// If [send] is `null`, this corresponds to this [Proto] on its own.
/// Otherwise this corresponds to this [Proto] followed by `.` or `?.`, an
/// identifier, and optionally type arguments and/or arguments.
Proto apply(IdentifierProto? send, {bool isNullAware = false}) {
if (send == null) {
return this;
} else if (isNullAware) {
return new InstanceAccessProto(
this,
send.text,
isNullAware: true,
).instantiate(send.typeArguments).invoke(send.arguments);
} else {
return access(
send.text,
).instantiate(send.typeArguments).invoke(send.arguments);
}
}
/// Returns the [Proto] corresponding to accessing the property [name].
///
/// If [name] is `null`, the [Proto] itself is returned. This functionality
/// is supported for ease of use.
Proto access(String? name);
/// Returns the [Proto] corresponding applying [typeArguments] to it.
///
/// If [typeArguments] is `null`, the [Proto] itself is returned. This
/// functionality is supported for ease of use.
Proto instantiate(List<TypeAnnotation>? typeArguments);
/// Returns the [Proto] corresponding invoke it with [arguments].
///
/// If [arguments] is `null`, the [Proto] itself is returned. This
/// functionality is supported for ease of use.
Proto invoke(List<Argument>? arguments);
/// Creates the [Expression] corresponding to this [Proto].
///
/// This corresponding to this [Proto] occurring in an expression context
/// on its own.
Expression toExpression();
/// Creates the [TypeAnnotation] corresponding to this [Proto].
///
/// This corresponding to this [Proto] occurring in a type annotation context
/// on its own.
TypeAnnotation toTypeAnnotation();
/// Returns the [Proto] corresponding to this [Proto] in which all
/// [UnresolvedIdentifier]s have been resolved within their scope.
///
/// If this didn't create a new [Proto], `null` is returned.
Proto? resolve();
}
/// An unresolved part of a proto, expression or type annotation.
sealed class Unresolved {
/// Returns this unresolved part as an [Expression] if it can be resolved.
Expression? resolveAsExpression();
/// Returns this unresolved part as a [TypeAnnotation] if it can be resolved.
TypeAnnotation? resolveAsTypeAnnotation();
}
/// The unresolved identifier [name] occurring in [scope].
class UnresolvedIdentifier extends Proto implements Unresolved {
final Scope scope;
final String name;
UnresolvedIdentifier(this.scope, this.name);
@override
Proto access(String? name) {
if (name == null) {
return this;
}
return new UnresolvedAccess(this, name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
if (typeArguments == null) {
return this;
}
return new UnresolvedInstantiate(this, typeArguments);
}
@override
Proto invoke(List<Argument>? arguments) {
if (arguments == null) {
return this;
}
return new UnresolvedInvoke(this, arguments);
}
@override
Expression toExpression() {
return new UnresolvedExpression(this);
}
@override
TypeAnnotation toTypeAnnotation() {
return new UnresolvedTypeAnnotation(this);
}
@override
String toString() => 'UnresolvedIdentifier($name)';
@override
Expression? resolveAsExpression() {
Proto? resolved = resolve();
return resolved == null ? null : resolved.toExpression();
}
@override
TypeAnnotation? resolveAsTypeAnnotation() {
Proto? resolved = resolve();
return resolved == null ? null : resolved.toTypeAnnotation();
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is UnresolvedIdentifier &&
runtimeType == other.runtimeType &&
name == other.name;
@override
int get hashCode => name.hashCode;
@override
Proto? resolve() {
Proto resolved = scope.lookup(name);
return this == resolved ? null : resolved;
}
}
/// The unresolved access to [name] on [prefix].
class UnresolvedAccess extends Proto implements Unresolved {
final Proto prefix;
final String name;
UnresolvedAccess(this.prefix, this.name);
@override
Proto access(String? name) {
if (name == null) {
return this;
}
return new UnresolvedAccess(this, name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
if (typeArguments == null) {
return this;
}
return new UnresolvedInstantiate(this, typeArguments);
}
@override
Proto invoke(List<Argument>? arguments) {
if (arguments == null) {
return this;
}
return new UnresolvedInvoke(this, arguments);
}
@override
Expression toExpression() {
return new UnresolvedExpression(this);
}
@override
TypeAnnotation toTypeAnnotation() {
return new UnresolvedTypeAnnotation(this);
}
@override
String toString() => 'UnresolvedAccess($prefix,$name)';
@override
Expression? resolveAsExpression() {
Proto? resolved = resolve();
return resolved == null ? null : resolved.toExpression();
}
@override
TypeAnnotation? resolveAsTypeAnnotation() {
Proto? resolved = resolve();
return resolved == null ? null : resolved.toTypeAnnotation();
}
@override
Proto? resolve() {
Proto? newPrefix = prefix.resolve();
return newPrefix == null ? null : newPrefix.access(name);
}
}
/// The application of [typeArguments] on [prefix].
///
/// The [prefix] is (partially) unresolved, so it cannot yet determined what
/// the application of [typeArguments] means. For instance `unresolved<int>`
/// could be instantiation of the generic class `unresolved` or the
/// instantiation of the generic method `unresolved`.
class UnresolvedInstantiate extends Proto implements Unresolved {
final Proto prefix;
final List<TypeAnnotation> typeArguments;
UnresolvedInstantiate(this.prefix, this.typeArguments);
@override
Proto access(String? name) {
if (name == null) {
return this;
}
return new UnresolvedAccess(this, name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
if (typeArguments == null) {
return this;
}
return new UnresolvedInstantiate(this, typeArguments);
}
@override
Proto invoke(List<Argument>? arguments) {
if (arguments == null) {
return this;
}
return new UnresolvedInvoke(this, arguments);
}
@override
Expression toExpression() {
return new UnresolvedExpression(this);
}
@override
TypeAnnotation toTypeAnnotation() {
return new UnresolvedTypeAnnotation(this);
}
@override
String toString() => 'UnresolvedInstantiate($prefix,$typeArguments)';
@override
Expression? resolveAsExpression() {
Proto? resolved = resolve();
return resolved == null ? null : resolved.toExpression();
}
@override
TypeAnnotation? resolveAsTypeAnnotation() {
Proto? resolved = resolve();
return resolved == null ? null : resolved.toTypeAnnotation();
}
@override
Proto? resolve() {
Proto? newPrefix = prefix.resolve();
List<TypeAnnotation>? newTypeArguments = typeArguments.resolve(
(t) => t.resolve(),
);
return newPrefix == null && newTypeArguments == null
? null
: (newPrefix ?? prefix).instantiate(newTypeArguments ?? typeArguments);
}
}
/// The invocation of [arguments] on [prefix].
///
/// The [prefix] is (partially) unresolved, so it cannot yet determined what
/// the invocation of [arguments] means. For instance `unresolved(0)`
/// could be the constructor invocation of the unnamed constructor of the class
/// `unresolved` or the invocation of the method `unresolved`.
class UnresolvedInvoke extends Proto implements Unresolved {
final Proto prefix;
final List<Argument> arguments;
UnresolvedInvoke(this.prefix, this.arguments);
@override
Proto access(String? name) {
if (name == null) {
return this;
}
return new UnresolvedAccess(this, name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
if (typeArguments == null) {
return this;
}
return new UnresolvedInstantiate(this, typeArguments);
}
@override
Proto invoke(List<Argument>? arguments) {
if (arguments == null) {
return this;
}
return new UnresolvedInvoke(this, arguments);
}
@override
Expression toExpression() {
return new UnresolvedExpression(this);
}
@override
TypeAnnotation toTypeAnnotation() {
return new UnresolvedTypeAnnotation(this);
}
@override
String toString() => 'UnresolvedInvoke($prefix,$arguments)';
@override
Expression? resolveAsExpression() {
Proto? resolved = resolve();
return resolved == null ? null : resolved.toExpression();
}
@override
TypeAnnotation? resolveAsTypeAnnotation() {
Proto? resolved = resolve();
return resolved == null ? null : resolved.toTypeAnnotation();
}
@override
Proto? resolve() {
Proto? newPrefix = prefix.resolve();
List<Argument>? newArguments = arguments.resolve((a) => a.resolve());
return newPrefix == null && newArguments == null
? null
: (newPrefix ?? prefix).invoke(newArguments ?? arguments);
}
}
/// A [reference] to a class.
///
/// The [Proto] includes the [scope] of the class, which is used to resolve
/// access to constructors and static members on the class.
class ClassProto extends Proto {
final ClassReference reference;
final TypeDeclarationScope scope;
ClassProto(this.reference, this.scope);
@override
String toString() => 'ClassProto($reference)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
if (name == 'new') {
name = '';
}
return scope.lookup(name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new GenericClassProto(reference, scope, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null ? access('new').invoke(arguments) : this;
}
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() => new NamedTypeAnnotation(reference);
@override
Proto? resolve() => null;
}
/// A [reference] to a class instantiated with [typeArguments].
///
/// The [Proto] includes the [scope] of the class, which is used to resolve
/// access to constructors on the class.
class GenericClassProto extends Proto {
final ClassReference reference;
final TypeDeclarationScope scope;
final List<TypeAnnotation> typeArguments;
GenericClassProto(this.reference, this.scope, this.typeArguments);
@override
String toString() => 'GenericClassProto($reference,$typeArguments)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
if (name == 'new') {
name = '';
}
return scope.lookup(name, typeArguments);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? throw new UnimplementedError('GenericClassProto.instantiate')
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null ? access('new').invoke(arguments) : this;
}
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() =>
new NamedTypeAnnotation(reference, typeArguments);
@override
Proto? resolve() {
List<TypeAnnotation>? newTypeArguments = typeArguments.resolve(
(a) => a.resolve(),
);
return newTypeArguments == null
? null
: new GenericClassProto(reference, scope, newTypeArguments);
}
}
/// A [reference] to an extension
///
/// The [Proto] includes the [scope] of the extension, which is used to resolve
/// access to static members on the extension.
class ExtensionProto extends Proto {
final ExtensionReference reference;
final TypeDeclarationScope scope;
ExtensionProto(this.reference, this.scope);
@override
String toString() => 'ExtensionProto($reference)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
if (name == 'new') {
name = '';
}
return scope.lookup(name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new InvalidInstantiationProto(this, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null
? new InvalidInvocationProto(this, const [], arguments)
: this;
}
@override
Expression toExpression() {
return new InvalidExpression();
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
Proto? resolve() => null;
}
/// A [reference] to an enum
///
/// The [Proto] includes the [scope] of the enum, which is used to resolve
/// access to static members on the enum.
class EnumProto extends Proto {
final EnumReference reference;
final TypeDeclarationScope scope;
EnumProto(this.reference, this.scope);
@override
String toString() => 'EnumProto($reference)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
if (name == 'new') {
name = '';
}
return scope.lookup(name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new GenericEnumProto(reference, scope, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null
? new InvalidInvocationProto(this, const [], arguments)
: this;
}
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() => new NamedTypeAnnotation(reference);
@override
Proto? resolve() => null;
}
/// A [reference] to an enum instantiated with [typeArguments].
///
/// The [Proto] includes the [scope] of the enum, which is used to resolve
/// access to constructors on the enum.
class GenericEnumProto extends Proto {
final EnumReference reference;
final TypeDeclarationScope scope;
final List<TypeAnnotation> typeArguments;
GenericEnumProto(this.reference, this.scope, this.typeArguments);
@override
String toString() => 'GenericEnumProto($reference,$typeArguments)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
if (name == 'new') {
name = '';
}
return scope.lookup(name, typeArguments);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? throw new UnimplementedError('$this.instantiate')
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null ? access('new').invoke(arguments) : this;
}
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() =>
new NamedTypeAnnotation(reference, typeArguments);
@override
Proto? resolve() {
List<TypeAnnotation>? newTypeArguments = typeArguments.resolve(
(a) => a.resolve(),
);
return newTypeArguments == null
? null
: new GenericEnumProto(reference, scope, newTypeArguments);
}
}
/// A [reference] to a mixin
///
/// The [Proto] includes the [scope] of the mixin, which is used to resolve
/// access to static members on the mixin.
class MixinProto extends Proto {
final MixinReference reference;
final TypeDeclarationScope scope;
MixinProto(this.reference, this.scope);
@override
String toString() => 'MixinProto($reference)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
if (name == 'new') {
name = '';
}
return scope.lookup(name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new GenericMixinProto(reference, scope, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null
? new InvalidInvocationProto(this, const [], arguments)
: this;
}
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() => new NamedTypeAnnotation(reference);
@override
Proto? resolve() => null;
}
/// A [reference] to an enum instantiated with [typeArguments].
///
/// The [Proto] includes the [scope] of the enum, which is used to resolve
/// access to constructors on the enum.
class GenericMixinProto extends Proto {
final MixinReference reference;
final TypeDeclarationScope scope;
final List<TypeAnnotation> typeArguments;
GenericMixinProto(this.reference, this.scope, this.typeArguments);
@override
String toString() => 'GenericMixinProto($reference,$typeArguments)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
if (name == 'new') {
name = '';
}
return scope.lookup(name, typeArguments);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? throw new UnimplementedError('$this.instantiate')
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null ? access('new').invoke(arguments) : this;
}
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() =>
new NamedTypeAnnotation(reference, typeArguments);
@override
Proto? resolve() {
List<TypeAnnotation>? newTypeArguments = typeArguments.resolve(
(a) => a.resolve(),
);
return newTypeArguments == null
? null
: new GenericMixinProto(reference, scope, newTypeArguments);
}
}
/// A [reference] to an extension type.
///
/// The [Proto] includes the [scope] of the extension type, which is used to
/// resolve access to constructors and static members on the extension type.
class ExtensionTypeProto extends Proto {
final ExtensionTypeReference reference;
final TypeDeclarationScope scope;
ExtensionTypeProto(this.reference, this.scope);
@override
String toString() => 'ExtensionTypeProto($reference)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
if (name == 'new') {
name = '';
}
return scope.lookup(name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new GenericExtensionTypeProto(reference, scope, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null ? access('new').invoke(arguments) : this;
}
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() => new NamedTypeAnnotation(reference);
@override
Proto? resolve() => null;
}
/// A [reference] to an extension type instantiated with [typeArguments].
///
/// The [Proto] includes the [scope] of the extension type, which is used to
/// resolve access to constructors on the class.
class GenericExtensionTypeProto extends Proto {
final ExtensionTypeReference reference;
final TypeDeclarationScope scope;
final List<TypeAnnotation> typeArguments;
GenericExtensionTypeProto(this.reference, this.scope, this.typeArguments);
@override
String toString() => 'GenericExtensionTypeProto($reference,$typeArguments)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
if (name == 'new') {
name = '';
}
return scope.lookup(name, typeArguments);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? throw new UnimplementedError('GenericExtensionTypeProto.instantiate')
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null ? access('new').invoke(arguments) : this;
}
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() =>
new NamedTypeAnnotation(reference, typeArguments);
@override
Proto? resolve() {
List<TypeAnnotation>? newTypeArguments = typeArguments.resolve(
(a) => a.resolve(),
);
return newTypeArguments == null
? null
: new GenericExtensionTypeProto(reference, scope, newTypeArguments);
}
}
/// A [reference] to a typedef.
///
/// The [Proto] includes the [scope] of the typedef, which is used to resolve
/// access to constructors through the typedef.
class TypedefProto extends Proto {
final TypedefReference reference;
final TypeDeclarationScope scope;
TypedefProto(this.reference, this.scope);
@override
String toString() => 'TypedefProto($reference)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
if (name == 'new') {
name = '';
}
return scope.lookup(name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new GenericTypedefProto(reference, scope, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null ? access('new').invoke(arguments) : this;
}
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() => new NamedTypeAnnotation(reference);
@override
Proto? resolve() => null;
}
/// A [reference] to a typedef instantiated with [typeArguments].
///
/// The [Proto] includes the [scope] of the typedef, which is used to resolve
/// access to constructors through the typedef.
class GenericTypedefProto extends Proto {
final TypedefReference reference;
final TypeDeclarationScope scope;
final List<TypeAnnotation> typeArguments;
GenericTypedefProto(this.reference, this.scope, this.typeArguments);
@override
String toString() => 'GenericTypedefProto($reference,$typeArguments)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
if (name == 'new') {
name = '';
}
return scope.lookup(name, typeArguments);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? throw new UnimplementedError('GenericTypedefProto.instantiate')
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null ? access('new').invoke(arguments) : this;
}
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() =>
new NamedTypeAnnotation(reference, typeArguments);
@override
Proto? resolve() {
List<TypeAnnotation>? newTypeArguments = typeArguments.resolve(
(a) => a.resolve(),
);
return newTypeArguments == null
? null
: new GenericTypedefProto(reference, scope, newTypeArguments);
}
}
/// A [reference] to a `void`.
class VoidProto extends Proto {
final Reference reference;
VoidProto(this.reference);
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() => new VoidTypeAnnotation(reference);
@override
Proto access(String? name) {
return name == null ? this : new InvalidAccessProto(this, name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments == null
? this
: new InvalidInstantiationProto(this, typeArguments);
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments == null
? this
: new InvalidInvocationProto(this, const [], arguments);
}
@override
String toString() => 'VoidProto()';
@override
Proto? resolve() => null;
}
/// A [reference] to a `dynamic`.
class DynamicProto extends Proto {
final Reference reference;
DynamicProto(this.reference);
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() => new DynamicTypeAnnotation(reference);
@override
Proto access(String? name) {
return name == null ? this : new InvalidAccessProto(this, name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments == null
? this
: new InvalidInstantiationProto(this, typeArguments);
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments == null
? this
: new InvalidInvocationProto(this, const [], arguments);
}
@override
String toString() => 'DynamicProto()';
@override
Proto? resolve() => null;
}
/// A [reference] to a constructor, including a reference to the [type] on which
/// it was accessed, as well as the [typeArguments] applied to [type].
class ConstructorProto extends Proto {
final Reference type;
final List<TypeAnnotation> typeArguments;
final ConstructorReference reference;
ConstructorProto(this.type, this.typeArguments, this.reference);
@override
String toString() => 'ConstructorProto($type,$typeArguments,$reference)';
@override
Proto access(String? name) {
return name != null ? new InvalidAccessProto(this, name) : this;
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new ExpressionInstantiationProto(this, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null
? new ExpressionProto(
new ConstructorInvocation(
new NamedTypeAnnotation(type, typeArguments),
reference,
arguments,
),
)
: this;
}
@override
Expression toExpression() {
return new ConstructorTearOff(
new NamedTypeAnnotation(type, typeArguments),
reference,
);
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
Proto? resolve() => null;
}
/// A [reference] to a static or top level field.
class FieldProto extends Proto {
final FieldReference reference;
FieldProto(this.reference);
@override
String toString() => 'FieldProto($reference)';
@override
Proto access(String? name) {
return name != null ? new InstanceAccessProto(this, name) : this;
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new ExpressionInstantiationProto(this, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null
? new InstanceInvocationProto(this, const [], arguments)
: this;
}
@override
Expression toExpression() {
return new StaticGet(reference);
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
Proto? resolve() => null;
}
/// A [reference] to a static or top level method.
class FunctionProto extends Proto {
final FunctionReference reference;
FunctionProto(this.reference);
@override
String toString() => 'FunctionProto($reference)';
@override
Proto access(String? name) {
return name != null ? new InstanceAccessProto(this, name) : this;
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new FunctionInstantiationProto(reference, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null
? new ExpressionProto(
new StaticInvocation(reference, const [], arguments),
)
: this;
}
@override
Expression toExpression() {
return new FunctionTearOff(reference);
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
Proto? resolve() => null;
}
/// A [reference] to a static or top level method instantiated with
/// [typeArguments].
class FunctionInstantiationProto extends Proto {
final FunctionReference reference;
final List<TypeAnnotation> typeArguments;
FunctionInstantiationProto(this.reference, this.typeArguments);
@override
String toString() => 'FunctionInstantiationProto($reference,$typeArguments)';
@override
Proto access(String? name) {
return name != null ? new InstanceAccessProto(this, name) : this;
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new ExpressionInstantiationProto(this, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null
? new ExpressionProto(
new StaticInvocation(reference, typeArguments, arguments),
)
: this;
}
@override
Expression toExpression() {
return new Instantiation(new FunctionTearOff(reference), typeArguments);
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
Proto? resolve() => null;
}
/// A reference to the [prefix].
///
/// The [Proto] includes the [scope] of the prefix, which is used to resolve
/// access to imported members and types through the prefix.
class PrefixProto extends Proto {
final String prefix;
final Scope scope;
PrefixProto(this.prefix, this.scope);
@override
String toString() => 'PrefixProto($prefix)';
@override
Proto access(String? name) {
if (name == null) {
return this;
}
return scope.lookup(name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new InvalidInstantiationProto(this, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null
? new InvalidInvocationProto(this, const [], arguments)
: this;
}
@override
Expression toExpression() {
return new InvalidExpression();
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
Proto? resolve() => null;
}
/// The suffix of an access to the property [text] including applied
/// [typeArguments] and [arguments], if any.
///
/// This is not really a [Proto] but just a sequence of information needed to
/// call [Proto.access], [Proto.instantiate], and [Proto.invoke]. Unfortunately
/// the parser currently provides these as a "send" which is not the ideal model
/// for parsing Dart.
class IdentifierProto extends Proto {
final String text;
final List<TypeAnnotation>? typeArguments;
final List<Argument>? arguments;
IdentifierProto(String text) : this._(text, null, null);
IdentifierProto._(this.text, this.typeArguments, this.arguments);
@override
Proto access(String? name) {
return name == null
? this
: throw new UnimplementedError('IdentifierProto.access');
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments == null
? this
: new IdentifierProto._(text, typeArguments, null);
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments == null
? this
: new IdentifierProto._(text, typeArguments, arguments);
}
@override
Expression toExpression() {
throw new UnimplementedError('IdentifierProto.toExpression');
}
@override
TypeAnnotation toTypeAnnotation() {
throw new UnimplementedError('IdentifierProto.toTypeAnnotation');
}
@override
String toString() => 'IdentifierProto($text)';
@override
Proto? resolve() => null;
}
/// An access to the [text] property on [receiver].
///
/// If [isNullAware] is `true`, this is a `?.` access, otherwise it is a '.'
/// access.
///
/// This is used for the when the [text] property is known to be an instance
/// member of [receiver]. Either because the [receiver] cannot be the prefix
/// of a static access or if [isNullAware] is `true`.
class InstanceAccessProto extends Proto {
final Proto receiver;
final String text;
final bool isNullAware;
InstanceAccessProto(this.receiver, this.text, {this.isNullAware = false});
@override
Proto access(String? name) {
if (name == null) {
return this;
}
throw new UnimplementedError('$this.access');
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
if (typeArguments == null) {
return this;
}
throw new UnimplementedError('$this.instantiate');
}
@override
Proto invoke(List<Argument>? arguments) {
if (arguments == null) {
return this;
}
throw new UnimplementedError('$this.invoke');
}
@override
Expression toExpression() {
return isNullAware
? new NullAwarePropertyGet(receiver.toExpression(), text)
: new PropertyGet(receiver.toExpression(), text);
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
String toString() => 'InstanceAccessProto($receiver,$text)';
@override
Proto? resolve() => null;
}
/// The application of [typeArguments] on [receiver] which is known to be an
/// expression.
class ExpressionInstantiationProto extends Proto {
final Proto receiver;
final List<TypeAnnotation> typeArguments;
ExpressionInstantiationProto(this.receiver, this.typeArguments);
@override
Proto access(String? name) {
return name != null
? new ExpressionProto(new PropertyGet(receiver.toExpression(), name))
: this;
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
throw new UnimplementedError('InstanceInstantiationProto.instantiate');
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null
? new ExpressionProto(
new ImplicitInvocation(
receiver.toExpression(),
const [],
arguments,
),
)
: this;
}
@override
Expression toExpression() {
return new Instantiation(receiver.toExpression(), typeArguments);
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
String toString() => 'InstanceInstantiationProto($receiver,$typeArguments)';
@override
Proto? resolve() => null;
}
/// The application of [typeArguments] and [arguments] on [receiver] which is
/// known to be an expression.
class InstanceInvocationProto extends Proto {
final Proto receiver;
final List<TypeAnnotation> typeArguments;
final List<Argument> arguments;
InstanceInvocationProto(this.receiver, this.typeArguments, this.arguments);
@override
Proto access(String? name) {
throw new UnimplementedError('InstanceInvocationProto.access');
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
throw new UnimplementedError('InstanceInvocationProto.instantiate');
}
@override
Proto invoke(List<Argument>? arguments) {
throw new UnimplementedError('InstanceInvocationProto.invoke');
}
@override
Expression toExpression() {
return new ImplicitInvocation(
receiver.toExpression(),
typeArguments,
arguments,
);
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
String toString() =>
'InstanceInvocationProto($receiver,$typeArguments,$arguments)';
@override
Proto? resolve() => null;
}
/// The access of [text] on [receiver] when this is known to be an invalid
/// construct.
// TODO(johnniwinther): This is not valid for non-const expressions. Expand
// this if used for non-const expressions.
class InvalidAccessProto extends Proto {
final Proto receiver;
final String text;
InvalidAccessProto(this.receiver, this.text);
@override
Proto access(String? name) {
throw new UnimplementedError('InvalidAccessProto.access');
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
throw new UnimplementedError('InvalidAccessProto.instantiate');
}
@override
Proto invoke(List<Argument>? arguments) {
throw new UnimplementedError('InvalidAccessProto.invoke');
}
@override
Expression toExpression() {
return new InvalidExpression();
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
String toString() => 'InvalidAccessProto($receiver,$text)';
@override
Proto? resolve() => null;
}
/// The application of [typeArguments] on [receiver] when this is known to be an
/// invalid construct.
// TODO(johnniwinther): This might not be valid for non-const expressions.
// Expand this if used for non-const expressions.
class InvalidInstantiationProto extends Proto {
final Proto receiver;
final List<TypeAnnotation> typeArguments;
InvalidInstantiationProto(this.receiver, this.typeArguments);
@override
Proto access(String? name) {
throw new UnimplementedError('InvalidInstantiationProto.access');
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
throw new UnimplementedError('InvalidInstantiationProto.instantiate');
}
@override
Proto invoke(List<Argument>? arguments) {
throw new UnimplementedError('InvalidInstantiationProto.invoke');
}
@override
Expression toExpression() {
return new InvalidExpression();
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
String toString() => 'InvalidInstantiationProto($receiver,$typeArguments)';
@override
Proto? resolve() => null;
}
/// The application of [typeArguments] and [arguments] on [receiver] when this
/// is known to be an invalid construct.
// TODO(johnniwinther): This might not be valid for non-const expressions.
// Expand this if used for non-const expressions.
class InvalidInvocationProto extends Proto {
final Proto receiver;
final List<TypeAnnotation> typeArguments;
final List<Argument> arguments;
InvalidInvocationProto(this.receiver, this.typeArguments, this.arguments);
@override
Proto access(String? name) {
throw new UnimplementedError('InvalidInvocationProto.access');
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
throw new UnimplementedError('InvalidInvocationProto.instantiate');
}
@override
Proto invoke(List<Argument>? arguments) {
throw new UnimplementedError('InvalidInvocationProto.invoke');
}
@override
Expression toExpression() {
return new InvalidExpression();
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
String toString() =>
'InvalidInvocationProto($receiver,$typeArguments,$arguments)';
@override
Proto? resolve() => null;
}
/// An [expression] occurring as a [Proto].
class ExpressionProto extends Proto {
final Expression expression;
ExpressionProto(this.expression);
@override
Proto access(String? name) {
return name != null ? new InstanceAccessProto(this, name) : this;
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments != null
? new ExpressionInstantiationProto(this, typeArguments)
: this;
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null
? new ExpressionProto(
new ImplicitInvocation(expression, const [], arguments),
)
: this;
}
@override
Expression toExpression() {
return expression;
}
@override
TypeAnnotation toTypeAnnotation() => new InvalidTypeAnnotation();
@override
String toString() => 'ExpressionProto($expression)';
@override
Proto? resolve() => null;
}
/// A reference to a [functionTypeParameter].
class FunctionTypeParameterProto extends Proto {
final FunctionTypeParameter functionTypeParameter;
FunctionTypeParameterProto(this.functionTypeParameter);
@override
Proto access(String? name) {
return name == null ? this : new InvalidAccessProto(this, name);
}
@override
Proto instantiate(List<TypeAnnotation>? typeArguments) {
return typeArguments == null
? this
: new ExpressionInstantiationProto(this, typeArguments);
}
@override
Proto invoke(List<Argument>? arguments) {
return arguments != null
? new ExpressionProto(
new ImplicitInvocation(toExpression(), const [], arguments),
)
: this;
}
@override
Proto? resolve() => null;
@override
Expression toExpression() {
return new TypeLiteral(toTypeAnnotation());
}
@override
TypeAnnotation toTypeAnnotation() {
return new FunctionTypeParameterType(functionTypeParameter);
}
}