Version 2.14.0-62.0.dev

Merge commit 'a89fccb482cb5d263ebbe0191dbd201f7af2603a' into 'dev'
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
index 5a8d94b..317b6a8 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/perform_refactor.dart
@@ -164,6 +164,11 @@
             InlineLocalRefactoring(server.searchEngine, result, offset);
         return success(refactor);
 
+      case RefactoringKind.INLINE_METHOD:
+        final refactor =
+            InlineMethodRefactoring(server.searchEngine, result, offset);
+        return success(refactor);
+
       default:
         return error(ServerErrorCodes.InvalidCommandArguments,
             'Unknown RefactoringKind $kind was supplied to $commandName');
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index 2d786af..322e9d8d 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -409,6 +409,7 @@
         }
       }
 
+      // Inlines
       if (shouldIncludeKind(CodeActionKind.RefactorInline)) {
         // Inline Local Variable
         if (InlineLocalRefactoring(server.searchEngine, unit, offset)
@@ -416,6 +417,13 @@
           refactorActions.add(createRefactor(CodeActionKind.RefactorInline,
               'Inline Local Variable', RefactoringKind.INLINE_LOCAL_VARIABLE));
         }
+
+        // Inline Method
+        if (InlineMethodRefactoring(server.searchEngine, unit, offset)
+            .isAvailable()) {
+          refactorActions.add(createRefactor(CodeActionKind.RefactorInline,
+              'Inline Method', RefactoringKind.INLINE_METHOD));
+        }
       }
 
       return refactorActions;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart b/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
index 47362ca..1cff243 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
@@ -290,6 +290,39 @@
     return Future.value(change);
   }
 
+  @override
+  bool isAvailable() {
+    return !_checkOffset().hasFatalError;
+  }
+
+  /// Checks if [offset] is a method that can be inlined.
+  RefactoringStatus _checkOffset() {
+    var fatalStatus = RefactoringStatus.fatal(
+        'Method declaration or reference must be selected to activate this refactoring.');
+
+    var identifier = NodeLocator(offset).searchWithin(resolveResult.unit);
+    if (identifier is! SimpleIdentifier) {
+      return fatalStatus;
+    }
+    var element = identifier.writeOrReadElement;
+    if (element is! ExecutableElement) {
+      return fatalStatus;
+    }
+    if (element.isSynthetic) {
+      return fatalStatus;
+    }
+    // maybe operator
+    if (element.isOperator) {
+      return RefactoringStatus.fatal('Cannot inline operator.');
+    }
+    // maybe [a]sync*
+    if (element.isGenerator) {
+      return RefactoringStatus.fatal('Cannot inline a generator.');
+    }
+
+    return RefactoringStatus();
+  }
+
   _SourcePart _createSourcePart(SourceRange range) {
     var source = _methodUtils.getRangeText(range);
     var prefix = getLinePrefix(source);
diff --git a/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart b/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
index 23b8552..cdb3485 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
@@ -242,6 +242,10 @@
 
   /// The name of the method (or function) being inlined.
   String? get methodName;
+
+  /// Return `true` if refactoring is available, possibly without checking all
+  /// initial conditions.
+  bool isAvailable();
 }
 
 /// [Refactoring] to move/rename a file.
diff --git a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
index 8d87a88..791c627d 100644
--- a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
@@ -19,6 +19,7 @@
     defineReflectiveTests(ExtractWidgetRefactorCodeActionsTest);
     defineReflectiveTests(ExtractVariableRefactorCodeActionsTest);
     defineReflectiveTests(InlineLocalVariableRefactorCodeActionsTest);
+    defineReflectiveTests(InlineMethodRefactorCodeActionsTest);
   });
 }
 
@@ -515,7 +516,7 @@
 @reflectiveTest
 class InlineLocalVariableRefactorCodeActionsTest
     extends AbstractCodeActionsTest {
-  final extractVariableTitle = 'Inline Local Variable';
+  final inlineVariableTitle = 'Inline Local Variable';
 
   Future<void> test_appliesCorrectEdits() async {
     const content = '''
@@ -539,7 +540,86 @@
     final codeActions = await getCodeActions(mainFileUri.toString(),
         position: positionFromMarker(content));
     final codeAction = findCommand(
-        codeActions, Commands.performRefactor, extractVariableTitle)!;
+        codeActions, Commands.performRefactor, inlineVariableTitle)!;
+
+    await verifyCodeActionEdits(
+        codeAction, withoutMarkers(content), expectedContent);
+  }
+}
+
+@reflectiveTest
+class InlineMethodRefactorCodeActionsTest extends AbstractCodeActionsTest {
+  final inlineMethodTitle = 'Inline Method';
+
+  Future<void> test_inlineAtCallSite() async {
+    const content = '''
+void foo1() {
+  ba^r();
+}
+
+void foo2() {
+  bar();
+}
+
+void bar() {
+  print('test');
+}
+    ''';
+    const expectedContent = '''
+void foo1() {
+  print('test');
+}
+
+void foo2() {
+  bar();
+}
+
+void bar() {
+  print('test');
+}
+    ''';
+    newFile(mainFilePath, content: withoutMarkers(content));
+    await initialize();
+
+    final codeActions = await getCodeActions(mainFileUri.toString(),
+        position: positionFromMarker(content));
+    final codeAction =
+        findCommand(codeActions, Commands.performRefactor, inlineMethodTitle)!;
+
+    await verifyCodeActionEdits(
+        codeAction, withoutMarkers(content), expectedContent);
+  }
+
+  Future<void> test_inlineAtMethod() async {
+    const content = '''
+void foo1() {
+  bar();
+}
+
+void foo2() {
+  bar();
+}
+
+void ba^r() {
+  print('test');
+}
+    ''';
+    const expectedContent = '''
+void foo1() {
+  print('test');
+}
+
+void foo2() {
+  print('test');
+}
+    ''';
+    newFile(mainFilePath, content: withoutMarkers(content));
+    await initialize();
+
+    final codeActions = await getCodeActions(mainFileUri.toString(),
+        position: positionFromMarker(content));
+    final codeAction =
+        findCommand(codeActions, Commands.performRefactor, inlineMethodTitle)!;
 
     await verifyCodeActionEdits(
         codeAction, withoutMarkers(content), expectedContent);
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index c4fe216..d200491 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -3056,7 +3056,21 @@
         if (value is AbortConstant) return value;
         env.addVariableValue(parameter, value);
       }
-      return executeBody(function.body);
+
+      final Constant result = executeBody(function.body);
+      if (result is NullConstant &&
+          function.returnType.nullability == Nullability.nonNullable) {
+        // Ensure that the evaluated constant returned is not null if the
+        // function has a non-nullable return type.
+        return createErrorConstant(
+            function,
+            templateConstEvalInvalidType.withArguments(
+                result,
+                function.returnType,
+                result.getType(_staticTypeContext),
+                isNonNullableByDefault));
+      }
+      return result;
     }
 
     if (functionEnvironment != null) {
@@ -3822,6 +3836,8 @@
     if (node.initializer != null) {
       value = evaluate(node.initializer);
       if (value is AbortConstant) return new AbortStatus(value);
+    } else {
+      value = new NullConstant();
     }
     exprEvaluator.env.addVariableValue(node, value);
     return const ProceedStatus();
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart
index 20071a4..7f6d6fe 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart
@@ -62,6 +62,12 @@
   return a;
 }
 
+const var9 = function9();
+int? function9() {
+  int? x;
+  return x;
+}
+
 void main() {
   Expect.equals(var1, 4);
   Expect.equals(var1_1, 5);
@@ -72,4 +78,5 @@
   Expect.equals(var6, 2);
   Expect.equals(var7, 2);
   Expect.equals(var8, 2);
+  Expect.equals(var9, null);
 }
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.expect
index e424e71..d270f74 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.expect
@@ -14,6 +14,7 @@
 static const field core::int var6 = #C5;
 static const field core::int var7 = #C5;
 static const field core::int var8 = #C5;
+static const field core::int? var9 = #C7;
 static method function1(core::int a, core::int b) → core::int {
   core::int x = 1.{core::num::+}(a).{core::num::+}(b);
   return x;
@@ -52,6 +53,10 @@
   a = 2;
   return a as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
 }
+static method function9() → core::int? {
+  core::int? x;
+  return x;
+}
 static method main() → void {
   exp::Expect::equals(#C1, 4);
   exp::Expect::equals(#C2, 5);
@@ -62,6 +67,7 @@
   exp::Expect::equals(#C5, 2);
   exp::Expect::equals(#C5, 2);
   exp::Expect::equals(#C5, 2);
+  exp::Expect::equals(#C7, null);
 }
 
 constants  {
@@ -71,4 +77,5 @@
   #C4 = 6
   #C5 = 2
   #C6 = -2
+  #C7 = null
 }
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.transformed.expect
index 611523b..3ec97cd 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.transformed.expect
@@ -14,6 +14,7 @@
 static const field core::int var6 = #C5;
 static const field core::int var7 = #C5;
 static const field core::int var8 = #C5;
+static const field core::int? var9 = #C7;
 static method function1(core::int a, core::int b) → core::int {
   core::int x = 1.{core::num::+}(a).{core::num::+}(b);
   return x;
@@ -52,6 +53,10 @@
   a = 2;
   return a as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
 }
+static method function9() → core::int? {
+  core::int? x;
+  return x;
+}
 static method main() → void {
   exp::Expect::equals(#C1, 4);
   exp::Expect::equals(#C2, 5);
@@ -62,6 +67,7 @@
   exp::Expect::equals(#C5, 2);
   exp::Expect::equals(#C5, 2);
   exp::Expect::equals(#C5, 2);
+  exp::Expect::equals(#C7, null);
 }
 
 constants  {
@@ -71,8 +77,9 @@
   #C4 = 6
   #C5 = 2
   #C6 = -2
+  #C7 = null
 }
 
 Extra constant evaluation status:
-Evaluated: MethodInvocation @ org-dartlang-testcase:///const_functions_variable_declarations.dart:71:23 -> IntConstant(-2)
-Extra constant evaluation: evaluated: 33, effectively constant: 1
+Evaluated: MethodInvocation @ org-dartlang-testcase:///const_functions_variable_declarations.dart:77:23 -> IntConstant(-2)
+Extra constant evaluation: evaluated: 35, effectively constant: 1
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline.expect
index 1c817fd..2a0d7a9 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline.expect
@@ -17,4 +17,6 @@
 int function7() {}
 const var8 = function8();
 int function8() {}
+const var9 = function9();
+int? function9() {}
 void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline_modelled.expect
index 2d98733..519ab22 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline_modelled.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline_modelled.expect
@@ -10,6 +10,8 @@
 const var6 = function6();
 const var7 = function7();
 const var8 = function8();
+const var9 = function9();
+int? function9() {}
 int function1(int a, int b) {}
 int function3() {}
 int function4() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.expect
index e424e71..d270f74 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.expect
@@ -14,6 +14,7 @@
 static const field core::int var6 = #C5;
 static const field core::int var7 = #C5;
 static const field core::int var8 = #C5;
+static const field core::int? var9 = #C7;
 static method function1(core::int a, core::int b) → core::int {
   core::int x = 1.{core::num::+}(a).{core::num::+}(b);
   return x;
@@ -52,6 +53,10 @@
   a = 2;
   return a as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
 }
+static method function9() → core::int? {
+  core::int? x;
+  return x;
+}
 static method main() → void {
   exp::Expect::equals(#C1, 4);
   exp::Expect::equals(#C2, 5);
@@ -62,6 +67,7 @@
   exp::Expect::equals(#C5, 2);
   exp::Expect::equals(#C5, 2);
   exp::Expect::equals(#C5, 2);
+  exp::Expect::equals(#C7, null);
 }
 
 constants  {
@@ -71,4 +77,5 @@
   #C4 = 6
   #C5 = 2
   #C6 = -2
+  #C7 = null
 }
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.outline.expect
index 50fbc0f..09275b2 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.outline.expect
@@ -13,6 +13,7 @@
 static const field core::int var6 = self::function6();
 static const field core::int var7 = self::function7();
 static const field core::int var8 = self::function8();
+static const field core::int? var9 = self::function9();
 static method function1(core::int a, core::int b) → core::int
   ;
 static method function2() → core::String
@@ -29,5 +30,7 @@
   ;
 static method function8() → core::int
   ;
+static method function9() → core::int?
+  ;
 static method main() → void
   ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.transformed.expect
index 611523b..3ec97cd 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.transformed.expect
@@ -14,6 +14,7 @@
 static const field core::int var6 = #C5;
 static const field core::int var7 = #C5;
 static const field core::int var8 = #C5;
+static const field core::int? var9 = #C7;
 static method function1(core::int a, core::int b) → core::int {
   core::int x = 1.{core::num::+}(a).{core::num::+}(b);
   return x;
@@ -52,6 +53,10 @@
   a = 2;
   return a as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
 }
+static method function9() → core::int? {
+  core::int? x;
+  return x;
+}
 static method main() → void {
   exp::Expect::equals(#C1, 4);
   exp::Expect::equals(#C2, 5);
@@ -62,6 +67,7 @@
   exp::Expect::equals(#C5, 2);
   exp::Expect::equals(#C5, 2);
   exp::Expect::equals(#C5, 2);
+  exp::Expect::equals(#C7, null);
 }
 
 constants  {
@@ -71,8 +77,9 @@
   #C4 = 6
   #C5 = 2
   #C6 = -2
+  #C7 = null
 }
 
 Extra constant evaluation status:
-Evaluated: MethodInvocation @ org-dartlang-testcase:///const_functions_variable_declarations.dart:71:23 -> IntConstant(-2)
-Extra constant evaluation: evaluated: 33, effectively constant: 1
+Evaluated: MethodInvocation @ org-dartlang-testcase:///const_functions_variable_declarations.dart:77:23 -> IntConstant(-2)
+Extra constant evaluation: evaluated: 35, effectively constant: 1
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart
new file mode 100644
index 0000000..e8b7643
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2021, 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.
+
+// Tests erroneous variable declaration usage within const functions.
+
+import "package:expect/expect.dart";
+
+const var1 = fn1();
+int fn1() {
+  var a;
+  return a;
+}
+
+const var2 = fn2();
+int fn2() {
+  var x;
+  x = "string";
+  return x;
+}
+
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.strong.expect
new file mode 100644
index 0000000..3943426
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.strong.expect
@@ -0,0 +1,41 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:9:14: Error: Constant evaluation error:
+// const var1 = fn1();
+//              ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:12:10: Context: Expected constant 'null' to be of type 'int', but was of type 'Null'.
+//   return a;
+//          ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:9:7: Context: While analyzing:
+// const var1 = fn1();
+//       ^
+//
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:15:14: Error: Constant evaluation error:
+// const var2 = fn2();
+//              ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:19:10: Context: Expected constant '"string"' to be of type 'int', but was of type 'String'.
+//   return x;
+//          ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:15:7: Context: While analyzing:
+// const var2 = fn2();
+//       ^
+//
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = invalid-expression "Expected constant 'null' to be of type 'int', but was of type 'Null'.";
+static const field core::int var2 = invalid-expression "Expected constant '\"string\"' to be of type 'int', but was of type 'String'.";
+static method fn1() → core::int {
+  dynamic a;
+  return a as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
+}
+static method fn2() → core::int {
+  dynamic x;
+  x = "string";
+  return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
+}
+static method main() → void {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.strong.transformed.expect
new file mode 100644
index 0000000..3943426
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.strong.transformed.expect
@@ -0,0 +1,41 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:9:14: Error: Constant evaluation error:
+// const var1 = fn1();
+//              ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:12:10: Context: Expected constant 'null' to be of type 'int', but was of type 'Null'.
+//   return a;
+//          ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:9:7: Context: While analyzing:
+// const var1 = fn1();
+//       ^
+//
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:15:14: Error: Constant evaluation error:
+// const var2 = fn2();
+//              ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:19:10: Context: Expected constant '"string"' to be of type 'int', but was of type 'String'.
+//   return x;
+//          ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:15:7: Context: While analyzing:
+// const var2 = fn2();
+//       ^
+//
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = invalid-expression "Expected constant 'null' to be of type 'int', but was of type 'Null'.";
+static const field core::int var2 = invalid-expression "Expected constant '\"string\"' to be of type 'int', but was of type 'String'.";
+static method fn1() → core::int {
+  dynamic a;
+  return a as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
+}
+static method fn2() → core::int {
+  dynamic x;
+  x = "string";
+  return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
+}
+static method main() → void {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.textual_outline.expect
new file mode 100644
index 0000000..422a5ed
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.textual_outline.expect
@@ -0,0 +1,7 @@
+import "package:expect/expect.dart";
+
+const var1 = fn1();
+int fn1() {}
+const var2 = fn2();
+int fn2() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..d67a0d9
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.textual_outline_modelled.expect
@@ -0,0 +1,7 @@
+import "package:expect/expect.dart";
+
+const var1 = fn1();
+const var2 = fn2();
+int fn1() {}
+int fn2() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.weak.expect
new file mode 100644
index 0000000..9d6fa65
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.weak.expect
@@ -0,0 +1,41 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:9:14: Error: Constant evaluation error:
+// const var1 = fn1();
+//              ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:10:8: Context: Expected constant 'null' to be of type 'int', but was of type 'Null'.
+// int fn1() {
+//        ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:9:7: Context: While analyzing:
+// const var1 = fn1();
+//       ^
+//
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:15:14: Error: Constant evaluation error:
+// const var2 = fn2();
+//              ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:19:10: Context: Expected constant '"string"' to be of type 'int', but was of type 'String'.
+//   return x;
+//          ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:15:7: Context: While analyzing:
+// const var2 = fn2();
+//       ^
+//
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = invalid-expression "Expected constant 'null' to be of type 'int', but was of type 'Null'.";
+static const field core::int var2 = invalid-expression "Expected constant '\"string\"' to be of type 'int', but was of type 'String'.";
+static method fn1() → core::int {
+  dynamic a;
+  return a as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
+}
+static method fn2() → core::int {
+  dynamic x;
+  x = "string";
+  return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
+}
+static method main() → void {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.weak.outline.expect
new file mode 100644
index 0000000..68a781d
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.weak.outline.expect
@@ -0,0 +1,14 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = self::fn1();
+static const field core::int var2 = self::fn2();
+static method fn1() → core::int
+  ;
+static method fn2() → core::int
+  ;
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.weak.transformed.expect
new file mode 100644
index 0000000..9d6fa65
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart.weak.transformed.expect
@@ -0,0 +1,41 @@
+library /*isNonNullableByDefault*/;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:9:14: Error: Constant evaluation error:
+// const var1 = fn1();
+//              ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:10:8: Context: Expected constant 'null' to be of type 'int', but was of type 'Null'.
+// int fn1() {
+//        ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:9:7: Context: While analyzing:
+// const var1 = fn1();
+//       ^
+//
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:15:14: Error: Constant evaluation error:
+// const var2 = fn2();
+//              ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:19:10: Context: Expected constant '"string"' to be of type 'int', but was of type 'String'.
+//   return x;
+//          ^
+// pkg/front_end/testcases/const_functions/const_functions_variable_declarations_error.dart:15:7: Context: While analyzing:
+// const var2 = fn2();
+//       ^
+//
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = invalid-expression "Expected constant 'null' to be of type 'int', but was of type 'Null'.";
+static const field core::int var2 = invalid-expression "Expected constant '\"string\"' to be of type 'int', but was of type 'String'.";
+static method fn1() → core::int {
+  dynamic a;
+  return a as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
+}
+static method fn2() → core::int {
+  dynamic x;
+  x = "string";
+  return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::int;
+}
+static method main() → void {}
diff --git a/runtime/vm/compiler/relocation.cc b/runtime/vm/compiler/relocation.cc
index e2f44aa..428a84f 100644
--- a/runtime/vm/compiler/relocation.cc
+++ b/runtime/vm/compiler/relocation.cc
@@ -81,13 +81,8 @@
   auto& current_caller = Code::Handle(zone);
   auto& call_targets = Array::Handle(zone);
 
-  // Do one linear pass over all code objects and determine:
-  //
-  //    * the maximum instruction size
-  //    * the maximum number of calls
-  //    * the maximum offset into a target instruction
-  //
-  FindLargestInstruction();
+  auto& next_caller = Code::Handle(zone);
+  auto& next_caller_targets = Array::Handle(zone);
 
   // Emit all instructions and do relocations on the way.
   for (intptr_t i = 0; i < code_objects_->length(); ++i) {
@@ -106,8 +101,14 @@
 
     // If we have forward/backwards calls which are almost out-of-range, we'll
     // create trampolines now.
-    BuildTrampolinesForAlmostOutOfRangeCalls(
-        /*force=*/(i == (code_objects_->length() - 1)));
+    if (i < (code_objects_->length() - 1)) {
+      next_caller = (*code_objects_)[i + 1];
+      next_caller_targets = next_caller.static_calls_target_table();
+    } else {
+      next_caller = Code::null();
+      next_caller_targets = Array::null();
+    }
+    BuildTrampolinesForAlmostOutOfRangeCalls(next_caller, next_caller_targets);
   }
 
   // We're guaranteed to have all calls resolved, since
@@ -143,40 +144,6 @@
   // however we might need it to write information into V8 snapshot profile.
 }
 
-void CodeRelocator::FindLargestInstruction() {
-  auto zone = thread_->zone();
-  auto& current_caller = Code::Handle(zone);
-  auto& call_targets = Array::Handle(zone);
-
-  for (intptr_t i = 0; i < code_objects_->length(); ++i) {
-    current_caller = (*code_objects_)[i];
-    const intptr_t size =
-        ImageWriter::SizeInSnapshot(current_caller.instructions());
-    if (size > max_instructions_size_) {
-      max_instructions_size_ = size;
-    }
-
-    call_targets = current_caller.static_calls_target_table();
-    if (!call_targets.IsNull()) {
-      intptr_t num_calls = 0;
-      StaticCallsTable calls(call_targets);
-      for (auto call : calls) {
-        kind_type_and_offset_ = call.Get<Code::kSCallTableKindAndOffset>();
-        const auto kind =
-            Code::KindField::decode(kind_type_and_offset_.Value());
-        if (kind == Code::kCallViaCode) {
-          continue;
-        }
-        num_calls++;
-      }
-
-      if (num_calls > max_calls_) {
-        max_calls_ = num_calls;
-      }
-    }
-  }
-}
-
 bool CodeRelocator::AddInstructionsToText(CodePtr code) {
   InstructionsPtr instructions = Code::InstructionsOf(code);
 
@@ -418,11 +385,11 @@
   const auto forward_distance =
       target_text_offset - unresolved_call->text_offset;
   if (unresolved_call->is_tail_call) {
-    return TailCallDistanceLimits::Lower() < forward_distance &&
-           forward_distance < TailCallDistanceLimits::Upper();
+    return TailCallDistanceLimits::Lower() <= forward_distance &&
+           forward_distance <= TailCallDistanceLimits::Upper();
   } else {
-    return CallDistanceLimits::Lower() < forward_distance &&
-           forward_distance < CallDistanceLimits::Upper();
+    return CallDistanceLimits::Lower() <= forward_distance &&
+           forward_distance <= CallDistanceLimits::Upper();
   }
 }
 
@@ -478,21 +445,37 @@
   return destination_.ptr();
 }
 
-void CodeRelocator::BuildTrampolinesForAlmostOutOfRangeCalls(bool force) {
+void CodeRelocator::BuildTrampolinesForAlmostOutOfRangeCalls(
+    const Code& next_caller,
+    const Array& next_caller_targets) {
+  const bool all_functions_emitted = next_caller.IsNull();
+
+  uword next_size = 0;
+  uword next_call_count = 0;
+  if (!all_functions_emitted) {
+    next_size = ImageWriter::SizeInSnapshot(next_caller.instructions());
+    if (!next_caller_targets.IsNull()) {
+      StaticCallsTable calls(next_caller_targets);
+      next_call_count = calls.Length();
+    }
+  }
+
   while (!all_unresolved_calls_.IsEmpty()) {
     UnresolvedCall* unresolved_call = all_unresolved_calls_.First();
 
-    // If we can emit another instructions object without causing the unresolved
-    // forward calls to become out-of-range, we'll not resolve it yet (maybe the
-    // target function will come very soon and we don't need a trampoline at
-    // all).
-    const intptr_t future_boundary =
-        next_text_offset_ + max_instructions_size_ +
-        kTrampolineSize *
-            (unresolved_calls_by_destination_.Length() + max_calls_);
-    if (IsTargetInRangeFor(unresolved_call, future_boundary) &&
-        !FLAG_always_generate_trampolines_for_testing && !force) {
-      break;
+    if (!all_functions_emitted) {
+      // If we can emit another instructions object without causing the
+      // unresolved forward calls to become out-of-range, we'll not resolve it
+      // yet (maybe the target function will come very soon and we don't need
+      // a trampoline at all).
+      const intptr_t future_boundary =
+          next_text_offset_ + next_size +
+          kTrampolineSize *
+              (unresolved_calls_by_destination_.Length() + next_call_count - 1);
+      if (IsTargetInRangeFor(unresolved_call, future_boundary) &&
+          !FLAG_always_generate_trampolines_for_testing) {
+        break;
+      }
     }
 
     // We have a "critical" [unresolved_call] we have to resolve.  If an
diff --git a/runtime/vm/compiler/relocation.h b/runtime/vm/compiler/relocation.h
index bd9cc15..d3bf294 100644
--- a/runtime/vm/compiler/relocation.h
+++ b/runtime/vm/compiler/relocation.h
@@ -183,7 +183,9 @@
                                 intptr_t destination_text);
   void ResolveTrampoline(UnresolvedTrampoline* unresolved_trampoline);
 
-  void BuildTrampolinesForAlmostOutOfRangeCalls(bool force);
+  void BuildTrampolinesForAlmostOutOfRangeCalls(
+      const Code& next_caller,
+      const Array& next_caller_targets);
 
   intptr_t FindDestinationInText(const InstructionsPtr destination,
                                  intptr_t offset_into_target);
diff --git a/runtime/vm/compiler/relocation_test.cc b/runtime/vm/compiler/relocation_test.cc
index 3da5aa7..fa73d3a 100644
--- a/runtime/vm/compiler/relocation_test.cc
+++ b/runtime/vm/compiler/relocation_test.cc
@@ -23,6 +23,20 @@
 DECLARE_FLAG(int, upper_pc_relative_call_distance);
 
 struct RelocatorTestHelper {
+  const intptr_t kTrampolineSize =
+      Utils::RoundUp(PcRelativeTrampolineJumpPattern::kLengthInBytes,
+                     compiler::target::Instructions::kBarePayloadAlignment);
+
+  // The callers on arm/arm64 have to save LR before calling, so the call
+  // instruction will be 4 byte sinto the instruction stream.
+#if defined(TARGET_ARCH_ARM64)
+  static const intptr_t kOffsetOfCall = 4;
+#elif defined(TARGET_ARCH_ARM)
+  static const intptr_t kOffsetOfCall = 4;
+#else
+  static const intptr_t kOffsetOfCall = 0;
+#endif
+
   explicit RelocatorTestHelper(Thread* thread)
       : thread(thread),
         locker(thread, thread->isolate_group()->program_lock()),
@@ -67,11 +81,6 @@
 
     EmitCodeFor(code, [&](compiler::Assembler* assembler) {
 #if defined(TARGET_ARCH_ARM64)
-      // TODO(kustermann): Remove conservative approximation in relocator and
-      // make tests precise.
-      __ mov(R0, R0);
-      __ mov(R0, R0);
-      __ mov(R0, R0);
       SPILLS_RETURN_ADDRESS_FROM_LR_TO_REGISTER(
           __ stp(LR, R1,
                  compiler::Address(CSP, -2 * kWordSize,
@@ -260,7 +269,18 @@
 
 ISOLATE_UNIT_TEST_CASE(CodeRelocator_DirectForwardCall) {
   RelocatorTestHelper helper(thread);
-  helper.CreateInstructions({32, 36, 32});
+  const intptr_t fmax = FLAG_upper_pc_relative_call_distance;
+
+  // The gap is 8 bytes smaller than what could be directly forward-called,
+  // because the relocator's decision when to insert a trampoline is purely
+  // based on whether unresolved calls can reach such a trampoline if the next
+  // instruction is emitted (not taking into account that the next instruction
+  // might actually make some of those unresolved calls resolved).
+  helper.CreateInstructions({
+      16,  // caller (call instruction @helper.kOffsetOfCall)
+      fmax - (16 - helper.kOffsetOfCall) - 8,  // 8 bytes less than maximum gap
+      8                                        // forward call target
+  });
   helper.EmitPcRelativeCallFunction(0, 2);
   helper.EmitReturn42Function(2);
   helper.BuildImageAndRunTest(
@@ -280,8 +300,13 @@
 
 ISOLATE_UNIT_TEST_CASE(CodeRelocator_OutOfRangeForwardCall) {
   RelocatorTestHelper helper(thread);
-  helper.CreateInstructions(
-      {32, FLAG_upper_pc_relative_call_distance - 32 + 4, 32});
+  const intptr_t fmax = FLAG_upper_pc_relative_call_distance;
+
+  helper.CreateInstructions({
+      16,  // caller (call instruction @helper.kOffsetOfCall)
+      fmax - (16 - helper.kOffsetOfCall) + 4,  // 4 bytes above maximum gap
+      8                                        // forwards call target
+  });
   helper.EmitPcRelativeCallFunction(0, 2);
   helper.EmitReturn42Function(2);
   helper.BuildImageAndRunTest([&](const GrowableArray<ImageWriterCommand>&
@@ -305,7 +330,13 @@
 
 ISOLATE_UNIT_TEST_CASE(CodeRelocator_DirectBackwardCall) {
   RelocatorTestHelper helper(thread);
-  helper.CreateInstructions({32, 32, 32});
+  const intptr_t bmax = -FLAG_lower_pc_relative_call_distance;
+
+  helper.CreateInstructions({
+      8,                                // backwards call target
+      bmax - 8 - helper.kOffsetOfCall,  // maximize out backwards call range
+      16  // caller (call instruction @helper.kOffsetOfCall)
+  });
   helper.EmitReturn42Function(0);
   helper.EmitPcRelativeCallFunction(2, 0);
   helper.BuildImageAndRunTest(
@@ -325,58 +356,73 @@
 
 ISOLATE_UNIT_TEST_CASE(CodeRelocator_OutOfRangeBackwardCall) {
   RelocatorTestHelper helper(thread);
-  helper.CreateInstructions({32, 32, 32, 32 + 4, 32, 32, 32, 32, 32});
+  const intptr_t bmax = -FLAG_lower_pc_relative_call_distance;
+  const intptr_t fmax = FLAG_upper_pc_relative_call_distance;
+
+  helper.CreateInstructions({
+      8,                                    // backward call target
+      bmax - 8 - helper.kOffsetOfCall + 4,  // 4 bytes exceeding backwards range
+      16,  // caller (call instruction @helper.kOffsetOfCall)
+      fmax - (16 - helper.kOffsetOfCall) -
+          4,  // 4 bytes less than forward range
+      4,
+      4,  // out-of-range, so trampoline has to be inserted before this
+  });
   helper.EmitReturn42Function(0);
-  helper.EmitPcRelativeCallFunction(4, 0);
+  helper.EmitPcRelativeCallFunction(2, 0);
   helper.BuildImageAndRunTest([&](const GrowableArray<ImageWriterCommand>&
                                       commands,
                                   uword* entry_point) {
-    EXPECT_EQ(10, commands.length());
+    EXPECT_EQ(7, commands.length());
 
     // This is the backwards call target.
     EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[0].op);
     EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[1].op);
-    EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[2].op);
-    EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[3].op);
     // This makes an out-of-range backwards call. The relocator will make the
     // call go to a trampoline instead. It will delay insertion of the
     // trampoline until it almost becomes out-of-range.
+    EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[2].op);
+    EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[3].op);
     EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[4].op);
-    EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[5].op);
-    EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[6].op);
     // This is the last change the relocator thinks it can ensure the
     // out-of-range call above can call a trampoline - so it injets it here and
     // no later.
-    EXPECT_EQ(ImageWriterCommand::InsertBytesOfTrampoline, commands[7].op);
-    EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[8].op);
-    EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[9].op);
+    EXPECT_EQ(ImageWriterCommand::InsertBytesOfTrampoline, commands[5].op);
+    EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[6].op);
 
-    *entry_point = commands[4].expected_offset;
+    *entry_point = commands[2].expected_offset;
   });
 }
 
 ISOLATE_UNIT_TEST_CASE(CodeRelocator_OutOfRangeBackwardCall2) {
   RelocatorTestHelper helper(thread);
-  helper.CreateInstructions({32, 32, 32, 32 + 4, 32});
+  const intptr_t bmax = -FLAG_lower_pc_relative_call_distance;
+
+  helper.CreateInstructions({
+      8,                                    // backwards call target
+      bmax - 8 - helper.kOffsetOfCall + 4,  // 4 bytes exceeding backwards range
+      16,  // caller (call instruction @helper.kOffsetOfCall)
+      4,
+  });
   helper.EmitReturn42Function(0);
-  helper.EmitPcRelativeCallFunction(4, 0);
+  helper.EmitPcRelativeCallFunction(2, 0);
   helper.BuildImageAndRunTest(
       [&](const GrowableArray<ImageWriterCommand>& commands,
           uword* entry_point) {
-        EXPECT_EQ(6, commands.length());
+        EXPECT_EQ(5, commands.length());
 
         // This is the backwards call target.
         EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[0].op);
         EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[1].op);
-        EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[2].op);
-        EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[3].op);
         // This makes an out-of-range backwards call. The relocator will make
         // the call go to a trampoline instead. It will delay insertion of the
-        // trampoline until it almost becomes out-of-range.
-        EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[4].op);
+        // trampoline until it almost becomes out-of-range (or in this case no
+        // more instructions follow).
+        EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[2].op);
+        EXPECT_EQ(ImageWriterCommand::InsertInstructionOfCode, commands[3].op);
         // There's no other instructions coming, so the relocator will resolve
         // any pending out-of-range calls by inserting trampolines at the end.
-        EXPECT_EQ(ImageWriterCommand::InsertBytesOfTrampoline, commands[5].op);
+        EXPECT_EQ(ImageWriterCommand::InsertBytesOfTrampoline, commands[4].op);
 
         *entry_point = commands[4].expected_offset;
       });
diff --git a/tests/language/const_functions/const_functions_variable_declarations_error_test.dart b/tests/language/const_functions/const_functions_variable_declarations_error_test.dart
index 56cf083..59cce22 100644
--- a/tests/language/const_functions/const_functions_variable_declarations_error_test.dart
+++ b/tests/language/const_functions/const_functions_variable_declarations_error_test.dart
@@ -16,3 +16,13 @@
   var a;
   return a;
 }
+
+const var2 = fn2();
+//           ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+// [cfe] Constant evaluation error:
+int fn2() {
+  var x;
+  x = "string";
+  return x;
+}
diff --git a/tests/language/const_functions/const_functions_variable_declarations_test.dart b/tests/language/const_functions/const_functions_variable_declarations_test.dart
index ec6a8ec..29a87c0 100644
--- a/tests/language/const_functions/const_functions_variable_declarations_test.dart
+++ b/tests/language/const_functions/const_functions_variable_declarations_test.dart
@@ -82,6 +82,14 @@
   return a;
 }
 
+const var9 = function9();
+//           ^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int? function9() {
+  int? x;
+  return x;
+}
+
 void main() {
   Expect.equals(var1, 4);
   Expect.equals(var1_1, 5);
@@ -92,4 +100,5 @@
   Expect.equals(var6, 2);
   Expect.equals(var7, 2);
   Expect.equals(var8, 2);
+  Expect.equals(var9, null);
 }
diff --git a/tools/VERSION b/tools/VERSION
index 749c442..8d5bbaf 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 61
+PRERELEASE 62
 PRERELEASE_PATCH 0
\ No newline at end of file