[cfe] Handle simple use of extension instance members
Change-Id: Ic1a949c0dca9327763e01458211e477de13c222f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/114504 Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
e9fdeb2db8
commit
a147d4e8d2
@@ -4,12 +4,6 @@
|
||||
|
||||
library fasta.procedure_builder;
|
||||
|
||||
// Note: we're deliberately using AsyncMarker and ProcedureKind from kernel
|
||||
// outside the kernel-specific builders. This is simpler than creating
|
||||
// additional enums.
|
||||
import 'package:kernel/ast.dart'
|
||||
show AsyncMarker, ProcedureKind, VariableDeclaration;
|
||||
|
||||
import 'package:kernel/type_algebra.dart' show containsTypeVariable, substitute;
|
||||
import 'package:kernel/type_algebra.dart';
|
||||
|
||||
@@ -30,6 +24,7 @@ import 'package:kernel/ast.dart'
|
||||
show
|
||||
Arguments,
|
||||
AsyncMarker,
|
||||
Block,
|
||||
Class,
|
||||
Constructor,
|
||||
ConstructorInvocation,
|
||||
@@ -45,6 +40,7 @@ import 'package:kernel/ast.dart'
|
||||
Procedure,
|
||||
ProcedureKind,
|
||||
RedirectingInitializer,
|
||||
ReturnStatement,
|
||||
Statement,
|
||||
StaticInvocation,
|
||||
StringLiteral,
|
||||
@@ -53,6 +49,7 @@ import 'package:kernel/ast.dart'
|
||||
TypeParameter,
|
||||
TypeParameterType,
|
||||
VariableDeclaration,
|
||||
VariableGet,
|
||||
setParents;
|
||||
|
||||
import '../../scanner/token.dart' show Token;
|
||||
@@ -235,7 +232,7 @@ abstract class FunctionBuilder extends MemberBuilder {
|
||||
|
||||
FunctionNode function;
|
||||
|
||||
Statement actualBody;
|
||||
Statement _body;
|
||||
|
||||
FunctionBuilder get actualOrigin;
|
||||
|
||||
@@ -247,7 +244,7 @@ abstract class FunctionBuilder extends MemberBuilder {
|
||||
// newBody.fileOffset, fileUri);
|
||||
// }
|
||||
// }
|
||||
actualBody = newBody;
|
||||
_body = newBody;
|
||||
if (function != null) {
|
||||
// A forwarding semi-stub is a method that is abstract in the source code,
|
||||
// but which needs to have a forwarding stub body in order to ensure that
|
||||
@@ -264,18 +261,18 @@ abstract class FunctionBuilder extends MemberBuilder {
|
||||
}
|
||||
|
||||
void setRedirectingFactoryBody(Member target, List<DartType> typeArguments) {
|
||||
if (actualBody != null) {
|
||||
unexpected("null", "${actualBody.runtimeType}", charOffset, fileUri);
|
||||
if (_body != null) {
|
||||
unexpected("null", "${_body.runtimeType}", charOffset, fileUri);
|
||||
}
|
||||
actualBody = new RedirectingFactoryBody(target, typeArguments);
|
||||
function.body = actualBody;
|
||||
actualBody?.parent = function;
|
||||
_body = new RedirectingFactoryBody(target, typeArguments);
|
||||
function.body = _body;
|
||||
_body?.parent = function;
|
||||
if (isPatch) {
|
||||
actualOrigin.setRedirectingFactoryBody(target, typeArguments);
|
||||
}
|
||||
}
|
||||
|
||||
Statement get body => actualBody ??= new EmptyStatement();
|
||||
Statement get body => _body ??= new EmptyStatement();
|
||||
|
||||
bool get isNative => nativeMethodName != null;
|
||||
|
||||
@@ -506,10 +503,63 @@ class ProcedureBuilder extends FunctionBuilder {
|
||||
AsyncMarker get asyncModifier => actualAsyncModifier;
|
||||
|
||||
Statement get body {
|
||||
if (actualBody == null && !isAbstract && !isExternal) {
|
||||
actualBody = new EmptyStatement();
|
||||
if (_body == null && !isAbstract && !isExternal) {
|
||||
_body = new EmptyStatement();
|
||||
}
|
||||
return actualBody;
|
||||
return _body;
|
||||
}
|
||||
|
||||
/// If this is an extension instance setter, wrap the setter body to return
|
||||
/// the rhs value from the method.
|
||||
///
|
||||
/// That is, this setter
|
||||
///
|
||||
/// extension E on A {
|
||||
/// void set property(B value) {
|
||||
/// value++;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// is converted into this top level method
|
||||
///
|
||||
/// B E|property(A #this, B value) {
|
||||
/// final #t1 = value;
|
||||
/// value++;
|
||||
/// return #t1;
|
||||
/// }
|
||||
///
|
||||
void _updateExtensionSetterBody() {
|
||||
if (isExtensionInstanceMember && isSetter) {
|
||||
// TODO(johnniwinther): Avoid the synthetic variable if the parameter is
|
||||
// never modified.
|
||||
VariableDeclaration value = procedure.function.positionalParameters[1];
|
||||
procedure.function.returnType = value.type;
|
||||
Statement body = procedure.function.body;
|
||||
List<Statement> statements = [];
|
||||
Block block = new Block(statements);
|
||||
VariableDeclaration variableDeclaration =
|
||||
new VariableDeclarationJudgment.forValue(
|
||||
new VariableGet(value)..fileOffset = procedure.fileOffset)
|
||||
..type = value.type;
|
||||
statements.add(variableDeclaration);
|
||||
if (body is Block) {
|
||||
statements.addAll(body.statements);
|
||||
} else {
|
||||
statements.add(body);
|
||||
}
|
||||
ReturnStatement returnStatement = new ReturnStatement(
|
||||
new VariableGet(variableDeclaration)
|
||||
..fileOffset = procedure.fileEndOffset);
|
||||
statements.add(returnStatement);
|
||||
setParents(block.statements, block);
|
||||
procedure.function.body = block;
|
||||
block.parent = procedure.function;
|
||||
}
|
||||
}
|
||||
|
||||
void set body(Statement newBody) {
|
||||
super.body = newBody;
|
||||
_updateExtensionSetterBody();
|
||||
}
|
||||
|
||||
void set asyncModifier(AsyncMarker newModifier) {
|
||||
@@ -896,12 +946,12 @@ class RedirectingFactoryBuilder extends ProcedureBuilder {
|
||||
nativeMethodName);
|
||||
|
||||
@override
|
||||
Statement get body => actualBody;
|
||||
Statement get body => _body;
|
||||
|
||||
@override
|
||||
void setRedirectingFactoryBody(Member target, List<DartType> typeArguments) {
|
||||
if (actualBody != null) {
|
||||
unexpected("null", "${actualBody.runtimeType}", charOffset, fileUri);
|
||||
if (_body != null) {
|
||||
unexpected("null", "${_body.runtimeType}", charOffset, fileUri);
|
||||
}
|
||||
|
||||
// Ensure that constant factories only have constant targets/bodies.
|
||||
@@ -910,9 +960,9 @@ class RedirectingFactoryBuilder extends ProcedureBuilder {
|
||||
noLength, fileUri);
|
||||
}
|
||||
|
||||
actualBody = new RedirectingFactoryBody(target, typeArguments);
|
||||
function.body = actualBody;
|
||||
actualBody?.parent = function;
|
||||
_body = new RedirectingFactoryBody(target, typeArguments);
|
||||
function.body = _body;
|
||||
_body?.parent = function;
|
||||
if (isPatch) {
|
||||
if (function.typeParameters != null) {
|
||||
Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{};
|
||||
|
||||
@@ -577,8 +577,8 @@ class InferenceVisitor
|
||||
// To replicate analyzer behavior, we base type inference on the write
|
||||
// member. TODO(paulberry): would it be better to use the read member
|
||||
// when doing compound assignment?
|
||||
FunctionType calleeType = inferrer.getCalleeFunctionType(
|
||||
inferrer.getCalleeType(writeTarget, receiverType), false);
|
||||
FunctionType calleeType =
|
||||
inferrer.getFunctionType(writeTarget, receiverType, false);
|
||||
DartType expectedIndexTypeForWrite;
|
||||
DartType indexContext = const UnknownType();
|
||||
DartType writeContext = const UnknownType();
|
||||
@@ -604,8 +604,8 @@ class InferenceVisitor
|
||||
if (read != null) {
|
||||
ObjectAccessTarget readMember = inferrer
|
||||
.findMethodInvocationMember(receiverType, read, instrumented: false);
|
||||
FunctionType calleeFunctionType = inferrer.getCalleeFunctionType(
|
||||
inferrer.getCalleeType(readMember, receiverType), false);
|
||||
FunctionType calleeFunctionType =
|
||||
inferrer.getFunctionType(readMember, receiverType, false);
|
||||
inferrer.ensureAssignable(
|
||||
getPositionalParameterType(calleeFunctionType, 0),
|
||||
indexType,
|
||||
@@ -1681,7 +1681,7 @@ class InferenceVisitor
|
||||
if (node.read != null) {
|
||||
ObjectAccessTarget readTarget = inferrer
|
||||
.findPropertyGetMember(receiverType, node.read, instrumented: false);
|
||||
readType = inferrer.getCalleeType(readTarget, receiverType);
|
||||
readType = inferrer.getGetterType(readTarget, receiverType);
|
||||
inferrer.handlePropertyGetContravariance(
|
||||
node.receiver,
|
||||
readTarget,
|
||||
@@ -1702,8 +1702,21 @@ class InferenceVisitor
|
||||
DartType inferredType =
|
||||
node._inferRhs(inferrer, readType, writeContext).inferredType;
|
||||
node.nullAwareGuard?.staticType = node.inferredType;
|
||||
node._replaceWithDesugared();
|
||||
return new ExpressionInferenceResult(inferredType);
|
||||
Expression replacement;
|
||||
if (writeTarget.isExtensionMember) {
|
||||
node.parent.replaceChild(
|
||||
node,
|
||||
replacement = inferrer.helper.forest.createStaticInvocation(
|
||||
node.fileOffset,
|
||||
writeTarget.member,
|
||||
inferrer.helper.forest.createArguments(
|
||||
node.fileOffset, [node.receiver, node.rhs])));
|
||||
inferrer.storeInferredType(replacement, inferredType);
|
||||
} else {
|
||||
node._replaceWithDesugared();
|
||||
}
|
||||
|
||||
return new ExpressionInferenceResult(inferredType, replacement);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1711,7 +1724,7 @@ class InferenceVisitor
|
||||
PropertyGet node, DartType typeContext) {
|
||||
return inferrer.inferPropertyGet(
|
||||
node, node.receiver, node.fileOffset, typeContext,
|
||||
desugaredGet: node);
|
||||
desugaredGet: node, allowExtensionMethods: true);
|
||||
}
|
||||
|
||||
void visitRedirectingInitializerJudgment(
|
||||
|
||||
@@ -293,7 +293,7 @@ abstract class ComplexAssignmentJudgment extends SyntheticExpressionJudgment {
|
||||
Expression read;
|
||||
|
||||
/// The expression appearing on the RHS of the assignment.
|
||||
final Expression rhs;
|
||||
Expression rhs;
|
||||
|
||||
/// The expression that performs the write (e.g. `a.[]=(b, a.[](b) + 1)` in
|
||||
/// `++a[b]`).
|
||||
@@ -367,8 +367,8 @@ abstract class ComplexAssignmentJudgment extends SyntheticExpressionJudgment {
|
||||
combinerTarget.member, readType);
|
||||
}
|
||||
DartType rhsType;
|
||||
FunctionType combinerType = inferrer.getCalleeFunctionType(
|
||||
inferrer.getCalleeType(combinerTarget, readType), false);
|
||||
FunctionType combinerType =
|
||||
inferrer.getFunctionType(combinerTarget, readType, false);
|
||||
if (isPreIncDec || isPostIncDec) {
|
||||
rhsType = inferrer.coreTypes.intClass.rawType;
|
||||
} else {
|
||||
@@ -378,8 +378,12 @@ abstract class ComplexAssignmentJudgment extends SyntheticExpressionJudgment {
|
||||
assert(identical(combiner.arguments.positional.first, rhs));
|
||||
// Analyzer uses a null context for the RHS here.
|
||||
// TODO(paulberry): improve on this.
|
||||
inferrer.inferExpression(rhs, const UnknownType(), true);
|
||||
rhsType = getInferredType(rhs, inferrer);
|
||||
ExpressionInferenceResult rhsResult =
|
||||
inferrer.inferExpression(rhs, const UnknownType(), true);
|
||||
if (rhsResult.replacement != null) {
|
||||
rhs = rhsResult.replacement;
|
||||
}
|
||||
rhsType = rhsResult.inferredType;
|
||||
// Do not use rhs after this point because it may be a Shadow node
|
||||
// that has been replaced in the tree with its desugaring.
|
||||
DartType expectedType = getPositionalParameterType(combinerType, 0);
|
||||
@@ -413,6 +417,9 @@ abstract class ComplexAssignmentJudgment extends SyntheticExpressionJudgment {
|
||||
ExpressionInferenceResult rhsResult = inferrer.inferExpression(
|
||||
rhs, writeContext ?? const UnknownType(), true,
|
||||
isVoidAllowed: true);
|
||||
if (rhsResult.replacement != null) {
|
||||
rhs = rhsResult.replacement;
|
||||
}
|
||||
DartType rhsType = rhsResult.inferredType;
|
||||
Expression replacedRhs = inferrer.ensureAssignable(
|
||||
writeContext, rhsType, rhs, writeOffset,
|
||||
|
||||
@@ -8,11 +8,15 @@ import 'package:kernel/core_types.dart' show CoreTypes;
|
||||
|
||||
import '../fasta_codes.dart' show LocatedMessage, Message;
|
||||
|
||||
import '../kernel/forest.dart';
|
||||
|
||||
abstract class InferenceHelper {
|
||||
CoreTypes get coreTypes;
|
||||
|
||||
Uri get uri;
|
||||
|
||||
Forest get forest;
|
||||
|
||||
set transformSetLiterals(bool value);
|
||||
|
||||
Expression buildProblem(Message message, int charOffset, int length,
|
||||
|
||||
@@ -67,6 +67,10 @@ import '../../base/instrumentation.dart'
|
||||
InstrumentationValueForType,
|
||||
InstrumentationValueForTypeArgs;
|
||||
|
||||
import '../builder/extension_builder.dart';
|
||||
import '../builder/member_builder.dart';
|
||||
import '../builder/procedure_builder.dart';
|
||||
|
||||
import '../fasta_codes.dart'
|
||||
show
|
||||
LocatedMessage,
|
||||
@@ -649,7 +653,7 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
PropertyGet tearOff =
|
||||
new PropertyGet(new VariableGet(t), callName, callMember)
|
||||
..fileOffset = fileOffset;
|
||||
actualType = getCalleeTypeForMemberTarget(callMember, actualType);
|
||||
actualType = getGetterTypeForMemberTarget(callMember, actualType);
|
||||
ConditionalExpression conditional = new ConditionalExpression(
|
||||
nullCheck,
|
||||
new NullLiteral()..fileOffset = fileOffset,
|
||||
@@ -743,7 +747,8 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
Expression expression,
|
||||
Expression receiver,
|
||||
bool setter: false,
|
||||
bool instrumented: true}) {
|
||||
bool instrumented: true,
|
||||
bool includeExtensionMethods: false}) {
|
||||
assert(receiverType != null && isKnown(receiverType));
|
||||
|
||||
receiverType = resolveTypeParameter(receiverType);
|
||||
@@ -757,15 +762,39 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
: coreTypes.objectClass;
|
||||
Member interfaceMember =
|
||||
_getInterfaceMember(classNode, name, setter, fileOffset);
|
||||
ObjectAccessTarget target = interfaceMember != null
|
||||
? new ObjectAccessTarget.interfaceMember(interfaceMember)
|
||||
: const ObjectAccessTarget.unresolved();
|
||||
if (instrumented &&
|
||||
receiverType != const DynamicType() &&
|
||||
interfaceMember != null) {
|
||||
target.isInstanceMember) {
|
||||
instrumentation?.record(uri, fileOffset, 'target',
|
||||
new InstrumentationValueForMember(interfaceMember));
|
||||
new InstrumentationValueForMember(target.member));
|
||||
}
|
||||
|
||||
if (target.isUnresolved && includeExtensionMethods) {
|
||||
// TODO(johnniwinther): Find the most specific method.
|
||||
library.scope.forEachExtension((ExtensionBuilder extensionBuilder) {
|
||||
MemberBuilder memberBuilder =
|
||||
extensionBuilder.lookupLocalMember(name.name, setter: setter);
|
||||
if (memberBuilder != null && !memberBuilder.isStatic) {
|
||||
ProcedureBuilder procedureBuilder = memberBuilder;
|
||||
if (procedureBuilder != null) {
|
||||
DartType onType = extensionBuilder.extension.onType;
|
||||
// TODO(johnniwinther): Handle generic extensions.
|
||||
if (typeSchemaEnvironment.isSubtypeOf(receiverType, onType)) {
|
||||
target = new ObjectAccessTarget.extensionMember(
|
||||
procedureBuilder.target,
|
||||
procedureBuilder.kind,
|
||||
extensionBuilder.typeParameters?.length ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!isTopLevel &&
|
||||
interfaceMember == null &&
|
||||
target.isUnresolved &&
|
||||
receiverType is! DynamicType &&
|
||||
receiverType is! InvalidType &&
|
||||
!(receiverType == coreTypes.functionClass.rawType &&
|
||||
@@ -783,9 +812,7 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
fileOffset,
|
||||
length)));
|
||||
}
|
||||
return interfaceMember != null
|
||||
? new ObjectAccessTarget.interfaceMember(interfaceMember)
|
||||
: const ObjectAccessTarget.unresolved();
|
||||
return target;
|
||||
}
|
||||
|
||||
/// Finds a member of [receiverType] called [name] and records it in
|
||||
@@ -801,7 +828,8 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
errorTemplate: templateUndefinedMethod,
|
||||
expression: methodInvocation,
|
||||
receiver: methodInvocation.receiver,
|
||||
instrumented: instrumented);
|
||||
instrumented: instrumented,
|
||||
includeExtensionMethods: true);
|
||||
if (interfaceTarget.isInstanceMember) {
|
||||
Member interfaceMember = interfaceTarget.member;
|
||||
if (receiverType == const DynamicType() &&
|
||||
@@ -893,7 +921,8 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
expression: propertySet,
|
||||
receiver: propertySet.receiver,
|
||||
setter: true,
|
||||
instrumented: instrumented);
|
||||
instrumented: instrumented,
|
||||
includeExtensionMethods: true);
|
||||
if (writeTarget.isInstanceMember) {
|
||||
if (instrumented &&
|
||||
instrumentation != null &&
|
||||
@@ -919,53 +948,72 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
}
|
||||
}
|
||||
|
||||
FunctionType getCalleeFunctionType(DartType calleeType, bool followCall) {
|
||||
if (calleeType is FunctionType) {
|
||||
return calleeType;
|
||||
} else if (followCall && calleeType is InterfaceType) {
|
||||
Member member =
|
||||
_getInterfaceMember(calleeType.classNode, callName, false, -1);
|
||||
DartType callType = getCalleeTypeForMemberTarget(member, calleeType);
|
||||
if (callType is FunctionType) {
|
||||
return callType;
|
||||
}
|
||||
}
|
||||
return unknownFunction;
|
||||
}
|
||||
|
||||
DartType getCalleeType(ObjectAccessTarget target, DartType receiverType) {
|
||||
/// Returns the type of [target] when accessed as a getter on [receiverType].
|
||||
///
|
||||
/// For instance
|
||||
///
|
||||
/// class Class<T> {
|
||||
/// T method() {}
|
||||
/// T getter => null;
|
||||
/// }
|
||||
///
|
||||
/// Class<int> c = ...
|
||||
/// c.method; // The getter type is `int Function()`.
|
||||
/// c.getter; // The getter type is `int`.
|
||||
///
|
||||
DartType getGetterType(ObjectAccessTarget target, DartType receiverType) {
|
||||
switch (target.kind) {
|
||||
case ObjectAccessTargetKind.callFunction:
|
||||
return receiverType;
|
||||
case ObjectAccessTargetKind.unresolved:
|
||||
return const DynamicType();
|
||||
case ObjectAccessTargetKind.instanceMember:
|
||||
return getCalleeTypeForMemberTarget(target.member, receiverType);
|
||||
default:
|
||||
throw unhandled(
|
||||
target.runtimeType.toString(), 'getCalleeType', null, null);
|
||||
return getGetterTypeForMemberTarget(target.member, receiverType);
|
||||
case ObjectAccessTargetKind.extensionMember:
|
||||
switch (target.extensionMethodKind) {
|
||||
case ProcedureKind.Method:
|
||||
FunctionType functionType = target.member.function.functionType;
|
||||
// TODO(johnniwinther): Handle tear off of generic extensions.
|
||||
return new FunctionType(
|
||||
functionType.positionalParameters.skip(1).toList(),
|
||||
functionType.returnType,
|
||||
namedParameters: functionType.namedParameters,
|
||||
typeParameters: functionType.typeParameters
|
||||
.skip(target.extensionTypeParameterCount)
|
||||
.toList(),
|
||||
requiredParameterCount:
|
||||
functionType.requiredParameterCount - 1);
|
||||
case ProcedureKind.Getter:
|
||||
return target.member.function.returnType;
|
||||
case ProcedureKind.Setter:
|
||||
case ProcedureKind.Factory:
|
||||
case ProcedureKind.Operator:
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw unhandled('$target', 'getGetterType', null, null);
|
||||
}
|
||||
|
||||
DartType getCalleeTypeForMemberTarget(
|
||||
/// Returns the getter type of [member] on a receiver of type [receiverType].
|
||||
///
|
||||
/// For instance
|
||||
///
|
||||
/// class Class<T> {
|
||||
/// T method() {}
|
||||
/// T getter => null;
|
||||
/// }
|
||||
///
|
||||
/// Class<int> c = ...
|
||||
/// c.method; // The getter type is `int Function()`.
|
||||
/// c.getter; // The getter type is `int`.
|
||||
///
|
||||
DartType getGetterTypeForMemberTarget(
|
||||
Member interfaceMember, DartType receiverType) {
|
||||
if (interfaceMember == null) {
|
||||
return const DynamicType();
|
||||
}
|
||||
Class memberClass = interfaceMember.enclosingClass;
|
||||
DartType calleeType;
|
||||
if (interfaceMember is Procedure) {
|
||||
if (interfaceMember.kind == ProcedureKind.Getter) {
|
||||
calleeType = interfaceMember.function.returnType;
|
||||
} else {
|
||||
calleeType = interfaceMember.function.functionType;
|
||||
}
|
||||
} else if (interfaceMember is Field) {
|
||||
calleeType = interfaceMember.type;
|
||||
} else {
|
||||
throw unhandled(
|
||||
interfaceMember.runtimeType.toString(), 'getCalleeType', null, null);
|
||||
}
|
||||
assert(
|
||||
interfaceMember is kernel.Field || interfaceMember is kernel.Procedure,
|
||||
"Unexpected interface member $interfaceMember.");
|
||||
DartType calleeType = interfaceMember.getterType;
|
||||
if (memberClass.typeParameters.isNotEmpty) {
|
||||
receiverType = resolveTypeParameter(receiverType);
|
||||
if (receiverType is InterfaceType) {
|
||||
@@ -978,6 +1026,67 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
return calleeType;
|
||||
}
|
||||
|
||||
/// Returns the type of [target] when accessed as an invocation on
|
||||
/// [receiverType].
|
||||
///
|
||||
/// If the target is known not to be invokable [unknownFunction] is returned.
|
||||
///
|
||||
/// For instance
|
||||
///
|
||||
/// class Class<T> {
|
||||
/// T method() {}
|
||||
/// T Function() getter1 => null;
|
||||
/// T getter2 => null;
|
||||
/// }
|
||||
///
|
||||
/// Class<int> c = ...
|
||||
/// c.method; // The getter type is `int Function()`.
|
||||
/// c.getter1; // The getter type is `int Function()`.
|
||||
/// c.getter2; // The getter type is [unknownFunction].
|
||||
///
|
||||
FunctionType getFunctionType(
|
||||
ObjectAccessTarget target, DartType receiverType, bool followCall) {
|
||||
switch (target.kind) {
|
||||
case ObjectAccessTargetKind.callFunction:
|
||||
return _getFunctionType(receiverType, followCall);
|
||||
case ObjectAccessTargetKind.unresolved:
|
||||
return unknownFunction;
|
||||
case ObjectAccessTargetKind.instanceMember:
|
||||
return _getFunctionType(
|
||||
getGetterTypeForMemberTarget(target.member, receiverType),
|
||||
followCall);
|
||||
case ObjectAccessTargetKind.extensionMember:
|
||||
switch (target.extensionMethodKind) {
|
||||
case ProcedureKind.Method:
|
||||
return target.member.function.functionType;
|
||||
case ProcedureKind.Getter:
|
||||
// TODO(johnniwinther): Handle implicit .call on extension getter.
|
||||
return _getFunctionType(target.member.function.returnType, false);
|
||||
case ProcedureKind.Setter:
|
||||
case ProcedureKind.Factory:
|
||||
case ProcedureKind.Operator:
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw unhandled('$target', 'getFunctionType', null, null);
|
||||
}
|
||||
|
||||
FunctionType _getFunctionType(DartType calleeType, bool followCall) {
|
||||
if (calleeType is FunctionType) {
|
||||
return calleeType;
|
||||
} else if (followCall && calleeType is InterfaceType) {
|
||||
Member member =
|
||||
_getInterfaceMember(calleeType.classNode, callName, false, -1);
|
||||
if (member != null) {
|
||||
DartType callType = getGetterTypeForMemberTarget(member, calleeType);
|
||||
if (callType is FunctionType) {
|
||||
return callType;
|
||||
}
|
||||
}
|
||||
}
|
||||
return unknownFunction;
|
||||
}
|
||||
|
||||
DartType getDerivedTypeArgumentOf(DartType type, Class class_) {
|
||||
if (type is InterfaceType) {
|
||||
InterfaceType typeAsInstanceOfClass =
|
||||
@@ -1033,11 +1142,22 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
}
|
||||
}
|
||||
return setterType;
|
||||
case ObjectAccessTargetKind.extensionMember:
|
||||
switch (target.extensionMethodKind) {
|
||||
case ProcedureKind.Setter:
|
||||
// TODO(johnniwinther): Is this valid if the method is generic?
|
||||
return target.member.function.positionalParameters[1].type;
|
||||
case ProcedureKind.Method:
|
||||
case ProcedureKind.Getter:
|
||||
case ProcedureKind.Factory:
|
||||
case ProcedureKind.Operator:
|
||||
break;
|
||||
}
|
||||
// TODO(johnniwinther): Compute the right setter type.
|
||||
return const DynamicType();
|
||||
case ObjectAccessTargetKind.callFunction:
|
||||
default:
|
||||
throw unhandled(
|
||||
target.runtimeType.toString(), 'getSetterType', null, null);
|
||||
}
|
||||
throw unhandled(target.runtimeType.toString(), 'getSetterType', null, null);
|
||||
}
|
||||
|
||||
DartType getTypeArgumentOf(DartType type, Class class_) {
|
||||
@@ -1580,9 +1700,9 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
target.member is Procedure &&
|
||||
typeSchemaEnvironment.isOverloadedArithmeticOperatorAndType(
|
||||
target.member, receiverType);
|
||||
DartType calleeType = getCalleeType(target, receiverType);
|
||||
DartType calleeType = getGetterType(target, receiverType);
|
||||
FunctionType functionType =
|
||||
getCalleeFunctionType(calleeType, !isImplicitCall);
|
||||
getFunctionType(target, receiverType, !isImplicitCall);
|
||||
|
||||
if (!target.isUnresolved &&
|
||||
calleeType is! DynamicType &&
|
||||
@@ -1601,6 +1721,18 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
desugaredInvocation,
|
||||
arguments,
|
||||
expression);
|
||||
Expression replacement;
|
||||
if (target.isExtensionMember) {
|
||||
expression.parent.replaceChild(
|
||||
expression,
|
||||
replacement = helper.forest.createStaticInvocation(
|
||||
expression.fileOffset,
|
||||
target.member,
|
||||
arguments = helper.forest.createArguments(
|
||||
arguments.fileOffset, [receiver, ...arguments.positional],
|
||||
named: arguments.named,
|
||||
types: getExplicitTypeArguments(arguments))));
|
||||
}
|
||||
DartType inferredType = inferInvocation(typeContext, fileOffset,
|
||||
functionType, functionType.returnType, arguments,
|
||||
isOverloadedArithmeticOperator: isOverloadedArithmeticOperator,
|
||||
@@ -1659,7 +1791,7 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
inferred: getExplicitTypeArguments(arguments) == null);
|
||||
}
|
||||
|
||||
return new ExpressionInferenceResult(inferredType);
|
||||
return new ExpressionInferenceResult(inferredType, replacement);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1682,7 +1814,8 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
{VariableDeclaration receiverVariable,
|
||||
PropertyGet desugaredGet,
|
||||
ObjectAccessTarget readTarget,
|
||||
Name propertyName}) {
|
||||
Name propertyName,
|
||||
bool allowExtensionMethods: false}) {
|
||||
// First infer the receiver so we can look up the getter that was invoked.
|
||||
DartType receiverType;
|
||||
if (receiver == null) {
|
||||
@@ -1698,7 +1831,8 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
readTarget = findInterfaceMember(receiverType, propertyName, fileOffset,
|
||||
errorTemplate: templateUndefinedGetter,
|
||||
expression: expression,
|
||||
receiver: receiver);
|
||||
receiver: receiver,
|
||||
includeExtensionMethods: allowExtensionMethods);
|
||||
if (readTarget.isInstanceMember) {
|
||||
if (instrumentation != null && receiverType == const DynamicType()) {
|
||||
instrumentation.record(uri, desugaredGet.fileOffset, 'target',
|
||||
@@ -1707,18 +1841,38 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
desugaredGet.interfaceTarget = readTarget.member;
|
||||
}
|
||||
}
|
||||
DartType inferredType = getCalleeType(readTarget, receiverType);
|
||||
DartType inferredType = getGetterType(readTarget, receiverType);
|
||||
Expression replacedExpression = handlePropertyGetContravariance(receiver,
|
||||
readTarget, desugaredGet, expression, inferredType, fileOffset);
|
||||
Expression replacement;
|
||||
if (readTarget.isInstanceMember) {
|
||||
Member member = readTarget.member;
|
||||
if (member is Procedure && member.kind == ProcedureKind.Method) {
|
||||
inferredType =
|
||||
instantiateTearOff(inferredType, typeContext, replacedExpression);
|
||||
}
|
||||
} else if (readTarget.isExtensionMember) {
|
||||
switch (readTarget.extensionMethodKind) {
|
||||
case kernel.ProcedureKind.Getter:
|
||||
expression.parent.replaceChild(
|
||||
expression,
|
||||
replacement = expression = helper.forest.createStaticInvocation(
|
||||
expression.fileOffset,
|
||||
readTarget.member,
|
||||
helper.forest
|
||||
.createArguments(expression.fileOffset, [receiver])));
|
||||
break;
|
||||
case kernel.ProcedureKind.Method:
|
||||
// TODO(johnniwinther): Handle extension method tearoff.
|
||||
case kernel.ProcedureKind.Setter:
|
||||
case kernel.ProcedureKind.Factory:
|
||||
case kernel.ProcedureKind.Operator:
|
||||
unhandled('$readTarget', "inferPropertyGet", fileOffset, uri);
|
||||
break;
|
||||
}
|
||||
}
|
||||
storeInferredType(expression, inferredType);
|
||||
return new ExpressionInferenceResult(inferredType);
|
||||
return new ExpressionInferenceResult(inferredType, replacement);
|
||||
}
|
||||
|
||||
/// Modifies a type as appropriate when inferring a closure return type.
|
||||
@@ -1800,7 +1954,7 @@ abstract class TypeInferrerImpl extends TypeInferrer {
|
||||
if (interfaceMember is Field ||
|
||||
interfaceMember is Procedure &&
|
||||
interfaceMember.kind == ProcedureKind.Getter) {
|
||||
DartType getType = getCalleeType(target, receiverType);
|
||||
DartType getType = getGetterType(target, receiverType);
|
||||
if (getType is DynamicType) {
|
||||
return MethodContravarianceCheckKind.none;
|
||||
}
|
||||
@@ -2121,6 +2275,7 @@ enum ObjectAccessTargetKind {
|
||||
instanceMember,
|
||||
callFunction,
|
||||
unresolved,
|
||||
extensionMember,
|
||||
}
|
||||
|
||||
/// Result for performing an access on an object, like `o.foo`, `o.foo()` and
|
||||
@@ -2138,6 +2293,11 @@ class ObjectAccessTarget {
|
||||
ObjectAccessTargetKind.instanceMember, member);
|
||||
}
|
||||
|
||||
/// Creates an access to the extension [member].
|
||||
factory ObjectAccessTarget.extensionMember(
|
||||
Member member, ProcedureKind kind, int typeParameters) =
|
||||
ExtensionAccessTarget;
|
||||
|
||||
/// Creates an access to a 'call' method on a function, i.e. a function
|
||||
/// invocation.
|
||||
const ObjectAccessTarget.callFunction()
|
||||
@@ -2153,11 +2313,42 @@ class ObjectAccessTarget {
|
||||
/// Returns `true` if this is an access to an instance member.
|
||||
bool get isInstanceMember => kind == ObjectAccessTargetKind.instanceMember;
|
||||
|
||||
/// Returns `true` if this is an access to an extension member.
|
||||
bool get isExtensionMember => kind == ObjectAccessTargetKind.extensionMember;
|
||||
|
||||
/// Returns `true` if this is an access to the 'call' method on a function.
|
||||
bool get isCallFunction => kind == ObjectAccessTargetKind.callFunction;
|
||||
|
||||
/// Returns `true` if this is an access without a known target.
|
||||
bool get isUnresolved => kind == ObjectAccessTargetKind.unresolved;
|
||||
|
||||
/// Returns the original procedure kind, if this is an extension method
|
||||
/// target.
|
||||
///
|
||||
/// This is need because getters, setters, and methods are converted into
|
||||
/// top level methods, but access and invocation should still be treated as
|
||||
/// if they are the original procedure kind.
|
||||
ProcedureKind get extensionMethodKind =>
|
||||
throw new UnsupportedError('ObjectAccessTarget.extensionMethodKind');
|
||||
|
||||
/// Returns the number of type parameters of an extension method that comes
|
||||
/// from the extension declaration.
|
||||
int get extensionTypeParameterCount => throw new UnsupportedError(
|
||||
'ObjectAccessTarget.extensionTypeParameterCount');
|
||||
|
||||
@override
|
||||
String toString() => 'ObjectAccessTarget($kind,$member)';
|
||||
}
|
||||
|
||||
class ExtensionAccessTarget extends ObjectAccessTarget {
|
||||
final ProcedureKind extensionMethodKind;
|
||||
final int extensionTypeParameterCount;
|
||||
|
||||
ExtensionAccessTarget(
|
||||
Member member, this.extensionMethodKind, this.extensionTypeParameterCount)
|
||||
: super.internal(ObjectAccessTargetKind.extensionMember, member);
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'ExtensionAccessTarget($kind,$member,$extensionMethodKind)';
|
||||
}
|
||||
|
||||
@@ -1412,6 +1412,7 @@ investigate
|
||||
invocation
|
||||
invocation's
|
||||
invocations
|
||||
invokable
|
||||
invoke
|
||||
invoked
|
||||
invokes
|
||||
|
||||
@@ -72,8 +72,10 @@ static method Extension|get#tearOffGetterNamed(final self::Class* #this) → dyn
|
||||
return ({dynamic value = #C1}) → dynamic => self::Extension|writeSetterNamed(#this, value: value);
|
||||
static method Extension|get#property(final self::Class* #this) → dynamic
|
||||
return #this.{self::Class::field};
|
||||
static method Extension|set#property(final self::Class* #this, dynamic value) → void {
|
||||
static method Extension|set#property(final self::Class* #this, dynamic value) → dynamic {
|
||||
final dynamic #t1 = value;
|
||||
#this.{self::Class::field} = value;
|
||||
return #t1;
|
||||
}
|
||||
static method Extension|invocations(final self::Class* #this, dynamic value) → dynamic {
|
||||
self::Extension|readGetter(#this);
|
||||
@@ -126,8 +128,10 @@ static method GenericExtension|genericWriteSetterNamed<#T extends core::Object*
|
||||
}
|
||||
static method GenericExtension|get#property<#T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|get#property::#T*>* #this) → self::GenericExtension|get#property::#T*
|
||||
return #this.{self::GenericClass::field};
|
||||
static method GenericExtension|set#property<#T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|set#property::#T*>* #this, self::GenericExtension|set#property::#T* value) → void {
|
||||
static method GenericExtension|set#property<#T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|set#property::#T*>* #this, self::GenericExtension|set#property::#T* value) → self::GenericExtension|set#property::#T* {
|
||||
final self::GenericExtension|set#property::#T* #t2 = value;
|
||||
#this.{self::GenericClass::field} = value;
|
||||
return #t2;
|
||||
}
|
||||
static method GenericExtension|get#tearOffGetterNoArgs<#T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|get#tearOffGetterNoArgs::#T*>* #this) → dynamic
|
||||
return () → self::GenericExtension|get#tearOffGetterNoArgs::#T* => self::GenericExtension|readGetter<self::GenericExtension|get#tearOffGetterNoArgs::#T*>(#this);
|
||||
|
||||
+6
-2
@@ -72,8 +72,10 @@ static method Extension|get#tearOffGetterNamed(final self::Class* #this) → dyn
|
||||
return ({dynamic value = #C1}) → dynamic => self::Extension|writeSetterNamed(#this, value: value);
|
||||
static method Extension|get#property(final self::Class* #this) → dynamic
|
||||
return #this.{self::Class::field};
|
||||
static method Extension|set#property(final self::Class* #this, dynamic value) → void {
|
||||
static method Extension|set#property(final self::Class* #this, dynamic value) → dynamic {
|
||||
final dynamic #t1 = value;
|
||||
#this.{self::Class::field} = value;
|
||||
return #t1;
|
||||
}
|
||||
static method Extension|invocations(final self::Class* #this, dynamic value) → dynamic {
|
||||
self::Extension|readGetter(#this);
|
||||
@@ -126,8 +128,10 @@ static method GenericExtension|genericWriteSetterNamed<#T extends core::Object*
|
||||
}
|
||||
static method GenericExtension|get#property<#T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|get#property::#T*>* #this) → self::GenericExtension|get#property::#T*
|
||||
return #this.{self::GenericClass::field};
|
||||
static method GenericExtension|set#property<#T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|set#property::#T*>* #this, self::GenericExtension|set#property::#T* value) → void {
|
||||
static method GenericExtension|set#property<#T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|set#property::#T*>* #this, self::GenericExtension|set#property::#T* value) → self::GenericExtension|set#property::#T* {
|
||||
final self::GenericExtension|set#property::#T* #t2 = value;
|
||||
#this.{self::GenericClass::field} = value;
|
||||
return #t2;
|
||||
}
|
||||
static method GenericExtension|get#tearOffGetterNoArgs<#T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|get#tearOffGetterNoArgs::#T*>* #this) → dynamic
|
||||
return () → self::GenericExtension|get#tearOffGetterNoArgs::#T* => self::GenericExtension|readGetter<self::GenericExtension|get#tearOffGetterNoArgs::#T*>(#this);
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
library;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/extension_methods.dart:19:26: Error: The getter 'two' isn't defined for the class 'C'.
|
||||
// - 'C' is from 'pkg/front_end/testcases/extensions/extension_methods.dart'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'two'.
|
||||
// var result = c.one + c.two;
|
||||
// ^^^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "package:expect/expect.dart" as exp;
|
||||
@@ -28,10 +19,6 @@ static method E|get#two(final self::C* #this) → core::int*
|
||||
return 2;
|
||||
static method main() → dynamic {
|
||||
self::C* c = new self::C::•();
|
||||
core::num* result = c.{self::C::one}.{core::num::+}(invalid-expression "pkg/front_end/testcases/extensions/extension_methods.dart:19:26: Error: The getter 'two' isn't defined for the class 'C'.
|
||||
- 'C' is from 'pkg/front_end/testcases/extensions/extension_methods.dart'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'two'.
|
||||
var result = c.one + c.two;
|
||||
^^^" as{TypeError} core::num*);
|
||||
core::int* result = c.{self::C::one}.{core::num::+}(self::E|get#two(c));
|
||||
exp::Expect::equals(result, 3);
|
||||
}
|
||||
|
||||
+1
-14
@@ -1,13 +1,4 @@
|
||||
library;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/extension_methods.dart:19:26: Error: The getter 'two' isn't defined for the class 'C'.
|
||||
// - 'C' is from 'pkg/front_end/testcases/extensions/extension_methods.dart'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'two'.
|
||||
// var result = c.one + c.two;
|
||||
// ^^^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "package:expect/expect.dart" as exp;
|
||||
@@ -28,10 +19,6 @@ static method E|get#two(final self::C* #this) → core::int*
|
||||
return 2;
|
||||
static method main() → dynamic {
|
||||
self::C* c = new self::C::•();
|
||||
core::num* result = c.{self::C::one}.{core::num::+}(invalid-expression "pkg/front_end/testcases/extensions/extension_methods.dart:19:26: Error: The getter 'two' isn't defined for the class 'C'.
|
||||
- 'C' is from 'pkg/front_end/testcases/extensions/extension_methods.dart'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'two'.
|
||||
var result = c.one + c.two;
|
||||
^^^" as{TypeError} core::num*);
|
||||
core::int* result = c.{self::C::one}.{core::num::+}(self::E|get#two(c));
|
||||
exp::Expect::equals(result, 3);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2019, 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.
|
||||
|
||||
class Class1 {
|
||||
int field;
|
||||
|
||||
Class1(this.field);
|
||||
|
||||
String toString() => 'Class1($field)';
|
||||
}
|
||||
|
||||
class Class2 {
|
||||
int field;
|
||||
|
||||
Class2(this.field);
|
||||
|
||||
String toString() => 'Class2($field)';
|
||||
}
|
||||
|
||||
extension Extension1 on Class1 {
|
||||
int method() {
|
||||
print('Extension1.method on $this');
|
||||
return field;
|
||||
}
|
||||
int genericMethod<T extends num>(T t) {
|
||||
print('Extension1.genericMethod<$T>($t) on $this');
|
||||
return field + t;
|
||||
}
|
||||
int get property {
|
||||
print('Extension1.property get on $this');
|
||||
return field;
|
||||
}
|
||||
set property(int value) {
|
||||
field = value;
|
||||
print('Extension1.property set($value) on $this');
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Extension2 on Class2 {
|
||||
int method() {
|
||||
print('Extension2.method on $this');
|
||||
return field + 3;
|
||||
}
|
||||
int genericMethod<T extends num>(T t) {
|
||||
print('Extension2.genericMethod<$T>($t) on $this');
|
||||
return field + t + 4;
|
||||
}
|
||||
int get property {
|
||||
print('Extension2.property get on $this');
|
||||
return field + 5;
|
||||
}
|
||||
set property(int value) {
|
||||
print('Extension2.property set($value) on $this');
|
||||
value++;
|
||||
field = value;
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
testExtension1();
|
||||
testExtension2();
|
||||
}
|
||||
|
||||
testExtension1() {
|
||||
Class1 c0 = new Class1(0);
|
||||
Class1 c1 = new Class1(1);
|
||||
expect(0, c0.method());
|
||||
expect(1, c1.method());
|
||||
expect(42, c0.genericMethod(42));
|
||||
expect(43, c0.genericMethod<num>(43));
|
||||
expect(88, c1.genericMethod(87));
|
||||
expect(89, c1.genericMethod<num>(88));
|
||||
expect(0, c0.property);
|
||||
expect(42, c0.property = 42);
|
||||
expect(1, c1.property);
|
||||
expect(87, c0.property = 87);
|
||||
expect(27, c0.property = c1.property = 27);
|
||||
expect(37, c1.property = c0.property = 37);
|
||||
expect(77, c1.property = c0.property = c1.property = 77);
|
||||
expect(67, c0.property = c1.property = c0.property = 67);
|
||||
}
|
||||
|
||||
testExtension2() {
|
||||
Class2 c0 = new Class2(0);
|
||||
Class2 c1 = new Class2(1);
|
||||
expect(3, c0.method());
|
||||
expect(4, c1.method());
|
||||
expect(46, c0.genericMethod(42));
|
||||
expect(47, c0.genericMethod<num>(43));
|
||||
expect(92, c1.genericMethod(87));
|
||||
expect(93, c1.genericMethod<num>(88));
|
||||
expect(5, c0.property);
|
||||
expect(42, c0.property = 42);
|
||||
expect(48, c0.property);
|
||||
expect(6, c1.property);
|
||||
expect(43, c1.property = 43);
|
||||
expect(49, c1.property);
|
||||
expect(49, c0.property = c1.property);
|
||||
expect(55, c1.property = c0.property);
|
||||
expect(61, c1.property = c0.property = c1.property);
|
||||
expect(67, c0.property = c1.property = c0.property);
|
||||
}
|
||||
|
||||
expect(expected, actual) {
|
||||
if (expected != actual) {
|
||||
throw 'Mismatch: expected=$expected, actual=$actual';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class Class1 extends core::Object {
|
||||
field core::int* field;
|
||||
constructor •(core::int* field) → self::Class1*
|
||||
;
|
||||
method toString() → core::String*
|
||||
;
|
||||
}
|
||||
class Class2 extends core::Object {
|
||||
field core::int* field;
|
||||
constructor •(core::int* field) → self::Class2*
|
||||
;
|
||||
method toString() → core::String*
|
||||
;
|
||||
}
|
||||
extension Extension1 on self::Class1* {
|
||||
method method = self::Extension1|method;
|
||||
method genericMethod = self::Extension1|genericMethod;
|
||||
get property = self::Extension1|get#property;
|
||||
set property = self::Extension1|set#property;
|
||||
}
|
||||
extension Extension2 on self::Class2* {
|
||||
method method = self::Extension2|method;
|
||||
method genericMethod = self::Extension2|genericMethod;
|
||||
get property = self::Extension2|get#property;
|
||||
set property = self::Extension2|set#property;
|
||||
}
|
||||
static method Extension1|method(final self::Class1* #this) → core::int*
|
||||
;
|
||||
static method Extension1|genericMethod<T extends core::num* = dynamic>(final self::Class1* #this, self::Extension1|genericMethod::T* t) → core::int*
|
||||
;
|
||||
static method Extension1|get#property(final self::Class1* #this) → core::int*
|
||||
;
|
||||
static method Extension1|set#property(final self::Class1* #this, core::int* value) → void
|
||||
;
|
||||
static method Extension2|method(final self::Class2* #this) → core::int*
|
||||
;
|
||||
static method Extension2|genericMethod<T extends core::num* = dynamic>(final self::Class2* #this, self::Extension2|genericMethod::T* t) → core::int*
|
||||
;
|
||||
static method Extension2|get#property(final self::Class2* #this) → core::int*
|
||||
;
|
||||
static method Extension2|set#property(final self::Class2* #this, core::int* value) → void
|
||||
;
|
||||
static method main() → dynamic
|
||||
;
|
||||
static method testExtension1() → dynamic
|
||||
;
|
||||
static method testExtension2() → dynamic
|
||||
;
|
||||
static method expect(dynamic expected, dynamic actual) → dynamic
|
||||
;
|
||||
@@ -0,0 +1,117 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class Class1 extends core::Object {
|
||||
field core::int* field;
|
||||
constructor •(core::int* field) → self::Class1*
|
||||
: self::Class1::field = field, super core::Object::•()
|
||||
;
|
||||
method toString() → core::String*
|
||||
return "Class1(${this.{self::Class1::field}})";
|
||||
}
|
||||
class Class2 extends core::Object {
|
||||
field core::int* field;
|
||||
constructor •(core::int* field) → self::Class2*
|
||||
: self::Class2::field = field, super core::Object::•()
|
||||
;
|
||||
method toString() → core::String*
|
||||
return "Class2(${this.{self::Class2::field}})";
|
||||
}
|
||||
extension Extension1 on self::Class1* {
|
||||
method method = self::Extension1|method;
|
||||
method genericMethod = self::Extension1|genericMethod;
|
||||
get property = self::Extension1|get#property;
|
||||
set property = self::Extension1|set#property;
|
||||
}
|
||||
extension Extension2 on self::Class2* {
|
||||
method method = self::Extension2|method;
|
||||
method genericMethod = self::Extension2|genericMethod;
|
||||
get property = self::Extension2|get#property;
|
||||
set property = self::Extension2|set#property;
|
||||
}
|
||||
static method Extension1|method(final self::Class1* #this) → core::int* {
|
||||
core::print("Extension1.method on ${#this}");
|
||||
return #this.{self::Class1::field};
|
||||
}
|
||||
static method Extension1|genericMethod<T extends core::num* = dynamic>(final self::Class1* #this, self::Extension1|genericMethod::T* t) → core::int* {
|
||||
core::print("Extension1.genericMethod<${self::Extension1|genericMethod::T*}>(${t}) on ${#this}");
|
||||
return #this.{self::Class1::field}.{core::num::+}(t) as{TypeError} core::int*;
|
||||
}
|
||||
static method Extension1|get#property(final self::Class1* #this) → core::int* {
|
||||
core::print("Extension1.property get on ${#this}");
|
||||
return #this.{self::Class1::field};
|
||||
}
|
||||
static method Extension1|set#property(final self::Class1* #this, core::int* value) → core::int* {
|
||||
final core::int* #t1 = value;
|
||||
#this.{self::Class1::field} = value;
|
||||
core::print("Extension1.property set(${value}) on ${#this}");
|
||||
value = value.{core::num::+}(1);
|
||||
return #t1;
|
||||
}
|
||||
static method Extension2|method(final self::Class2* #this) → core::int* {
|
||||
core::print("Extension2.method on ${#this}");
|
||||
return #this.{self::Class2::field}.{core::num::+}(3);
|
||||
}
|
||||
static method Extension2|genericMethod<T extends core::num* = dynamic>(final self::Class2* #this, self::Extension2|genericMethod::T* t) → core::int* {
|
||||
core::print("Extension2.genericMethod<${self::Extension2|genericMethod::T*}>(${t}) on ${#this}");
|
||||
return #this.{self::Class2::field}.{core::num::+}(t).{core::num::+}(4) as{TypeError} core::int*;
|
||||
}
|
||||
static method Extension2|get#property(final self::Class2* #this) → core::int* {
|
||||
core::print("Extension2.property get on ${#this}");
|
||||
return #this.{self::Class2::field}.{core::num::+}(5);
|
||||
}
|
||||
static method Extension2|set#property(final self::Class2* #this, core::int* value) → core::int* {
|
||||
final core::int* #t2 = value;
|
||||
core::print("Extension2.property set(${value}) on ${#this}");
|
||||
value = value.{core::num::+}(1);
|
||||
#this.{self::Class2::field} = value;
|
||||
return #t2;
|
||||
}
|
||||
static method main() → dynamic {
|
||||
self::testExtension1();
|
||||
self::testExtension2();
|
||||
}
|
||||
static method testExtension1() → dynamic {
|
||||
self::Class1* c0 = new self::Class1::•(0);
|
||||
self::Class1* c1 = new self::Class1::•(1);
|
||||
self::expect(0, self::Extension1|method(c0));
|
||||
self::expect(1, self::Extension1|method(c1));
|
||||
self::expect(42, self::Extension1|genericMethod<core::int*>(c0, 42));
|
||||
self::expect(43, self::Extension1|genericMethod<core::num*>(c0, 43));
|
||||
self::expect(88, self::Extension1|genericMethod<core::int*>(c1, 87));
|
||||
self::expect(89, self::Extension1|genericMethod<core::num*>(c1, 88));
|
||||
self::expect(0, self::Extension1|get#property(c0));
|
||||
self::expect(42, self::Extension1|set#property(c0, 42));
|
||||
self::expect(1, self::Extension1|get#property(c1));
|
||||
self::expect(87, self::Extension1|set#property(c0, 87));
|
||||
self::expect(27, self::Extension1|set#property(c0, self::Extension1|set#property(c1, 27)));
|
||||
self::expect(37, self::Extension1|set#property(c1, self::Extension1|set#property(c0, 37)));
|
||||
self::expect(77, self::Extension1|set#property(c1, self::Extension1|set#property(c0, self::Extension1|set#property(c1, 77))));
|
||||
self::expect(67, self::Extension1|set#property(c0, self::Extension1|set#property(c1, self::Extension1|set#property(c0, 67))));
|
||||
}
|
||||
static method testExtension2() → dynamic {
|
||||
self::Class2* c0 = new self::Class2::•(0);
|
||||
self::Class2* c1 = new self::Class2::•(1);
|
||||
self::expect(3, self::Extension2|method(c0));
|
||||
self::expect(4, self::Extension2|method(c1));
|
||||
self::expect(46, self::Extension2|genericMethod<core::int*>(c0, 42));
|
||||
self::expect(47, self::Extension2|genericMethod<core::num*>(c0, 43));
|
||||
self::expect(92, self::Extension2|genericMethod<core::int*>(c1, 87));
|
||||
self::expect(93, self::Extension2|genericMethod<core::num*>(c1, 88));
|
||||
self::expect(5, self::Extension2|get#property(c0));
|
||||
self::expect(42, self::Extension2|set#property(c0, 42));
|
||||
self::expect(48, self::Extension2|get#property(c0));
|
||||
self::expect(6, self::Extension2|get#property(c1));
|
||||
self::expect(43, self::Extension2|set#property(c1, 43));
|
||||
self::expect(49, self::Extension2|get#property(c1));
|
||||
self::expect(49, self::Extension2|set#property(c0, self::Extension2|get#property(c1)));
|
||||
self::expect(55, self::Extension2|set#property(c1, self::Extension2|get#property(c0)));
|
||||
self::expect(61, self::Extension2|set#property(c1, self::Extension2|set#property(c0, self::Extension2|get#property(c1))));
|
||||
self::expect(67, self::Extension2|set#property(c0, self::Extension2|set#property(c1, self::Extension2|get#property(c0))));
|
||||
}
|
||||
static method expect(dynamic expected, dynamic actual) → dynamic {
|
||||
if(!expected.{core::Object::==}(actual)) {
|
||||
throw "Mismatch: expected=${expected}, actual=${actual}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class Class1 extends core::Object {
|
||||
field core::int* field;
|
||||
constructor •(core::int* field) → self::Class1*
|
||||
: self::Class1::field = field, super core::Object::•()
|
||||
;
|
||||
method toString() → core::String*
|
||||
return "Class1(${this.{self::Class1::field}})";
|
||||
}
|
||||
class Class2 extends core::Object {
|
||||
field core::int* field;
|
||||
constructor •(core::int* field) → self::Class2*
|
||||
: self::Class2::field = field, super core::Object::•()
|
||||
;
|
||||
method toString() → core::String*
|
||||
return "Class2(${this.{self::Class2::field}})";
|
||||
}
|
||||
extension Extension1 on self::Class1* {
|
||||
method method = self::Extension1|method;
|
||||
method genericMethod = self::Extension1|genericMethod;
|
||||
get property = self::Extension1|get#property;
|
||||
set property = self::Extension1|set#property;
|
||||
}
|
||||
extension Extension2 on self::Class2* {
|
||||
method method = self::Extension2|method;
|
||||
method genericMethod = self::Extension2|genericMethod;
|
||||
get property = self::Extension2|get#property;
|
||||
set property = self::Extension2|set#property;
|
||||
}
|
||||
static method Extension1|method(final self::Class1* #this) → core::int* {
|
||||
core::print("Extension1.method on ${#this}");
|
||||
return #this.{self::Class1::field};
|
||||
}
|
||||
static method Extension1|genericMethod<T extends core::num* = dynamic>(final self::Class1* #this, self::Extension1|genericMethod::T* t) → core::int* {
|
||||
core::print("Extension1.genericMethod<${self::Extension1|genericMethod::T*}>(${t}) on ${#this}");
|
||||
return #this.{self::Class1::field}.{core::num::+}(t) as{TypeError} core::int*;
|
||||
}
|
||||
static method Extension1|get#property(final self::Class1* #this) → core::int* {
|
||||
core::print("Extension1.property get on ${#this}");
|
||||
return #this.{self::Class1::field};
|
||||
}
|
||||
static method Extension1|set#property(final self::Class1* #this, core::int* value) → core::int* {
|
||||
final core::int* #t1 = value;
|
||||
#this.{self::Class1::field} = value;
|
||||
core::print("Extension1.property set(${value}) on ${#this}");
|
||||
value = value.{core::num::+}(1);
|
||||
return #t1;
|
||||
}
|
||||
static method Extension2|method(final self::Class2* #this) → core::int* {
|
||||
core::print("Extension2.method on ${#this}");
|
||||
return #this.{self::Class2::field}.{core::num::+}(3);
|
||||
}
|
||||
static method Extension2|genericMethod<T extends core::num* = dynamic>(final self::Class2* #this, self::Extension2|genericMethod::T* t) → core::int* {
|
||||
core::print("Extension2.genericMethod<${self::Extension2|genericMethod::T*}>(${t}) on ${#this}");
|
||||
return #this.{self::Class2::field}.{core::num::+}(t).{core::num::+}(4) as{TypeError} core::int*;
|
||||
}
|
||||
static method Extension2|get#property(final self::Class2* #this) → core::int* {
|
||||
core::print("Extension2.property get on ${#this}");
|
||||
return #this.{self::Class2::field}.{core::num::+}(5);
|
||||
}
|
||||
static method Extension2|set#property(final self::Class2* #this, core::int* value) → core::int* {
|
||||
final core::int* #t2 = value;
|
||||
core::print("Extension2.property set(${value}) on ${#this}");
|
||||
value = value.{core::num::+}(1);
|
||||
#this.{self::Class2::field} = value;
|
||||
return #t2;
|
||||
}
|
||||
static method main() → dynamic {
|
||||
self::testExtension1();
|
||||
self::testExtension2();
|
||||
}
|
||||
static method testExtension1() → dynamic {
|
||||
self::Class1* c0 = new self::Class1::•(0);
|
||||
self::Class1* c1 = new self::Class1::•(1);
|
||||
self::expect(0, self::Extension1|method(c0));
|
||||
self::expect(1, self::Extension1|method(c1));
|
||||
self::expect(42, self::Extension1|genericMethod<core::int*>(c0, 42));
|
||||
self::expect(43, self::Extension1|genericMethod<core::num*>(c0, 43));
|
||||
self::expect(88, self::Extension1|genericMethod<core::int*>(c1, 87));
|
||||
self::expect(89, self::Extension1|genericMethod<core::num*>(c1, 88));
|
||||
self::expect(0, self::Extension1|get#property(c0));
|
||||
self::expect(42, self::Extension1|set#property(c0, 42));
|
||||
self::expect(1, self::Extension1|get#property(c1));
|
||||
self::expect(87, self::Extension1|set#property(c0, 87));
|
||||
self::expect(27, self::Extension1|set#property(c0, self::Extension1|set#property(c1, 27)));
|
||||
self::expect(37, self::Extension1|set#property(c1, self::Extension1|set#property(c0, 37)));
|
||||
self::expect(77, self::Extension1|set#property(c1, self::Extension1|set#property(c0, self::Extension1|set#property(c1, 77))));
|
||||
self::expect(67, self::Extension1|set#property(c0, self::Extension1|set#property(c1, self::Extension1|set#property(c0, 67))));
|
||||
}
|
||||
static method testExtension2() → dynamic {
|
||||
self::Class2* c0 = new self::Class2::•(0);
|
||||
self::Class2* c1 = new self::Class2::•(1);
|
||||
self::expect(3, self::Extension2|method(c0));
|
||||
self::expect(4, self::Extension2|method(c1));
|
||||
self::expect(46, self::Extension2|genericMethod<core::int*>(c0, 42));
|
||||
self::expect(47, self::Extension2|genericMethod<core::num*>(c0, 43));
|
||||
self::expect(92, self::Extension2|genericMethod<core::int*>(c1, 87));
|
||||
self::expect(93, self::Extension2|genericMethod<core::num*>(c1, 88));
|
||||
self::expect(5, self::Extension2|get#property(c0));
|
||||
self::expect(42, self::Extension2|set#property(c0, 42));
|
||||
self::expect(48, self::Extension2|get#property(c0));
|
||||
self::expect(6, self::Extension2|get#property(c1));
|
||||
self::expect(43, self::Extension2|set#property(c1, 43));
|
||||
self::expect(49, self::Extension2|get#property(c1));
|
||||
self::expect(49, self::Extension2|set#property(c0, self::Extension2|get#property(c1)));
|
||||
self::expect(55, self::Extension2|set#property(c1, self::Extension2|get#property(c0)));
|
||||
self::expect(61, self::Extension2|set#property(c1, self::Extension2|set#property(c0, self::Extension2|get#property(c1))));
|
||||
self::expect(67, self::Extension2|set#property(c0, self::Extension2|set#property(c1, self::Extension2|get#property(c0))));
|
||||
}
|
||||
static method expect(dynamic expected, dynamic actual) → dynamic {
|
||||
if(!expected.{core::Object::==}(actual)) {
|
||||
throw "Mismatch: expected=${expected}, actual=${actual}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pkg/front_end/testcases/extensions/instance_access.dart:37:10: Context: Write to value@755
|
||||
value++;
|
||||
^^
|
||||
pkg/front_end/testcases/extensions/instance_access.dart:57:10: Context: Write to value@1217
|
||||
value++;
|
||||
^^
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2019, 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.
|
||||
|
||||
class Class1 {
|
||||
}
|
||||
|
||||
extension Extension1 on Class1 {
|
||||
static staticMethod() {
|
||||
print('Extension1.staticMethod()');
|
||||
}
|
||||
|
||||
static get staticProperty {
|
||||
print('Extension1.staticProperty()');
|
||||
}
|
||||
static set staticProperty(int value) {
|
||||
print('Extension1.staticProperty($value)');
|
||||
value++;
|
||||
}
|
||||
|
||||
static var staticField = 42;
|
||||
}
|
||||
|
||||
main() {
|
||||
Class1 c = new Class1();
|
||||
c.staticMethod();
|
||||
c.staticMethod;
|
||||
c.staticProperty;
|
||||
c.staticProperty = 42;
|
||||
c.staticField;
|
||||
c.staticField = 42;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class Class1 extends core::Object {
|
||||
synthetic constructor •() → self::Class1*
|
||||
;
|
||||
}
|
||||
extension Extension1 on self::Class1* {
|
||||
static method staticMethod = self::Extension1|staticMethod;
|
||||
static get staticProperty = get self::Extension1|staticProperty;
|
||||
static field staticField = self::Extension1|staticField;
|
||||
static set staticProperty = set self::Extension1|staticProperty;
|
||||
}
|
||||
static field core::int* Extension1|staticField;
|
||||
static method Extension1|staticMethod() → dynamic
|
||||
;
|
||||
static get Extension1|staticProperty() → dynamic
|
||||
;
|
||||
static set Extension1|staticProperty(core::int* value) → void
|
||||
;
|
||||
static method main() → dynamic
|
||||
;
|
||||
@@ -0,0 +1,98 @@
|
||||
library;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:26:5: Error: The method 'staticMethod' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing method, or defining a method named 'staticMethod'.
|
||||
// c.staticMethod();
|
||||
// ^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:27:5: Error: The getter 'staticMethod' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticMethod'.
|
||||
// c.staticMethod;
|
||||
// ^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:28:5: Error: The getter 'staticProperty' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticProperty'.
|
||||
// c.staticProperty;
|
||||
// ^^^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:29:5: Error: The setter 'staticProperty' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticProperty'.
|
||||
// c.staticProperty = 42;
|
||||
// ^^^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:30:5: Error: The getter 'staticField' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticField'.
|
||||
// c.staticField;
|
||||
// ^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:31:5: Error: The setter 'staticField' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticField'.
|
||||
// c.staticField = 42;
|
||||
// ^^^^^^^^^^^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class Class1 extends core::Object {
|
||||
synthetic constructor •() → self::Class1*
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
extension Extension1 on self::Class1* {
|
||||
static method staticMethod = self::Extension1|staticMethod;
|
||||
static get staticProperty = get self::Extension1|staticProperty;
|
||||
static field staticField = self::Extension1|staticField;
|
||||
static set staticProperty = set self::Extension1|staticProperty;
|
||||
}
|
||||
static field core::int* Extension1|staticField = 42;
|
||||
static method Extension1|staticMethod() → dynamic {
|
||||
core::print("Extension1.staticMethod()");
|
||||
}
|
||||
static get Extension1|staticProperty() → dynamic {
|
||||
core::print("Extension1.staticProperty()");
|
||||
}
|
||||
static set Extension1|staticProperty(core::int* value) → void {
|
||||
core::print("Extension1.staticProperty(${value})");
|
||||
value = value.{core::num::+}(1);
|
||||
}
|
||||
static method main() → dynamic {
|
||||
self::Class1* c = new self::Class1::•();
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:26:5: Error: The method 'staticMethod' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing method, or defining a method named 'staticMethod'.
|
||||
c.staticMethod();
|
||||
^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:27:5: Error: The getter 'staticMethod' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticMethod'.
|
||||
c.staticMethod;
|
||||
^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:28:5: Error: The getter 'staticProperty' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticProperty'.
|
||||
c.staticProperty;
|
||||
^^^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:29:5: Error: The setter 'staticProperty' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticProperty'.
|
||||
c.staticProperty = 42;
|
||||
^^^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:30:5: Error: The getter 'staticField' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticField'.
|
||||
c.staticField;
|
||||
^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:31:5: Error: The setter 'staticField' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticField'.
|
||||
c.staticField = 42;
|
||||
^^^^^^^^^^^";
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
library;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:26:5: Error: The method 'staticMethod' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing method, or defining a method named 'staticMethod'.
|
||||
// c.staticMethod();
|
||||
// ^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:27:5: Error: The getter 'staticMethod' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticMethod'.
|
||||
// c.staticMethod;
|
||||
// ^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:28:5: Error: The getter 'staticProperty' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticProperty'.
|
||||
// c.staticProperty;
|
||||
// ^^^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:29:5: Error: The setter 'staticProperty' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticProperty'.
|
||||
// c.staticProperty = 42;
|
||||
// ^^^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:30:5: Error: The getter 'staticField' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticField'.
|
||||
// c.staticField;
|
||||
// ^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/instance_access_of_static.dart:31:5: Error: The setter 'staticField' isn't defined for the class 'Class1'.
|
||||
// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
// Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticField'.
|
||||
// c.staticField = 42;
|
||||
// ^^^^^^^^^^^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class Class1 extends core::Object {
|
||||
synthetic constructor •() → self::Class1*
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
extension Extension1 on self::Class1* {
|
||||
static method staticMethod = self::Extension1|staticMethod;
|
||||
static get staticProperty = get self::Extension1|staticProperty;
|
||||
static field staticField = self::Extension1|staticField;
|
||||
static set staticProperty = set self::Extension1|staticProperty;
|
||||
}
|
||||
static field core::int* Extension1|staticField = 42;
|
||||
static method Extension1|staticMethod() → dynamic {
|
||||
core::print("Extension1.staticMethod()");
|
||||
}
|
||||
static get Extension1|staticProperty() → dynamic {
|
||||
core::print("Extension1.staticProperty()");
|
||||
}
|
||||
static set Extension1|staticProperty(core::int* value) → void {
|
||||
core::print("Extension1.staticProperty(${value})");
|
||||
value = value.{core::num::+}(1);
|
||||
}
|
||||
static method main() → dynamic {
|
||||
self::Class1* c = new self::Class1::•();
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:26:5: Error: The method 'staticMethod' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing method, or defining a method named 'staticMethod'.
|
||||
c.staticMethod();
|
||||
^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:27:5: Error: The getter 'staticMethod' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticMethod'.
|
||||
c.staticMethod;
|
||||
^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:28:5: Error: The getter 'staticProperty' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticProperty'.
|
||||
c.staticProperty;
|
||||
^^^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:29:5: Error: The setter 'staticProperty' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticProperty'.
|
||||
c.staticProperty = 42;
|
||||
^^^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:30:5: Error: The getter 'staticField' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticField'.
|
||||
c.staticField;
|
||||
^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:31:5: Error: The setter 'staticField' isn't defined for the class 'Class1'.
|
||||
- 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'.
|
||||
Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticField'.
|
||||
c.staticField = 42;
|
||||
^^^^^^^^^^^";
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
pkg/front_end/testcases/extensions/instance_access_of_static.dart:18:10: Context: Write to value@447
|
||||
value++;
|
||||
^^
|
||||
@@ -30,8 +30,10 @@ extension A2 on self::A1* {
|
||||
static field core::int* A2|staticField = self::A1::getStaticField();
|
||||
static method A2|get#instanceProperty(final self::A1* #this) → core::int*
|
||||
return #this.{self::A1::getInstanceField}();
|
||||
static method A2|set#instanceProperty(final self::A1* #this, core::int* value) → void {
|
||||
static method A2|set#instanceProperty(final self::A1* #this, core::int* value) → core::int* {
|
||||
final core::int* #t1 = value;
|
||||
#this.{self::A1::setInstanceField}(value);
|
||||
return #t1;
|
||||
}
|
||||
static method A2|+(final self::A1* #this, core::int* value) → core::int* {
|
||||
return #this.{self::A1::getInstanceField}().{core::num::+}(value);
|
||||
|
||||
@@ -30,8 +30,10 @@ extension A2 on self::A1* {
|
||||
static field core::int* A2|staticField = self::A1::getStaticField();
|
||||
static method A2|get#instanceProperty(final self::A1* #this) → core::int*
|
||||
return #this.{self::A1::getInstanceField}();
|
||||
static method A2|set#instanceProperty(final self::A1* #this, core::int* value) → void {
|
||||
static method A2|set#instanceProperty(final self::A1* #this, core::int* value) → core::int* {
|
||||
final core::int* #t1 = value;
|
||||
#this.{self::A1::setInstanceField}(value);
|
||||
return #t1;
|
||||
}
|
||||
static method A2|+(final self::A1* #this, core::int* value) → core::int* {
|
||||
return #this.{self::A1::getInstanceField}().{core::num::+}(value);
|
||||
|
||||
@@ -10,6 +10,10 @@ extension Extension on Class {
|
||||
static get property => 42;
|
||||
static set property(value) {}
|
||||
static var field;
|
||||
|
||||
instanceMethod() {}
|
||||
get instanceProperty => 42;
|
||||
set instanceProperty(value) {}
|
||||
}
|
||||
|
||||
main() {
|
||||
@@ -22,4 +26,4 @@ main() {
|
||||
Extension.property = 42;
|
||||
Extension.field;
|
||||
Extension.field = 42;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@ extension Extension on self::Class* {
|
||||
static method genericMethod = self::Extension|genericMethod;
|
||||
static get property = get self::Extension|property;
|
||||
static field field = self::Extension|field;
|
||||
method instanceMethod = self::Extension|instanceMethod;
|
||||
get instanceProperty = self::Extension|get#instanceProperty;
|
||||
static set property = set self::Extension|property;
|
||||
set instanceProperty = self::Extension|set#instanceProperty;
|
||||
}
|
||||
static field dynamic Extension|field;
|
||||
static method Extension|method() → dynamic
|
||||
@@ -22,5 +25,11 @@ static get Extension|property() → dynamic
|
||||
;
|
||||
static set Extension|property(dynamic value) → void
|
||||
;
|
||||
static method Extension|instanceMethod(final self::Class* #this) → dynamic
|
||||
;
|
||||
static method Extension|get#instanceProperty(final self::Class* #this) → dynamic
|
||||
;
|
||||
static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → void
|
||||
;
|
||||
static method main() → dynamic
|
||||
;
|
||||
|
||||
@@ -12,7 +12,10 @@ extension Extension on self::Class* {
|
||||
static method genericMethod = self::Extension|genericMethod;
|
||||
static get property = get self::Extension|property;
|
||||
static field field = self::Extension|field;
|
||||
method instanceMethod = self::Extension|instanceMethod;
|
||||
get instanceProperty = self::Extension|get#instanceProperty;
|
||||
static set property = set self::Extension|property;
|
||||
set instanceProperty = self::Extension|set#instanceProperty;
|
||||
}
|
||||
static field dynamic Extension|field;
|
||||
static method Extension|method() → dynamic {}
|
||||
@@ -20,6 +23,13 @@ static method Extension|genericMethod<T extends core::Object* = dynamic>(self::E
|
||||
static get Extension|property() → dynamic
|
||||
return 42;
|
||||
static set Extension|property(dynamic value) → void {}
|
||||
static method Extension|instanceMethod(final self::Class* #this) → dynamic {}
|
||||
static method Extension|get#instanceProperty(final self::Class* #this) → dynamic
|
||||
return 42;
|
||||
static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → dynamic {
|
||||
final dynamic #t1 = value;
|
||||
return #t1;
|
||||
}
|
||||
static method main() → dynamic {
|
||||
self::Extension|method();
|
||||
self::Extension|genericMethod<core::int*>(42);
|
||||
|
||||
@@ -12,7 +12,10 @@ extension Extension on self::Class* {
|
||||
static method genericMethod = self::Extension|genericMethod;
|
||||
static get property = get self::Extension|property;
|
||||
static field field = self::Extension|field;
|
||||
method instanceMethod = self::Extension|instanceMethod;
|
||||
get instanceProperty = self::Extension|get#instanceProperty;
|
||||
static set property = set self::Extension|property;
|
||||
set instanceProperty = self::Extension|set#instanceProperty;
|
||||
}
|
||||
static field dynamic Extension|field;
|
||||
static method Extension|method() → dynamic {}
|
||||
@@ -20,6 +23,13 @@ static method Extension|genericMethod<T extends core::Object* = dynamic>(self::E
|
||||
static get Extension|property() → dynamic
|
||||
return 42;
|
||||
static set Extension|property(dynamic value) → void {}
|
||||
static method Extension|instanceMethod(final self::Class* #this) → dynamic {}
|
||||
static method Extension|get#instanceProperty(final self::Class* #this) → dynamic
|
||||
return 42;
|
||||
static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → dynamic {
|
||||
final dynamic #t1 = value;
|
||||
return #t1;
|
||||
}
|
||||
static method main() → dynamic {
|
||||
self::Extension|method();
|
||||
self::Extension|genericMethod<core::int*>(42);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2019, 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.
|
||||
|
||||
class Class {}
|
||||
|
||||
extension Extension on Class {
|
||||
instanceMethod() {}
|
||||
get instanceProperty => 42;
|
||||
set instanceProperty(value) {}
|
||||
}
|
||||
|
||||
main() {
|
||||
Extension.instanceMethod();
|
||||
Extension.instanceMethod;
|
||||
Extension.instanceProperty;
|
||||
Extension.instanceProperty = 42;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class Class extends core::Object {
|
||||
synthetic constructor •() → self::Class*
|
||||
;
|
||||
}
|
||||
extension Extension on self::Class* {
|
||||
method instanceMethod = self::Extension|instanceMethod;
|
||||
get instanceProperty = self::Extension|get#instanceProperty;
|
||||
set instanceProperty = self::Extension|set#instanceProperty;
|
||||
}
|
||||
static method Extension|instanceMethod(final self::Class* #this) → dynamic
|
||||
;
|
||||
static method Extension|get#instanceProperty(final self::Class* #this) → dynamic
|
||||
;
|
||||
static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → void
|
||||
;
|
||||
static method main() → dynamic
|
||||
;
|
||||
@@ -0,0 +1,54 @@
|
||||
library;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/static_access_of_instance.dart:14:13: Error: Method not found: 'Extension.instanceMethod'.
|
||||
// Extension.instanceMethod();
|
||||
// ^^^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/static_access_of_instance.dart:15:13: Error: Getter not found: 'instanceMethod'.
|
||||
// Extension.instanceMethod;
|
||||
// ^^^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/static_access_of_instance.dart:16:13: Error: Getter not found: 'instanceProperty'.
|
||||
// Extension.instanceProperty;
|
||||
// ^^^^^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/static_access_of_instance.dart:17:13: Error: Setter not found: 'instanceProperty'.
|
||||
// Extension.instanceProperty = 42;
|
||||
// ^^^^^^^^^^^^^^^^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class Class extends core::Object {
|
||||
synthetic constructor •() → self::Class*
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
extension Extension on self::Class* {
|
||||
method instanceMethod = self::Extension|instanceMethod;
|
||||
get instanceProperty = self::Extension|get#instanceProperty;
|
||||
set instanceProperty = self::Extension|set#instanceProperty;
|
||||
}
|
||||
static method Extension|instanceMethod(final self::Class* #this) → dynamic {}
|
||||
static method Extension|get#instanceProperty(final self::Class* #this) → dynamic
|
||||
return 42;
|
||||
static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → dynamic {
|
||||
final dynamic #t1 = value;
|
||||
return #t1;
|
||||
}
|
||||
static method main() → dynamic {
|
||||
invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:14:13: Error: Method not found: 'Extension.instanceMethod'.
|
||||
Extension.instanceMethod();
|
||||
^^^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:15:13: Error: Getter not found: 'instanceMethod'.
|
||||
Extension.instanceMethod;
|
||||
^^^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:16:13: Error: Getter not found: 'instanceProperty'.
|
||||
Extension.instanceProperty;
|
||||
^^^^^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:17:13: Error: Setter not found: 'instanceProperty'.
|
||||
Extension.instanceProperty = 42;
|
||||
^^^^^^^^^^^^^^^^";
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
library;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/static_access_of_instance.dart:14:13: Error: Method not found: 'Extension.instanceMethod'.
|
||||
// Extension.instanceMethod();
|
||||
// ^^^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/static_access_of_instance.dart:15:13: Error: Getter not found: 'instanceMethod'.
|
||||
// Extension.instanceMethod;
|
||||
// ^^^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/static_access_of_instance.dart:16:13: Error: Getter not found: 'instanceProperty'.
|
||||
// Extension.instanceProperty;
|
||||
// ^^^^^^^^^^^^^^^^
|
||||
//
|
||||
// pkg/front_end/testcases/extensions/static_access_of_instance.dart:17:13: Error: Setter not found: 'instanceProperty'.
|
||||
// Extension.instanceProperty = 42;
|
||||
// ^^^^^^^^^^^^^^^^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class Class extends core::Object {
|
||||
synthetic constructor •() → self::Class*
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
extension Extension on self::Class* {
|
||||
method instanceMethod = self::Extension|instanceMethod;
|
||||
get instanceProperty = self::Extension|get#instanceProperty;
|
||||
set instanceProperty = self::Extension|set#instanceProperty;
|
||||
}
|
||||
static method Extension|instanceMethod(final self::Class* #this) → dynamic {}
|
||||
static method Extension|get#instanceProperty(final self::Class* #this) → dynamic
|
||||
return 42;
|
||||
static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → dynamic {
|
||||
final dynamic #t1 = value;
|
||||
return #t1;
|
||||
}
|
||||
static method main() → dynamic {
|
||||
invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:14:13: Error: Method not found: 'Extension.instanceMethod'.
|
||||
Extension.instanceMethod();
|
||||
^^^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:15:13: Error: Getter not found: 'instanceMethod'.
|
||||
Extension.instanceMethod;
|
||||
^^^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:16:13: Error: Getter not found: 'instanceProperty'.
|
||||
Extension.instanceProperty;
|
||||
^^^^^^^^^^^^^^^^";
|
||||
invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:17:13: Error: Setter not found: 'instanceProperty'.
|
||||
Extension.instanceProperty = 42;
|
||||
^^^^^^^^^^^^^^^^";
|
||||
}
|
||||
@@ -6,7 +6,8 @@
|
||||
# Kernel ASTs directly, that is, code in pkg/fasta/lib/src/kernel/ with
|
||||
# strong-mode enabled.
|
||||
|
||||
extensions/extension_methods: RuntimeError
|
||||
extensions/instance_access_of_static: RuntimeError
|
||||
extensions/static_access_of_instance: RuntimeError
|
||||
general/abstract_members: TypeCheckError
|
||||
general/accessors: RuntimeError
|
||||
general/ambiguous_exports: RuntimeError # Expected, this file exports two main methods.
|
||||
|
||||
@@ -11,11 +11,14 @@ expression/main: TextSerializationFailure # Was: Pass
|
||||
extensions/explicit_this: TextSerializationFailure
|
||||
extensions/extension_methods: TextSerializationFailure
|
||||
extensions/implicit_this: TextSerializationFailure
|
||||
extensions/instance_access: TextSerializationFailure
|
||||
extensions/instance_access_of_static: TextSerializationFailure
|
||||
extensions/direct_instance_access: TextSerializationFailure
|
||||
extensions/direct_static_access: TextSerializationFailure
|
||||
extensions/instance_members: TextSerializationFailure
|
||||
extensions/other_kinds: TextSerializationFailure
|
||||
extensions/static_access: TextSerializationFailure
|
||||
extensions/static_access_of_instance: TextSerializationFailure
|
||||
extensions/type_variables: TextSerializationFailure
|
||||
extensions/use_this: TextSerializationFailure
|
||||
general/abstract_members: TypeCheckError
|
||||
|
||||
Reference in New Issue
Block a user