blob: 0cbfac99a2d212e8c4d1f389c349e395346e708b [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.
/// A library to help generate expression.
library;
import 'package:_fe_analyzer_shared/src/parser/parser.dart'
show lengthForToken, lengthOfSpan;
import 'package:_fe_analyzer_shared/src/scanner/token.dart' show Token;
import 'package:kernel/ast.dart';
import 'package:kernel/names.dart'
show
ampersandName,
barName,
callName,
caretName,
divisionName,
equalsName,
indexGetName,
indexSetName,
leftShiftName,
lengthName,
minusName,
multiplyName,
mustacheName,
percentName,
plusName,
rightShiftName,
tripleShiftName;
import 'package:kernel/src/unaliasing.dart';
import 'package:kernel/text/ast_to_text.dart';
import 'package:kernel/type_algebra.dart';
import '../base/constant_context.dart' show ConstantContext;
import '../base/lookup_result.dart';
import '../base/problems.dart';
import '../builder/builder.dart';
import '../builder/declaration_builders.dart';
import '../builder/factory_builder.dart';
import '../builder/member_builder.dart';
import '../builder/method_builder.dart';
import '../builder/named_type_builder.dart';
import '../builder/nullability_builder.dart';
import '../builder/prefix_builder.dart';
import '../builder/property_builder.dart';
import '../builder/type_builder.dart';
import '../codes/cfe_codes.dart';
import '../source/source_member_builder.dart';
import '../source/stack_listener_impl.dart' show offsetForToken;
import 'constness.dart' show Constness;
import 'expression_generator_helper.dart';
import 'forest.dart';
import 'internal_ast.dart';
import 'load_library_builder.dart';
import 'utils.dart';
/// A generator represents a subexpression for which we can't yet build an
/// expression because we don't yet know the context in which it's used.
///
/// Once the context is known, a generator can be converted into an expression
/// by calling a `build` method.
///
/// For example, when building a kernel representation for `a[x] = b`, after
/// parsing `a[x]` but before parsing `= b`, we don't yet know whether to
/// generate an invocation of `operator[]` or `operator[]=`, so we create a
/// [Generator] object. Later, after `= b` is parsed, [buildAssignment] will
/// be called.
abstract class Generator {
/// Helper that provides access to contextual information.
final ExpressionGeneratorHelper _helper;
/// A token that defines a position subexpression that being built.
final Token token;
final int fileOffset;
Generator(this._helper, this.token) : fileOffset = offsetForToken(token);
/// Easy access to the [Forest] factory object.
Forest get _forest => _helper.forest;
// TODO(johnniwinther): Improve the semantic precision of this property or
// remove it. It's unclear if the semantics is inconsistent. It's for instance
// used both for the name of a variable in [VariableUseGenerator] and for
// `[]` in [IndexedAccessGenerator], and while the former text occurs in the
// underlying source code, the latter doesn't.
String get _plainNameForRead;
/// Internal name used for debugging.
String get _debugName;
/// The source uri for use in error messaging.
Uri get _uri => _helper.uri;
/// Builds an [Expression] representing a read from the generator.
///
/// The read of this subexpression does _not_ need to support a simultaneous
/// write of the same subexpression.
Expression buildSimpleRead();
/// Builds an [Expression] representing an assignment with the generator on
/// the LHS and [value] on the RHS.
///
/// The returned expression evaluates to the assigned value, unless
/// [voidContext] is true, in which case it may evaluate to anything.
Expression buildAssignment(Expression value, {bool voidContext = false});
/// Returns an [Expression] representing a null-aware assignment (`??=`) with
/// the generator on the LHS and [value] on the RHS.
///
/// The returned expression evaluates to the assigned value, unless
/// [voidContext] is true, in which case it may evaluate to anything.
///
/// [type] is the static type of the RHS.
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
});
/// Returns an [Expression] representing a compound assignment (e.g. `+=`)
/// with the generator on the LHS and [value] on the RHS.
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
});
/// Returns an [Expression] representing a pre-increment or pre-decrement of
/// the generator.
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return buildCompoundAssignment(
binaryOperator,
_forest.createIntLiteral(operatorOffset, 1),
operatorOffset: operatorOffset,
// TODO(johnniwinther): We are missing some void contexts here. For
// instance `++a?.b;` is not providing a void context making it default
// `true`.
voidContext: voidContext,
isPreIncDec: true,
);
}
/// Returns an [Expression] representing a post-increment or post-decrement of
/// the generator.
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
});
/// Returns a [Generator] or [Expression] representing an index access
/// (e.g. `a[b]`) with the generator on the receiver and [index] as the
/// index expression.
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
});
/// Returns an [Expression] representing a compile-time error.
///
/// At runtime, an exception will be thrown.
Expression _makeInvalidRead({
required UnresolvedKind unresolvedKind,
bool errorHasBeenReported = false,
}) {
return _helper.buildUnresolvedError(
_plainNameForRead,
fileOffset,
kind: unresolvedKind,
errorHasBeenReported: errorHasBeenReported,
);
}
/// Returns an [Expression] representing a compile-time error wrapping
/// [value].
///
/// At runtime, [value] will be evaluated before throwing an exception.
Expression _makeInvalidWrite({
required Expression value,
bool errorHasBeenReported = false,
}) {
return _helper.buildUnresolvedError(
_plainNameForRead,
fileOffset,
rhs: value,
kind: UnresolvedKind.Setter,
errorHasBeenReported: errorHasBeenReported,
);
}
Expression buildForEffect() => buildSimpleRead();
List<Initializer> buildFieldInitializer(Map<String, int>? initializedFields) {
return <Initializer>[
_helper.buildInvalidInitializer(
_helper.buildProblem(
codeInvalidInitializer,
fileOffset,
lengthForToken(token),
),
fileOffset,
),
];
}
/// Returns an expression, generator or initializer for an invocation of this
/// subexpression with [typeArguments] and [arguments] at [offset]. Callers
/// must pass `isInForest: true` iff [typeArguments] have already been added
/// to [forest].
///
/// For instance:
/// * If this is a [PropertyAccessGenerator] for `a.b`, this will create
/// a [MethodInvocation] for `a.b(...)`.
/// * If this is a [ThisAccessGenerator] for `this` in an initializer list,
/// this will create a [RedirectingInitializer] for `this(...)`.
/// * If this is an [IncompleteErrorGenerator], this will return the error
/// generator itself.
///
/// If the invocation has explicit type arguments
/// [buildTypeWithResolvedArguments] called instead.
Expression_Generator_Initializer doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
ArgumentsImpl arguments, {
bool isTypeArgumentsInForest = false,
});
Expression_Generator_Initializer buildSelectorAccess(
Selector selector,
int operatorOffset,
bool isNullAware,
) {
selector.reportNewAsSelector();
if (selector is InvocationSelector) {
return _helper.buildMethodInvocation(
buildSimpleRead(),
selector.name,
selector.arguments,
offsetForToken(selector.token),
isNullAware: isNullAware,
isConstantExpression: selector.isPotentiallyConstant,
);
} else {
if (_helper.constantContext != ConstantContext.none &&
selector.name != lengthName) {
_helper.addProblem(
codeNotAConstantExpression,
fileOffset,
token.length,
);
}
return PropertyAccessGenerator.make(
_helper,
selector.token,
buildSimpleRead(),
selector.name,
isNullAware,
);
}
}
Expression_Generator buildEqualsOperation(
Token token,
Expression right, {
required bool isNot,
}) {
return _forest.createEquals(
offsetForToken(token),
buildSimpleRead(),
right,
isNot: isNot,
);
}
Expression_Generator buildBinaryOperation(
Token token,
Name binaryName,
Expression right,
) {
return _forest.createBinary(
offsetForToken(token),
buildSimpleRead(),
binaryName,
right,
);
}
Expression_Generator buildUnaryOperation(Token token, Name unaryName) {
return _forest.createUnary(
offsetForToken(token),
unaryName,
buildSimpleRead(),
);
}
Expression_Generator applyTypeArguments(
int fileOffset,
List<TypeBuilder>? typeArguments,
) {
return new Instantiation(
buildSimpleRead(),
_helper.buildDartTypeArguments(
typeArguments,
TypeUse.tearOffTypeArgument,
allowPotentiallyConstantType: true,
),
)..fileOffset = fileOffset;
}
/// Returns a [TypeBuilder] for this subexpression instantiated with the
/// type [arguments]. If no type arguments are provided [arguments] is `null`.
///
/// The type arguments have not been resolved and should be resolved to
/// create a [TypeBuilder] for a valid type.
TypeBuilder buildTypeWithResolvedArguments(
NullabilityBuilder nullabilityBuilder,
List<TypeBuilder>? arguments, {
required bool allowPotentiallyConstantType,
required bool performTypeCanonicalization,
}) {
Message message = codeNotAType.withArguments(token.lexeme);
_helper.libraryBuilder.addProblem(
message,
fileOffset,
lengthForToken(token),
_uri,
);
return new NamedTypeBuilderImpl.forInvalidType(
token.lexeme,
nullabilityBuilder,
message.withLocation(_uri, fileOffset, lengthForToken(token)),
);
}
Expression_Generator qualifiedLookup(Token name) {
return new UnexpectedQualifiedUseGenerator(
_helper,
name,
this,
errorHasBeenReported: false,
);
}
Expression invokeConstructor(
List<TypeBuilder>? typeArguments,
String name,
Arguments arguments,
Token nameToken,
Token nameLastToken,
Constness constness, {
required bool inImplicitCreationContext,
}) {
return _helper.createInstantiationAndInvocation(
() => buildSimpleRead(),
typeArguments,
_plainNameForRead,
name,
arguments,
instantiationOffset: fileOffset,
invocationOffset: nameLastToken.charOffset,
inImplicitCreationContext: inImplicitCreationContext,
);
}
void printOn(StringSink sink);
@override
String toString() {
StringBuffer buffer = new StringBuffer();
buffer.write(_debugName);
buffer.write("(offset: ");
buffer.write("${fileOffset}");
printOn(buffer);
buffer.write(")");
return "$buffer";
}
}
/// [VariableUseGenerator] represents the subexpression whose prefix is a
/// local variable or parameter name.
///
/// For instance:
///
/// method(a) {
/// var b;
/// a; // a VariableUseGenerator is created for `a`.
/// b = a[]; // a VariableUseGenerator is created for `a` and `b`.
/// b(); // a VariableUseGenerator is created for `b`.
/// b.c = a.d; // a VariableUseGenerator is created for `a` and `b`.
/// }
///
/// If the variable is final or read-only (like a parameter in a catch clause) a
/// [ReadOnlyAccessGenerator] is created instead.
class VariableUseGenerator extends Generator {
final VariableDeclaration variable;
VariableUseGenerator(
ExpressionGeneratorHelper helper,
Token nameToken,
this.variable,
) : assert(variable.isAssignable, 'Variable $variable is not assignable'),
super(helper, nameToken);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "VariableUseGenerator";
@override
String get _plainNameForRead => variable.name!;
int get _nameOffset => fileOffset;
@override
Expression buildSimpleRead() {
return _createRead();
}
Expression _createRead() {
return _helper.createVariableGet(variable, fileOffset);
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _createWrite(fileOffset, value);
}
void _checkAssignment(int offset) {
if (_helper.isDeclaredInEnclosingCase(variable)) {
_helper.addProblem(
codePatternVariableAssignmentInsideGuard,
offset,
noLength,
);
}
}
Expression _createWrite(int offset, Expression value) {
_checkAssignment(offset);
_helper.registerVariableAssignment(variable);
return new VariableSet(variable, value)..fileOffset = offset;
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
Expression read = _createRead();
Expression write = _createWrite(fileOffset, value);
return new IfNullSet(read, write, forEffect: voidContext)
..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
Expression binary = _helper.forest.createBinary(
operatorOffset,
_createRead(),
binaryOperator,
value,
);
return _createWrite(fileOffset, binary);
}
Expression _buildPrePostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
required bool forEffect,
required bool isPost,
}) {
_checkAssignment(_nameOffset);
_helper.registerVariableRead(variable);
_helper.registerVariableAssignment(variable);
return new LocalIncDec(
variable: variable as VariableDeclarationImpl,
forEffect: forEffect,
isPost: isPost,
isInc: binaryOperator == plusName,
nameOffset: _nameOffset,
operatorOffset: operatorOffset,
)..fileOffset = _nameOffset;
}
@override
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: false,
);
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: true,
);
/*
Expression value = _forest.createIntLiteral(operatorOffset, 1);
if (voidContext) {
return buildCompoundAssignment(binaryOperator, value,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPostIncDec: true);
}
VariableDeclarationImpl read =
_helper.createVariableDeclarationForValue(_createRead());
Expression binary = _helper.forest.createBinary(operatorOffset,
_helper.createVariableGet(read, fileOffset), binaryOperator, value);
VariableDeclarationImpl write = _helper.createVariableDeclarationForValue(
_createWrite(operatorOffset, binary));
return new LocalIncDec(read, write)..fileOffset = operatorOffset;*/
}
@override
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return _helper.forest.createExpressionInvocation(
adjustForImplicitCall(_plainNameForRead, offset),
buildSimpleRead(),
arguments,
);
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
sink.write(", variable: ");
printNodeOn(variable, sink, syntheticNames: syntheticNames);
}
}
/// A [VariableUseGenerator] subclass for late final for-in loop variables
///
/// The special case of late final for-in loop variables is determined by the
/// following requirements to the error reporting.
///
/// * Even though the loop can be executed only once, initializing the
/// variable exactly once, it is still reasonable to report the error for
/// assigning to the late variable.
///
/// * The variable should be considered assigned in the statements following
/// the loop.
///
/// To have both of the effect, [ForInLateFinalVariableUseGenerator] is emitted
/// for the assignments of such variables. It extends [VariableUseGenerator],
/// but reports an error on assignment, similarly to
/// [AbstractReadOnlyAccessGenerator].
class ForInLateFinalVariableUseGenerator extends VariableUseGenerator {
ForInLateFinalVariableUseGenerator(
ExpressionGeneratorHelper helper,
Token token,
VariableDeclaration variable,
) : super(helper, token, variable);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "ForInLateFinalVariableUseGenerator";
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
InvalidExpression error = _helper.buildProblem(
codeCannotAssignToFinalVariable.withArguments(variable.name!),
fileOffset,
lengthForToken(token),
)..parent = variable;
Expression assignment = super.buildAssignment(
value,
voidContext: voidContext,
);
if (assignment is VariableSet) {
assignment.value = error..parent = assignment;
}
return assignment;
}
}
/// A [PropertyAccessGenerator] represents a subexpression whose prefix is
/// an explicit property access.
///
/// For instance
///
/// method(a) {
/// a.b; // a PropertyAccessGenerator is created for `a.b`.
/// a.b(); // a PropertyAccessGenerator is created for `a.b`.
/// a.b = c; // a PropertyAccessGenerator is created for `a.b`.
/// a.b += c; // a PropertyAccessGenerator is created for `a.b`.
/// }
///
/// If the receiver is `this`, a [ThisPropertyAccessGenerator] is created
/// instead. If the access is null-aware, e.g. `a?.b`, a
/// [NullAwarePropertyAccessGenerator] is created instead.
class PropertyAccessGenerator extends Generator {
/// The receiver expression. `a` in the examples in the class documentation.
final Expression receiver;
/// The name for the accessed property. `b` in the examples in the class
/// documentation.
final Name name;
PropertyAccessGenerator(
ExpressionGeneratorHelper helper,
Token nameToken,
this.receiver,
this.name,
) : super(helper, nameToken);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "PropertyAccessGenerator";
@override
String get _plainNameForRead => name.text;
/// The file offset for the [name].
int get _nameOffset => fileOffset;
@override
// Coverage-ignore(suite): Not run.
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return _helper.buildMethodInvocation(receiver, name, arguments, offset);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
sink.write(", receiver: ");
printNodeOn(receiver, sink, syntheticNames: syntheticNames);
sink.write(", name: ");
sink.write(name.text);
}
@override
Expression buildSimpleRead() {
return _forest.createPropertyGet(
fileOffset,
receiver,
name,
isNullAware: false,
);
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _helper.forest.createPropertySet(
fileOffset,
receiver,
name,
value,
forEffect: voidContext,
isNullAware: false,
);
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return new IfNullPropertySet(
receiver,
name,
value,
forEffect: voidContext,
readOffset: fileOffset,
writeOffset: fileOffset,
isNullAware: false,
)..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return new CompoundPropertySet(
receiver,
name,
binaryOperator,
value,
forEffect: voidContext,
readOffset: fileOffset,
binaryOffset: operatorOffset,
writeOffset: fileOffset,
isNullAware: false,
)..fileOffset = operatorOffset;
}
Expression _buildPrePostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
required bool forEffect,
required bool isPost,
}) {
return new PropertyIncDec(
receiver,
name,
forEffect: forEffect,
isInc: binaryOperator == plusName,
isPost: isPost,
isNullAware: false,
operatorOffset: operatorOffset,
nameOffset: _nameOffset,
)..fileOffset = _nameOffset;
}
@override
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: false,
);
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: true,
);
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
/// Creates a [Generator] for the access of property [name] on [receiver].
static Generator make(
ExpressionGeneratorHelper helper,
Token token,
Expression receiver,
Name name,
bool isNullAware,
) {
if (helper.forest.isThisExpression(receiver)) {
// Coverage-ignore-block(suite): Not run.
return new ThisPropertyAccessGenerator(
helper,
token,
name,
thisVariable: null,
thisOffset: receiver.fileOffset,
isNullAware: isNullAware,
);
} else {
return isNullAware
? new NullAwarePropertyAccessGenerator(helper, token, receiver, name)
: new PropertyAccessGenerator(helper, token, receiver, name);
}
}
}
/// A [ThisPropertyAccessGenerator] represents a subexpression whose prefix is
/// an implicit or explicit access on `this`.
///
/// For instance
///
/// class C {
/// var b;
/// method() {
/// b; // a ThisPropertyAccessGenerator is created for `b`.
/// b(); // a ThisPropertyAccessGenerator is created for `b`.
/// b = c; // a ThisPropertyAccessGenerator is created for `b`.
/// b += c; // a ThisPropertyAccessGenerator is created for `b`.
/// this.b; // a ThisPropertyAccessGenerator is created for `this.b`.
/// this.b(); // a ThisPropertyAccessGenerator is created for `this.b`.
/// this.b = c; // a ThisPropertyAccessGenerator is created for `this.b`.
/// this.b += c; // a ThisPropertyAccessGenerator is created for `this.b`.
/// }
/// }
///
/// This is a special case of [PropertyAccessGenerator] to avoid creating an
/// indirect access to 'this' in for instance `this.b += c` which by
/// [PropertyAccessGenerator] would have been created as
///
/// let #1 = this in #.b = #.b + c
///
/// instead of
///
/// this.b = this.b + c
///
class ThisPropertyAccessGenerator extends Generator {
/// The name for the accessed property. `b` in the examples in the class
/// documentation.
final Name name;
/// The offset of `this` if explicit. Otherwise `null`.
final int? thisOffset;
final bool isNullAware;
/// The synthetic variable used for 'this' in instance extension members
/// and instance extension type members/constructor bodies.
VariableDeclaration? thisVariable;
ThisPropertyAccessGenerator(
ExpressionGeneratorHelper helper,
Token nameToken,
this.name, {
this.thisVariable,
this.thisOffset,
this.isNullAware = false,
}) : super(helper, nameToken);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "ThisPropertyAccessGenerator";
@override
String get _plainNameForRead => name.text;
/// The file offset for the [name].
int get _nameOffset => fileOffset;
Expression get _thisExpression => thisVariable != null
? _forest.createVariableGet(thisOffset ?? fileOffset, thisVariable!)
: _forest.createThisExpression(thisOffset ?? fileOffset);
@override
Expression buildSimpleRead() {
return _createRead();
}
Expression _createRead() {
return _forest.createPropertyGet(
fileOffset,
_thisExpression,
name,
isNullAware: false,
);
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _createWrite(fileOffset, value, forEffect: voidContext);
}
Expression _createWrite(
int offset,
Expression value, {
required bool forEffect,
}) {
return _helper.forest.createPropertySet(
fileOffset,
_thisExpression,
name,
value,
forEffect: forEffect,
isNullAware: false,
);
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return new IfNullSet(
_createRead(),
_createWrite(offset, value, forEffect: voidContext),
forEffect: voidContext,
)..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
Expression binary = _helper.forest.createBinary(
operatorOffset,
_createRead(),
binaryOperator,
value,
);
return _createWrite(fileOffset, binary, forEffect: voidContext);
}
Expression _buildPrePostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
required bool forEffect,
required bool isPost,
}) {
return new PropertyIncDec(
_thisExpression,
name,
forEffect: forEffect,
isInc: binaryOperator == plusName,
isPost: isPost,
isNullAware: false,
operatorOffset: operatorOffset,
nameOffset: _nameOffset,
)..fileOffset = fileOffset;
}
@override
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: false,
);
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: true,
);
}
@override
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return _helper.buildMethodInvocation(
_thisExpression,
name,
arguments,
offset,
);
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", name: ");
sink.write(name.text);
}
}
class NullAwarePropertyAccessGenerator extends Generator {
final Expression receiver;
final Name name;
NullAwarePropertyAccessGenerator(
ExpressionGeneratorHelper helper,
Token nameToken,
this.receiver,
this.name,
) : super(helper, nameToken);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "NullAwarePropertyAccessGenerator";
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead => name.text;
/// The file offset of the [name].
int get _nameOffset => fileOffset;
@override
Expression buildSimpleRead() {
return _forest.createPropertyGet(
fileOffset,
receiver,
name,
isNullAware: true,
);
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _forest.createPropertySet(
fileOffset,
receiver,
name,
value,
forEffect: voidContext,
isNullAware: true,
);
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return new IfNullPropertySet(
receiver,
name,
value,
forEffect: voidContext,
readOffset: fileOffset,
writeOffset: fileOffset,
isNullAware: true,
)..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return new CompoundPropertySet(
receiver,
name,
binaryOperator,
value,
forEffect: voidContext,
readOffset: fileOffset,
binaryOffset: operatorOffset,
writeOffset: fileOffset,
isNullAware: true,
)..fileOffset = operatorOffset;
}
Expression _buildPrePostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
required bool forEffect,
required bool isPost,
}) {
return new PropertyIncDec(
receiver,
name,
forEffect: forEffect,
isInc: binaryOperator == plusName,
isPost: isPost,
isNullAware: true,
operatorOffset: operatorOffset,
nameOffset: _nameOffset,
)..fileOffset = fileOffset;
}
@override
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: false,
);
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: true,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return unsupported("doInvocation", offset, _uri);
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
sink.write(", receiver: ");
printNodeOn(receiver, sink, syntheticNames: syntheticNames);
sink.write(", name: ");
sink.write(name.text);
}
}
class SuperPropertyAccessGenerator extends Generator {
final Name name;
final Member? getter;
final Member? setter;
SuperPropertyAccessGenerator(
ExpressionGeneratorHelper helper,
Token nameToken,
this.name,
this.getter,
this.setter,
) : super(helper, nameToken);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "SuperPropertyAccessGenerator";
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead => name.text;
int get _nameOffset => fileOffset;
@override
Expression buildSimpleRead() {
return _createRead();
}
Expression _createRead() {
Member? getter = this.getter;
if (getter == null) {
return _helper.buildUnresolvedError(
name.text,
fileOffset,
isSuper: true,
kind: UnresolvedKind.Getter,
);
} else {
return new SuperPropertyGet(name, getter)..fileOffset = fileOffset;
}
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _createWrite(fileOffset, value);
}
Expression _createWrite(int offset, Expression value) {
Member? setter = this.setter;
if (setter == null) {
return _helper.buildUnresolvedError(
name.text,
fileOffset,
rhs: value,
isSuper: true,
kind: UnresolvedKind.Setter,
);
} else {
return new SuperPropertySet(name, value, setter)..fileOffset = offset;
}
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
Expression binary = _helper.forest.createBinary(
operatorOffset,
_createRead(),
binaryOperator,
value,
);
return _createWrite(fileOffset, binary);
}
Expression _buildPrePostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
required bool voidContext,
required bool isPost,
}) {
Member? getter = this.getter;
Member? setter = this.setter;
if (getter == null) {
return _helper.buildUnresolvedError(
name.text,
fileOffset,
isSuper: true,
kind: UnresolvedKind.Getter,
);
} else if (setter == null) {
return _helper.buildUnresolvedError(
name.text,
fileOffset,
isSuper: true,
kind: UnresolvedKind.Setter,
);
}
return new SuperIncDec(
getter: getter,
setter: setter,
name: name,
forEffect: voidContext,
isPost: isPost,
isInc: binaryOperator == plusName,
nameOffset: _nameOffset,
operatorOffset: operatorOffset,
)..fileOffset = _nameOffset;
}
@override
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPost: false,
);
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPost: true,
);
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return new IfNullSet(
_createRead(),
_createWrite(fileOffset, value),
forEffect: voidContext,
)..fileOffset = offset;
}
@override
// Coverage-ignore(suite): Not run.
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
if (_helper.constantContext != ConstantContext.none) {
// TODO(brianwilkerson) Fix the length
_helper.addProblem(codeNotAConstantExpression, offset, 1);
}
if (getter == null) {
return _helper.buildUnresolvedError(
name.text,
fileOffset,
arguments: arguments,
isSuper: true,
kind: UnresolvedKind.Method,
);
} else if (isFieldOrGetter(getter)) {
return _helper.forest.createExpressionInvocation(
offset,
buildSimpleRead(),
arguments,
);
} else {
// TODO(ahe): This could be something like "super.property(...)" where
// property is a setter.
return unhandled("${getter.runtimeType}", "doInvocation", offset, _uri);
}
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", name: ");
sink.write(name.text);
sink.write(", getter: ");
printQualifiedNameOn(getter, sink);
sink.write(", setter: ");
printQualifiedNameOn(setter, sink);
}
}
class IndexedAccessGenerator extends Generator {
final Expression receiver;
final Expression index;
final bool isNullAware;
IndexedAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.receiver,
this.index, {
required this.isNullAware,
}) : super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead => "[]";
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "IndexedAccessGenerator";
@override
Expression buildSimpleRead() {
return _forest.createIndexGet(
fileOffset,
receiver,
index,
isNullAware: isNullAware,
);
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _forest.createIndexSet(
fileOffset,
receiver,
index,
value,
forEffect: voidContext,
isNullAware: isNullAware,
);
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return new IfNullIndexSet(
receiver,
index,
value,
readOffset: fileOffset,
testOffset: offset,
writeOffset: fileOffset,
forEffect: voidContext,
isNullAware: isNullAware,
)..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return new CompoundIndexSet(
receiver,
index,
binaryOperator,
value,
readOffset: fileOffset,
binaryOffset: operatorOffset,
writeOffset: fileOffset,
forEffect: voidContext,
forPostIncDec: isPostIncDec,
isNullAware: isNullAware,
)..fileOffset = operatorOffset;
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
Expression value = _forest.createIntLiteral(operatorOffset, 1);
return buildCompoundAssignment(
binaryOperator,
value,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPostIncDec: true,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return _helper.forest.createExpressionInvocation(
arguments.fileOffset,
buildSimpleRead(),
arguments,
);
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
sink.write(", receiver: ");
printNodeOn(receiver, sink, syntheticNames: syntheticNames);
sink.write(", index: ");
printNodeOn(index, sink, syntheticNames: syntheticNames);
sink.write(", isNullAware: ${isNullAware}");
}
static Generator make(
ExpressionGeneratorHelper helper,
Token token,
Expression receiver,
Expression index, {
required bool isNullAware,
}) {
if (helper.forest.isThisExpression(receiver)) {
// Coverage-ignore-block(suite): Not run.
return new ThisIndexedAccessGenerator(
helper,
token,
index,
thisOffset: receiver.fileOffset,
isNullAware: isNullAware,
);
} else {
return new IndexedAccessGenerator(
helper,
token,
receiver,
index,
isNullAware: isNullAware,
);
}
}
}
/// Special case of [IndexedAccessGenerator] to avoid creating an indirect
/// access to 'this'.
class ThisIndexedAccessGenerator extends Generator {
final Expression index;
final int? thisOffset;
final bool isNullAware;
ThisIndexedAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.index, {
this.thisOffset,
this.isNullAware = false,
}) : super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead => "[]";
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "ThisIndexedAccessGenerator";
@override
Expression buildSimpleRead() {
Expression receiver = _helper.forest.createThisExpression(fileOffset);
return _forest.createIndexGet(
fileOffset,
receiver,
index,
isNullAware: isNullAware,
);
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
Expression receiver = _helper.forest.createThisExpression(fileOffset);
return _forest.createIndexSet(
fileOffset,
receiver,
index,
value,
forEffect: voidContext,
isNullAware: isNullAware,
);
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
Expression receiver = _helper.forest.createThisExpression(fileOffset);
return new IfNullIndexSet(
receiver,
index,
value,
readOffset: fileOffset,
testOffset: offset,
writeOffset: fileOffset,
forEffect: voidContext,
isNullAware: isNullAware,
)..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
Expression receiver = _helper.forest.createThisExpression(fileOffset);
return new CompoundIndexSet(
receiver,
index,
binaryOperator,
value,
readOffset: fileOffset,
binaryOffset: operatorOffset,
writeOffset: fileOffset,
forEffect: voidContext,
forPostIncDec: isPostIncDec,
isNullAware: isNullAware,
)..fileOffset = operatorOffset;
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
Expression value = _forest.createIntLiteral(operatorOffset, 1);
return buildCompoundAssignment(
binaryOperator,
value,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPostIncDec: true,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return _helper.forest.createExpressionInvocation(
offset,
buildSimpleRead(),
arguments,
);
}
@override
// Coverage-ignore(suite): Not run.
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
sink.write(", index: ");
printNodeOn(index, sink, syntheticNames: syntheticNames);
}
}
class SuperIndexedAccessGenerator extends Generator {
final Expression index;
final Procedure? getter;
final Procedure? setter;
SuperIndexedAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.index,
this.getter,
this.setter,
) : super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead => "[]";
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "SuperIndexedAccessGenerator";
@override
Expression buildSimpleRead() {
Procedure? getter = this.getter;
if (getter == null) {
return _helper.buildUnresolvedError(
indexGetName.text,
fileOffset,
isSuper: true,
arguments: _helper.forest.createArguments(fileOffset, <Expression>[
index,
]),
kind: UnresolvedKind.Method,
length: noLength,
);
} else {
return _helper.forest.createSuperMethodInvocation(
fileOffset,
indexGetName,
getter,
_helper.forest.createArguments(fileOffset, <Expression>[index]),
);
}
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
Procedure? setter = this.setter;
if (setter == null) {
return _helper.buildUnresolvedError(
indexSetName.text,
fileOffset,
isSuper: true,
arguments: _helper.forest.createArguments(fileOffset, <Expression>[
index,
value,
]),
kind: UnresolvedKind.Method,
length: noLength,
);
} else {
if (voidContext) {
return _helper.forest.createSuperMethodInvocation(
fileOffset,
indexSetName,
setter,
_helper.forest.createArguments(fileOffset, <Expression>[
index,
value,
]),
);
} else {
return new SuperIndexSet(setter, index, value)..fileOffset = fileOffset;
}
}
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return new IfNullSuperIndexSet(
getter,
setter,
index,
value,
readOffset: fileOffset,
testOffset: offset,
writeOffset: fileOffset,
forEffect: voidContext,
)..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
Procedure? getter = this.getter;
Procedure? setter = this.setter;
if (getter == null || setter == null) {
return buildAssignment(
buildBinaryOperation(token, binaryOperator, value),
);
} else {
return new CompoundSuperIndexSet(
getter,
setter,
index,
binaryOperator,
value,
readOffset: fileOffset,
binaryOffset: operatorOffset,
writeOffset: fileOffset,
forEffect: voidContext,
forPostIncDec: isPostIncDec,
);
}
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
Expression value = _forest.createIntLiteral(operatorOffset, 1);
return buildCompoundAssignment(
binaryOperator,
value,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPostIncDec: true,
);
}
@override
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return _helper.forest.createExpressionInvocation(
offset,
buildSimpleRead(),
arguments,
);
}
@override
// Coverage-ignore(suite): Not run.
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
sink.write(", index: ");
printNodeOn(index, sink, syntheticNames: syntheticNames);
sink.write(", getter: ");
printQualifiedNameOn(getter, sink);
sink.write(", setter: ");
printQualifiedNameOn(setter, sink);
}
}
/// A [StaticAccessGenerator] represents a subexpression whose prefix is
/// a static or top-level member, including static extension members.
///
/// For instance
///
/// get property => 0;
/// set property(_) {}
/// var field;
/// method() {}
///
/// main() {
/// property; // a StaticAccessGenerator is created for `property`.
/// property = 0; // a StaticAccessGenerator is created for `property`.
/// field = 0; // a StaticAccessGenerator is created for `field`.
/// method; // a StaticAccessGenerator is created for `method`.
/// method(); // a StaticAccessGenerator is created for `method`.
/// }
///
/// class A {}
/// extension B on A {
/// static get property => 0;
/// static set property(_) {}
/// static var field;
/// static method() {
/// property; // this StaticAccessGenerator is created for `property`.
/// property = 0; // this StaticAccessGenerator is created for `property`.
/// field = 0; // this StaticAccessGenerator is created for `field`.
/// method; // this StaticAccessGenerator is created for `method`.
/// method(); // this StaticAccessGenerator is created for `method`.
/// }
/// }
///
class StaticAccessGenerator extends Generator {
/// The name of the original target;
final Name targetName;
/// The static [Member] used for performing a read on this subexpression.
///
/// This can be `null` if the subexpression doesn't have a readable target.
/// For instance if the subexpression is a setter without a corresponding
/// getter.
final Member? readTarget;
/// The static [Member] used for performing an invocation on this
/// subexpression.
///
/// This can be `null` if the subexpression doesn't have an invokable target.
/// For instance if the subexpression is a setter without a corresponding
/// getter.
final Member? invokeTarget;
/// The static [Member] used for performing a write on this subexpression.
///
/// This can be `null` if the subexpression doesn't have a writable target.
/// For instance if the subexpression is a final field, a method, or a getter
/// without a corresponding setter.
final Member? writeTarget;
/// The offset of the type name if explicit. Otherwise `null`.
final int? typeOffset;
final bool isNullAware;
StaticAccessGenerator(
ExpressionGeneratorHelper helper,
Token nameToken,
this.targetName,
this.readTarget,
this.invokeTarget,
this.writeTarget, {
this.typeOffset,
this.isNullAware = false,
}) : assert(
readTarget != null || invokeTarget != null || writeTarget != null,
"No targets for $targetName.",
),
super(helper, nameToken);
factory StaticAccessGenerator.fromBuilder(
ExpressionGeneratorHelper helper,
Name targetName,
Token nameToken,
MemberBuilder? getterBuilder,
MemberBuilder? setterBuilder, {
int? typeOffset,
bool isNullAware = false,
}) {
// If both [getterBuilder] and [setterBuilder] exist, they must both be
// either top level (potentially from different libraries) or from the same
// class/extension.
assert(
getterBuilder == null ||
setterBuilder == null ||
getterBuilder.declarationBuilder == setterBuilder.declarationBuilder,
"Invalid builders for $targetName: $getterBuilder vs $setterBuilder.",
);
return new StaticAccessGenerator(
helper,
nameToken,
targetName,
getterBuilder?.readTarget,
getterBuilder?.invokeTarget,
setterBuilder?.writeTarget,
typeOffset: typeOffset,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "StaticAccessGenerator";
@override
String get _plainNameForRead => targetName.text;
int get _nameOffset => fileOffset;
@override
Expression buildSimpleRead() {
return _createRead();
}
Expression _createRead() {
Expression read;
Member? readTarget = this.readTarget;
if (readTarget == null) {
read = _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
} else {
if (readTarget is Procedure && readTarget.kind == ProcedureKind.Method) {
read = _helper.forest.createStaticTearOff(fileOffset, readTarget);
} else {
read = _helper.forest.createStaticGet(fileOffset, readTarget);
}
}
return read;
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _createWrite(fileOffset, value);
}
Expression _createWrite(int offset, Expression value) {
Expression write;
if (writeTarget == null) {
write = _makeInvalidWrite(value: value);
} else {
write = new StaticSet(writeTarget!, value)..fileOffset = offset;
}
return write;
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return new IfNullSet(
_createRead(),
_createWrite(offset, value),
forEffect: voidContext,
)..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
Expression binary = _helper.forest.createBinary(
operatorOffset,
_createRead(),
binaryOperator,
value,
);
return _createWrite(fileOffset, binary);
}
Expression _buildPrePostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
required bool voidContext,
required bool isPost,
}) {
Member? getter = readTarget;
Member? setter = writeTarget;
if (getter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
} else if (setter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Setter);
}
return new StaticIncDec(
getter: getter,
setter: setter,
name: targetName,
forEffect: voidContext,
isPost: isPost,
isInc: binaryOperator == plusName,
nameOffset: _nameOffset,
operatorOffset: operatorOffset,
)..fileOffset = _nameOffset;
}
@override
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPost: false,
);
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPost: true,
);
}
@override
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
if (_helper.constantContext != ConstantContext.none &&
!_helper.isIdentical(invokeTarget) &&
!_helper.libraryFeatures.constFunctions.isEnabled) {
return _helper.buildProblem(
codeNotConstantExpression.withArguments('Method invocation'),
offset,
invokeTarget?.name.text.length ?? 0,
);
}
if (invokeTarget == null ||
(readTarget != null && isFieldOrGetter(readTarget!))) {
return _helper.forest.createExpressionInvocation(
offset + (readTarget?.name.text.length ?? 0),
buildSimpleRead(),
arguments,
);
} else {
return _helper.buildStaticInvocation(
invokeTarget as Procedure,
arguments,
charOffset: offset,
isConstructorInvocation: false,
);
}
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", targetName: ");
sink.write(targetName);
sink.write(", readTarget: ");
printQualifiedNameOn(readTarget, sink);
sink.write(", writeTarget: ");
printQualifiedNameOn(writeTarget, sink);
}
}
/// An [ExtensionInstanceAccessGenerator] represents a subexpression whose
/// prefix is an extension instance member.
///
/// For instance
///
/// class A {}
/// extension B on A {
/// get property => 0;
/// set property(_) {}
/// method() {
/// property; // this generator is created for `property`.
/// property = 0; // this generator is created for `property`.
/// method; // this generator is created for `method`.
/// method(); // this generator is created for `method`.
/// }
/// }
///
/// These can only occur within an extension instance member.
class ExtensionInstanceAccessGenerator extends Generator {
final Extension extension;
/// The original name of the target.
final Name targetName;
/// The static [Member] generated for an instance extension member which is
/// used for performing a read on this subexpression.
///
/// This can be `null` if the subexpression doesn't have a readable target.
/// For instance if the subexpression is a setter without a corresponding
/// getter.
final Procedure? readTarget;
/// The static [Member] generated for an instance extension member which is
/// used for performing an invocation on this subexpression.
///
/// This can be `null` if the subexpression doesn't have an invokable target.
/// For instance if the subexpression is a getter or setter.
final Procedure? invokeTarget;
/// The static [Member] generated for an instance extension member which is
/// used for performing a write on this subexpression.
///
/// This can be `null` if the subexpression doesn't have a writable target.
/// For instance if the subexpression is a final field, a method, or a getter
/// without a corresponding setter.
final Procedure? writeTarget;
/// The parameter holding the value for `this` within the current extension
/// instance method.
// TODO(johnniwinther): Handle static access to extension instance members,
// in which case the access is erroneous and [extensionThis] is `null`.
final VariableDeclaration extensionThis;
/// The type parameters synthetically added to the current extension
/// instance method.
final List<TypeParameter>? extensionTypeParameters;
ExtensionInstanceAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.extension,
this.targetName,
this.readTarget,
this.invokeTarget,
this.writeTarget,
this.extensionThis,
this.extensionTypeParameters,
) : assert(readTarget != null || invokeTarget != null || writeTarget != null),
super(helper, token);
factory ExtensionInstanceAccessGenerator.fromBuilder(
ExpressionGeneratorHelper helper,
Token token,
Extension extension,
Name targetName,
VariableDeclaration extensionThis,
List<TypeParameter>? extensionTypeParameters,
MemberBuilder? getterBuilder,
MemberBuilder? setterBuilder,
) {
Procedure? readTarget;
Procedure? invokeTarget;
if (getterBuilder != null) {
assert(!getterBuilder.isStatic);
if (getterBuilder is PropertyBuilder) {
assert(!getterBuilder.hasConcreteField);
readTarget = getterBuilder.readTarget as Procedure?;
} else if (getterBuilder is MethodBuilder) {
if (getterBuilder.isOperator) {
// Coverage-ignore-block(suite): Not run.
invokeTarget = getterBuilder.invokeTarget as Procedure?;
} else {
readTarget = getterBuilder.readTarget as Procedure?;
invokeTarget = getterBuilder.invokeTarget as Procedure?;
}
} else {
return unhandled(
"${getterBuilder.runtimeType}",
"ExtensionInstanceAccessGenerator.fromBuilder",
offsetForToken(token),
helper.uri,
);
}
}
Procedure? writeTarget;
if (setterBuilder != null) {
if (setterBuilder is PropertyBuilder) {
assert(!setterBuilder.isStatic && !setterBuilder.hasConcreteField);
writeTarget = setterBuilder.writeTarget as Procedure?;
} else {
return unhandled(
"${setterBuilder.runtimeType}",
"ExtensionInstanceAccessGenerator.fromBuilder",
offsetForToken(token),
helper.uri,
);
}
}
return new ExtensionInstanceAccessGenerator(
helper,
token,
extension,
targetName,
readTarget,
invokeTarget,
writeTarget,
extensionThis,
extensionTypeParameters,
);
}
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "InstanceExtensionAccessGenerator";
@override
String get _plainNameForRead => targetName.text;
/// Creates an access to the implicit `this` variable.
Expression _createThisAccess() =>
_helper.createVariableGet(extensionThis, fileOffset);
/// Creates the implicit type arguments for the extension access. These
/// are the type parameter type of the extension type parameters.
List<DartType>? _createThisTypeArguments() {
List<DartType>? extensionTypeArguments;
if (extensionTypeParameters != null) {
extensionTypeArguments = [];
for (TypeParameter typeParameter in extensionTypeParameters!) {
extensionTypeArguments.add(
_forest.createTypeParameterTypeWithDefaultNullabilityForLibrary(
typeParameter,
extension.enclosingLibrary,
),
);
}
}
return extensionTypeArguments;
}
/// Returns `true` if performing a read operation is a tear off.
///
/// This is the case if [invokeTarget] is non-null, since extension methods
/// have both a [readTarget] and an [invokeTarget], whereas extension getters
/// only have a [readTarget].
bool get isReadTearOff => invokeTarget != null;
@override
Expression buildSimpleRead() {
return _createRead();
}
Expression _createRead() {
Procedure? getter = readTarget;
Expression read;
if (getter == null) {
read = _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
} else if (isReadTearOff) {
read = new ExtensionTearOff.implicit(
extension: extension,
thisTypeArguments: _createThisTypeArguments(),
thisAccess: _createThisAccess(),
name: targetName,
tearOff: getter,
)..fileOffset = fileOffset;
} else {
read = new ExtensionGet.implicit(
extension: extension,
thisTypeArguments: _createThisTypeArguments(),
thisAccess: _createThisAccess(),
name: targetName,
getter: getter,
)..fileOffset = fileOffset;
}
return read;
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _createWrite(fileOffset, value, forEffect: voidContext);
}
Expression _createWrite(
int offset,
Expression value, {
required bool forEffect,
}) {
Procedure? setter = writeTarget;
if (setter == null) {
return _makeInvalidWrite(value: value);
} else {
return new ExtensionSet.implicit(
extension: extension,
thisTypeArguments: _createThisTypeArguments(),
thisAccess: _createThisAccess(),
name: targetName,
setter: setter,
value: value,
forEffect: forEffect,
)..fileOffset = offset;
}
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
Procedure? getter = readTarget;
Procedure? setter = writeTarget;
// Coverage-ignore(suite): Not run.
if (getter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
} else if (setter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Setter);
}
return new ExtensionIfNullSet.implicit(
extension: extension,
thisTypeArguments: _createThisTypeArguments(),
thisAccess: _createThisAccess(),
propertyName: targetName,
getter: getter,
rhs: value,
setter: setter,
forEffect: voidContext,
readOffset: fileOffset,
binaryOffset: offset,
writeOffset: fileOffset,
)..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
Procedure? getter = readTarget;
Procedure? setter = writeTarget;
// Coverage-ignore(suite): Not run.
if (getter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
} else if (setter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Setter);
}
return new ExtensionCompoundSet.implicit(
extension: extension,
thisTypeArguments: _createThisTypeArguments(),
thisAccess: _createThisAccess(),
propertyName: targetName,
getter: getter,
binaryName: binaryOperator,
rhs: value,
setter: setter,
forEffect: voidContext,
readOffset: fileOffset,
binaryOffset: operatorOffset,
writeOffset: fileOffset,
);
}
Expression _buildPrePostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
required bool forEffect,
required bool isPost,
}) {
Procedure? getter = readTarget;
Procedure? setter = writeTarget;
// Coverage-ignore(suite): Not run.
if (getter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
} else if (setter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Setter);
}
return new ExtensionIncDec.implicit(
extension: extension,
thisTypeArguments: _createThisTypeArguments(),
thisAccess: _createThisAccess(),
name: targetName,
getter: getter,
setter: setter,
isPost: isPost,
isInc: binaryOperator == plusName,
forEffect: forEffect,
)..fileOffset = operatorOffset;
}
@override
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: false,
);
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: true,
);
}
@override
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
ArgumentsImpl arguments, {
bool isTypeArgumentsInForest = false,
}) {
Procedure? method = invokeTarget;
Procedure? getter = readTarget;
if (method != null) {
Expression thisAccess = _createThisAccess();
List<TypeParameter> typeParameters = method.function.typeParameters;
LocatedMessage? argMessage = _helper.checkArgumentsForFunction(
method.function,
arguments,
offset,
typeParameters,
extension: extension,
);
if (argMessage != null) {
// Coverage-ignore-block(suite): Not run.
return _helper.buildUnresolvedError(
targetName.text,
fileOffset,
arguments: arguments,
candidate: method,
message: argMessage,
kind: UnresolvedKind.Method,
);
}
return new ExtensionMethodInvocation.implicit(
extension: extension,
thisTypeArguments: _createThisTypeArguments(),
thisAccess: thisAccess,
name: targetName,
target: method,
arguments: arguments,
)..fileOffset = fileOffset;
} else if (getter != null) {
Expression thisAccess = _createThisAccess();
return new ExtensionGetterInvocation.implicit(
extension: extension,
thisTypeArguments: _createThisTypeArguments(),
thisAccess: thisAccess,
name: targetName,
target: getter,
arguments: arguments,
)..fileOffset = fileOffset;
} else {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
}
}
@override
// Coverage-ignore(suite): Not run.
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", targetName: ");
sink.write(targetName);
sink.write(", readTarget: ");
printQualifiedNameOn(readTarget, sink);
sink.write(", writeTarget: ");
printQualifiedNameOn(writeTarget, sink);
}
}
/// An [ExplicitExtensionInstanceAccessGenerator] represents a subexpression
/// whose prefix is a forced extension instance member access.
///
/// For instance
///
/// class A<T> {}
/// extension B on A<int> {
/// method() {}
/// }
/// extension C<T> {
/// T get field => 0;
/// set field(T _) {}
/// }
///
/// method(A a) {
/// B(a).method; // this generator is created for `B(a).method`.
/// B(a).method(); // this generator is created for `B(a).method`.
/// C<int>(a).field; // this generator is created for `C<int>(a).field`.
/// C(a).field = 0; // this generator is created for `C(a).field`.
/// }
///
class ExplicitExtensionInstanceAccessGenerator extends Generator {
/// The file offset used for the explicit extension application type
/// arguments.
final int extensionTypeArgumentOffset;
final Extension extension;
/// The name of the original target;
final Name targetName;
/// The static [Member] generated for an instance extension member which is
/// used for performing a read on this subexpression.
///
/// This can be `null` if the subexpression doesn't have a readable target.
/// For instance if the subexpression is a setter without a corresponding
/// getter.
final Procedure? readTarget;
/// The static [Member] generated for an instance extension member which is
/// used for performing an invocation on this subexpression.
///
/// This can be `null` if the subexpression doesn't have an invokable target.
/// For instance if the subexpression is a getter or setter.
final Procedure? invokeTarget;
/// The static [Member] generated for an instance extension member which is
/// used for performing a write on this subexpression.
///
/// This can be `null` if the subexpression doesn't have a writable target.
/// For instance if the subexpression is a final field, a method, or a getter
/// without a corresponding setter.
final Procedure? writeTarget;
/// The expression holding the receiver value for the explicit extension
/// access, that is, `a` in `Extension<int>(a).method<String>()`.
final Expression receiver;
/// The type arguments explicitly passed to the explicit extension access,
/// like `<int>` in `Extension<int>(a).method<String>()`.
final List<DartType>? explicitTypeArguments;
/// The number of type parameters declared on the extension declaration.
final int extensionTypeParameterCount;
/// If `true` the access is null-aware, like `Extension(c)?.foo`.
final bool isNullAware;
ExplicitExtensionInstanceAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.extensionTypeArgumentOffset,
this.extension,
this.targetName,
this.readTarget,
this.invokeTarget,
this.writeTarget,
this.receiver,
this.explicitTypeArguments,
this.extensionTypeParameterCount, {
required this.isNullAware,
}) : assert(
readTarget != null || invokeTarget != null || writeTarget != null,
),
super(helper, token);
factory ExplicitExtensionInstanceAccessGenerator.fromBuilder(
ExpressionGeneratorHelper helper,
Token token,
int extensionTypeArgumentOffset,
Extension extension,
Name targetName,
MemberBuilder? getterBuilder,
MemberBuilder? setterBuilder,
Expression receiver,
List<DartType>? explicitTypeArguments,
int extensionTypeParameterCount, {
required bool isNullAware,
}) {
assert(getterBuilder != null || setterBuilder != null);
Procedure? readTarget;
Procedure? invokeTarget;
if (getterBuilder != null) {
assert(!getterBuilder.isStatic);
if (getterBuilder is PropertyBuilder) {
assert(!getterBuilder.hasConcreteField);
readTarget = getterBuilder.readTarget as Procedure?;
} else if (getterBuilder is MethodBuilder) {
if (getterBuilder.isOperator) {
invokeTarget = getterBuilder.invokeTarget as Procedure?;
} else {
readTarget = getterBuilder.readTarget as Procedure?;
invokeTarget = getterBuilder.invokeTarget as Procedure?;
}
} else {
return unhandled(
"$getterBuilder (${getterBuilder.runtimeType})",
"InstanceExtensionAccessGenerator.fromBuilder",
offsetForToken(token),
helper.uri,
);
}
}
Procedure? writeTarget;
if (setterBuilder != null) {
assert(!setterBuilder.isStatic);
if (setterBuilder is PropertyBuilder) {
if (setterBuilder.hasSetter) {
writeTarget = setterBuilder.writeTarget as Procedure?;
}
} else {
return unhandled(
"$setterBuilder (${setterBuilder.runtimeType})",
"InstanceExtensionAccessGenerator.fromBuilder",
offsetForToken(token),
helper.uri,
);
}
}
return new ExplicitExtensionInstanceAccessGenerator(
helper,
token,
extensionTypeArgumentOffset,
extension,
targetName,
readTarget,
invokeTarget,
writeTarget,
receiver,
explicitTypeArguments,
extensionTypeParameterCount,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "ExplicitExtensionIndexedAccessGenerator";
@override
String get _plainNameForRead => targetName.text;
/// Returns `true` if performing a read operation is a tear off.
///
/// This is the case if [invokeTarget] is non-null, since extension methods
/// have both a [readTarget] and an [invokeTarget], whereas extension getters
/// only have a [readTarget].
bool get isReadTearOff => invokeTarget != null;
@override
Expression buildSimpleRead() {
return _createRead(receiver, isNullAware: isNullAware);
}
Expression _createRead(Expression receiver, {required bool isNullAware}) {
Procedure? getter = readTarget;
if (getter == null) {
// Coverage-ignore-block(suite): Not run.
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
} else if (isReadTearOff) {
return new ExtensionTearOff.explicit(
extension: extension,
explicitTypeArguments: explicitTypeArguments,
receiver: receiver,
name: targetName,
tearOff: getter,
isNullAware: isNullAware,
)..fileOffset = fileOffset;
} else {
return new ExtensionGet.explicit(
extension: extension,
explicitTypeArguments: explicitTypeArguments,
receiver: receiver,
name: targetName,
getter: getter,
isNullAware: isNullAware,
)..fileOffset = fileOffset;
}
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _createWrite(
fileOffset,
receiver,
value,
forEffect: voidContext,
isNullAware: isNullAware,
);
}
Expression _createWrite(
int offset,
Expression receiver,
Expression value, {
required bool forEffect,
required bool isNullAware,
}) {
Procedure? setter = writeTarget;
if (setter == null) {
// Coverage-ignore-block(suite): Not run.
return _makeInvalidWrite(value: value);
} else {
return new ExtensionSet.explicit(
extension: extension,
explicitTypeArguments: explicitTypeArguments,
receiver: receiver,
name: targetName,
setter: setter,
value: value,
forEffect: forEffect,
isNullAware: isNullAware,
)..fileOffset = offset;
}
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
Procedure? getter = readTarget;
Procedure? setter = writeTarget;
// Coverage-ignore(suite): Not run.
if (getter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
} else if (setter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Setter);
}
return new ExtensionIfNullSet.explicit(
extension: extension,
explicitTypeArguments: explicitTypeArguments,
receiver: receiver,
propertyName: targetName,
getter: getter,
rhs: value,
setter: setter,
forEffect: voidContext,
readOffset: fileOffset,
binaryOffset: offset,
writeOffset: fileOffset,
isNullAware: isNullAware,
)..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
Procedure? getter = readTarget;
Procedure? setter = writeTarget;
// Coverage-ignore(suite): Not run.
if (getter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
} else if (setter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Setter);
}
return new ExtensionCompoundSet.explicit(
extension: extension,
explicitTypeArguments: explicitTypeArguments,
receiver: receiver,
propertyName: targetName,
getter: getter,
binaryName: binaryOperator,
rhs: value,
setter: setter,
forEffect: voidContext,
readOffset: fileOffset,
binaryOffset: operatorOffset,
writeOffset: fileOffset,
isNullAware: isNullAware,
)..fileOffset = operatorOffset;
}
Expression _buildPrePostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
required bool forEffect,
required bool isPost,
}) {
Procedure? getter = readTarget;
Procedure? setter = writeTarget;
if (getter == null) {
// Coverage-ignore-block(suite): Not run.
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
} else if (setter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Setter);
}
return new ExtensionIncDec.explicit(
extension: extension,
explicitTypeArguments: explicitTypeArguments,
receiver: receiver,
name: targetName,
getter: getter,
setter: setter,
isPost: isPost,
isInc: binaryOperator == plusName,
forEffect: forEffect,
isNullAware: isNullAware,
)..fileOffset = operatorOffset;
}
@override
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: false,
);
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _buildPrePostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
forEffect: voidContext,
isPost: true,
);
}
@override
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
Procedure? method = invokeTarget;
Procedure? getter = readTarget;
if (method != null) {
List<TypeParameter> typeParameters = method.function.typeParameters;
LocatedMessage? argMessage = _helper.checkArgumentsForFunction(
method.function,
arguments,
offset,
typeParameters,
extension: extension,
);
if (argMessage != null) {
return _helper.buildUnresolvedError(
targetName.text,
offset,
arguments: arguments,
candidate: method,
message: argMessage,
kind: UnresolvedKind.Method,
);
}
return new ExtensionMethodInvocation.explicit(
extension: extension,
explicitTypeArguments: explicitTypeArguments,
receiver: receiver,
name: targetName,
target: method,
arguments: arguments as ArgumentsImpl,
isNullAware: isNullAware,
extensionTypeArgumentOffset: extensionTypeArgumentOffset,
)..fileOffset = fileOffset;
} else if (getter != null) {
return new ExtensionGetterInvocation.explicit(
extension: extension,
explicitTypeArguments: explicitTypeArguments,
receiver: receiver,
name: targetName,
target: getter,
arguments: arguments as ArgumentsImpl,
isNullAware: isNullAware,
extensionTypeArgumentOffset: extensionTypeArgumentOffset,
)..fileOffset = fileOffset;
} else {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Getter);
}
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", targetName: ");
sink.write(targetName);
sink.write(", readTarget: ");
printQualifiedNameOn(readTarget, sink);
sink.write(", writeTarget: ");
printQualifiedNameOn(writeTarget, sink);
}
}
class ExplicitExtensionIndexedAccessGenerator extends Generator {
/// The file offset used for the explicit extension application type
/// arguments.
final int extensionTypeArgumentOffset;
final Extension extension;
/// The static [Member] generated for the [] operation.
///
/// This can be `null` if the extension doesn't have an [] method.
final Procedure? readTarget;
/// The static [Member] generated for the []= operation.
///
/// This can be `null` if the extension doesn't have an []= method.
final Procedure? writeTarget;
/// The expression holding the receiver value for the explicit extension
/// access, that is, `a` in `Extension<int>(a)[index]`.
final Expression receiver;
/// The index expression;
final Expression index;
/// The type arguments explicitly passed to the explicit extension access,
/// like `<int>` in `Extension<int>(a)[b]`.
final List<DartType>? explicitTypeArguments;
/// The number of type parameters declared on the extension declaration.
final int extensionTypeParameterCount;
final bool isNullAware;
ExplicitExtensionIndexedAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.extensionTypeArgumentOffset,
this.extension,
this.readTarget,
this.writeTarget,
this.receiver,
this.index,
this.explicitTypeArguments,
this.extensionTypeParameterCount, {
required this.isNullAware,
}) : assert(readTarget != null || writeTarget != null),
super(helper, token);
factory ExplicitExtensionIndexedAccessGenerator.fromBuilder(
ExpressionGeneratorHelper helper,
Token token,
int extensionTypeArgumentOffset,
Extension extension,
MemberBuilder? getterBuilder,
MemberBuilder? setterBuilder,
Expression receiver,
Expression index,
List<DartType>? explicitTypeArguments,
int extensionTypeParameterCount, {
required bool isNullAware,
}) {
Procedure? readTarget = getterBuilder?.invokeTarget as Procedure?;
Procedure? writeTarget = setterBuilder?.invokeTarget as Procedure?;
return new ExplicitExtensionIndexedAccessGenerator(
helper,
token,
extensionTypeArgumentOffset,
extension,
readTarget,
writeTarget,
receiver,
index,
explicitTypeArguments,
extensionTypeParameterCount,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead => "[]";
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "ExplicitExtensionIndexedAccessGenerator";
@override
Expression buildSimpleRead() {
Procedure? getter = readTarget;
if (getter == null) {
// Coverage-ignore-block(suite): Not run.
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Method);
}
return new ExtensionIndexGet(
extension,
explicitTypeArguments,
receiver,
getter,
index,
isNullAware: isNullAware,
)..fileOffset = fileOffset;
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
Procedure? setter = writeTarget;
if (setter == null) {
// Coverage-ignore-block(suite): Not run.
return _makeInvalidWrite(value: value);
}
return new ExtensionIndexSet(
extension,
explicitTypeArguments,
receiver,
setter,
index,
value,
isNullAware: isNullAware,
forEffect: voidContext,
)..fileOffset = fileOffset;
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
Procedure? getter = readTarget;
Procedure? setter = writeTarget;
// Coverage-ignore(suite): Not run.
if (getter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Member);
} else if (setter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Member);
}
return new ExtensionIfNullIndexSet(
extension,
explicitTypeArguments,
receiver,
getter,
setter,
index,
value,
readOffset: fileOffset,
testOffset: offset,
writeOffset: fileOffset,
forEffect: voidContext,
isNullAware: isNullAware,
)..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
Procedure? getter = readTarget;
Procedure? setter = writeTarget;
// Coverage-ignore(suite): Not run.
if (getter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Member);
} else if (setter == null) {
return _makeInvalidRead(unresolvedKind: UnresolvedKind.Member);
}
return new ExtensionCompoundIndexSet(
extension: extension,
explicitTypeArguments: explicitTypeArguments,
receiver: receiver,
getter: getter,
setter: setter,
index: index,
binaryName: binaryOperator,
rhs: value,
readOffset: fileOffset,
binaryOffset: operatorOffset,
writeOffset: fileOffset,
forEffect: voidContext,
forPostIncDec: isPostIncDec,
isNullAware: isNullAware,
);
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
Expression value = _forest.createIntLiteral(operatorOffset, 1);
return buildCompoundAssignment(
binaryOperator,
value,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPostIncDec: true,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return _helper.forest.createExpressionInvocation(
offset,
buildSimpleRead(),
arguments,
);
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
sink.write(", index: ");
printNodeOn(index, sink, syntheticNames: syntheticNames);
sink.write(", readTarget: ");
printQualifiedNameOn(readTarget, sink);
sink.write(", writeTarget: ");
printQualifiedNameOn(writeTarget, sink);
}
}
/// An [ExplicitExtensionAccessGenerator] represents a subexpression whose
/// prefix is an explicit extension application.
///
/// For instance
///
/// class A<T> {}
/// extension B on A<int> {
/// method() {}
/// }
/// extension C<T> on A<T> {
/// T get field => 0;
/// set field(T _) {}
/// }
///
/// method(A a) {
/// B(a).method; // this generator is created for `B(a)`.
/// B(a).method(); // this generator is created for `B(a)`.
/// C<int>(a).field; // this generator is created for `C<int>(a)`.
/// C(a).field = 0; // this generator is created for `C(a)`.
/// }
///
/// When an access is performed on this generator a
/// [ExplicitExtensionInstanceAccessGenerator] is created.
class ExplicitExtensionAccessGenerator extends Generator {
final ExtensionBuilder extensionBuilder;
final Expression receiver;
final List<DartType>? explicitTypeArguments;
ExplicitExtensionAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.extensionBuilder,
this.receiver,
this.explicitTypeArguments,
) : super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead {
return unsupported(
"ExplicitExtensionAccessGenerator.plainNameForRead",
fileOffset,
_uri,
);
}
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "ExplicitExtensionAccessGenerator";
@override
Expression buildSimpleRead() {
return _makeInvalidRead();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _makeInvalidWrite(value: value);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return _makeInvalidRead();
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return _makeInvalidRead();
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _makeInvalidRead();
}
Generator _createInstanceAccess(
Token token,
Name name, {
bool isNullAware = false,
}) {
MemberBuilder? getter = extensionBuilder.lookupLocalMemberByName(name);
if (getter != null && getter.isStatic) {
getter = null;
}
MemberBuilder? setter = extensionBuilder.lookupLocalMemberByName(
name,
setter: true,
);
if (setter != null && setter.isStatic) {
setter = null;
}
if (getter == null && setter == null) {
return new UnresolvedNameGenerator(
_helper,
token,
name,
unresolvedReadKind: UnresolvedKind.Member,
);
}
return new ExplicitExtensionInstanceAccessGenerator.fromBuilder(
_helper,
token,
// TODO(johnniwinther): Improve this. This is the name of the extension
// and not the type arguments (or arguments if type arguments are
// omitted).
fileOffset,
extensionBuilder.extension,
name,
getter,
setter,
receiver,
explicitTypeArguments,
extensionBuilder.typeParameters?.length ?? 0,
isNullAware: isNullAware,
);
}
@override
Expression_Generator buildSelectorAccess(
Selector selector,
int operatorOffset,
bool isNullAware,
) {
selector.reportNewAsSelector();
if (_helper.constantContext != ConstantContext.none) {
// Coverage-ignore-block(suite): Not run.
_helper.addProblem(codeNotAConstantExpression, fileOffset, token.length);
}
Generator generator = _createInstanceAccess(
selector.token,
selector.name,
isNullAware: isNullAware,
);
if (selector.arguments != null) {
return generator.doInvocation(
offsetForToken(selector.token),
selector.typeArguments,
selector.arguments! as ArgumentsImpl,
isTypeArgumentsInForest: selector.isTypeArgumentsInForest,
);
} else {
return generator;
}
}
@override
Expression_Generator buildBinaryOperation(
Token token,
Name binaryName,
Expression right,
) {
int fileOffset = offsetForToken(token);
Generator generator = _createInstanceAccess(token, binaryName);
return generator.doInvocation(
fileOffset,
null,
_forest.createArguments(fileOffset, <Expression>[right]),
);
}
@override
Expression_Generator buildUnaryOperation(Token token, Name unaryName) {
int fileOffset = offsetForToken(token);
Generator generator = _createInstanceAccess(token, unaryName);
return generator.doInvocation(
fileOffset,
null,
_forest.createArgumentsEmpty(fileOffset),
);
}
@override
Expression_Generator_Initializer doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
ArgumentsImpl arguments, {
bool isTypeArgumentsInForest = false,
}) {
Generator generator = _createInstanceAccess(token, callName);
return generator.doInvocation(
offset,
typeArguments,
arguments,
isTypeArgumentsInForest: isTypeArgumentsInForest,
);
}
@override
Expression _makeInvalidRead({
UnresolvedKind? unresolvedKind,
bool errorHasBeenReported = false,
}) {
return _helper.buildProblem(
codeExplicitExtensionAsExpression,
fileOffset,
lengthForToken(token),
errorHasBeenReported: errorHasBeenReported,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression _makeInvalidWrite({
Expression? value,
bool errorHasBeenReported = false,
}) {
return _helper.buildProblem(
codeExplicitExtensionAsLvalue,
fileOffset,
lengthForToken(token),
);
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
MemberBuilder? getter = extensionBuilder.lookupLocalMemberByName(
indexGetName,
);
MemberBuilder? setter = extensionBuilder.lookupLocalMemberByName(
indexSetName,
);
if (getter == null && setter == null) {
// Coverage-ignore-block(suite): Not run.
return new UnresolvedNameGenerator(
_helper,
token,
indexGetName,
unresolvedReadKind: UnresolvedKind.Method,
);
}
return new ExplicitExtensionIndexedAccessGenerator.fromBuilder(
_helper,
token,
// TODO(johnniwinther): Improve this. This is the name of the extension
// and not the type arguments (or arguments if type arguments are
// omitted).
fileOffset,
extensionBuilder.extension,
getter,
setter,
receiver,
index,
explicitTypeArguments,
extensionBuilder.typeParameters?.length ?? 0,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", extensionBuilder: ");
sink.write(extensionBuilder);
sink.write(", receiver: ");
sink.write(receiver);
}
}
class LoadLibraryGenerator extends Generator {
final LoadLibraryBuilder builder;
LoadLibraryGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.builder,
) : super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead => 'loadLibrary';
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "LoadLibraryGenerator";
@override
Expression buildSimpleRead() {
builder.importDependency.targetLibrary;
LoadLibraryTearOff read = new LoadLibraryTearOff(
builder.importDependency,
builder.createTearoffMethod(_helper.forest),
)..fileOffset = fileOffset;
return read;
}
@override
// Coverage-ignore(suite): Not run.
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _makeInvalidWrite(value: value);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
Expression read = buildSimpleRead();
Expression write = _makeInvalidWrite(value: value);
return new IfNullSet(read, write, forEffect: voidContext)
..fileOffset = offset;
}
@override
// Coverage-ignore(suite): Not run.
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
Expression binary = _helper.forest.createBinary(
operatorOffset,
buildSimpleRead(),
binaryOperator,
value,
);
return _makeInvalidWrite(value: binary);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
Expression value = _forest.createIntLiteral(operatorOffset, 1);
return buildCompoundAssignment(
binaryOperator,
value,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPostIncDec: true,
);
}
@override
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
if (_forest.argumentsPositional(arguments).length > 0 ||
_forest.argumentsNamed(arguments).length > 0) {
_helper.addProblemErrorIfConst(
codeLoadLibraryTakesNoArguments,
offset,
'loadLibrary'.length,
);
}
return builder.createLoadLibrary(offset, _forest, arguments);
}
@override
// Coverage-ignore(suite): Not run.
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", builder: ");
sink.write(builder);
}
}
class DeferredAccessGenerator extends Generator {
final PrefixUseGenerator prefixGenerator;
final Generator suffixGenerator;
DeferredAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.prefixGenerator,
this.suffixGenerator,
) : super(helper, token);
@override
Expression buildSimpleRead() {
return _helper.wrapInDeferredCheck(
suffixGenerator.buildSimpleRead(),
prefixGenerator.prefix,
token.charOffset,
);
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _helper.wrapInDeferredCheck(
suffixGenerator.buildAssignment(value, voidContext: voidContext),
prefixGenerator.prefix,
token.charOffset,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return _helper.wrapInDeferredCheck(
suffixGenerator.buildIfNullAssignment(
value,
type,
offset,
voidContext: voidContext,
),
prefixGenerator.prefix,
token.charOffset,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return _helper.wrapInDeferredCheck(
suffixGenerator.buildCompoundAssignment(
binaryOperator,
value,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPreIncDec: isPreIncDec,
isPostIncDec: isPostIncDec,
),
prefixGenerator.prefix,
token.charOffset,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _helper.wrapInDeferredCheck(
suffixGenerator.buildPostfixIncrement(
binaryOperator,
operatorOffset: operatorOffset,
voidContext: voidContext,
),
prefixGenerator.prefix,
token.charOffset,
);
}
@override
Expression_Generator buildSelectorAccess(
Selector selector,
int operatorOffset,
bool isNullAware,
) {
selector.reportNewAsSelector();
Object propertyAccess = suffixGenerator.buildSelectorAccess(
selector,
operatorOffset,
isNullAware,
);
if (propertyAccess is Generator) {
return new DeferredAccessGenerator(
_helper,
token,
prefixGenerator,
propertyAccess,
);
} else {
Expression expression = propertyAccess as Expression;
return _helper.wrapInDeferredCheck(
expression,
prefixGenerator.prefix,
token.charOffset,
);
}
}
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead {
return unsupported("deferredAccessor.plainNameForRead", fileOffset, _uri);
}
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "DeferredAccessGenerator";
@override
TypeBuilder buildTypeWithResolvedArguments(
NullabilityBuilder nullabilityBuilder,
List<TypeBuilder>? arguments, {
required bool allowPotentiallyConstantType,
required bool performTypeCanonicalization,
}) {
String name =
"${prefixGenerator._plainNameForRead}."
"${suffixGenerator._plainNameForRead}";
TypeBuilder type = suffixGenerator.buildTypeWithResolvedArguments(
nullabilityBuilder,
arguments,
allowPotentiallyConstantType: allowPotentiallyConstantType,
performTypeCanonicalization: performTypeCanonicalization,
);
LocatedMessage message;
TypeDeclarationBuilder? declaration = type.declaration;
if (declaration is InvalidBuilder) {
// Coverage-ignore-block(suite): Not run.
message = declaration.message;
} else {
int charOffset = offsetForToken(prefixGenerator.token);
message = codeDeferredTypeAnnotation
.withArguments(
_helper.buildDartType(
type,
TypeUse.deferredTypeError,
allowPotentiallyConstantType: allowPotentiallyConstantType,
),
prefixGenerator._plainNameForRead,
)
.withLocation(
_uri,
charOffset,
lengthOfSpan(prefixGenerator.token, token),
);
}
_helper.libraryBuilder.addProblem(
message.messageObject,
message.charOffset,
message.length,
message.uri,
);
return new NamedTypeBuilderImpl.forInvalidType(
name,
nullabilityBuilder,
message,
);
}
@override
Expression_Generator_Initializer doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
ArgumentsImpl arguments, {
bool isTypeArgumentsInForest = false,
}) {
Object suffix = suffixGenerator.doInvocation(
offset,
typeArguments,
arguments,
isTypeArgumentsInForest: isTypeArgumentsInForest,
);
if (suffix is Expression) {
return _helper.wrapInDeferredCheck(
suffix,
prefixGenerator.prefix,
fileOffset,
);
} else {
return new DeferredAccessGenerator(
_helper,
token,
prefixGenerator,
suffix as Generator,
);
}
}
@override
Expression invokeConstructor(
List<TypeBuilder>? typeArguments,
String name,
Arguments arguments,
Token nameToken,
Token nameLastToken,
Constness constness, {
required bool inImplicitCreationContext,
}) {
return _helper.wrapInDeferredCheck(
suffixGenerator.invokeConstructor(
typeArguments,
name,
arguments,
nameToken,
nameLastToken,
constness,
inImplicitCreationContext: inImplicitCreationContext,
),
prefixGenerator.prefix,
offsetForToken(suffixGenerator.token),
);
}
@override
// Coverage-ignore(suite): Not run.
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", prefixGenerator: ");
sink.write(prefixGenerator);
sink.write(", suffixGenerator: ");
sink.write(suffixGenerator);
}
}
/// [TypeUseGenerator] represents the subexpression whose prefix is the name of
/// a class, enum, type parameter, typedef, mixin declaration, extension
/// declaration or built-in type, like dynamic and void.
///
/// For instance:
///
/// class A<T> {}
/// typedef B = Function();
/// mixin C<T> on A<T> {}
/// extension D<T> on A<T> {}
///
/// method<T>() {
/// C<B> // a TypeUseGenerator is created for `C` and `B`.
/// B b; // a TypeUseGenerator is created for `B`.
/// D.foo(); // a TypeUseGenerator is created for `D`.
/// new A<T>(); // a TypeUseGenerator is created for `A` and `T`.
/// T(); // a TypeUseGenerator is created for `T`.
/// }
///
class TypeUseGenerator extends AbstractReadOnlyAccessGenerator {
final TypeDeclarationBuilder declaration;
List<TypeBuilder>? typeArguments;
final TypeName typeName;
Expression? _expression;
TypeUseGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.declaration,
this.typeName,
) : super(
helper,
token,
// TODO(johnniwinther): InvalidTypeDeclarationBuilder is currently
// misused for import conflict.
declaration is InvalidBuilder
? ReadOnlyAccessKind.InvalidDeclaration
: ReadOnlyAccessKind.TypeLiteral,
);
@override
String get targetName => typeName.name;
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "TypeUseGenerator";
@override
TypeBuilder buildTypeWithResolvedArguments(
NullabilityBuilder nullabilityBuilder,
List<TypeBuilder>? arguments, {
required bool allowPotentiallyConstantType,
required bool performTypeCanonicalization,
}) {
return new NamedTypeBuilderImpl(
typeName,
nullabilityBuilder,
arguments: arguments,
fileUri: _uri,
charOffset: fileOffset,
instanceTypeParameterAccess: _helper.instanceTypeParameterAccessState,
)..bind(_helper.libraryBuilder, declaration);
}
@override
Expression invokeConstructor(
List<TypeBuilder>? typeArguments,
String name,
Arguments arguments,
Token nameToken,
Token nameLastToken,
Constness constness, {
required bool inImplicitCreationContext,
}) {
return _helper.buildConstructorInvocation(
declaration,
nameToken,
nameLastToken,
arguments,
name,
typeArguments,
offsetForToken(nameToken),
constness,
unresolvedKind: UnresolvedKind.Constructor,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", declaration: ");
sink.write(declaration);
sink.write(", plainNameForRead: ");
sink.write(_plainNameForRead);
}
@override
Expression get expression {
if (_expression == null) {
if (declaration is InvalidBuilder) {
InvalidBuilder declaration = this.declaration as InvalidBuilder;
_expression = _helper.buildProblemErrorIfConst(
declaration.message.messageObject,
fileOffset,
token.length,
);
} else {
_expression = _forest.createTypeLiteral(
offsetForToken(token),
_helper.buildDartType(
buildTypeWithResolvedArguments(
const NullabilityBuilder.omitted(),
typeArguments,
allowPotentiallyConstantType: true,
performTypeCanonicalization: true,
),
TypeUse.typeLiteral,
allowPotentiallyConstantType:
_helper.libraryFeatures.constructorTearoffs.isEnabled,
),
);
}
}
return _expression!;
}
@override
Expression_Generator buildSelectorAccess(
Selector send,
int operatorOffset,
bool isNullAware,
) {
int nameOffset = offsetForToken(send.token);
Name name = send.name;
ArgumentsImpl? arguments = send.arguments as ArgumentsImpl?;
TypeDeclarationBuilder? declarationBuilder = declaration;
TypeAliasBuilder? aliasBuilder;
List<TypeBuilder>? unaliasedTypeArguments;
bool isGenericTypedefTearOff = false;
if (declarationBuilder is TypeAliasBuilder) {
aliasBuilder = declarationBuilder;
declarationBuilder = aliasBuilder.unaliasDeclaration(
null,
isUsedAsClass: true,
usedAsClassCharOffset: this.fileOffset,
usedAsClassFileUri: _uri,
);
bool supportsConstructorTearOff =
_helper.libraryFeatures.constructorTearoffs.isEnabled &&
switch (declarationBuilder) {
ClassBuilder() => true,
ExtensionBuilder() => false,
ExtensionTypeDeclarationBuilder() => true,
// Coverage-ignore(suite): Not run.
TypeAliasBuilder() => false,
// Coverage-ignore(suite): Not run.
NominalParameterBuilder() => false,
// Coverage-ignore(suite): Not run.
StructuralParameterBuilder() => false,
// Coverage-ignore(suite): Not run.
InvalidBuilder() => false,
// Coverage-ignore(suite): Not run.
BuiltinTypeDeclarationBuilder() => false,
null => false,
};
bool isConstructorTearOff =
send is PropertySelector && supportsConstructorTearOff;
List<TypeBuilder>? aliasedTypeArguments = typeArguments
?.map(
(unknownType) => _helper.validateTypeParameterUse(
unknownType,
allowPotentiallyConstantType: isConstructorTearOff,
),
)
.toList();
if (aliasedTypeArguments != null &&
aliasedTypeArguments.length != aliasBuilder.typeParametersCount) {
// Coverage-ignore-block(suite): Not run.
_helper.libraryBuilder.addProblem(
codeTypeArgumentMismatch.withArguments(
aliasBuilder.typeParametersCount,
),
fileOffset,
noLength,
_uri,
);
} else {
if (declarationBuilder is DeclarationBuilder) {
if (aliasedTypeArguments != null) {
new NamedTypeBuilderImpl(
typeName,
const NullabilityBuilder.omitted(),
arguments: aliasedTypeArguments,
fileUri: _uri,
charOffset: fileOffset,
instanceTypeParameterAccess:
_helper.instanceTypeParameterAccessState,
)
..bind(_helper.libraryBuilder, aliasBuilder)
..build(_helper.libraryBuilder, TypeUse.instantiation);
}
// If the arguments weren't supplied, the tear off is treated as
// generic, and the aliased type arguments match type parameters of
// the type alias.
if (aliasedTypeArguments == null &&
aliasBuilder.typeParametersCount != 0) {
isGenericTypedefTearOff = true;
aliasedTypeArguments = <TypeBuilder>[];
for (NominalParameterBuilder typeParameter
in aliasBuilder.typeParameters!) {
aliasedTypeArguments.add(
new NamedTypeBuilderImpl(
new SyntheticTypeName(typeParameter.name, fileOffset),
const NullabilityBuilder.omitted(),
fileUri: _uri,
charOffset: fileOffset,
instanceTypeParameterAccess:
_helper.instanceTypeParameterAccessState,
)..bind(_helper.libraryBuilder, typeParameter),
);
}
}
unaliasedTypeArguments = aliasBuilder.unaliasTypeArguments(
aliasedTypeArguments,
);
}
}
}
if (declarationBuilder is DeclarationBuilder) {
MemberLookupResult? result = declarationBuilder.findStaticBuilder(
name.text,
nameOffset,
_uri,
_helper.libraryBuilder,
);
if (result != null && result.isInvalidLookup) {
return new DuplicateDeclarationGenerator(
_helper,
send.token,
result,
name,
name.text.length,
);
}
Generator generator;
bool supportsConstructorTearOff =
_helper.libraryFeatures.constructorTearoffs.isEnabled &&
switch (declarationBuilder) {
ClassBuilder() => true,
ExtensionBuilder() => false,
ExtensionTypeDeclarationBuilder() => true,
};
if (result == null) {
// If we find a setter, [member] is an [AccessErrorBuilder], not null.
if (send is PropertySelector) {
assert(
send.typeArguments == null,
"Unexpected non-null typeArguments of "
"an IncompletePropertyAccessGenerator object: "
"'${send.typeArguments.runtimeType}'.",
);
if (supportsConstructorTearOff) {
MemberLookupResult? result = declarationBuilder
.findConstructorOrFactory(name.text, _helper.libraryBuilder);
Expression? tearOffExpression;
if (result != null && !result.isInvalidLookup) {
MemberBuilder? constructor = result.getable;
Member? tearOff = constructor?.readTarget;
if (tearOff is Constructor) {
if (declarationBuilder is ClassBuilder &&
declarationBuilder.isAbstract) {
return _helper.buildProblem(
codeAbstractClassConstructorTearOff,
nameOffset,
name.text.length,
);
} else if (declarationBuilder.isEnum) {
return _helper.buildProblem(
codeEnumConstructorTearoff,
nameOffset,
name.text.length,
);
}
tearOffExpression = _helper.forest.createConstructorTearOff(
token.charOffset,
tearOff,
);
} else if (tearOff is Procedure) {
if (tearOff.isRedirectingFactory) {
tearOffExpression = _helper.forest
.createRedirectingFactoryTearOff(
token.charOffset,
tearOff,
);
} else if (tearOff.isFactory) {
tearOffExpression = _helper.forest.createConstructorTearOff(
token.charOffset,
tearOff,
);
} else {
tearOffExpression = _helper.forest.createStaticTearOff(
token.charOffset,
tearOff,
);
}
} else if (tearOff != null) {
unhandled(
"${tearOff.runtimeType}",
"buildPropertyAccess",
operatorOffset,
_helper.uri,
);
}
}
if (tearOffExpression != null) {
List<DartType>? builtTypeArguments;
if (unaliasedTypeArguments != null) {
if (unaliasedTypeArguments.length !=
declarationBuilder.typeParametersCount) {
// The type arguments are either aren't provided or mismatch
// in number with the type parameters of the RHS declaration.
// We substitute them with the default types here: in the
// first case that would be exactly what type inference fills
// in for the RHS, and in the second case it's a reasonable
// fallback, as the error is reported during a check on the
// typedef.
builtTypeArguments = <DartType>[];
switch (declarationBuilder) {
case ClassBuilder():
for (TypeParameter typeParameter
in declarationBuilder.cls.typeParameters) {
builtTypeArguments.add(typeParameter.defaultType);
}
// Coverage-ignore(suite): Not run.
case ExtensionTypeDeclarationBuilder():
for (TypeParameter typeParameter
in declarationBuilder
.extensionTypeDeclaration
.typeParameters) {
builtTypeArguments.add(typeParameter.defaultType);
}
// Coverage-ignore(suite): Not run.
case ExtensionBuilder():
throw new UnsupportedError(
"Unexpected declaration $declarationBuilder",
);
}
} else {
builtTypeArguments = unaliasTypes(
declarationBuilder.buildAliasedTypeArguments(
_helper.libraryBuilder,
unaliasedTypeArguments,
/* hierarchy = */ null,
),
)!;
}
} else if (typeArguments != null) {
builtTypeArguments = _helper.buildDartTypeArguments(
typeArguments,
TypeUse.tearOffTypeArgument,
allowPotentiallyConstantType: true,
);
}
if (isGenericTypedefTearOff) {
if (isProperRenameForTypeDeclaration(
_helper.typeEnvironment,
aliasBuilder!.typedef,
aliasBuilder.libraryBuilder.library,
)) {
return tearOffExpression;
}
Procedure? tearOffLowering = aliasBuilder
.findConstructorOrFactory(
name.text,
nameOffset,
_uri,
_helper.libraryBuilder,
);
if (tearOffLowering != null) {
if (tearOffLowering.isFactory) {
// Coverage-ignore-block(suite): Not run.
return _helper.forest.createConstructorTearOff(
token.charOffset,
tearOffLowering,
);
} else {
return _helper.forest.createStaticTearOff(
token.charOffset,
tearOffLowering,
);
}
}
FreshStructuralParametersFromTypeParameters
freshTypeParameters =
getFreshStructuralParametersFromTypeParameters(
aliasBuilder.typedef.typeParameters,
);
List<DartType>? substitutedTypeArguments;
if (builtTypeArguments != null) {
substitutedTypeArguments = <DartType>[];
for (DartType builtTypeArgument in builtTypeArguments) {
substitutedTypeArguments.add(
freshTypeParameters.substitute(builtTypeArgument),
);
}
}
substitutedTypeArguments = unaliasTypes(
substitutedTypeArguments,
);
tearOffExpression = _helper.forest.createTypedefTearOff(
token.charOffset,
freshTypeParameters.freshTypeParameters,
tearOffExpression,
substitutedTypeArguments ?? const <DartType>[],
);
} else {
if (builtTypeArguments != null &&
builtTypeArguments.isNotEmpty) {
builtTypeArguments = unaliasTypes(builtTypeArguments)!;
tearOffExpression = _helper.forest.createInstantiation(
token.charOffset,
tearOffExpression,
builtTypeArguments,
);
}
}
return tearOffExpression;
}
}
generator = new UnresolvedNameGenerator(
_helper,
send.token,
name,
unresolvedReadKind: UnresolvedKind.Member,
);
} else {
return _helper.buildConstructorInvocation(
declarationBuilder,
send.token,
send.token,
arguments!,
name.text,
send.typeArguments,
token.charOffset,
Constness.implicit,
isTypeArgumentsInForest: send.isTypeArgumentsInForest,
typeAliasBuilder: aliasBuilder,
unresolvedKind: isNullAware
? UnresolvedKind.Method
: UnresolvedKind.Member,
);
}
} else {
Builder? getable = result.getable;
Builder? setable = result.setable;
if (getable != null) {
if (getable.isStatic &&
getable is! FactoryBuilder &&
typeArguments != null) {
return _helper.buildProblem(
codeStaticTearOffFromInstantiatedClass,
send.fileOffset,
send.name.text.length,
);
} else {
generator = new StaticAccessGenerator.fromBuilder(
_helper,
name,
send.token,
getable is MemberBuilder ? getable : null,
setable is MemberBuilder ? setable : null,
typeOffset: fileOffset,
isNullAware: isNullAware,
);
}
} else {
generator = new StaticAccessGenerator.fromBuilder(
_helper,
name,
send.token,
getable is MemberBuilder ? getable : null,
setable is MemberBuilder ? setable : null,
typeOffset: fileOffset,
isNullAware: isNullAware,
);
}
}
return arguments == null
? generator
: generator.doInvocation(
offsetForToken(send.token),
send.typeArguments,
arguments,
isTypeArgumentsInForest: send.isTypeArgumentsInForest,
);
} else {
// `SomeType?.toString` is the same as `SomeType.toString`, not
// `(SomeType).toString`.
return super.buildSelectorAccess(send, operatorOffset, isNullAware);
}
}
@override
Expression_Generator_Builder doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
if (declaration is ExtensionBuilder) {
ExtensionBuilder extensionBuilder = declaration as ExtensionBuilder;
if (arguments.positional.length != 1 || arguments.named.isNotEmpty) {
return _helper.buildProblem(
codeExplicitExtensionArgumentMismatch,
fileOffset,
lengthForToken(token),
);
}
List<DartType>? explicitTypeArguments = getExplicitTypeArguments(
arguments,
);
if (explicitTypeArguments != null) {
int typeParameterCount = extensionBuilder.typeParameters?.length ?? 0;
if (explicitTypeArguments.length != typeParameterCount) {
return _helper.buildProblem(
codeExplicitExtensionTypeArgumentMismatch.withArguments(
extensionBuilder.name,
typeParameterCount,
),
fileOffset,
lengthForToken(token),
);
}
}
// TODO(johnniwinther): Check argument and type argument count.
return new ExplicitExtensionAccessGenerator(
_helper,
token,
declaration as ExtensionBuilder,
arguments.positional.single,
explicitTypeArguments,
);
} else {
return _helper.buildConstructorInvocation(
declaration,
token,
token,
arguments,
"",
typeArguments,
token.charOffset,
Constness.implicit,
isTypeArgumentsInForest: isTypeArgumentsInForest,
unresolvedKind: UnresolvedKind.Constructor,
);
}
}
@override
Expression_Generator applyTypeArguments(
int fileOffset,
List<TypeBuilder>? typeArguments,
) {
return new TypeUseGenerator(_helper, token, declaration, typeName)
..typeArguments = typeArguments;
}
}
enum ReadOnlyAccessKind {
ConstVariable,
FinalVariable,
ExtensionThis,
LetVariable,
TypeLiteral,
ParenthesizedExpression,
InvalidDeclaration,
}
/// [ReadOnlyAccessGenerator] represents the subexpression whose prefix is the
/// name of final local variable, final parameter, or catch clause variable or
/// `this` in an instance method in an extension declaration.
///
/// For instance:
///
/// method(final a) {
/// final b = null;
/// a; // a ReadOnlyAccessGenerator is created for `a`.
/// a[]; // a ReadOnlyAccessGenerator is created for `a`.
/// b(); // a ReadOnlyAccessGenerator is created for `b`.
/// b.c = a.d; // a ReadOnlyAccessGenerator is created for `a` and `b`.
///
/// try {
/// } catch (a) {
/// a; // a ReadOnlyAccessGenerator is created for `a`.
/// }
/// }
///
/// extension on Foo {
/// method() {
/// this; // a ReadOnlyAccessGenerator is created for `this`.
/// this.a; // a ReadOnlyAccessGenerator is created for `this`.
/// this.b(); // a ReadOnlyAccessGenerator is created for `this`.
/// }
/// }
///
class ReadOnlyAccessGenerator extends AbstractReadOnlyAccessGenerator {
@override
final String targetName;
@override
Expression expression;
ReadOnlyAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.expression,
this.targetName,
ReadOnlyAccessKind kind,
) : super(helper, token, kind);
}
abstract class AbstractReadOnlyAccessGenerator extends Generator {
final ReadOnlyAccessKind kind;
AbstractReadOnlyAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.kind,
) : super(helper, token);
String get targetName;
Expression get expression;
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "ReadOnlyAccessGenerator";
@override
String get _plainNameForRead => targetName;
@override
Expression buildSimpleRead() => _createRead();
Expression _createRead() => expression;
@override
Expression _makeInvalidWrite({
required Expression value,
bool errorHasBeenReported = false,
}) {
switch (kind) {
case ReadOnlyAccessKind.ConstVariable:
return _helper.buildProblem(
codeCannotAssignToConstVariable.withArguments(targetName),
fileOffset,
lengthForToken(token),
errorHasBeenReported: errorHasBeenReported,
);
case ReadOnlyAccessKind.FinalVariable:
return _helper.buildProblem(
codeCannotAssignToFinalVariable.withArguments(targetName),
fileOffset,
lengthForToken(token),
errorHasBeenReported: errorHasBeenReported,
);
case ReadOnlyAccessKind.ExtensionThis:
return _helper.buildProblem(
codeCannotAssignToExtensionThis,
fileOffset,
lengthForToken(token),
errorHasBeenReported: errorHasBeenReported,
);
case ReadOnlyAccessKind.TypeLiteral:
return _helper.buildProblem(
codeCannotAssignToTypeLiteral,
fileOffset,
lengthForToken(token),
errorHasBeenReported: errorHasBeenReported,
);
case ReadOnlyAccessKind.ParenthesizedExpression:
return _helper.buildProblem(
codeCannotAssignToParenthesizedExpression,
fileOffset,
lengthForToken(token),
errorHasBeenReported: errorHasBeenReported,
);
case ReadOnlyAccessKind.LetVariable:
case ReadOnlyAccessKind.InvalidDeclaration:
break;
}
return super._makeInvalidWrite(
value: value,
errorHasBeenReported: errorHasBeenReported,
);
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _makeInvalidWrite(value: value);
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
Expression read = _createRead();
Expression write = _makeInvalidWrite(value: value);
return new IfNullSet(read, write, forEffect: voidContext)
..fileOffset = offset;
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
Expression binary = _helper.forest.createBinary(
operatorOffset,
_createRead(),
binaryOperator,
value,
);
return _makeInvalidWrite(value: binary);
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
Expression value = _forest.createIntLiteral(operatorOffset, 1);
return buildCompoundAssignment(
binaryOperator,
value,
operatorOffset: operatorOffset,
voidContext: voidContext,
isPostIncDec: true,
);
}
@override
Expression_Generator_Initializer doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return _helper.forest.createExpressionInvocation(
adjustForImplicitCall(targetName, offset),
_createRead(),
arguments,
);
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
// TODO(johnniwinther): The read-only quality of the variable should be
// passed on to the generator.
return new IndexedAccessGenerator(
_helper,
token,
_createRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
sink.write(", expression: ");
printNodeOn(expression, sink, syntheticNames: syntheticNames);
sink.write(", plainNameForRead: ");
sink.write(targetName);
sink.write(", kind: ");
sink.write(kind);
}
}
abstract class ErroneousExpressionGenerator extends Generator {
ErroneousExpressionGenerator(ExpressionGeneratorHelper helper, Token token)
: super(helper, token);
Expression buildError({
Arguments? arguments,
Expression? rhs,
required UnresolvedKind kind,
int? charOffset,
bool errorHasBeenReported = false,
});
// Coverage-ignore(suite): Not run.
Name get name => unsupported("name", fileOffset, _uri);
@override
String get _plainNameForRead => name.text;
@override
List<Initializer> buildFieldInitializer(Map<String, int>? initializedFields) {
return <Initializer>[
_helper.buildInvalidInitializer(buildError(kind: UnresolvedKind.Setter)),
];
}
@override
Expression_Generator_Initializer doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return buildError(
arguments: arguments,
charOffset: offset,
kind: UnresolvedKind.Method,
);
}
@override
Expression_Generator buildSelectorAccess(
Selector send,
int operatorOffset,
bool isNullAware,
) {
return send.withReceiver(
buildSimpleRead(),
operatorOffset,
isNullAware: isNullAware,
);
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return buildError(rhs: value, kind: UnresolvedKind.Setter);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return buildError(rhs: value, kind: UnresolvedKind.Getter);
}
@override
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return buildError(
arguments: _forest.createArguments(fileOffset, <Expression>[
_forest.createIntLiteral(operatorOffset, 1),
]),
kind: UnresolvedKind.Getter,
)..fileOffset = operatorOffset;
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return buildError(
arguments: _forest.createArguments(fileOffset, <Expression>[
_forest.createIntLiteral(operatorOffset, 1),
]),
kind: UnresolvedKind.Getter,
)..fileOffset = operatorOffset;
}
@override
// Coverage-ignore(suite): Not run.
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return buildError(rhs: value, kind: UnresolvedKind.Setter);
}
@override
Expression buildSimpleRead() {
return buildError(kind: UnresolvedKind.Member);
}
@override
// Coverage-ignore(suite): Not run.
Expression _makeInvalidRead({
required UnresolvedKind unresolvedKind,
bool errorHasBeenReported = false,
}) {
return buildError(
kind: unresolvedKind,
errorHasBeenReported: errorHasBeenReported,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression _makeInvalidWrite({
required Expression value,
bool errorHasBeenReported = false,
}) {
return buildError(
rhs: value,
kind: UnresolvedKind.Setter,
errorHasBeenReported: errorHasBeenReported,
);
}
@override
Expression invokeConstructor(
List<TypeBuilder>? typeArguments,
String name,
Arguments arguments,
Token nameToken,
Token nameLastToken,
Constness constness, {
required bool inImplicitCreationContext,
}) {
if (typeArguments != null) {
assert(_forest.argumentsTypeArguments(arguments).isEmpty);
_forest.argumentsSetTypeArguments(
arguments,
_helper.buildDartTypeArguments(
typeArguments,
TypeUse.constructorTypeArgument,
allowPotentiallyConstantType: false,
),
);
}
return buildError(arguments: arguments, kind: UnresolvedKind.Constructor);
}
@override
// Coverage-ignore(suite): Not run.
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
}
class DuplicateDeclarationGenerator extends ErroneousExpressionGenerator {
final LookupResult _lookupResult;
@override
final Name name;
final int _nameLength;
DuplicateDeclarationGenerator(
super.helper,
super.token,
this._lookupResult,
this.name,
this._nameLength,
);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => 'DuplicateDeclarationGenerator';
LocatedMessage _createInvalidMessage() {
return LookupResult.createDuplicateMessage(
_lookupResult,
name: name.text,
fileUri: _helper.uri,
fileOffset: fileOffset,
length: _nameLength,
);
}
Expression _createInvalidExpression() {
return LookupResult.createDuplicateExpression(
_lookupResult,
context: _helper.libraryBuilder.loader.target.context,
name: name.text,
fileUri: _helper.uri,
fileOffset: fileOffset,
length: _nameLength,
);
}
@override
Expression buildError({
Arguments? arguments,
Expression? rhs,
required UnresolvedKind kind,
int? charOffset,
bool errorHasBeenReported = false,
}) {
return _createInvalidExpression();
}
@override
// Coverage-ignore(suite): Not run.
Expression _makeInvalidRead({
UnresolvedKind? unresolvedKind,
bool errorHasBeenReported = false,
}) {
return _createInvalidExpression();
}
@override
// Coverage-ignore(suite): Not run.
Expression _makeInvalidWrite({
Expression? value,
bool errorHasBeenReported = false,
}) {
return _createInvalidExpression();
}
@override
// Coverage-ignore(suite): Not run.
List<Initializer> buildFieldInitializer(Map<String, int>? initializedFields) {
return <Initializer>[
_helper.buildInvalidInitializer(_createInvalidExpression(), fileOffset),
];
}
@override
Expression_Generator qualifiedLookup(Token name) {
return new UnexpectedQualifiedUseGenerator(
_helper,
name,
this,
errorHasBeenReported: true,
);
}
@override
TypeBuilder buildTypeWithResolvedArguments(
NullabilityBuilder nullabilityBuilder,
List<TypeBuilder>? arguments, {
required bool allowPotentiallyConstantType,
required bool performTypeCanonicalization,
}) {
return new NamedTypeBuilderImpl.forInvalidType(
token.lexeme,
nullabilityBuilder,
_createInvalidMessage(),
);
}
@override
Expression invokeConstructor(
List<TypeBuilder>? typeArguments,
String name,
Arguments arguments,
Token nameToken,
Token nameLastToken,
Constness constness, {
required bool inImplicitCreationContext,
}) {
return _createInvalidExpression();
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", name: ");
sink.write(name.text);
}
}
class UnresolvedNameGenerator extends ErroneousExpressionGenerator {
@override
final Name name;
final UnresolvedKind unresolvedReadKind;
factory UnresolvedNameGenerator(
ExpressionGeneratorHelper helper,
Token token,
Name name, {
required UnresolvedKind unresolvedReadKind,
}) {
if (name.text.isEmpty) {
unhandled("empty", "name", offsetForToken(token), helper.uri);
}
return new UnresolvedNameGenerator.internal(
helper,
token,
name,
unresolvedReadKind,
);
}
UnresolvedNameGenerator.internal(
ExpressionGeneratorHelper helper,
Token token,
this.name,
this.unresolvedReadKind,
) : super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "UnresolvedNameGenerator";
@override
Expression doInvocation(
int charOffset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return buildError(
arguments: arguments,
charOffset: charOffset,
kind: UnresolvedKind.Method,
);
}
@override
Expression buildError({
Arguments? arguments,
Expression? rhs,
required UnresolvedKind kind,
int? charOffset,
bool errorHasBeenReported = false,
}) {
charOffset ??= fileOffset;
return _helper.buildUnresolvedError(
_plainNameForRead,
charOffset,
arguments: arguments,
rhs: rhs,
kind: kind,
errorHasBeenReported: errorHasBeenReported,
);
}
@override
Expression_Generator qualifiedLookup(Token name) {
return new UnexpectedQualifiedUseGenerator(
_helper,
name,
this,
errorHasBeenReported: false,
);
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _buildUnresolvedVariableAssignment(false, value);
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return _buildUnresolvedVariableAssignment(true, value);
}
@override
Expression buildSimpleRead() {
return buildError(kind: unresolvedReadKind)..fileOffset = fileOffset;
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", name: ");
sink.write(name.text);
}
Expression _buildUnresolvedVariableAssignment(
bool isCompound,
Expression value,
) {
return buildError(rhs: value, kind: UnresolvedKind.Setter);
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
}
abstract class ContextAwareGenerator extends Generator {
final Generator generator;
ContextAwareGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.generator,
) : super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead {
return unsupported("plainNameForRead", token.charOffset, _helper.uri);
}
@override
// Coverage-ignore(suite): Not run.
Never doInvocation(
int charOffset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return unhandled("${runtimeType}", "doInvocation", charOffset, _uri);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _makeInvalidWrite(value: value);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return _makeInvalidWrite(value: value);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return _makeInvalidWrite(value: value);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _makeInvalidWrite();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _makeInvalidWrite();
}
@override
// Coverage-ignore(suite): Not run.
Never _makeInvalidRead({
UnresolvedKind? unresolvedKind,
bool errorHasBeenReported = false,
}) {
return unsupported("makeInvalidRead", token.charOffset, _helper.uri);
}
@override
// Coverage-ignore(suite): Not run.
Expression _makeInvalidWrite({
Expression? value,
bool errorHasBeenReported = false,
}) {
return _helper.buildProblem(
codeIllegalAssignmentToNonAssignable,
fileOffset,
lengthForToken(token),
errorHasBeenReported: errorHasBeenReported,
);
}
@override
// Coverage-ignore(suite): Not run.
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
}
class DelayedAssignment extends ContextAwareGenerator {
final Expression value;
String assignmentOperator;
DelayedAssignment(
ExpressionGeneratorHelper helper,
Token token,
Generator generator,
this.value,
this.assignmentOperator,
) : super(helper, token, generator);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "DelayedAssignment";
@override
Expression buildSimpleRead() {
return handleAssignment(false);
}
@override
Expression buildForEffect() {
return handleAssignment(true);
}
Expression handleAssignment(bool voidContext) {
if (_helper.constantContext != ConstantContext.none) {
return _helper.buildProblem(
codeNotAConstantExpression,
fileOffset,
token.length,
);
}
if (identical("=", assignmentOperator)) {
return generator.buildAssignment(value, voidContext: voidContext);
} else if (identical("+=", assignmentOperator)) {
return generator.buildCompoundAssignment(
plusName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else if (identical("-=", assignmentOperator)) {
return generator.buildCompoundAssignment(
minusName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else if (identical("*=", assignmentOperator)) {
return generator.buildCompoundAssignment(
multiplyName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else if (identical("%=", assignmentOperator)) {
// Coverage-ignore-block(suite): Not run.
return generator.buildCompoundAssignment(
percentName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else if (identical("&=", assignmentOperator)) {
return generator.buildCompoundAssignment(
ampersandName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else if (identical("/=", assignmentOperator)) {
// Coverage-ignore-block(suite): Not run.
return generator.buildCompoundAssignment(
divisionName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else if (identical("<<=", assignmentOperator)) {
// Coverage-ignore-block(suite): Not run.
return generator.buildCompoundAssignment(
leftShiftName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else if (identical(">>=", assignmentOperator)) {
// Coverage-ignore-block(suite): Not run.
return generator.buildCompoundAssignment(
rightShiftName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else if (identical(">>>=", assignmentOperator)) {
return generator.buildCompoundAssignment(
tripleShiftName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else if (identical("??=", assignmentOperator)) {
return generator.buildIfNullAssignment(
value,
const DynamicType(),
fileOffset,
voidContext: voidContext,
);
}
// Coverage-ignore(suite): Not run.
else if (identical("^=", assignmentOperator)) {
return generator.buildCompoundAssignment(
caretName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else if (identical("|=", assignmentOperator)) {
return generator.buildCompoundAssignment(
barName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else if (identical("~/=", assignmentOperator)) {
return generator.buildCompoundAssignment(
mustacheName,
value,
operatorOffset: fileOffset,
voidContext: voidContext,
);
} else {
return unhandled(
assignmentOperator,
"handleAssignment",
token.charOffset,
_helper.uri,
);
}
}
@override
List<Initializer> buildFieldInitializer(Map<String, int>? initializedFields) {
if (!identical("=", assignmentOperator) ||
generator is! ThisPropertyAccessGenerator) {
return generator.buildFieldInitializer(initializedFields);
}
return _helper.buildFieldInitializer(
generator._plainNameForRead,
offsetForToken(generator.token),
fileOffset,
value,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", value: ");
printNodeOn(value, sink);
sink.write(", assignmentOperator: ");
sink.write(assignmentOperator);
}
}
class DelayedPostfixIncrement extends ContextAwareGenerator {
final Name binaryOperator;
DelayedPostfixIncrement(
ExpressionGeneratorHelper helper,
Token token,
Generator generator,
this.binaryOperator,
) : super(helper, token, generator);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "DelayedPostfixIncrement";
@override
Expression buildSimpleRead() {
return generator.buildPostfixIncrement(
binaryOperator,
operatorOffset: fileOffset,
voidContext: false,
);
}
@override
Expression buildForEffect() {
return generator.buildPostfixIncrement(
binaryOperator,
operatorOffset: fileOffset,
voidContext: true,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", binaryOperator: ");
sink.write(binaryOperator.text);
}
}
class PrefixUseGenerator extends Generator {
final PrefixBuilder prefix;
PrefixUseGenerator(ExpressionGeneratorHelper helper, Token token, this.prefix)
: super(helper, token);
@override
String get _plainNameForRead => prefix.name;
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "PrefixUseGenerator";
@override
Expression buildSimpleRead() => _makeInvalidRead();
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _makeInvalidWrite(value: value);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return _makeInvalidRead();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return _makeInvalidRead();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _makeInvalidRead();
}
@override
Generator qualifiedLookup(Token nameToken) {
if (_helper.constantContext != ConstantContext.none && prefix.deferred) {
_helper.addProblem(
codeCantUseDeferredPrefixAsConstant.withArguments(token),
fileOffset,
lengthForToken(token),
);
}
String name = nameToken.lexeme;
Generator result = _helper.processLookupResult(
lookupResult: prefix.prefixScope.lookup(name),
name: name,
nameToken: nameToken,
nameOffset: nameToken.charOffset,
scopeKind: prefix.prefixScope.kind,
prefix: prefix,
prefixToken: token,
);
if (prefix.deferred) {
if (result is! LoadLibraryGenerator) {
result = new DeferredAccessGenerator(_helper, nameToken, this, result);
}
}
return result;
}
@override
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return _helper.wrapInLocatedProblem(
_helper.evaluateArgumentsBefore(
arguments,
_forest.createNullLiteral(fileOffset),
),
codeCantUsePrefixAsExpression.withLocation(
_helper.uri,
fileOffset,
lengthForToken(token),
),
);
}
@override
Expression_Generator buildSelectorAccess(
Selector selector,
int operatorOffset,
bool isNullAware,
) {
assert(
selector.name.text == selector.token.lexeme,
"'${selector.name.text}' != ${selector.token.lexeme}",
);
selector.reportNewAsSelector();
Object result = qualifiedLookup(selector.token);
if (selector is InvocationSelector) {
result = _helper.finishSend(
result,
selector.typeArguments,
selector.arguments as ArgumentsImpl,
selector.fileOffset,
isTypeArgumentsInForest: selector.isTypeArgumentsInForest,
);
}
if (isNullAware) {
result = _helper.wrapInLocatedProblem(
_helper.toValue(result),
codeCantUsePrefixWithNullAware.withLocation(
_helper.uri,
fileOffset,
lengthForToken(token),
),
);
}
return result;
}
@override
Expression _makeInvalidRead({
UnresolvedKind? unresolvedKind,
bool errorHasBeenReported = false,
}) {
return _helper.buildProblem(
codeCantUsePrefixAsExpression,
fileOffset,
lengthForToken(token),
errorHasBeenReported: errorHasBeenReported,
);
}
@override
Expression _makeInvalidWrite({
Expression? value,
bool errorHasBeenReported = false,
}) => _makeInvalidRead(errorHasBeenReported: errorHasBeenReported);
@override
// Coverage-ignore(suite): Not run.
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", prefix: ");
sink.write(prefix.name);
sink.write(", deferred: ");
sink.write(prefix.deferred);
}
}
class UnexpectedQualifiedUseGenerator extends Generator {
final Generator prefixGenerator;
/// If `true` an error has already been reported.
final bool errorHasBeenReported;
UnexpectedQualifiedUseGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.prefixGenerator, {
required this.errorHasBeenReported,
}) : super(helper, token);
@override
String get _plainNameForRead =>
"${prefixGenerator._plainNameForRead}.${token.lexeme}";
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "UnexpectedQualifiedUseGenerator";
@override
Expression buildSimpleRead() => _makeInvalidRead(
unresolvedKind: UnresolvedKind.Member,
errorHasBeenReported: errorHasBeenReported,
);
@override
// Coverage-ignore(suite): Not run.
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _makeInvalidWrite(
value: value,
errorHasBeenReported: errorHasBeenReported,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return _makeInvalidRead(
unresolvedKind: UnresolvedKind.Member,
errorHasBeenReported: errorHasBeenReported,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return _makeInvalidRead(
unresolvedKind: UnresolvedKind.Member,
errorHasBeenReported: errorHasBeenReported,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return _makeInvalidRead(
unresolvedKind: UnresolvedKind.Member,
errorHasBeenReported: errorHasBeenReported,
);
}
@override
// Coverage-ignore(suite): Not run.
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return _helper.buildUnresolvedError(
_plainNameForRead,
fileOffset,
arguments: arguments,
kind: UnresolvedKind.Method,
errorHasBeenReported: errorHasBeenReported,
);
}
@override
TypeBuilder buildTypeWithResolvedArguments(
NullabilityBuilder nullabilityBuilder,
List<TypeBuilder>? arguments, {
required bool allowPotentiallyConstantType,
required bool performTypeCanonicalization,
}) {
Message message = codeNotAPrefixInTypeAnnotation.withArguments(
prefixGenerator.token.lexeme,
token.lexeme,
);
if (!errorHasBeenReported) {
_helper.libraryBuilder.addProblem(
message,
offsetForToken(prefixGenerator.token),
lengthOfSpan(prefixGenerator.token, token),
_uri,
);
}
return new NamedTypeBuilderImpl.forInvalidType(
_plainNameForRead,
nullabilityBuilder,
message.withLocation(
_uri,
offsetForToken(prefixGenerator.token),
lengthOfSpan(prefixGenerator.token, token),
),
);
}
@override
Expression invokeConstructor(
List<TypeBuilder>? typeArguments,
String name,
Arguments arguments,
Token nameToken,
Token nameLastToken,
Constness constness, {
required bool inImplicitCreationContext,
}) {
Message message = codeConstructorNotFound.withArguments(
_helper.constructorNameForDiagnostics(name, className: _plainNameForRead),
);
return _helper.buildProblem(
message,
offsetForToken(prefixGenerator.token),
lengthOfSpan(prefixGenerator.token, nameLastToken),
errorHasBeenReported: errorHasBeenReported,
);
}
@override
// Coverage-ignore(suite): Not run.
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", prefixGenerator: ");
prefixGenerator.printOn(sink);
}
}
class ParserErrorGenerator extends Generator {
final Message message;
ParserErrorGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.message,
) : super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead => "#parser-error";
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "ParserErrorGenerator";
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {}
Expression buildProblem() {
return buildProblemExpression(_helper, message, fileOffset);
}
static Expression buildProblemExpression(
ExpressionGeneratorHelper _helper,
Message message,
int fileOffset,
) {
return _helper.buildProblem(
message,
fileOffset,
noLength,
errorHasBeenReported: true,
);
}
@override
Expression buildSimpleRead() => buildProblem();
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return buildProblem();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return buildProblem();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return buildProblem();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return buildProblem();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return buildProblem();
}
@override
// Coverage-ignore(suite): Not run.
Expression _makeInvalidRead({
UnresolvedKind? unresolvedKind,
bool errorHasBeenReported = false,
}) => buildProblem();
@override
// Coverage-ignore(suite): Not run.
Expression _makeInvalidWrite({
Expression? value,
bool errorHasBeenReported = false,
}) => buildProblem();
@override
List<Initializer> buildFieldInitializer(Map<String, int>? initializedFields) {
return <Initializer>[_helper.buildInvalidInitializer(buildProblem())];
}
@override
Expression doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
return buildProblem();
}
@override
Expression buildSelectorAccess(
Selector send,
int operatorOffset,
bool isNullAware,
) {
return buildProblem();
}
@override
TypeBuilder buildTypeWithResolvedArguments(
NullabilityBuilder nullabilityBuilder,
List<TypeBuilder>? arguments, {
required bool allowPotentiallyConstantType,
required bool performTypeCanonicalization,
}) {
_helper.libraryBuilder.addProblem(message, fileOffset, noLength, _uri);
return new NamedTypeBuilderImpl.forInvalidType(
token.lexeme,
nullabilityBuilder,
message.withLocation(_uri, fileOffset, noLength),
);
}
TypeBuilder buildTypeWithResolvedArgumentsDoNotAddProblem(
NullabilityBuilder nullabilityBuilder,
) {
return new NamedTypeBuilderImpl.forInvalidType(
token.lexeme,
nullabilityBuilder,
message.withLocation(_uri, fileOffset, noLength),
);
}
@override
// Coverage-ignore(suite): Not run.
Expression qualifiedLookup(Token name) {
return buildProblem();
}
@override
// Coverage-ignore(suite): Not run.
Expression invokeConstructor(
List<TypeBuilder>? typeArguments,
String name,
Arguments arguments,
Token nameToken,
Token nameLastToken,
Constness constness, {
required bool inImplicitCreationContext,
}) {
return buildProblem();
}
@override
// Coverage-ignore(suite): Not run.
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
return new IndexedAccessGenerator(
_helper,
token,
buildSimpleRead(),
index,
isNullAware: isNullAware,
);
}
}
/// A [ThisAccessGenerator] represents a subexpression whose prefix is `this`
/// or `super`.
///
/// For instance
///
/// class C {
/// var b = this.c; // a ThisAccessGenerator is created for `this`.
/// var c;
/// C(this.c) : // a ThisAccessGenerator is created for `this`.
/// this.b = c; // a ThisAccessGenerator is created for `this`.
/// method() {
/// this.b; // a ThisAccessGenerator is created for `this`.
/// super.b(); // a ThisAccessGenerator is created for `super`.
/// this.b = c; // a ThisAccessGenerator is created for `this`.
/// this.b += c; // a ThisAccessGenerator is created for `this`.
/// }
/// }
///
/// If this `this` occurs in an instance member on an extension declaration,
/// a [ReadOnlyAccessGenerator] is created instead.
///
class ThisAccessGenerator extends Generator {
/// `true` if this access is in an initializer list.
///
/// For instance in `<init>` in
///
/// class Class {
/// Class() : <init>;
/// }
///
final bool isInitializer;
/// `true` if this access is in a field initializer either directly or within
/// an initializer list.
///
/// For instance in `<init>` in
///
/// var foo = <init>;
/// class Class {
/// var bar = <init>;
/// Class() : <init>;
/// }
///
final bool inFieldInitializer;
/// `true` if this access is directly in a field initializer of a late field.
///
/// For instance in `<init>` in
///
/// late var foo = <init>;
/// class Class {
/// late var bar = <init>;
/// Class() : bar = 42;
/// }
///
final bool inLateFieldInitializer;
/// `true` if this subexpression represents a `super` prefix.
final bool isSuper;
ThisAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.isInitializer,
this.inFieldInitializer,
this.inLateFieldInitializer, {
this.isSuper = false,
}) : super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead {
return unsupported(
"${isSuper ? 'super' : 'this'}.plainNameForRead",
fileOffset,
_uri,
);
}
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "ThisAccessGenerator";
@override
Expression buildSimpleRead() {
if (!isSuper) {
if (inFieldInitializer && !inLateFieldInitializer) {
return buildFieldInitializerError(null);
} else {
return _forest.createThisExpression(fileOffset);
}
} else {
return _helper.buildProblem(
codeSuperAsExpression,
fileOffset,
lengthForToken(token),
);
}
}
Expression buildFieldInitializerError(Map<String, int>? initializedFields) {
String keyword = isSuper ? "super" : "this";
return _helper.buildProblem(
codeThisOrSuperAccessInFieldInitializer.withArguments(keyword),
fileOffset,
keyword.length,
);
}
@override
List<Initializer> buildFieldInitializer(Map<String, int>? initializedFields) {
Expression error = buildFieldInitializerError(initializedFields);
return <Initializer>[
_helper.buildInvalidInitializer(error, error.fileOffset),
];
}
@override
Expression_Generator_Initializer buildSelectorAccess(
Selector selector,
int operatorOffset,
bool isNullAware,
) {
Name name = selector.name;
Arguments? arguments = selector.arguments;
int offset = offsetForToken(selector.token);
if (isInitializer && selector is InvocationSelector) {
if (isNullAware) {
_helper.addProblem(codeInvalidUseOfNullAwareAccess, operatorOffset, 2);
}
return buildConstructorInitializer(offset, name, arguments!);
}
selector.reportNewAsSelector();
if (inFieldInitializer && !inLateFieldInitializer && !isInitializer) {
return buildFieldInitializerError(null);
}
if (selector is InvocationSelector) {
// Notice that 'this' or 'super' can't be null. So we can ignore the
// value of [isNullAware].
if (isSuper) {
return _helper.buildSuperInvocation(
name,
selector.arguments,
offsetForToken(selector.token),
);
} else {
return _helper.buildMethodInvocation(
_forest.createThisExpression(fileOffset),
name,
selector.arguments,
offsetForToken(selector.token),
);
}
} else {
if (isSuper) {
Member? getter = _helper.lookupSuperMember(name);
Member? setter = _helper.lookupSuperMember(name, isSetter: true);
return new SuperPropertyAccessGenerator(
_helper,
// TODO(ahe): This is not the 'super' token.
selector.token,
name,
getter,
setter,
);
} else {
return new ThisPropertyAccessGenerator(
_helper,
// TODO(ahe): This is not the 'this' token.
selector.token,
name,
thisVariable: null,
thisOffset: fileOffset,
isNullAware: isNullAware,
);
}
}
}
@override
Expression_Generator_Initializer doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) {
if (isInitializer) {
return buildConstructorInitializer(offset, new Name(""), arguments);
} else if (isSuper) {
return _helper.buildSuperInvocation(
Name.callName,
arguments,
offset,
isImplicitCall: true,
);
} else {
return _helper.forest.createExpressionInvocation(
offset,
_forest.createThisExpression(fileOffset),
arguments,
);
}
}
@override
Expression_Generator buildEqualsOperation(
Token token,
Expression right, {
required bool isNot,
}) {
if (isSuper) {
int offset = offsetForToken(token);
Expression result = _helper.buildSuperInvocation(
equalsName,
_forest.createArguments(offset, <Expression>[right]),
offset,
);
if (isNot) {
result = _forest.createNot(offset, result);
}
return result;
}
// Coverage-ignore(suite): Not run.
return super.buildEqualsOperation(token, right, isNot: isNot);
}
@override
Expression_Generator buildBinaryOperation(
Token token,
Name binaryName,
Expression right,
) {
if (isSuper) {
int offset = offsetForToken(token);
return _helper.buildSuperInvocation(
binaryName,
_forest.createArguments(offset, <Expression>[right]),
offset,
);
}
// Coverage-ignore(suite): Not run.
return super.buildBinaryOperation(token, binaryName, right);
}
@override
Expression_Generator buildUnaryOperation(Token token, Name unaryName) {
if (isSuper) {
int offset = offsetForToken(token);
return _helper.buildSuperInvocation(
unaryName,
_forest.createArgumentsEmpty(offset),
offset,
);
}
// Coverage-ignore(suite): Not run.
return super.buildUnaryOperation(token, unaryName);
}
Expression_Initializer buildConstructorInitializer(
int offset,
Name name,
Arguments arguments,
) {
if (isSuper) {
MemberLookupResult? result = _helper.lookupSuperConstructor(
name.text,
_helper.libraryBuilder,
);
Constructor? constructor;
if (result != null) {
if (result.isInvalidLookup) {
return _helper.buildInvalidInitializer(
LookupResult.createDuplicateExpression(
result,
context: _helper.libraryBuilder.loader.target.context,
name: name.text,
fileUri: _helper.uri,
fileOffset: offset,
length: noLength,
),
offset,
);
}
MemberBuilder? memberBuilder = result.getable;
Member? member = memberBuilder?.invokeTarget;
// TODO(johnniwinther): Passing the library builder to
// `lookupSuperConstructor` doesn't correctly account for privacy
// checking when the target class is a mixin application. In this case
// the constructor name space can include private constructors from
// another library but the mixin application itself is the current
// library. Change `lookupSuperConstructor` to avoid this deficiency.
if (member is Constructor &&
member.name.libraryReference == name.libraryReference) {
constructor = member;
}
}
if (constructor == null) {
String fullName = _helper.superConstructorNameForDiagnostics(name.text);
LocatedMessage message = codeSuperclassHasNoConstructor
.withArguments(fullName)
.withLocation(_uri, fileOffset, lengthForToken(token));
return _helper.buildInvalidInitializer(
_helper.buildUnresolvedError(
_helper.superConstructorNameForDiagnostics(name.text),
offset,
arguments: arguments,
isSuper: true,
message: message,
kind: UnresolvedKind.Constructor,
),
offset,
);
} else {
return _helper.buildSuperInitializer(
false,
constructor,
arguments,
offset,
);
}
} else {
return _helper.buildRedirectingInitializer(
name,
arguments,
fileOffset: offset,
);
}
}
@override
// Coverage-ignore(suite): Not run.
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return buildAssignmentError();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
return buildAssignmentError();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
return buildAssignmentError();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildPrefixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return buildAssignmentError();
}
@override
// Coverage-ignore(suite): Not run.
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
return buildAssignmentError();
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
if (isSuper) {
return new SuperIndexedAccessGenerator(
_helper,
token,
index,
_helper.lookupSuperMember(indexGetName) as Procedure?,
_helper.lookupSuperMember(indexSetName) as Procedure?,
);
} else {
return new ThisIndexedAccessGenerator(
_helper,
token,
index,
thisOffset: fileOffset,
isNullAware: isNullAware,
);
}
}
// Coverage-ignore(suite): Not run.
Expression buildAssignmentError() {
return _helper.buildProblem(
isSuper ? codeCannotAssignToSuper : codeNotAnLvalue,
fileOffset,
token.length,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", isInitializer: ");
sink.write(isInitializer);
sink.write(", inFieldInitializer: ");
sink.write(inFieldInitializer);
sink.write(", inLateFieldInitializer: ");
sink.write(inLateFieldInitializer);
sink.write(", isSuper: ");
sink.write(isSuper);
}
}
class IncompleteErrorGenerator extends ErroneousExpressionGenerator {
final Message message;
IncompleteErrorGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.message,
) : super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _plainNameForRead => token.lexeme;
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "IncompleteErrorGenerator";
@override
Expression buildError({
Arguments? arguments,
Expression? rhs,
required UnresolvedKind kind,
String? name,
int? charOffset,
int? charLength,
bool errorHasBeenReported = false,
}) {
if (charOffset == null) {
charOffset = fileOffset;
charLength ??= lengthForToken(token);
}
charLength ??= noLength;
return _helper.buildProblem(
message,
charOffset,
charLength,
errorHasBeenReported: errorHasBeenReported,
);
}
@override
Generator doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
Arguments arguments, {
bool isTypeArgumentsInForest = false,
}) => this;
@override
Expression buildSimpleRead() {
return buildError(kind: UnresolvedKind.Member)..fileOffset = fileOffset;
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", message: ");
sink.write(message.code.name);
}
}
/// [ParenthesizedExpressionGenerator] represents the subexpression whose prefix
/// is a parenthesized expression.
///
/// For instance:
///
/// method(final a) {
/// final b = null;
/// (a); // this generator is created for `(a)`.
/// (a)[]; // this generator is created for `(a)`.
/// (b)(); // this generator is created for `(b)`.
/// (b).c = (a.d); // this generator is created for `(a.d)` and `(b)`.
/// }
///
// TODO(johnniwinther): Remove this in favor of [ParenthesizedExpression] when
// the [TypePromoter] is replaced by [FlowAnalysis].
class ParenthesizedExpressionGenerator extends AbstractReadOnlyAccessGenerator {
@override
final Expression expression;
ParenthesizedExpressionGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.expression,
) : super(helper, token, ReadOnlyAccessKind.ParenthesizedExpression);
@override
String get targetName => '';
@override
Expression buildSimpleRead() => _createRead();
@override
Expression _createRead() =>
_helper.forest.createParenthesized(expression.fileOffset, expression);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => "ParenthesizedExpressionGenerator";
@override
Expression_Generator buildSelectorAccess(
Selector selector,
int operatorOffset,
bool isNullAware,
) {
selector.reportNewAsSelector();
if (selector is InvocationSelector) {
return _helper.buildMethodInvocation(
_createRead(),
selector.name,
selector.arguments,
offsetForToken(selector.token),
isNullAware: isNullAware,
isConstantExpression: selector.isPotentiallyConstant,
);
} else {
if (_helper.constantContext != ConstantContext.none &&
// Coverage-ignore(suite): Not run.
selector.name != lengthName) {
// Coverage-ignore-block(suite): Not run.
_helper.addProblem(
codeNotAConstantExpression,
fileOffset,
token.length,
);
}
return PropertyAccessGenerator.make(
_helper,
selector.token,
_createRead(),
selector.name,
isNullAware,
);
}
}
}
int adjustForImplicitCall(String? name, int offset) {
// Normally the offset is at the start of the token, but in this case,
// because we insert a '.call', we want it at the end instead.
return offset + (name?.length ?? 0);
}
bool isFieldOrGetter(Member? member) {
return member is Field || (member is Procedure && member.isGetter);
}
/// A [Selector] is a part of an object access after `.` or `..` or `?.`,
/// including arguments, if present.
///
/// For instance, an [InvocationSelector] is created for `b()` in
///
/// a.b();
/// a..b();
/// a?.b();
///
/// and a [PropertySelector] is created for `b` in
///
/// a.b;
/// a.b = c;
/// a..b;
/// a..b = c;
/// a?.b;
/// a?.b = c;
///
abstract class Selector {
final ExpressionGeneratorHelper _helper;
final Token token;
Selector(this._helper, this.token);
int get fileOffset => offsetForToken(token);
Name get name;
/// Applies this selector to [receiver].
Expression_Generator withReceiver(
Object? receiver,
int operatorOffset, {
bool isNullAware = false,
});
List<TypeBuilder>? get typeArguments => null;
// Coverage-ignore(suite): Not run.
bool get isTypeArgumentsInForest => true;
Arguments? get arguments => null;
/// Internal name used for debugging.
String get _debugName;
void printOn(StringSink sink);
/// Report an error if the selector name "new" when the constructor-tearoff
/// feature is enabled.
void reportNewAsSelector() {
if (name.text == 'new' &&
_helper.libraryFeatures.constructorTearoffs.isEnabled) {
_helper.addProblem(codeNewAsSelector, fileOffset, name.text.length);
}
}
@override
String toString() {
StringBuffer buffer = new StringBuffer();
buffer.write(_debugName);
buffer.write("(offset: ");
buffer.write("${fileOffset}");
printOn(buffer);
buffer.write(")");
return "$buffer";
}
}
/// An [InvocationSelector] is the part of an object invocation after `.` or
/// `..` or `?.` including arguments.
///
/// For instance, an [InvocationSelector] is created for `b()` in
///
/// a.b();
/// a..b();
/// a?.b();
///
class InvocationSelector extends Selector {
@override
final Name name;
@override
final List<TypeBuilder>? typeArguments;
@override
final bool isTypeArgumentsInForest;
@override
final Arguments arguments;
final bool isPotentiallyConstant;
InvocationSelector(
ExpressionGeneratorHelper helper,
Token token,
this.name,
this.typeArguments,
this.arguments, {
this.isPotentiallyConstant = false,
this.isTypeArgumentsInForest = true,
}) : super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => 'InvocationSelector';
@override
Expression_Generator withReceiver(
Object? receiver,
int operatorOffset, {
bool isNullAware = false,
}) {
if (receiver is Generator) {
return receiver.buildSelectorAccess(this, operatorOffset, isNullAware);
}
reportNewAsSelector();
return _helper.buildMethodInvocation(
_helper.toValue(receiver),
name,
arguments,
fileOffset,
isNullAware: isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", name: ");
sink.write(name.text);
sink.write(", arguments: ");
printNodeOn(arguments, sink);
}
}
/// A [PropertySelector] is the part of an object access after `.` or `..` or
/// `?.`.
///
/// For instance a [PropertySelector] is created for `b` in
///
/// a.b;
/// a.b = c;
/// a..b;
/// a..b = c;
/// a?.b;
/// a?.b = c;
///
class PropertySelector extends Selector {
@override
final Name name;
PropertySelector(ExpressionGeneratorHelper helper, Token token, this.name)
: super(helper, token);
@override
// Coverage-ignore(suite): Not run.
String get _debugName => 'PropertySelector';
@override
Expression_Generator withReceiver(
Object? receiver,
int operatorOffset, {
bool isNullAware = false,
}) {
if (receiver is Generator) {
return receiver.buildSelectorAccess(this, operatorOffset, isNullAware);
}
reportNewAsSelector();
return PropertyAccessGenerator.make(
_helper,
token,
_helper.toValue(receiver),
name,
isNullAware,
);
}
@override
// Coverage-ignore(suite): Not run.
void printOn(StringSink sink) {
sink.write(", name: ");
sink.write(name.text);
}
}
// Coverage-ignore(suite): Not run.
class AugmentSuperAccessGenerator extends Generator {
final AugmentSuperTarget augmentSuperTarget;
AugmentSuperAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.augmentSuperTarget,
) : super(helper, token);
@override
String get _debugName => "AugmentSuperGenerator";
@override
String get _plainNameForRead {
return unsupported("augment super.plainNameForRead", fileOffset, _uri);
}
Expression _createRead() {
Member? readTarget = augmentSuperTarget.readTarget;
if (readTarget != null) {
return new AugmentSuperGet(readTarget, fileOffset: fileOffset);
} else {
return _helper.buildProblem(
codeNoAugmentSuperReadTarget,
fileOffset,
noLength,
);
}
}
@override
Expression buildAssignment(Expression value, {bool voidContext = false}) {
return _createWrite(fileOffset, value, forEffect: voidContext);
}
Expression _createWrite(
int offset,
Expression value, {
required bool forEffect,
}) {
Member? writeTarget = augmentSuperTarget.writeTarget;
if (writeTarget != null) {
return new AugmentSuperSet(
writeTarget,
value,
forEffect: forEffect,
fileOffset: fileOffset,
);
} else {
return _helper.buildProblem(
codeNoAugmentSuperWriteTarget,
offset,
noLength,
);
}
}
@override
Expression buildCompoundAssignment(
Name binaryOperator,
Expression value, {
required int operatorOffset,
bool voidContext = false,
bool isPreIncDec = false,
bool isPostIncDec = false,
}) {
// TODO(johnniwinther): Is this ever valid? Augment getters have no access
// to the augmented setter, augmenting setters have no access to the
// augmented getters, and augmenting fields only have read access to the
// augmented field initializer expression.
Expression binary = _helper.forest.createBinary(
operatorOffset,
_createRead(),
binaryOperator,
value,
);
return _createWrite(fileOffset, binary, forEffect: voidContext);
}
@override
Expression buildIfNullAssignment(
Expression value,
DartType type,
int offset, {
bool voidContext = false,
}) {
// TODO(johnniwinther): Is this ever valid? Augment getters have no access
// to the augmented setter, augmenting setters have no access to the
// augmented getters, and augmenting fields only have read access to the
// augmented field initializer expression.
return new IfNullSet(
_createRead(),
_createWrite(offset, value, forEffect: voidContext),
forEffect: voidContext,
)..fileOffset = offset;
}
@override
Generator buildIndexedAccess(
Expression index,
Token token, {
required bool isNullAware,
}) {
// TODO(johnniwinther): The semantics is unclear. Is this accessing the
// invoke target, which must be an `operator []` or the read target with a
// type that has an `operator []`.
throw new UnimplementedError();
}
@override
Expression buildPostfixIncrement(
Name binaryOperator, {
required int operatorOffset,
bool voidContext = false,
}) {
// TODO(johnniwinther): Is this ever valid? Augment getters have no access
// to the augmented setter, augmenting setters have no access to the
// augmented getters, and augmenting fields only have read access to the
// augmented field initializer expression.
throw new UnimplementedError();
}
@override
Expression buildSimpleRead() {
return _createRead();
}
@override
Expression_Generator_Initializer doInvocation(
int offset,
List<TypeBuilder>? typeArguments,
ArgumentsImpl arguments, {
bool isTypeArgumentsInForest = false,
}) {
Member? invokeTarget = augmentSuperTarget.invokeTarget;
if (invokeTarget != null) {
return new AugmentSuperInvocation(
invokeTarget,
arguments,
fileOffset: fileOffset,
);
} else {
return _helper.buildProblem(
codeNoAugmentSuperInvokeTarget,
offset,
noLength,
);
}
}
@override
void printOn(StringSink sink) {
sink.write(", augmentSuperTarget: ");
sink.write(augmentSuperTarget);
}
}