[cfe] handle inline class constructor

with formal parameters

Fixes #51277

Change-Id: I76cfe3e8017f8124a5ac014348459d5b276cbebe
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/281682
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/source/source_function_builder.dart b/pkg/front_end/lib/src/fasta/source/source_function_builder.dart
index 7c74b5f..9409085 100644
--- a/pkg/front_end/lib/src/fasta/source/source_function_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_function_builder.dart
@@ -10,6 +10,7 @@
 import '../../api_prototype/lowering_predicates.dart';
 import '../builder/builder.dart';
 import '../builder/class_builder.dart';
+import '../builder/constructor_builder.dart';
 import '../builder/declaration_builder.dart';
 import '../builder/formal_parameter_builder.dart';
 import '../builder/function_builder.dart';
@@ -424,7 +425,8 @@
 
   @override
   VariableDeclaration getFormalParameter(int index) {
-    if (isExtensionInstanceMember || isInlineClassInstanceMember) {
+    if (this is! ConstructorBuilder &&
+        (isExtensionInstanceMember || isInlineClassInstanceMember)) {
       return formals![index + 1].variable!;
     } else {
       return formals![index].variable!;
diff --git a/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart
new file mode 100644
index 0000000..8c5b28c
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2023, 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.
+
+inline class I {
+  final int it;
+
+  I(int x, [int? y]) : it = x + (y ?? 42);
+
+  void m(String s, [int i = 1]) {}
+}
+
+inline class I2 {
+  final int it;
+
+  I2(int x, {int? y}) : it = x + (y ?? 87);
+
+  void m(String s, {int i = 1}) {}
+}
+
+main() {
+  expect(42, I(0));
+  expect(0, I(0, 0));
+  expect(87, I2(0));
+  expect(0, I2(0, y: 0));
+}
+
+expect(expected, actual) {
+  if (expected != actual) {
+    throw 'Expected $expected, actual $actual';
+  }
+}
diff --git a/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.strong.expect b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.strong.expect
new file mode 100644
index 0000000..951d3d3
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.strong.expect
@@ -0,0 +1,50 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+inline class I /* declaredRepresentationType = core::int */ {
+  method m = self::I|m;
+  tearoff m = self::I|get#m;
+  constructor • = self::I|;
+  tearoff • = self::I|get#;
+}
+inline class I2 /* declaredRepresentationType = core::int */ {
+  method m = self::I2|m;
+  tearoff m = self::I2|get#m;
+  constructor • = self::I2|;
+  tearoff • = self::I2|get#;
+}
+static method I|(core::int x, [core::int? y = #C1]) → self::I {
+  final self::I #this = x.{core::num::+}(let final core::int? #t1 = y in #t1 == null ?{core::int} 42 : #t1{core::int}){(core::num) → core::int};
+  return #this;
+}
+static method I|get#(core::int x, [core::int? y]) → self::I
+  return self::I|(x, y);
+static method I|m(lowered final self::I #this, core::String s, [core::int i = #C2]) → void {}
+static method I|get#m(lowered final self::I #this) → (core::String, [core::int]) → void
+  return (core::String s, [core::int i = #C2]) → void => self::I|m(#this, s, i);
+static method I2|(core::int x, {core::int? y = #C1}) → self::I2 {
+  final self::I2 #this = x.{core::num::+}(let final core::int? #t2 = y in #t2 == null ?{core::int} 87 : #t2{core::int}){(core::num) → core::int};
+  return #this;
+}
+static method I2|get#(core::int x, {core::int? y}) → self::I2
+  return self::I2|(x, y: y);
+static method I2|m(lowered final self::I2 #this, core::String s, {core::int i = #C2}) → void {}
+static method I2|get#m(lowered final self::I2 #this) → (core::String, {i: core::int}) → void
+  return (core::String s, {core::int i = #C2}) → void => self::I2|m(#this, s, i: i);
+static method main() → dynamic {
+  self::expect(42, self::I|(0));
+  self::expect(0, self::I|(0, 0));
+  self::expect(87, self::I2|(0));
+  self::expect(0, self::I2|(0, y: 0));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual)) {
+    throw "Expected ${expected}, actual ${actual}";
+  }
+}
+
+constants  {
+  #C1 = null
+  #C2 = 1
+}
diff --git a/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.strong.transformed.expect b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.strong.transformed.expect
new file mode 100644
index 0000000..951d3d3
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.strong.transformed.expect
@@ -0,0 +1,50 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+inline class I /* declaredRepresentationType = core::int */ {
+  method m = self::I|m;
+  tearoff m = self::I|get#m;
+  constructor • = self::I|;
+  tearoff • = self::I|get#;
+}
+inline class I2 /* declaredRepresentationType = core::int */ {
+  method m = self::I2|m;
+  tearoff m = self::I2|get#m;
+  constructor • = self::I2|;
+  tearoff • = self::I2|get#;
+}
+static method I|(core::int x, [core::int? y = #C1]) → self::I {
+  final self::I #this = x.{core::num::+}(let final core::int? #t1 = y in #t1 == null ?{core::int} 42 : #t1{core::int}){(core::num) → core::int};
+  return #this;
+}
+static method I|get#(core::int x, [core::int? y]) → self::I
+  return self::I|(x, y);
+static method I|m(lowered final self::I #this, core::String s, [core::int i = #C2]) → void {}
+static method I|get#m(lowered final self::I #this) → (core::String, [core::int]) → void
+  return (core::String s, [core::int i = #C2]) → void => self::I|m(#this, s, i);
+static method I2|(core::int x, {core::int? y = #C1}) → self::I2 {
+  final self::I2 #this = x.{core::num::+}(let final core::int? #t2 = y in #t2 == null ?{core::int} 87 : #t2{core::int}){(core::num) → core::int};
+  return #this;
+}
+static method I2|get#(core::int x, {core::int? y}) → self::I2
+  return self::I2|(x, y: y);
+static method I2|m(lowered final self::I2 #this, core::String s, {core::int i = #C2}) → void {}
+static method I2|get#m(lowered final self::I2 #this) → (core::String, {i: core::int}) → void
+  return (core::String s, {core::int i = #C2}) → void => self::I2|m(#this, s, i: i);
+static method main() → dynamic {
+  self::expect(42, self::I|(0));
+  self::expect(0, self::I|(0, 0));
+  self::expect(87, self::I2|(0));
+  self::expect(0, self::I2|(0, y: 0));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual)) {
+    throw "Expected ${expected}, actual ${actual}";
+  }
+}
+
+constants  {
+  #C1 = null
+  #C2 = 1
+}
diff --git a/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.textual_outline.expect b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.textual_outline.expect
new file mode 100644
index 0000000..4397286
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.textual_outline.expect
@@ -0,0 +1,14 @@
+inline
+class I {
+  final int it;
+  I(int x, [int? y]) : it = x + (y ?? 42);
+  void m(String s, [int i = 1]) {}
+}
+inline
+class I2 {
+  final int it;
+  I2(int x, {int? y}) : it = x + (y ?? 87);
+  void m(String s, {int i = 1}) {}
+}
+main() {}
+expect(expected, actual) {}
diff --git a/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.expect b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.expect
new file mode 100644
index 0000000..951d3d3
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.expect
@@ -0,0 +1,50 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+inline class I /* declaredRepresentationType = core::int */ {
+  method m = self::I|m;
+  tearoff m = self::I|get#m;
+  constructor • = self::I|;
+  tearoff • = self::I|get#;
+}
+inline class I2 /* declaredRepresentationType = core::int */ {
+  method m = self::I2|m;
+  tearoff m = self::I2|get#m;
+  constructor • = self::I2|;
+  tearoff • = self::I2|get#;
+}
+static method I|(core::int x, [core::int? y = #C1]) → self::I {
+  final self::I #this = x.{core::num::+}(let final core::int? #t1 = y in #t1 == null ?{core::int} 42 : #t1{core::int}){(core::num) → core::int};
+  return #this;
+}
+static method I|get#(core::int x, [core::int? y]) → self::I
+  return self::I|(x, y);
+static method I|m(lowered final self::I #this, core::String s, [core::int i = #C2]) → void {}
+static method I|get#m(lowered final self::I #this) → (core::String, [core::int]) → void
+  return (core::String s, [core::int i = #C2]) → void => self::I|m(#this, s, i);
+static method I2|(core::int x, {core::int? y = #C1}) → self::I2 {
+  final self::I2 #this = x.{core::num::+}(let final core::int? #t2 = y in #t2 == null ?{core::int} 87 : #t2{core::int}){(core::num) → core::int};
+  return #this;
+}
+static method I2|get#(core::int x, {core::int? y}) → self::I2
+  return self::I2|(x, y: y);
+static method I2|m(lowered final self::I2 #this, core::String s, {core::int i = #C2}) → void {}
+static method I2|get#m(lowered final self::I2 #this) → (core::String, {i: core::int}) → void
+  return (core::String s, {core::int i = #C2}) → void => self::I2|m(#this, s, i: i);
+static method main() → dynamic {
+  self::expect(42, self::I|(0));
+  self::expect(0, self::I|(0, 0));
+  self::expect(87, self::I2|(0));
+  self::expect(0, self::I2|(0, y: 0));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual)) {
+    throw "Expected ${expected}, actual ${actual}";
+  }
+}
+
+constants  {
+  #C1 = null
+  #C2 = 1
+}
diff --git a/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.modular.expect b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.modular.expect
new file mode 100644
index 0000000..951d3d3
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.modular.expect
@@ -0,0 +1,50 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+inline class I /* declaredRepresentationType = core::int */ {
+  method m = self::I|m;
+  tearoff m = self::I|get#m;
+  constructor • = self::I|;
+  tearoff • = self::I|get#;
+}
+inline class I2 /* declaredRepresentationType = core::int */ {
+  method m = self::I2|m;
+  tearoff m = self::I2|get#m;
+  constructor • = self::I2|;
+  tearoff • = self::I2|get#;
+}
+static method I|(core::int x, [core::int? y = #C1]) → self::I {
+  final self::I #this = x.{core::num::+}(let final core::int? #t1 = y in #t1 == null ?{core::int} 42 : #t1{core::int}){(core::num) → core::int};
+  return #this;
+}
+static method I|get#(core::int x, [core::int? y]) → self::I
+  return self::I|(x, y);
+static method I|m(lowered final self::I #this, core::String s, [core::int i = #C2]) → void {}
+static method I|get#m(lowered final self::I #this) → (core::String, [core::int]) → void
+  return (core::String s, [core::int i = #C2]) → void => self::I|m(#this, s, i);
+static method I2|(core::int x, {core::int? y = #C1}) → self::I2 {
+  final self::I2 #this = x.{core::num::+}(let final core::int? #t2 = y in #t2 == null ?{core::int} 87 : #t2{core::int}){(core::num) → core::int};
+  return #this;
+}
+static method I2|get#(core::int x, {core::int? y}) → self::I2
+  return self::I2|(x, y: y);
+static method I2|m(lowered final self::I2 #this, core::String s, {core::int i = #C2}) → void {}
+static method I2|get#m(lowered final self::I2 #this) → (core::String, {i: core::int}) → void
+  return (core::String s, {core::int i = #C2}) → void => self::I2|m(#this, s, i: i);
+static method main() → dynamic {
+  self::expect(42, self::I|(0));
+  self::expect(0, self::I|(0, 0));
+  self::expect(87, self::I2|(0));
+  self::expect(0, self::I2|(0, y: 0));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual)) {
+    throw "Expected ${expected}, actual ${actual}";
+  }
+}
+
+constants  {
+  #C1 = null
+  #C2 = 1
+}
diff --git a/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.outline.expect b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.outline.expect
new file mode 100644
index 0000000..c6eb0fe
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.outline.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+inline class I /* declaredRepresentationType = core::int */ {
+  method m = self::I|m;
+  tearoff m = self::I|get#m;
+  constructor • = self::I|;
+  tearoff • = self::I|get#;
+}
+inline class I2 /* declaredRepresentationType = core::int */ {
+  method m = self::I2|m;
+  tearoff m = self::I2|get#m;
+  constructor • = self::I2|;
+  tearoff • = self::I2|get#;
+}
+static method I|(core::int x, [core::int? y]) → self::I
+  ;
+static method I|get#(core::int x, [core::int? y]) → self::I
+  return self::I|(x, y);
+static method I|m(lowered final self::I #this, core::String s, [has-declared-initializer core::int i]) → void
+  ;
+static method I|get#m(lowered final self::I #this) → (core::String, [core::int]) → void
+  return (core::String s, [core::int i]) → void => self::I|m(#this, s, i);
+static method I2|(core::int x, {core::int? y}) → self::I2
+  ;
+static method I2|get#(core::int x, {core::int? y}) → self::I2
+  return self::I2|(x, y: y);
+static method I2|m(lowered final self::I2 #this, core::String s, {has-declared-initializer core::int i}) → void
+  ;
+static method I2|get#m(lowered final self::I2 #this) → (core::String, {i: core::int}) → void
+  return (core::String s, {core::int i}) → void => self::I2|m(#this, s, i: i);
+static method main() → dynamic
+  ;
+static method expect(dynamic expected, dynamic actual) → dynamic
+  ;
diff --git a/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.transformed.expect b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.transformed.expect
new file mode 100644
index 0000000..951d3d3
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/constructor_formal_parameters.dart.weak.transformed.expect
@@ -0,0 +1,50 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+inline class I /* declaredRepresentationType = core::int */ {
+  method m = self::I|m;
+  tearoff m = self::I|get#m;
+  constructor • = self::I|;
+  tearoff • = self::I|get#;
+}
+inline class I2 /* declaredRepresentationType = core::int */ {
+  method m = self::I2|m;
+  tearoff m = self::I2|get#m;
+  constructor • = self::I2|;
+  tearoff • = self::I2|get#;
+}
+static method I|(core::int x, [core::int? y = #C1]) → self::I {
+  final self::I #this = x.{core::num::+}(let final core::int? #t1 = y in #t1 == null ?{core::int} 42 : #t1{core::int}){(core::num) → core::int};
+  return #this;
+}
+static method I|get#(core::int x, [core::int? y]) → self::I
+  return self::I|(x, y);
+static method I|m(lowered final self::I #this, core::String s, [core::int i = #C2]) → void {}
+static method I|get#m(lowered final self::I #this) → (core::String, [core::int]) → void
+  return (core::String s, [core::int i = #C2]) → void => self::I|m(#this, s, i);
+static method I2|(core::int x, {core::int? y = #C1}) → self::I2 {
+  final self::I2 #this = x.{core::num::+}(let final core::int? #t2 = y in #t2 == null ?{core::int} 87 : #t2{core::int}){(core::num) → core::int};
+  return #this;
+}
+static method I2|get#(core::int x, {core::int? y}) → self::I2
+  return self::I2|(x, y: y);
+static method I2|m(lowered final self::I2 #this, core::String s, {core::int i = #C2}) → void {}
+static method I2|get#m(lowered final self::I2 #this) → (core::String, {i: core::int}) → void
+  return (core::String s, {core::int i = #C2}) → void => self::I2|m(#this, s, i: i);
+static method main() → dynamic {
+  self::expect(42, self::I|(0));
+  self::expect(0, self::I|(0, 0));
+  self::expect(87, self::I2|(0));
+  self::expect(0, self::I2|(0, y: 0));
+}
+static method expect(dynamic expected, dynamic actual) → dynamic {
+  if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual)) {
+    throw "Expected ${expected}, actual ${actual}";
+  }
+}
+
+constants  {
+  #C1 = null
+  #C2 = 1
+}
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index 10c706a..1ab8295 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -129,6 +129,7 @@
 general/var_as_type_name: FormatterCrash
 inline_class/constructor_access: FormatterCrash
 inline_class/constructor_bodies: FormatterCrash
+inline_class/constructor_formal_parameters: FormatterCrash
 inline_class/constructors: FormatterCrash
 inline_class/external: FormatterCrash
 inline_class/field_access: FormatterCrash