[cfe] Handle Record(Index/Name)Get
Change-Id: I9550d1e73b7caf1924596b3208a16f51a8cfcf41 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/256362 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
committed by
Commit Bot
parent
3c370fc85d
commit
2da3806823
@@ -6021,6 +6021,32 @@ class InferenceVisitorImpl extends InferenceVisitorBase
|
||||
DartType receiverType = result.nullAwareActionType;
|
||||
|
||||
node.receiver = receiver..parent = node;
|
||||
|
||||
if (receiverType is RecordType) {
|
||||
// TODO(johnniwinther): Handle nullable record types and null shorting.
|
||||
String name = node.name.text;
|
||||
if (name.startsWith('\$')) {
|
||||
int? index = int.tryParse(name.substring(1));
|
||||
if (index != null) {
|
||||
if (index < receiverType.positional.length) {
|
||||
DartType fieldType = receiverType.positional[index];
|
||||
return new ExpressionInferenceResult(
|
||||
fieldType,
|
||||
new RecordIndexGet(receiver, receiverType, index)
|
||||
..fileOffset = node.fileOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (NamedType field in receiverType.named) {
|
||||
if (field.name == name) {
|
||||
return new ExpressionInferenceResult(
|
||||
field.type,
|
||||
new RecordNameGet(receiver, receiverType, name)
|
||||
..fileOffset = node.fileOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PropertyGetInferenceResult propertyGetInferenceResult = _computePropertyGet(
|
||||
node.fileOffset, receiver, receiverType, node.name, typeContext,
|
||||
isThisReceiver: node.receiver is ThisExpression);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2022, 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.
|
||||
|
||||
method() {
|
||||
(0, "").$0;
|
||||
(0, "").$1;
|
||||
(0, a: "", b: true).$0;
|
||||
(0, a: "", b: true).a;
|
||||
(0, a: "", b: true).b;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method method() → dynamic {
|
||||
(0, "").$0{core::int};
|
||||
(0, "").$1{core::String};
|
||||
(0, {a: "", b: true}).$0{core::int};
|
||||
(0, {a: "", b: true}).a{core::String};
|
||||
(0, {a: "", b: true}).b{core::bool};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method method() → dynamic {
|
||||
(0, "").$0{core::int};
|
||||
(0, "").$1{core::String};
|
||||
(0, {a: "", b: true}).$0{core::int};
|
||||
(0, {a: "", b: true}).a{core::String};
|
||||
(0, {a: "", b: true}).b{core::bool};
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
method() {}
|
||||
@@ -0,0 +1 @@
|
||||
method() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method method() → dynamic {
|
||||
(0, "").$0{core::int};
|
||||
(0, "").$1{core::String};
|
||||
(0, {a: "", b: true}).$0{core::int};
|
||||
(0, {a: "", b: true}).a{core::String};
|
||||
(0, {a: "", b: true}).b{core::bool};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method method() → dynamic {
|
||||
(0, "").$0{core::int};
|
||||
(0, "").$1{core::String};
|
||||
(0, {a: "", b: true}).$0{core::int};
|
||||
(0, {a: "", b: true}).a{core::String};
|
||||
(0, {a: "", b: true}).b{core::bool};
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
|
||||
static method method() → dynamic
|
||||
;
|
||||
@@ -0,0 +1,11 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method method() → dynamic {
|
||||
(0, "").$0{core::int};
|
||||
(0, "").$1{core::String};
|
||||
(0, {a: "", b: true}).$0{core::int};
|
||||
(0, {a: "", b: true}).a{core::String};
|
||||
(0, {a: "", b: true}).b{core::bool};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2022, 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.
|
||||
|
||||
method() {
|
||||
(0, "").$2;
|
||||
(0, a: "", b: true).c;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '$2' isn't defined for the class '(int, String)'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named '$2'.
|
||||
// (0, "").$2;
|
||||
// ^^
|
||||
//
|
||||
// pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'.
|
||||
// (0, a: "", b: true).c;
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method method() → dynamic {
|
||||
invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '\$2' isn't defined for the class '(int, String)'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named '\$2'.
|
||||
(0, \"\").\$2;
|
||||
^^" in (0, ""){<unresolved>}.$2;
|
||||
invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'.
|
||||
(0, a: \"\", b: true).c;
|
||||
^" in (0, {a: "", b: true}){<unresolved>}.c;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '$2' isn't defined for the class '(int, String)'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named '$2'.
|
||||
// (0, "").$2;
|
||||
// ^^
|
||||
//
|
||||
// pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'.
|
||||
// (0, a: "", b: true).c;
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method method() → dynamic {
|
||||
invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '\$2' isn't defined for the class '(int, String)'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named '\$2'.
|
||||
(0, \"\").\$2;
|
||||
^^" in (0, ""){<unresolved>}.$2;
|
||||
invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'.
|
||||
(0, a: \"\", b: true).c;
|
||||
^" in (0, {a: "", b: true}){<unresolved>}.c;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
method() {}
|
||||
@@ -0,0 +1 @@
|
||||
method() {}
|
||||
@@ -0,0 +1,27 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '$2' isn't defined for the class '(int, String)'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named '$2'.
|
||||
// (0, "").$2;
|
||||
// ^^
|
||||
//
|
||||
// pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'.
|
||||
// (0, a: "", b: true).c;
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method method() → dynamic {
|
||||
invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '\$2' isn't defined for the class '(int, String)'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named '\$2'.
|
||||
(0, \"\").\$2;
|
||||
^^" in (0, ""){<unresolved>}.$2;
|
||||
invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'.
|
||||
(0, a: \"\", b: true).c;
|
||||
^" in (0, {a: "", b: true}){<unresolved>}.c;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '$2' isn't defined for the class '(int, String)'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named '$2'.
|
||||
// (0, "").$2;
|
||||
// ^^
|
||||
//
|
||||
// pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'.
|
||||
// (0, a: "", b: true).c;
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method method() → dynamic {
|
||||
invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '\$2' isn't defined for the class '(int, String)'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named '\$2'.
|
||||
(0, \"\").\$2;
|
||||
^^" in (0, ""){<unresolved>}.$2;
|
||||
invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'.
|
||||
(0, a: \"\", b: true).c;
|
||||
^" in (0, {a: "", b: true}){<unresolved>}.c;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
import self as self;
|
||||
|
||||
static method method() → dynamic
|
||||
;
|
||||
@@ -0,0 +1,27 @@
|
||||
library /*isNonNullableByDefault*/;
|
||||
//
|
||||
// Problems in library:
|
||||
//
|
||||
// pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '$2' isn't defined for the class '(int, String)'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named '$2'.
|
||||
// (0, "").$2;
|
||||
// ^^
|
||||
//
|
||||
// pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'.
|
||||
// Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'.
|
||||
// (0, a: "", b: true).c;
|
||||
// ^
|
||||
//
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method method() → dynamic {
|
||||
invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:6:11: Error: The getter '\$2' isn't defined for the class '(int, String)'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named '\$2'.
|
||||
(0, \"\").\$2;
|
||||
^^" in (0, ""){<unresolved>}.$2;
|
||||
invalid-expression "pkg/front_end/testcases/records/record_get_errors.dart:7:23: Error: The getter 'c' isn't defined for the class '(int, {String a, bool b})'.
|
||||
Try correcting the name to the name of an existing getter, or defining a getter or field named 'c'.
|
||||
(0, a: \"\", b: true).c;
|
||||
^" in (0, {a: "", b: true}){<unresolved>}.c;
|
||||
}
|
||||
@@ -4288,7 +4288,9 @@ class RecordIndexGet extends Expression {
|
||||
RecordType receiverType;
|
||||
final int index;
|
||||
|
||||
RecordIndexGet(this.receiver, this.receiverType, this.index);
|
||||
RecordIndexGet(this.receiver, this.receiverType, this.index) {
|
||||
receiver.parent = this;
|
||||
}
|
||||
|
||||
@override
|
||||
DartType getStaticType(StaticTypeContext context) =>
|
||||
@@ -4330,7 +4332,7 @@ class RecordIndexGet extends Expression {
|
||||
@override
|
||||
void toTextInternal(AstPrinter printer) {
|
||||
printer.writeExpression(receiver);
|
||||
printer.write("[${index}]");
|
||||
printer.write(".\$${index}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4339,7 +4341,9 @@ class RecordNameGet extends Expression {
|
||||
RecordType receiverType;
|
||||
final String name;
|
||||
|
||||
RecordNameGet(this.receiver, this.receiverType, this.name);
|
||||
RecordNameGet(this.receiver, this.receiverType, this.name) {
|
||||
receiver.parent = this;
|
||||
}
|
||||
|
||||
@override
|
||||
DartType getStaticType(StaticTypeContext context) =>
|
||||
@@ -4391,7 +4395,7 @@ class RecordNameGet extends Expression {
|
||||
@override
|
||||
void toTextInternal(AstPrinter printer) {
|
||||
printer.writeExpression(receiver);
|
||||
printer.write("['${name}']");
|
||||
printer.write(".${name}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2234,7 +2234,7 @@ class BinaryBuilder {
|
||||
int offset = readOffset();
|
||||
Expression receiver = readExpression();
|
||||
RecordType receiverType = readDartType() as RecordType;
|
||||
String name = readString();
|
||||
String name = readStringReference();
|
||||
return RecordNameGet(receiver, receiverType, name)..fileOffset = offset;
|
||||
}
|
||||
|
||||
|
||||
@@ -2177,6 +2177,27 @@ class Printer extends Visitor<void> with VisitorVoidMixin {
|
||||
state = WORD;
|
||||
}
|
||||
|
||||
@override
|
||||
void visitRecordIndexGet(RecordIndexGet node) {
|
||||
writeExpression(node.receiver, Precedence.PRIMARY);
|
||||
writeSymbol('.\$${node.index}');
|
||||
writeSymbol('{');
|
||||
writeType(node.receiverType.positional[node.index]);
|
||||
writeSymbol('}');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitRecordNameGet(RecordNameGet node) {
|
||||
writeExpression(node.receiver, Precedence.PRIMARY);
|
||||
writeSymbol('.${node.name}');
|
||||
writeSymbol('{');
|
||||
// TODO(johnniwinther): Should we store the result type in the node?
|
||||
writeType(node.receiverType.named
|
||||
.singleWhere((element) => element.name == node.name)
|
||||
.type);
|
||||
writeSymbol('}');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitExpressionStatement(ExpressionStatement node) {
|
||||
writeIndentation();
|
||||
|
||||
Reference in New Issue
Block a user