[cfe] Implements recursiveScanner to solve copyWith ambiguities

Change-Id: I087e12702d8164ad0a7383bb5b0665d58a516f7c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/162743
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Javier López-Contreras <jlcontreras@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index 53d5493..375ac5c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -1128,7 +1128,7 @@
     if (loader.target.context.options
         .isExperimentEnabledGlobally(ExperimentalFlag.valueClass)) {
       valueClass.transformComponent(
-          component, loader.coreTypes, loader.hierarchy);
+          component, loader.coreTypes, loader.hierarchy, environment);
       ticker.logMs("Lowered value classes");
     }
 
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index 6c237da..660c22a 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -257,4 +257,5 @@
 set_literals/disambiguation_rule: RuntimeError
 value_class/simple: RuntimeError # Expected
 value_class/value_extends_non_value: RuntimeError # Expected
-value_class/value_implements_non_value: RuntimeError # Expected
\ No newline at end of file
+value_class/value_implements_non_value: RuntimeError # Expected
+value_class/copy_with_call_sites: RuntimeError # Expected
\ No newline at end of file
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index 825cdf1..07eb30c 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -255,3 +255,4 @@
 value_class/simple: RuntimeError # Expected
 value_class/value_extends_non_value: RuntimeError # Expected
 value_class/value_implements_non_value: RuntimeError # Expected
+value_class/copy_with_call_sites: RuntimeError # Expected
\ No newline at end of file
diff --git a/pkg/front_end/testcases/value_class/copy_with_call_sites.dart b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart
new file mode 100644
index 0000000..031deb0
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'value_class_support_lib.dart';
+
+class Animal {
+  final int numberOfLegs;
+  Animal({required this.numberOfLegs});
+}
+
+@valueClass
+class Cat extends Animal {
+  final int numberOfWhiskers;
+}
+
+main() {
+ Cat cat = new Cat(numberOfWhiskers: 20, numberOfLegs: 4);
+ (cat as dynamic).copyWith(numberOfWhiskers: 4);
+}
+
+
+
diff --git a/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.outline.expect b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.outline.expect
new file mode 100644
index 0000000..0e4993f
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.outline.expect
@@ -0,0 +1,34 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "value_class_support_lib.dart" as val;
+
+import "org-dartlang-testcase:///value_class_support_lib.dart";
+
+class Animal extends core::Object {
+  final field core::int numberOfLegs;
+  constructor •({required core::int numberOfLegs}) → self::Animal
+    ;
+}
+@val::valueClass
+class Cat extends self::Animal {
+  final field core::int numberOfWhiskers;
+  synthetic constructor •() → self::Cat
+    ;
+}
+static method main() → dynamic
+  ;
+
+library /*isNonNullableByDefault*/;
+import self as val;
+import "dart:core" as core;
+
+class JenkinsSmiHash extends core::Object {
+  synthetic constructor •() → val::JenkinsSmiHash
+    ;
+  static method combine(core::int hash, core::int value) → core::int
+    ;
+  static method finish(core::int hash) → core::int
+    ;
+}
+static const field core::String valueClass = "valueClass";
diff --git a/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.strong.expect b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.strong.expect
new file mode 100644
index 0000000..dd3b9ec
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.strong.expect
@@ -0,0 +1,72 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:18:20: Error: No named parameter with the name 'numberOfWhiskers'.
+//  Cat cat = new Cat(numberOfWhiskers: 20, numberOfLegs: 4);
+//                    ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:13:7: Context: The class 'Cat' has a constructor that takes no arguments.
+// class Cat extends Animal {
+//       ^
+//
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:14:13: Error: Final field 'numberOfWhiskers' is not initialized.
+// Try to initialize the field in the declaration or in every constructor.
+//   final int numberOfWhiskers;
+//             ^^^^^^^^^^^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+import "value_class_support_lib.dart" as val;
+
+import "org-dartlang-testcase:///value_class_support_lib.dart";
+
+class Animal extends core::Object {
+  final field core::int numberOfLegs;
+  constructor •({required core::int numberOfLegs = #C1}) → self::Animal
+    : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
+    ;
+}
+class Cat extends self::Animal {
+  final field core::int numberOfWhiskers = null;
+  synthetic constructor •({required core::int numberOfWhiskers, core::int numberOfLegs}) → self::Cat
+    : self::Cat::numberOfWhiskers = numberOfWhiskers, super self::Animal::•(numberOfLegs)
+    ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Animal::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
+  get /*isNullableByDefault*/ hashCode() → core::int
+    return val::JenkinsSmiHash::finish(val::JenkinsSmiHash::combine(val::JenkinsSmiHash::combine("org-dartlang-testcase:///copy_with_call_sites.dartCat".{core::String::hashCode}, this.{self::Animal::numberOfLegs}.{core::num::hashCode}), this.{self::Cat::numberOfWhiskers}.{core::num::hashCode}));
+  method /*isNullableByDefault*/ copyWith({core::int numberOfLegs, core::int numberOfWhiskers}) → dynamic
+    return new self::Cat::•(numberOfLegs: numberOfLegs, numberOfWhiskers: numberOfWhiskers);
+}
+static method main() → dynamic {
+  self::Cat cat = invalid-expression "pkg/front_end/testcases/value_class/copy_with_call_sites.dart:18:20: Error: No named parameter with the name 'numberOfWhiskers'.
+ Cat cat = new Cat(numberOfWhiskers: 20, numberOfLegs: 4);
+                   ^^^^^^^^^^^^^^^^" as{TypeError,ForDynamic,ForNonNullableByDefault} self::Cat;
+  let final dynamic #t1 = cat as{ForNonNullableByDefault} dynamic in #t1.copyWith(numberOfWhiskers: 4, numberOfLegs: #t1.numberOfLegs);
+}
+
+library /*isNonNullableByDefault*/;
+import self as val;
+import "dart:core" as core;
+
+class JenkinsSmiHash extends core::Object {
+  synthetic constructor •() → val::JenkinsSmiHash
+    : super core::Object::•()
+    ;
+  static method combine(core::int hash, core::int value) → core::int {
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(value));
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(524287.{core::int::&}(hash).{core::int::<<}(10)));
+    return hash.{core::int::^}(hash.{core::int::>>}(6));
+  }
+  static method finish(core::int hash) → core::int {
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(67108863.{core::int::&}(hash).{core::int::<<}(3)));
+    hash = hash.{core::int::^}(hash.{core::int::>>}(11));
+    return 536870911.{core::int::&}(hash.{core::num::+}(16383.{core::int::&}(hash).{core::int::<<}(15)));
+  }
+}
+static const field core::String valueClass = #C2;
+
+constants  {
+  #C1 = null
+  #C2 = "valueClass"
+}
diff --git a/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.strong.transformed.expect b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.strong.transformed.expect
new file mode 100644
index 0000000..f361669
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.strong.transformed.expect
@@ -0,0 +1,72 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:18:20: Error: No named parameter with the name 'numberOfWhiskers'.
+//  Cat cat = new Cat(numberOfWhiskers: 20, numberOfLegs: 4);
+//                    ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:13:7: Context: The class 'Cat' has a constructor that takes no arguments.
+// class Cat extends Animal {
+//       ^
+//
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:14:13: Error: Final field 'numberOfWhiskers' is not initialized.
+// Try to initialize the field in the declaration or in every constructor.
+//   final int numberOfWhiskers;
+//             ^^^^^^^^^^^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+import "value_class_support_lib.dart" as val;
+
+import "org-dartlang-testcase:///value_class_support_lib.dart";
+
+class Animal extends core::Object {
+  final field core::int numberOfLegs;
+  constructor •({required core::int numberOfLegs = #C1}) → self::Animal
+    : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
+    ;
+}
+class Cat extends self::Animal {
+  final field core::int numberOfWhiskers = null;
+  synthetic constructor •({required core::int numberOfWhiskers, core::int numberOfLegs}) → self::Cat
+    : self::Cat::numberOfWhiskers = numberOfWhiskers, super self::Animal::•(numberOfLegs)
+    ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Animal::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
+  get /*isNullableByDefault*/ hashCode() → core::int
+    return val::JenkinsSmiHash::finish(val::JenkinsSmiHash::combine(val::JenkinsSmiHash::combine("org-dartlang-testcase:///copy_with_call_sites.dartCat".{core::String::hashCode}, this.{self::Animal::numberOfLegs}.{core::num::hashCode}), this.{self::Cat::numberOfWhiskers}.{core::num::hashCode}));
+  method /*isNullableByDefault*/ copyWith({core::int numberOfLegs, core::int numberOfWhiskers}) → dynamic
+    return new self::Cat::•(numberOfLegs: numberOfLegs, numberOfWhiskers: numberOfWhiskers);
+}
+static method main() → dynamic {
+  self::Cat cat = invalid-expression "pkg/front_end/testcases/value_class/copy_with_call_sites.dart:18:20: Error: No named parameter with the name 'numberOfWhiskers'.
+ Cat cat = new Cat(numberOfWhiskers: 20, numberOfLegs: 4);
+                   ^^^^^^^^^^^^^^^^";
+  let final dynamic #t1 = cat as{ForNonNullableByDefault} dynamic in #t1.copyWith(numberOfWhiskers: 4, numberOfLegs: #t1.numberOfLegs);
+}
+
+library /*isNonNullableByDefault*/;
+import self as val;
+import "dart:core" as core;
+
+class JenkinsSmiHash extends core::Object {
+  synthetic constructor •() → val::JenkinsSmiHash
+    : super core::Object::•()
+    ;
+  static method combine(core::int hash, core::int value) → core::int {
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(value));
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(524287.{core::int::&}(hash).{core::int::<<}(10)));
+    return hash.{core::int::^}(hash.{core::int::>>}(6));
+  }
+  static method finish(core::int hash) → core::int {
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(67108863.{core::int::&}(hash).{core::int::<<}(3)));
+    hash = hash.{core::int::^}(hash.{core::int::>>}(11));
+    return 536870911.{core::int::&}(hash.{core::num::+}(16383.{core::int::&}(hash).{core::int::<<}(15)));
+  }
+}
+static const field core::String valueClass = #C2;
+
+constants  {
+  #C1 = null
+  #C2 = "valueClass"
+}
diff --git a/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.textual_outline.expect b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.textual_outline.expect
new file mode 100644
index 0000000..93a182d
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.textual_outline.expect
@@ -0,0 +1,13 @@
+import 'value_class_support_lib.dart';
+
+class Animal {
+  final int numberOfLegs;
+  Animal({required this.numberOfLegs});
+}
+
+@valueClass
+class Cat extends Animal {
+  final int numberOfWhiskers;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..ce2b2e3
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.textual_outline_modelled.expect
@@ -0,0 +1,13 @@
+import 'value_class_support_lib.dart';
+
+class Animal {
+  Animal({required this.numberOfLegs});
+  final int numberOfLegs;
+}
+
+@valueClass
+class Cat extends Animal {
+  final int numberOfWhiskers;
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.weak.expect b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.weak.expect
new file mode 100644
index 0000000..dd3b9ec
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.weak.expect
@@ -0,0 +1,72 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:18:20: Error: No named parameter with the name 'numberOfWhiskers'.
+//  Cat cat = new Cat(numberOfWhiskers: 20, numberOfLegs: 4);
+//                    ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:13:7: Context: The class 'Cat' has a constructor that takes no arguments.
+// class Cat extends Animal {
+//       ^
+//
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:14:13: Error: Final field 'numberOfWhiskers' is not initialized.
+// Try to initialize the field in the declaration or in every constructor.
+//   final int numberOfWhiskers;
+//             ^^^^^^^^^^^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+import "value_class_support_lib.dart" as val;
+
+import "org-dartlang-testcase:///value_class_support_lib.dart";
+
+class Animal extends core::Object {
+  final field core::int numberOfLegs;
+  constructor •({required core::int numberOfLegs = #C1}) → self::Animal
+    : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
+    ;
+}
+class Cat extends self::Animal {
+  final field core::int numberOfWhiskers = null;
+  synthetic constructor •({required core::int numberOfWhiskers, core::int numberOfLegs}) → self::Cat
+    : self::Cat::numberOfWhiskers = numberOfWhiskers, super self::Animal::•(numberOfLegs)
+    ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Animal::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
+  get /*isNullableByDefault*/ hashCode() → core::int
+    return val::JenkinsSmiHash::finish(val::JenkinsSmiHash::combine(val::JenkinsSmiHash::combine("org-dartlang-testcase:///copy_with_call_sites.dartCat".{core::String::hashCode}, this.{self::Animal::numberOfLegs}.{core::num::hashCode}), this.{self::Cat::numberOfWhiskers}.{core::num::hashCode}));
+  method /*isNullableByDefault*/ copyWith({core::int numberOfLegs, core::int numberOfWhiskers}) → dynamic
+    return new self::Cat::•(numberOfLegs: numberOfLegs, numberOfWhiskers: numberOfWhiskers);
+}
+static method main() → dynamic {
+  self::Cat cat = invalid-expression "pkg/front_end/testcases/value_class/copy_with_call_sites.dart:18:20: Error: No named parameter with the name 'numberOfWhiskers'.
+ Cat cat = new Cat(numberOfWhiskers: 20, numberOfLegs: 4);
+                   ^^^^^^^^^^^^^^^^" as{TypeError,ForDynamic,ForNonNullableByDefault} self::Cat;
+  let final dynamic #t1 = cat as{ForNonNullableByDefault} dynamic in #t1.copyWith(numberOfWhiskers: 4, numberOfLegs: #t1.numberOfLegs);
+}
+
+library /*isNonNullableByDefault*/;
+import self as val;
+import "dart:core" as core;
+
+class JenkinsSmiHash extends core::Object {
+  synthetic constructor •() → val::JenkinsSmiHash
+    : super core::Object::•()
+    ;
+  static method combine(core::int hash, core::int value) → core::int {
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(value));
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(524287.{core::int::&}(hash).{core::int::<<}(10)));
+    return hash.{core::int::^}(hash.{core::int::>>}(6));
+  }
+  static method finish(core::int hash) → core::int {
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(67108863.{core::int::&}(hash).{core::int::<<}(3)));
+    hash = hash.{core::int::^}(hash.{core::int::>>}(11));
+    return 536870911.{core::int::&}(hash.{core::num::+}(16383.{core::int::&}(hash).{core::int::<<}(15)));
+  }
+}
+static const field core::String valueClass = #C2;
+
+constants  {
+  #C1 = null
+  #C2 = "valueClass"
+}
diff --git a/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.weak.transformed.expect b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.weak.transformed.expect
new file mode 100644
index 0000000..f361669
--- /dev/null
+++ b/pkg/front_end/testcases/value_class/copy_with_call_sites.dart.weak.transformed.expect
@@ -0,0 +1,72 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:18:20: Error: No named parameter with the name 'numberOfWhiskers'.
+//  Cat cat = new Cat(numberOfWhiskers: 20, numberOfLegs: 4);
+//                    ^^^^^^^^^^^^^^^^
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:13:7: Context: The class 'Cat' has a constructor that takes no arguments.
+// class Cat extends Animal {
+//       ^
+//
+// pkg/front_end/testcases/value_class/copy_with_call_sites.dart:14:13: Error: Final field 'numberOfWhiskers' is not initialized.
+// Try to initialize the field in the declaration or in every constructor.
+//   final int numberOfWhiskers;
+//             ^^^^^^^^^^^^^^^^
+//
+import self as self;
+import "dart:core" as core;
+import "value_class_support_lib.dart" as val;
+
+import "org-dartlang-testcase:///value_class_support_lib.dart";
+
+class Animal extends core::Object {
+  final field core::int numberOfLegs;
+  constructor •({required core::int numberOfLegs = #C1}) → self::Animal
+    : self::Animal::numberOfLegs = numberOfLegs, super core::Object::•()
+    ;
+}
+class Cat extends self::Animal {
+  final field core::int numberOfWhiskers = null;
+  synthetic constructor •({required core::int numberOfWhiskers, core::int numberOfLegs}) → self::Cat
+    : self::Cat::numberOfWhiskers = numberOfWhiskers, super self::Animal::•(numberOfLegs)
+    ;
+  operator /*isNullableByDefault*/ ==(core::Object other) → core::bool
+    return other is self::Cat && this.{self::Animal::numberOfLegs}.{core::num::==}(other{self::Cat}.{self::Animal::numberOfLegs}) && this.{self::Cat::numberOfWhiskers}.{core::num::==}(other{self::Cat}.{self::Cat::numberOfWhiskers});
+  get /*isNullableByDefault*/ hashCode() → core::int
+    return val::JenkinsSmiHash::finish(val::JenkinsSmiHash::combine(val::JenkinsSmiHash::combine("org-dartlang-testcase:///copy_with_call_sites.dartCat".{core::String::hashCode}, this.{self::Animal::numberOfLegs}.{core::num::hashCode}), this.{self::Cat::numberOfWhiskers}.{core::num::hashCode}));
+  method /*isNullableByDefault*/ copyWith({core::int numberOfLegs, core::int numberOfWhiskers}) → dynamic
+    return new self::Cat::•(numberOfLegs: numberOfLegs, numberOfWhiskers: numberOfWhiskers);
+}
+static method main() → dynamic {
+  self::Cat cat = invalid-expression "pkg/front_end/testcases/value_class/copy_with_call_sites.dart:18:20: Error: No named parameter with the name 'numberOfWhiskers'.
+ Cat cat = new Cat(numberOfWhiskers: 20, numberOfLegs: 4);
+                   ^^^^^^^^^^^^^^^^";
+  let final dynamic #t1 = cat as{ForNonNullableByDefault} dynamic in #t1.copyWith(numberOfWhiskers: 4, numberOfLegs: #t1.numberOfLegs);
+}
+
+library /*isNonNullableByDefault*/;
+import self as val;
+import "dart:core" as core;
+
+class JenkinsSmiHash extends core::Object {
+  synthetic constructor •() → val::JenkinsSmiHash
+    : super core::Object::•()
+    ;
+  static method combine(core::int hash, core::int value) → core::int {
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(value));
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(524287.{core::int::&}(hash).{core::int::<<}(10)));
+    return hash.{core::int::^}(hash.{core::int::>>}(6));
+  }
+  static method finish(core::int hash) → core::int {
+    hash = 536870911.{core::int::&}(hash.{core::num::+}(67108863.{core::int::&}(hash).{core::int::<<}(3)));
+    hash = hash.{core::int::^}(hash.{core::int::>>}(11));
+    return 536870911.{core::int::&}(hash.{core::num::+}(16383.{core::int::&}(hash).{core::int::<<}(15)));
+  }
+}
+static const field core::String valueClass = #C2;
+
+constants  {
+  #C1 = null
+  #C2 = "valueClass"
+}
diff --git a/pkg/front_end/testcases/weak.status b/pkg/front_end/testcases/weak.status
index fee0310..eae95b4 100644
--- a/pkg/front_end/testcases/weak.status
+++ b/pkg/front_end/testcases/weak.status
@@ -75,4 +75,5 @@
 nnbd_mixed/never_opt_out: TypeCheckError
 value_class/simple: RuntimeError # Expected
 value_class/value_extends_non_value: RuntimeError # Expected
-value_class/value_implements_non_value: RuntimeError # Expected
\ No newline at end of file
+value_class/value_implements_non_value: RuntimeError # Expected
+value_class/copy_with_call_sites: RuntimeError # Expected
\ No newline at end of file
diff --git a/pkg/kernel/lib/transformations/scanner.dart b/pkg/kernel/lib/transformations/scanner.dart
index 6d4a690..ce4b9fc 100644
--- a/pkg/kernel/lib/transformations/scanner.dart
+++ b/pkg/kernel/lib/transformations/scanner.dart
@@ -11,7 +11,7 @@
 }
 
 class ScanResult<X extends TreeNode, Y extends TreeNode> {
-  Map<X, ScanResult<Y, TreeNode>> targets;
+  Map<X, ScanResult<Y, TreeNode>> targets = new Map();
   Map<X, ScanError> errors;
 }
 
@@ -26,11 +26,12 @@
 
   ScanResult<Class, Y> scan(TreeNode node) {
     ScanResult<Class, Y> result = new ScanResult();
-    result.targets = new Map();
 
     if (node is Class) {
       if (predicate(node)) {
         result.targets[node] = next?.scan(node);
+        // TODO(dmitryas): set result.errors when specification is landed,
+        //  same with all other places where targets is set
       }
     } else if (node is Library) {
       _scanLibrary(node, result);
@@ -65,12 +66,9 @@
 
   ScanResult<Field, Y> scan(TreeNode node) {
     ScanResult<Field, Y> result = new ScanResult();
-    result.targets = new Map();
 
     if (node is Field) {
-      if (predicate(node)) {
-        result.targets[node] = next?.scan(node);
-      }
+      _scanField(node, result);
     } else if (node is Class) {
       _scanClass(node, result);
     } else if (node is Library) {
@@ -82,11 +80,15 @@
     return result;
   }
 
+  void _scanField(Field field, ScanResult<Field, Y> result) {
+    if (predicate(field)) {
+      result.targets[field] = next?.scan(field);
+    }
+  }
+
   void _scanClass(Class cls, ScanResult<Field, Y> result) {
     for (Field field in cls.fields) {
-      if (predicate(field)) {
-        result.targets[field] = next?.scan(field);
-      }
+      _scanField(field, result);
     }
   }
 
@@ -95,9 +97,7 @@
       _scanClass(cls, result);
     }
     for (Field field in library.fields) {
-      if (predicate(field)) {
-        result.targets[field] = next?.scan(field);
-      }
+      _scanField(field, result);
     }
   }
 
@@ -108,6 +108,57 @@
   }
 }
 
+abstract class MemberScanner<Y extends TreeNode> implements Scanner<Member, Y> {
+  final Scanner<Y, TreeNode> next;
+
+  MemberScanner(this.next);
+
+  bool predicate(Member node);
+
+  ScanResult<Member, Y> scan(TreeNode node) {
+    ScanResult<Member, Y> result = new ScanResult();
+
+    if (node is Member) {
+      _scanMember(node, result);
+    } else if (node is Class) {
+      _scanClass(node, result);
+    } else if (node is Library) {
+      _scanLibrary(node, result);
+    } else if (node is Component) {
+      _scanComponent(node, result);
+    }
+
+    return result;
+  }
+
+  void _scanMember(Member member, ScanResult<Member, Y> result) {
+    if (predicate(member)) {
+      result.targets[member] = next?.scan(member);
+    }
+  }
+
+  void _scanClass(Class cls, ScanResult<Member, Y> result) {
+    for (Member member in cls.members) {
+      _scanMember(member, result);
+    }
+  }
+
+  void _scanLibrary(Library library, ScanResult<Member, Y> result) {
+    for (Class cls in library.classes) {
+      _scanClass(cls, result);
+    }
+    for (Member member in library.members) {
+      _scanMember(member, result);
+    }
+  }
+
+  void _scanComponent(Component component, ScanResult<Member, Y> result) {
+    for (Library library in component.libraries) {
+      _scanLibrary(library, result);
+    }
+  }
+}
+
 abstract class ProcedureScanner<Y extends TreeNode>
     implements Scanner<Procedure, Y> {
   final Scanner<Y, TreeNode> next;
@@ -118,12 +169,9 @@
 
   ScanResult<Procedure, Y> scan(TreeNode node) {
     ScanResult<Procedure, Y> result = new ScanResult();
-    result.targets = new Map();
 
     if (node is Procedure) {
-      if (predicate(node)) {
-        result.targets[node] = next?.scan(node);
-      }
+      _scanProcedure(node, result);
     } else if (node is Class) {
       _scanClass(node, result);
     } else if (node is Library) {
@@ -135,11 +183,15 @@
     return result;
   }
 
+  void _scanProcedure(Procedure procedure, ScanResult<Procedure, Y> result) {
+    if (predicate(procedure)) {
+      result.targets[procedure] = next?.scan(procedure);
+    }
+  }
+
   void _scanClass(Class cls, ScanResult<Procedure, Y> result) {
     for (Procedure procedure in cls.procedures) {
-      if (predicate(procedure)) {
-        result.targets[procedure] = next?.scan(procedure);
-      }
+      _scanProcedure(procedure, result);
     }
   }
 
@@ -148,9 +200,7 @@
       _scanClass(cls, result);
     }
     for (Procedure procedure in library.procedures) {
-      if (predicate(procedure)) {
-        result.targets[procedure] = next?.scan(procedure);
-      }
+      _scanProcedure(procedure, result);
     }
   }
 
@@ -160,3 +210,51 @@
     }
   }
 }
+
+abstract class ExpressionScanner<Y extends TreeNode>
+    extends RecursiveVisitor<void> implements Scanner<Expression, Y> {
+  final Scanner<Y, TreeNode> next;
+  ScanResult<Expression, Y> _result;
+
+  ExpressionScanner(this.next);
+
+  bool predicate(Expression node);
+
+  ScanResult<Expression, Y> scan(TreeNode node) {
+    ScanResult<Expression, Y> result = _result = new ScanResult();
+    node.accept(this);
+    _result = null;
+    return result;
+  }
+
+  void visitExpression(Expression node) {
+    if (predicate(node)) {
+      _result.targets[node] = next?.scan(node);
+      // TODO: Update result.errors.
+    }
+  }
+}
+
+abstract class MethodInvocationScanner<Y extends TreeNode>
+    extends RecursiveVisitor<void> implements Scanner<MethodInvocation, Y> {
+  final Scanner<Y, TreeNode> next;
+  ScanResult<MethodInvocation, Y> _result;
+
+  MethodInvocationScanner(this.next);
+
+  bool predicate(MethodInvocation node);
+
+  ScanResult<MethodInvocation, Y> scan(TreeNode node) {
+    ScanResult<MethodInvocation, Y> result = _result = new ScanResult();
+    node.accept(this);
+    _result = null;
+    return result;
+  }
+
+  void visitMethodInvocation(MethodInvocation node) {
+    if (predicate(node)) {
+      _result.targets[node] = next?.scan(node);
+      // TODO: Update result.errors.
+    }
+  }
+}
diff --git a/pkg/kernel/lib/transformations/value_class.dart b/pkg/kernel/lib/transformations/value_class.dart
index c760a89..c616cea 100644
--- a/pkg/kernel/lib/transformations/value_class.dart
+++ b/pkg/kernel/lib/transformations/value_class.dart
@@ -4,6 +4,8 @@
 
 library kernel.transformations.value_class;
 
+import 'package:kernel/type_environment.dart';
+
 import '../ast.dart';
 import '../kernel.dart';
 import '../core_types.dart' show CoreTypes;
@@ -13,18 +15,7 @@
 class ValueClassScanner extends ClassScanner<Null> {
   ValueClassScanner() : super(null);
 
-  bool predicate(Class node) {
-    for (Expression annotation in node.annotations) {
-      if (annotation is ConstantExpression &&
-          annotation.constant is StringConstant) {
-        StringConstant constant = annotation.constant;
-        if (constant.value == 'valueClass') {
-          return true;
-        }
-      }
-    }
-    return false;
-  }
+  bool predicate(Class node) => isValueClass(node);
 }
 
 class JenkinsClassScanner extends ClassScanner<Procedure> {
@@ -43,17 +34,48 @@
   }
 }
 
-void transformComponent(
-    Component node, CoreTypes coreTypes, ClassHierarchy hierarchy) {
-  ValueClassScanner scanner = new ValueClassScanner();
-  ScanResult<Class, Null> valueClasses = scanner.scan(node);
-  for (Class valueClass in valueClasses.targets.keys) {
-    transformValueClass(valueClass, coreTypes, hierarchy);
+class AllMemberScanner extends MemberScanner<MethodInvocation> {
+  AllMemberScanner(Scanner<MethodInvocation, TreeNode> next) : super(next);
+
+  bool predicate(Member member) => true;
+}
+
+// Scans and matches all copyWith invocations were the reciever is _ as dynamic
+// It will filter out the results that are not value classes afterwards
+class ValueClassCopyWithScanner extends MethodInvocationScanner<Null> {
+  ValueClassCopyWithScanner() : super(null);
+
+  // The matching construct followed in unit-tests is:
+  // @valueClass V {}
+  // V v;
+  // (v as dynamic).copyWith() as V
+  bool predicate(MethodInvocation node) {
+    return node.name.name == "copyWith" &&
+        _isValueClassAsConstruct(node.receiver);
+  }
+
+  bool _isValueClassAsConstruct(Expression node) {
+    return node is AsExpression && node.type is DynamicType;
   }
 }
 
-void transformValueClass(
-    Class cls, CoreTypes coreTypes, ClassHierarchy hierarchy) {
+void transformComponent(Component node, CoreTypes coreTypes,
+    ClassHierarchy hierarchy, TypeEnvironment typeEnvironment) {
+  ValueClassScanner scanner = new ValueClassScanner();
+  ScanResult<Class, Null> valueClasses = scanner.scan(node);
+  for (Class valueClass in valueClasses.targets.keys) {
+    transformValueClass(valueClass, coreTypes, hierarchy, typeEnvironment);
+  }
+
+  treatCopyWithCallSites(node, coreTypes, typeEnvironment, hierarchy);
+
+  for (Class valueClass in valueClasses.targets.keys) {
+    removeValueClassAnnotation(valueClass);
+  }
+}
+
+void transformValueClass(Class cls, CoreTypes coreTypes,
+    ClassHierarchy hierarchy, TypeEnvironment typeEnvironment) {
   List<VariableDeclaration> allVariables = queryAllInstanceVariables(cls);
   Constructor syntheticConstructor = null;
   for (Constructor constructor in cls.constructors) {
@@ -65,8 +87,8 @@
   addConstructor(cls, coreTypes, syntheticConstructor);
   addEqualsOperator(cls, coreTypes, hierarchy, allVariables.toList());
   addHashCode(cls, coreTypes, hierarchy, allVariables.toList());
-  addCopyWith(
-      cls, coreTypes, hierarchy, allVariables.toList(), syntheticConstructor);
+  addCopyWith(cls, coreTypes, hierarchy, allVariables.toList(),
+      syntheticConstructor, typeEnvironment);
 }
 
 void addConstructor(
@@ -103,21 +125,6 @@
     ..addAll(ownFields.values)
     ..addAll(superParameters);
   syntheticConstructor.initializers = initializersConstructor;
-
-  int valueClassAnnotationIndex;
-  for (int annotationIndex = 0;
-      annotationIndex < cls.annotations.length;
-      annotationIndex++) {
-    Expression annotation = cls.annotations[annotationIndex];
-    if (annotation is ConstantExpression &&
-        annotation.constant is StringConstant) {
-      StringConstant constant = annotation.constant;
-      if (constant.value == 'valueClass') {
-        valueClassAnnotationIndex = annotationIndex;
-      }
-    }
-  }
-  cls.annotations.removeAt(valueClassAnnotationIndex);
 }
 
 void addEqualsOperator(Class cls, CoreTypes coreTypes, ClassHierarchy hierarchy,
@@ -245,8 +252,13 @@
     ..fileOffset = cls.fileOffset);
 }
 
-void addCopyWith(Class cls, CoreTypes coreTypes, ClassHierarchy hierarchy,
-    List<VariableDeclaration> allVariables, Constructor syntheticConstructor) {
+void addCopyWith(
+    Class cls,
+    CoreTypes coreTypes,
+    ClassHierarchy hierarchy,
+    List<VariableDeclaration> allVariables,
+    Constructor syntheticConstructor,
+    TypeEnvironment typeEnvironment) {
   Map<VariableDeclaration, Member> targetsEquals = new Map();
   Map<VariableDeclaration, Member> targets = new Map();
   for (VariableDeclaration variable in allVariables) {
@@ -291,3 +303,98 @@
         ..addAll(cls.fields.map<VariableDeclaration>(
             (f) => VariableDeclaration(f.name.text, type: f.type)));
 }
+
+void removeValueClassAnnotation(Class cls) {
+  int valueClassAnnotationIndex;
+  for (int annotationIndex = 0;
+      annotationIndex < cls.annotations.length;
+      annotationIndex++) {
+    Expression annotation = cls.annotations[annotationIndex];
+    if (annotation is ConstantExpression &&
+        annotation.constant is StringConstant) {
+      StringConstant constant = annotation.constant;
+      if (constant.value == 'valueClass') {
+        valueClassAnnotationIndex = annotationIndex;
+      }
+    }
+  }
+  cls.annotations.removeAt(valueClassAnnotationIndex);
+}
+
+void treatCopyWithCallSites(Component component, CoreTypes coreTypes,
+    TypeEnvironment typeEnvironment, ClassHierarchy hierarchy) {
+  ValueClassCopyWithScanner valueCopyWithScanner =
+      new ValueClassCopyWithScanner();
+  AllMemberScanner copyWithScanner = AllMemberScanner(valueCopyWithScanner);
+  ScanResult<Member, MethodInvocation> copyWithCallSites =
+      copyWithScanner.scan(component);
+  for (Member memberWithCopyWith in copyWithCallSites.targets.keys) {
+    if (copyWithCallSites.targets[memberWithCopyWith].targets != null) {
+      StaticTypeContext staticTypeContext =
+          StaticTypeContext(memberWithCopyWith, typeEnvironment);
+      for (MethodInvocation copyWithCall
+          in copyWithCallSites.targets[memberWithCopyWith].targets.keys) {
+        AsExpression receiver = copyWithCall.receiver as AsExpression;
+
+        Expression valueClassInstance = receiver.operand;
+        DartType valueClassType =
+            valueClassInstance.getStaticType(staticTypeContext);
+        if (valueClassType is InterfaceType) {
+          Class valueClass = valueClassType.classNode;
+          if (isValueClass(valueClass)) {
+            treatCopyWithCallSite(
+                valueClass, copyWithCall, coreTypes, hierarchy);
+          }
+        }
+      }
+    }
+  }
+}
+
+void treatCopyWithCallSite(Class valueClass, MethodInvocation copyWithCall,
+    CoreTypes coreTypes, ClassHierarchy hierarchy) {
+  Map<String, Expression> preTransformationArguments = new Map();
+  for (NamedExpression argument in copyWithCall.arguments.named) {
+    preTransformationArguments[argument.name] = argument.value;
+  }
+  Constructor syntheticConstructor;
+  for (Constructor constructor in valueClass.constructors) {
+    if (constructor.isSynthetic) {
+      syntheticConstructor = constructor;
+    }
+  }
+  List<VariableDeclaration> allArguments =
+      syntheticConstructor.function.namedParameters;
+
+  VariableDeclaration letVariable =
+      VariableDeclaration.forValue(copyWithCall.receiver);
+  Arguments postTransformationArguments = Arguments.empty();
+  for (VariableDeclaration argument in allArguments) {
+    if (preTransformationArguments.containsKey(argument.name)) {
+      postTransformationArguments.named.add(NamedExpression(
+          argument.name, preTransformationArguments[argument.name])
+        ..parent = postTransformationArguments);
+    } else {
+      postTransformationArguments.named.add(NamedExpression(argument.name,
+          PropertyGet(VariableGet(letVariable), Name(argument.name)))
+        ..parent = postTransformationArguments);
+    }
+  }
+  copyWithCall.replaceWith(Let(
+      letVariable,
+      MethodInvocation(VariableGet(letVariable), Name("copyWith"),
+          postTransformationArguments)));
+}
+
+bool isValueClass(Class node) {
+  for (Expression annotation in node.annotations) {
+    if (annotation is ConstantExpression &&
+        annotation.constant is StringConstant) {
+      StringConstant constant = annotation.constant;
+      if (constant.value == "valueClass") {
+        return true;
+      }
+    }
+  }
+  return false;
+}