Version 2.16.0-9.0.dev

Merge commit '11dc2f4ff684e4c0d9bd874957a03978d657fb06' into 'dev'
diff --git a/pkg/analysis_server/benchmark/benchmarks.dart b/pkg/analysis_server/benchmark/benchmarks.dart
index c6b391f..0d3904b 100644
--- a/pkg/analysis_server/benchmark/benchmarks.dart
+++ b/pkg/analysis_server/benchmark/benchmarks.dart
@@ -241,8 +241,14 @@
 
     if (benchmark is FlutterBenchmark) {
       if (flutterRepository != null) {
-        (benchmark as FlutterBenchmark).flutterRepositoryPath =
-            flutterRepository;
+        if (path.isAbsolute(flutterRepository) &&
+            path.normalize(flutterRepository) == flutterRepository) {
+          (benchmark as FlutterBenchmark).flutterRepositoryPath =
+              flutterRepository;
+        } else {
+          print('The path must be absolute and normalized: $flutterRepository');
+          exit(1);
+        }
       } else {
         print('The option --flutter-repository is required to '
             "run '$benchmarkId'.");
diff --git a/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart b/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart
index e2bac08..78497cb 100644
--- a/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart
+++ b/pkg/analysis_server/benchmark/perf/flutter_completion_benchmark.dart
@@ -77,8 +77,10 @@
     // time analyzing, and do apply the filter.
     // Total number of suggestions: 2322.
     // Filtered to: 82.
+    // Long name: completion-smallFile-body
+    var name = 'completion-1';
     result.add(
-      'completion-smallFile-body',
+      name,
       BenchMarkResult(
         'micros',
         await _completionTiming(
@@ -86,7 +88,7 @@
           filePath: '$flutterPkgPath/lib/src/material/flutter_logo.dart',
           uniquePrefix: 'Widget build(BuildContext context) {',
           insertStringGenerator: () => 'M',
-          name: 'completion-smallFile-body',
+          name: name,
         ),
       ),
     );
@@ -98,8 +100,10 @@
       // JSON in the server, and deserializing on the client.
       // Total number of suggestions: 2322.
       // Filtered to: 2322.
+      // Long name: completion-smallFile-body-withoutPrefix
+      name = 'completion-2';
       result.add(
-        'completion-smallFile-body-withoutPrefix',
+        name,
         BenchMarkResult(
           'micros',
           await _completionTiming(
@@ -107,7 +111,7 @@
             filePath: '$flutterPkgPath/lib/src/material/flutter_logo.dart',
             uniquePrefix: 'Widget build(BuildContext context) {',
             insertStringGenerator: null,
-            name: 'completion-smallFile-body-withoutPrefix',
+            name: name,
           ),
         ),
       );
@@ -118,8 +122,10 @@
       // The target method body is small, so something could be optimized.
       // Total number of suggestions: 4654.
       // Filtered to: 182.
+      // Long name: completion-smallLibraryCycle-largeFile-smallBody
+      name = 'completion-3';
       result.add(
-        'completion-smallLibraryCycle-largeFile-smallBody',
+        name,
         BenchMarkResult(
           'micros',
           await _completionTiming(
@@ -127,7 +133,7 @@
             filePath: '$flutterPkgPath/test/material/text_field_test.dart',
             uniquePrefix: 'getOpacity(WidgetTester tester, Finder finder) {',
             insertStringGenerator: () => 'M',
-            name: 'completion-smallLibraryCycle-largeFile-smallBody',
+            name: name,
           ),
         ),
       );
@@ -143,8 +149,10 @@
       // TODO(scheglov) Remove the previous sentence when improved.
       // Total number of suggestions: 3429.
       // Filtered to: 133.
+      // Long name: completion-mediumLibraryCycle-mediumFile-smallBody
+      name = 'completion-4';
       result.add(
-        'completion-mediumLibraryCycle-mediumFile-smallBody',
+        name,
         BenchMarkResult(
           'micros',
           await _completionTiming(
@@ -152,7 +160,7 @@
             filePath: '$flutterPkgPath/lib/src/material/app_bar.dart',
             uniquePrefix: 'computeDryLayout(BoxConstraints constraints) {',
             insertStringGenerator: () => 'M',
-            name: 'completion-mediumLibraryCycle-mediumFile-smallBody',
+            name: name,
           ),
         ),
       );
@@ -163,8 +171,10 @@
       // cycle. This is expensive.
       // Total number of suggestions: 1510.
       // Filtered to: 0.
+      // Long name: completion-mediumLibraryCycle-mediumFile-api-parameterType
+      name = 'completion-5';
       result.add(
-        'completion-mediumLibraryCycle-mediumFile-api-parameterType',
+        name,
         BenchMarkResult(
           'micros',
           await _completionTiming(
@@ -172,7 +182,7 @@
             filePath: '$flutterPkgPath/lib/src/material/app_bar.dart',
             uniquePrefix: 'computeDryLayout(BoxConstraints',
             insertStringGenerator: _IncrementingStringGenerator(),
-            name: 'completion-mediumLibraryCycle-mediumFile-api-parameterType',
+            name: name,
           ),
         ),
       );
diff --git a/pkg/analysis_server/lib/src/computer/computer_color.dart b/pkg/analysis_server/lib/src/computer/computer_color.dart
new file mode 100644
index 0000000..56352d1
--- /dev/null
+++ b/pkg/analysis_server/lib/src/computer/computer_color.dart
@@ -0,0 +1,361 @@
+// 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.
+
+import 'package:analysis_server/src/utilities/flutter.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/constant/value.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/constant/value.dart' show GenericState;
+import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
+import 'package:analyzer/src/dart/element/type_system.dart';
+import 'package:analyzer/src/lint/linter.dart';
+import 'package:collection/collection.dart';
+
+/// Computer for dart:ui/Flutter Color references.
+class ColorComputer {
+  final ResolvedUnitResult resolvedUnit;
+  final LinterContext _linterContext;
+  final List<ColorReference> _colors = [];
+  final Flutter _flutter = Flutter.instance;
+
+  ColorComputer(this.resolvedUnit)
+      : _linterContext = LinterContextImpl(
+          [], // unused
+          LinterContextUnit(resolvedUnit.content, resolvedUnit.unit),
+          resolvedUnit.session.declaredVariables,
+          resolvedUnit.typeProvider,
+          resolvedUnit.typeSystem as TypeSystemImpl,
+          InheritanceManager3(), // unused
+          resolvedUnit.session.analysisContext.analysisOptions,
+          null,
+        );
+
+  /// Returns information about the color references in [resolvedUnit].
+  ///
+  /// This method should only be called once for any instance of this class.
+  List<ColorReference> compute() {
+    final visitor = _ColorBuilder(this);
+    resolvedUnit.unit.accept(visitor);
+    return _colors;
+  }
+
+  /// Tries to add a color for the [expression].
+  ///
+  /// If [target] is supplied, will be used instead of [expression] allowing
+  /// a value to be read from the member [memberName] or from a swatch value
+  /// with index [index].
+  bool tryAddColor(
+    Expression expression, {
+    Expression? target,
+    String? memberName,
+    int? index,
+  }) {
+    if (!_isColor(expression.staticType)) return false;
+
+    target ??= expression;
+
+    // Try to evaluate the constant target.
+    final colorConstResult = _linterContext.evaluateConstant(target);
+    var colorConst = colorConstResult.value;
+    if (colorConstResult.errors.isNotEmpty || colorConst == null) return false;
+
+    // If we want a specific member or swatch index, read that.
+    if (memberName != null) {
+      colorConst = _getMember(colorConst, memberName);
+    } else if (index != null) {
+      colorConst = _getSwatchValue(colorConst, index);
+    }
+
+    return _tryRecordColor(expression, colorConst);
+  }
+
+  /// Tries to add a color for the instance creation [expression].
+  ///
+  /// This handles constructor calls that cannot be evaluated (for example
+  /// because they are not const) but are simple well-known dart:ui/Flutter
+  /// color constructors that we can manually parse.
+  bool tryAddKnownColorConstructor(InstanceCreationExpression expression) {
+    if (!_isColor(expression.staticType)) return false;
+
+    final constructor = expression.constructorName;
+    final staticElement = constructor.staticElement;
+    final classElement = staticElement?.enclosingElement;
+    final className = classElement?.name;
+    final constructorName = constructor.name?.name;
+    final constructorArgs = expression.argumentList.arguments
+        .map((e) => e is Literal ? e : null)
+        .toList();
+
+    int? colorValue;
+    if (_isDartUi(classElement) && className == 'Color') {
+      colorValue = _getDartUiColorValue(constructorName, constructorArgs);
+    } else if (_isFlutterPainting(classElement) && className == 'ColorSwatch') {
+      colorValue =
+          _getFlutterSwatchColorValue(constructorName, constructorArgs);
+    } else if (_isFlutterMaterial(classElement) &&
+        className == 'MaterialAccentColor') {
+      colorValue =
+          _getFlutterMaterialAccentColorValue(constructorName, constructorArgs);
+    }
+
+    return _tryRecordColorValue(expression, colorValue);
+  }
+
+  /// Creates a [ColorInformation] by extracting the argb values from
+  /// [value] encoded as 0xAARRGGBB as in the dart:ui Color class.
+  ColorInformation _colorInformationForColorValue(int value) {
+    // Extract color information according to dart:ui Color values.
+    final alpha = (0xff000000 & value) >> 24;
+    final red = (0x00ff0000 & value) >> 16;
+    final blue = (0x000000ff & value) >> 0;
+    final green = (0x0000ff00 & value) >> 8;
+
+    return ColorInformation(alpha, red, green, blue);
+  }
+
+  /// Extracts the integer color value from the dart:ui Color constant [color].
+  int? _colorValueForColorConst(DartObject? color) {
+    if (color == null || color.isNull) return null;
+
+    // If the object has a "color" field, walk down to that, because some colors
+    // like CupertinoColors have a "value=0" with an overridden getter that
+    // would always result in a value representing black.
+    color = color.getFieldFromHierarchy('color') ?? color;
+
+    return color.getFieldFromHierarchy('value')?.toIntValue();
+  }
+
+  /// Converts ARGB values into a single int value as 0xAARRGGBB as used by
+  /// the dart:ui Color class.
+  int _colorValueForComponents(int alpha, int red, int green, int blue) {
+    return (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
+  }
+
+  /// Extracts the color value from dart:ui Color constructor args.
+  int? _getDartUiColorValue(String? name, List<Literal?> args) {
+    if (name == null && args.length == 1) {
+      final arg0 = args[0];
+      return arg0 is IntegerLiteral ? arg0.value : null;
+    } else if (name == 'fromARGB' && args.length == 4) {
+      final arg0 = args[0];
+      final arg1 = args[1];
+      final arg2 = args[2];
+      final arg3 = args[3];
+
+      final alpha = arg0 is IntegerLiteral ? arg0.value : null;
+      final red = arg1 is IntegerLiteral ? arg1.value : null;
+      final green = arg2 is IntegerLiteral ? arg2.value : null;
+      final blue = arg3 is IntegerLiteral ? arg3.value : null;
+
+      return alpha != null && red != null && green != null && blue != null
+          ? _colorValueForComponents(alpha, red, green, blue)
+          : null;
+    } else if (name == 'fromRGBO' && args.length == 4) {
+      final arg0 = args[0];
+      final arg1 = args[1];
+      final arg2 = args[2];
+      final arg3 = args[3];
+
+      final red = arg0 is IntegerLiteral ? arg0.value : null;
+      final green = arg1 is IntegerLiteral ? arg1.value : null;
+      final blue = arg2 is IntegerLiteral ? arg2.value : null;
+      final opacity = arg3 is IntegerLiteral
+          ? arg3.value
+          : arg3 is DoubleLiteral
+              ? arg3.value
+              : null;
+      final alpha = opacity != null ? (opacity * 255).toInt() : null;
+
+      return alpha != null && red != null && green != null && blue != null
+          ? _colorValueForComponents(alpha, red, green, blue)
+          : null;
+    }
+  }
+
+  /// Extracts the color value from Flutter MaterialAccentColor constructor args.
+  int? _getFlutterMaterialAccentColorValue(String? name, List<Literal?> args) =>
+      // MaterialAccentColor is a subclass of SwatchColor and has the same
+      // constructor.
+      _getFlutterSwatchColorValue(name, args);
+
+  /// Extracts the color value from Flutter ColorSwatch constructor args.
+  int? _getFlutterSwatchColorValue(String? name, List<Literal?> args) {
+    if (name == null && args.isNotEmpty) {
+      final arg0 = args[0];
+      return arg0 is IntegerLiteral ? arg0.value : null;
+    }
+  }
+
+  /// Extracts a named member from a color.
+  ///
+  /// Well-known getters like `shade500` will be mapped onto the swatch value
+  /// with a matching index.
+  DartObject? _getMember(DartObject target, String memberName) {
+    final colorValue = target.getFieldFromHierarchy(memberName);
+    if (colorValue != null) {
+      return colorValue;
+    }
+
+    // If we didn't get a value but it's a getter we know how to read from a
+    // swatch, try that.
+    if (memberName.startsWith('shade')) {
+      final shadeNumber = int.tryParse(memberName.substring(5));
+      if (shadeNumber != null) {
+        return _getSwatchValue(target, shadeNumber);
+      }
+    }
+  }
+
+  /// Extracts a specific shade index from a Flutter SwatchColor.
+  DartObject? _getSwatchValue(DartObject target, int swatchValue) {
+    final swatch = target.getFieldFromHierarchy('_swatch')?.toMapValue();
+    if (swatch == null) return null;
+
+    final key = swatch.keys.firstWhereOrNull(
+      (key) => key?.toIntValue() == swatchValue,
+    );
+    if (key == null) return null;
+
+    return swatch[key];
+  }
+
+  /// Checks whether [type] is - or extends - the dart:ui Color class.
+  bool _isColor(DartType? type) => type != null && _flutter.isColor(type);
+
+  /// Checks whether this elements library is dart:ui.
+  bool _isDartUi(Element? element) => element?.library?.name == 'dart.ui';
+
+  /// Checks whether this elements library is Flutter Material colors.
+  bool _isFlutterMaterial(Element? element) =>
+      element?.library?.identifier ==
+      'package:flutter/src/material/colors.dart';
+
+  /// Checks whether this elements library is Flutter Painting colors.
+  bool _isFlutterPainting(Element? element) =>
+      element?.library?.identifier ==
+      'package:flutter/src/painting/colors.dart';
+
+  /// Tries to record a color value from [colorConst] for [expression].
+  ///
+  /// Returns whether a valid color was found and recorded.
+  bool _tryRecordColor(Expression expression, DartObject? colorConst) =>
+      _tryRecordColorValue(expression, _colorValueForColorConst(colorConst));
+
+  /// Tries to record the [colorValue] for [expression].
+  ///
+  /// Returns whether a valid color was found and recorded.
+  bool _tryRecordColorValue(Expression expression, int? colorValue) {
+    if (colorValue == null) return false;
+
+    // Build color information from the Color value.
+    final color = _colorInformationForColorValue(colorValue);
+
+    // Record the color against the original entire expression.
+    _colors.add(ColorReference(expression.offset, expression.length, color));
+    return true;
+  }
+}
+
+/// Information about a color that is present in a document.
+class ColorInformation {
+  final int alpha;
+  final int red;
+  final int green;
+  final int blue;
+
+  ColorInformation(this.alpha, this.red, this.green, this.blue);
+}
+
+/// Information about a specific known location of a [ColorInformation]
+/// reference in a document.
+class ColorReference {
+  final int offset;
+  final int length;
+  final ColorInformation color;
+
+  ColorReference(this.offset, this.length, this.color);
+}
+
+class _ColorBuilder extends RecursiveAstVisitor<void> {
+  final ColorComputer computer;
+
+  _ColorBuilder(this.computer);
+
+  @override
+  void visitIndexExpression(IndexExpression node) {
+    // Colors.redAccent[500].
+    final index = node.index;
+    final indexValue = index is IntegerLiteral ? index.value : null;
+    if (indexValue != null) {
+      if (computer.tryAddColor(
+        node,
+        target: node.realTarget,
+        index: indexValue,
+      )) {
+        return;
+      }
+    }
+    super.visitIndexExpression(node);
+  }
+
+  @override
+  void visitInstanceCreationExpression(InstanceCreationExpression node) {
+    // Usually we return after finding a color, but constructors can
+    // have nested colors in their arguments so we walk all the way down.
+    if (!computer.tryAddColor(node)) {
+      // If we couldn't evaluate the constant, try the well-known color
+      // constructors for dart:ui/Flutter.
+      computer.tryAddKnownColorConstructor(node);
+    }
+
+    super.visitInstanceCreationExpression(node);
+  }
+
+  @override
+  void visitPrefixedIdentifier(PrefixedIdentifier node) {
+    // Try the whole node as a constant (eg. `MyThemeClass.staticField`).
+    if (computer.tryAddColor(node)) {
+      return;
+    }
+
+    // Try a field of a static, (eg. `const MyThemeClass().instanceField`).
+    if (computer.tryAddColor(
+      node,
+      target: node.prefix,
+      memberName: node.identifier.name,
+    )) {
+      return;
+    }
+
+    super.visitPrefixedIdentifier(node);
+  }
+
+  @override
+  void visitPropertyAccess(PropertyAccess node) {
+    // Handle things like CupterinoColors.activeBlue.darkColor where we can't
+    // evaluate the whole expression, but can evaluate CupterinoColors.activeBlue
+    // and read the darkColor.
+    if (computer.tryAddColor(
+      node,
+      target: node.realTarget,
+      memberName: node.propertyName.name,
+    )) {
+      return;
+    }
+
+    super.visitPropertyAccess(node);
+  }
+}
+
+extension _DartObjectExtensions on DartObject {
+  /// Reads the value of the field [field] from this object.
+  ///
+  /// If the field is not found, recurses up the super classes.
+  DartObject? getFieldFromHierarchy(String fieldName) =>
+      getField(fieldName) ??
+      getField(GenericState.SUPERCLASS_FIELD)?.getFieldFromHierarchy(fieldName);
+}
diff --git a/pkg/analysis_server/test/mock_packages/flutter/lib/cupertino.dart b/pkg/analysis_server/test/mock_packages/flutter/lib/cupertino.dart
new file mode 100644
index 0000000..828a456
--- /dev/null
+++ b/pkg/analysis_server/test/mock_packages/flutter/lib/cupertino.dart
@@ -0,0 +1,5 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+export 'src/cupertino/colors.dart';
diff --git a/pkg/analysis_server/test/mock_packages/flutter/lib/material.dart b/pkg/analysis_server/test/mock_packages/flutter/lib/material.dart
index 466ed8e..a1cecbd 100644
--- a/pkg/analysis_server/test/mock_packages/flutter/lib/material.dart
+++ b/pkg/analysis_server/test/mock_packages/flutter/lib/material.dart
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 export 'src/material/app_bar.dart';
+export 'src/material/colors.dart';
 export 'src/material/icons.dart';
 export 'src/material/scaffold.dart';
 export 'widgets.dart';
diff --git a/pkg/analysis_server/test/mock_packages/flutter/lib/painting.dart b/pkg/analysis_server/test/mock_packages/flutter/lib/painting.dart
index a3323f6..6eec7c5 100644
--- a/pkg/analysis_server/test/mock_packages/flutter/lib/painting.dart
+++ b/pkg/analysis_server/test/mock_packages/flutter/lib/painting.dart
@@ -5,6 +5,7 @@
 export 'src/painting/alignment.dart';
 export 'src/painting/basic_types.dart';
 export 'src/painting/box_decoration.dart';
+export 'src/painting/colors.dart';
 export 'src/painting/decoration.dart';
 export 'src/painting/edge_insets.dart';
 export 'src/painting/text_painter.dart';
diff --git a/pkg/analysis_server/test/mock_packages/flutter/lib/src/cupertino/colors.dart b/pkg/analysis_server/test/mock_packages/flutter/lib/src/cupertino/colors.dart
new file mode 100644
index 0000000..62356d5
--- /dev/null
+++ b/pkg/analysis_server/test/mock_packages/flutter/lib/src/cupertino/colors.dart
@@ -0,0 +1,102 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/painting.dart';
+
+class CupertinoColors {
+  CupertinoColors._();
+
+  static const CupertinoDynamicColor activeBlue = systemBlue;
+
+  static const Color black = Color(0xFF000000);
+  static const Color white = Color(0xFFFFFFFF);
+
+  static const CupertinoDynamicColor systemBlue =
+      CupertinoDynamicColor.withBrightnessAndContrast(
+    color: Color.fromARGB(255, 0, 0, 0xFF),
+    darkColor: Color.fromARGB(255, 0, 0, 0x99),
+    highContrastColor: Color.fromARGB(255, 0, 0, 0x66),
+    darkHighContrastColor: Color.fromARGB(255, 0, 0, 0x33),
+  );
+}
+
+class CupertinoDynamicColor extends Color {
+  const CupertinoDynamicColor({
+    Color color,
+    Color darkColor,
+    Color highContrastColor,
+    Color darkHighContrastColor,
+    Color elevatedColor,
+    Color darkElevatedColor,
+    Color highContrastElevatedColor,
+    Color darkHighContrastElevatedColor,
+  }) : this._(
+          color,
+          color,
+          darkColor,
+          highContrastColor,
+          darkHighContrastColor,
+          elevatedColor,
+          darkElevatedColor,
+          highContrastElevatedColor,
+          darkHighContrastElevatedColor,
+          null,
+        );
+
+  const CupertinoDynamicColor.withBrightnessAndContrast({
+    Color color,
+    Color darkColor,
+    Color highContrastColor,
+    Color darkHighContrastColor,
+  }) : this(
+          color: color,
+          darkColor: darkColor,
+          highContrastColor: highContrastColor,
+          darkHighContrastColor: darkHighContrastColor,
+          elevatedColor: color,
+          darkElevatedColor: darkColor,
+          highContrastElevatedColor: highContrastColor,
+          darkHighContrastElevatedColor: darkHighContrastColor,
+        );
+
+  const CupertinoDynamicColor.withBrightness({
+    Color color,
+    Color darkColor,
+  }) : this(
+          color: color,
+          darkColor: darkColor,
+          highContrastColor: color,
+          darkHighContrastColor: darkColor,
+          elevatedColor: color,
+          darkElevatedColor: darkColor,
+          highContrastElevatedColor: color,
+          darkHighContrastElevatedColor: darkColor,
+        );
+
+  const CupertinoDynamicColor._(
+    this._effectiveColor,
+    this.color,
+    this.darkColor,
+    this.highContrastColor,
+    this.darkHighContrastColor,
+    this.elevatedColor,
+    this.darkElevatedColor,
+    this.highContrastElevatedColor,
+    this.darkHighContrastElevatedColor,
+  ) : super(0);
+
+  final Color _effectiveColor;
+
+  @override
+  int get value => _effectiveColor.value;
+
+  final Color color;
+  final Color darkColor;
+  final Color highContrastColor;
+  final Color darkHighContrastColor;
+  final Color elevatedColor;
+  final Color darkElevatedColor;
+  final Color highContrastElevatedColor;
+  final Color darkHighContrastElevatedColor;
+}
diff --git a/pkg/analysis_server/test/mock_packages/flutter/lib/src/material/colors.dart b/pkg/analysis_server/test/mock_packages/flutter/lib/src/material/colors.dart
new file mode 100644
index 0000000..803f7a5
--- /dev/null
+++ b/pkg/analysis_server/test/mock_packages/flutter/lib/src/material/colors.dart
@@ -0,0 +1,71 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/painting.dart';
+
+class MaterialColor extends ColorSwatch<int> {
+  const MaterialColor(int primary, Map<int, Color> swatch)
+      : super(primary, swatch);
+
+  Color get shade100 => this[100];
+  Color get shade200 => this[200];
+  Color get shade300 => this[300];
+  Color get shade400 => this[400];
+  Color get shade50 => this[50];
+  Color get shade500 => this[500];
+  Color get shade600 => this[600];
+  Color get shade700 => this[700];
+  Color get shade800 => this[800];
+  Color get shade900 => this[900];
+}
+
+class MaterialAccentColor extends ColorSwatch<int> {
+  const MaterialAccentColor(int primary, Map<int, Color> swatch)
+      : super(primary, swatch);
+
+  Color get shade50 => this[50];
+  Color get shade100 => this[100];
+  Color get shade200 => this[200];
+  Color get shade400 => this[400];
+  Color get shade700 => this[700];
+}
+
+class Colors {
+  Colors._();
+
+  static const Color black = Color(0xFF000000);
+  static const Color white = Color(0xFFFFFFFF);
+
+  static const MaterialColor red = MaterialColor(
+    _redPrimaryValue,
+    <int, Color>{
+      // For simpler testing, these values are not the real Flutter values
+      // but just varying alphas on a primary value.
+      50: Color(0x05FF0000),
+      100: Color(0x10FF0000),
+      200: Color(0x20FF0000),
+      300: Color(0x30FF0000),
+      400: Color(0x40FF0000),
+      500: Color(0x50FF0000),
+      600: Color(0x60FF0000),
+      700: Color(0x70FF0000),
+      800: Color(0x80FF0000),
+      900: Color(0x90FF0000),
+    },
+  );
+  static const int _redPrimaryValue = 0xFFFF0000;
+
+  static const MaterialAccentColor redAccent = MaterialAccentColor(
+    _redAccentValue,
+    <int, Color>{
+      // For simpler testing, these values are not the real Flutter values
+      // but just varying alphas on a primary value.
+      100: Color(0x10FFAA00),
+      200: Color(0x20FFAA00),
+      400: Color(0x40FFAA00),
+      700: Color(0x70FFAA00),
+    },
+  );
+  static const int _redAccentValue = 0xFFFFAA00;
+}
diff --git a/pkg/analysis_server/test/mock_packages/flutter/lib/src/painting/colors.dart b/pkg/analysis_server/test/mock_packages/flutter/lib/src/painting/colors.dart
new file mode 100644
index 0000000..f0dbfae
--- /dev/null
+++ b/pkg/analysis_server/test/mock_packages/flutter/lib/src/painting/colors.dart
@@ -0,0 +1,16 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/painting.dart';
+
+@immutable
+class ColorSwatch<T> extends Color {
+  const ColorSwatch(int primary, this._swatch) : super(primary);
+
+  @protected
+  final Map<T, Color> _swatch;
+
+  Color operator [](T index) => _swatch[index];
+}
diff --git a/pkg/analysis_server/test/src/computer/color_computer_test.dart b/pkg/analysis_server/test/src/computer/color_computer_test.dart
new file mode 100644
index 0000000..190f0d9
--- /dev/null
+++ b/pkg/analysis_server/test/src/computer/color_computer_test.dart
@@ -0,0 +1,343 @@
+// 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.
+
+import 'package:analysis_server/src/computer/computer_color.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/diagnostic/diagnostic.dart';
+import 'package:collection/collection.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../abstract_context.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ColorComputerTest);
+  });
+}
+
+@reflectiveTest
+class ColorComputerTest extends AbstractContextTest {
+  /// A map of Dart source code that represents different types/formats
+  /// that are valid in const contexts.
+  ///
+  /// Values are the color that should be discovered (in 0xAARRGGBB format).
+  ///
+  /// Color values may not match the actual Flutter framework but are
+  /// values that are more identifyable for ease of testing. They are
+  /// defined in:
+  ///  - test/mock_packages/flutter/lib/src/material/colors.dart.
+  ///  - test/mock_packages/flutter/lib/src/cupertino/colors.dart.
+  ///
+  /// These values will be iterated in tests and inserted into various
+  /// code snippets for testing.
+  static const colorCodesConst = {
+    // dart:ui Colors
+    'Colors.white': 0xFFFFFFFF,
+    'Color(0xFF0000FF)': 0xFF0000FF,
+    'Color.fromARGB(255, 0, 0, 255)': 0xFF0000FF,
+    'Color.fromRGBO(0, 0, 255, 1)': 0xFF0000FF,
+    // Flutter Painting
+    'ColorSwatch(0xFF89ABCD, {})': 0xFF89ABCD,
+    // Flutter Material
+    'Colors.red': 0xFFFF0000,
+    'Colors.redAccent': 0xFFFFAA00,
+    'MaterialAccentColor(0xFF89ABCD, {})': 0xFF89ABCD,
+    // Flutter Cupertino
+    'CupertinoColors.black': 0xFF000000,
+    'CupertinoColors.systemBlue': 0xFF0000FF,
+    'CupertinoColors.activeBlue': 0xFF0000FF,
+  };
+
+  /// A map of Dart source code that represents different types/formats
+  /// that are not valid in const contexts.
+  ///
+  /// Values are the color that should be discovered (in 0xAARRGGBB format).
+  static const colorCodesNonConst = {
+    // Flutter Material
+    'Colors.red.shade100': 0x10FF0000,
+    'Colors.red[100]': 0x10FF0000,
+    // Flutter Cupertino
+    'CupertinoColors.systemBlue.color': 0xFF0000FF,
+    'CupertinoColors.systemBlue.darkColor': 0xFF000099,
+    'CupertinoColors.activeBlue.color': 0xFF0000FF,
+    'CupertinoColors.activeBlue.darkColor': 0xFF000099,
+    'CupertinoColors.activeBlue.highContrastColor': 0xFF000066,
+    'CupertinoColors.activeBlue.darkHighContrastColor': 0xFF000033,
+    'CupertinoColors.activeBlue.elevatedColor': 0xFF0000FF,
+    'CupertinoColors.activeBlue.darkElevatedColor': 0xFF000099,
+  };
+
+  /// A map of Dart source code that creates multiple nested color references.
+  ///
+  /// The key is the source code, and the value is a map of the expressions and
+  /// colors that should be produced (where the null key represents the
+  /// entire expression).
+  static const colorCodesNested = {
+    // TODO(dantup): Remove this "const" when we can evaluate constructors
+    // in non-const contexts.
+    'const CupertinoDynamicColor.withBrightness(color: CupertinoColors.white, darkColor: CupertinoColors.black)':
+        {
+      null: 0xFFFFFFFF,
+      'CupertinoColors.white': 0xFFFFFFFF,
+      'CupertinoColors.black': 0xFF000000,
+    },
+  };
+
+  late String testPath;
+  late String otherPath;
+
+  late ColorComputer computer;
+
+  /// Tests that all of the known color codes replaced into [code] produce the
+  /// expected nested color values.
+  ///
+  /// If [onlyConst] is `true`, only the test values that are const will be
+  /// tested.
+  Future<void> checkAllColors(String code, {bool onlyConst = false}) async {
+    // Combine the flat and nested colours into the same format.
+    final allColorCodes = <String, Map<String?, int>>{
+      ...colorCodesConst.map((key, value) => MapEntry(key, {key: value})),
+      if (!onlyConst)
+        ...colorCodesNonConst.map((key, value) => MapEntry(key, {key: value})),
+      ...colorCodesNested,
+    };
+
+    for (final entry in allColorCodes.entries) {
+      final colorDartCode = entry.key;
+      final expectedColorValues = entry.value.map(
+        // A null key means we should expect the full code.
+        (key, value) => MapEntry(key ?? colorDartCode, value),
+      );
+
+      await expectColors(
+        code.replaceAll('[[COLOR]]', colorDartCode),
+        expectedColorValues,
+      );
+    }
+  }
+
+  /// Checks that all of [expectedColorValues] are produced for [dartCode].
+  Future<void> expectColors(
+    String dartCode,
+    Map<String, int> expectedColorValues, {
+    String? otherCode,
+  }) async {
+    dartCode = _withCommonImports(dartCode);
+    otherCode = otherCode != null ? _withCommonImports(otherCode) : null;
+
+    newFile(testPath, content: dartCode);
+    if (otherCode != null) {
+      newFile(otherPath, content: otherCode);
+      final otherResult =
+          await session.getResolvedUnit(otherPath) as ResolvedUnitResult;
+      expectNoErrors(otherResult);
+    }
+    final result =
+        await session.getResolvedUnit(testPath) as ResolvedUnitResult;
+    expectNoErrors(result);
+
+    computer = ColorComputer(result);
+    final colors = computer.compute();
+
+    expect(
+      colors,
+      hasLength(expectedColorValues.length),
+      reason: '${expectedColorValues.length} colors should be detected in:\n'
+          '$dartCode',
+    );
+
+    expectedColorValues.entries.forEachIndexed((i, expectedColor) {
+      final color = colors[i];
+      final expectedColorCode = expectedColor.key;
+      final expectedColorValue = expectedColor.value;
+      final expectedAlpha = (0xff000000 & expectedColorValue) >> 24;
+      final expectedRed = (0x00ff0000 & expectedColorValue) >> 16;
+      final expectedGreen = (0x0000ff00 & expectedColorValue) >> 8;
+      final expectedBlue = (0x000000ff & expectedColorValue) >> 0;
+
+      final regionText =
+          dartCode.substring(color.offset, color.offset + color.length);
+      expect(
+        regionText,
+        equals(expectedColorCode),
+        reason: 'Color $i expected $expectedColorCode but was $regionText',
+      );
+
+      void expectComponent(int actual, int expected, String name) => expect(
+            actual,
+            expected,
+            reason: '$name value for $expectedColorCode is not correct',
+          );
+
+      expectComponent(color.color.alpha, expectedAlpha, 'Alpha');
+      expectComponent(color.color.red, expectedRed, 'Red');
+      expectComponent(color.color.green, expectedGreen, 'Green');
+      expectComponent(color.color.blue, expectedBlue, 'Blue');
+    });
+  }
+
+  void expectNoErrors(ResolvedUnitResult result) {
+    // If the test code has errors, generate a suitable failure to help debug.
+    final errors = result.errors
+        .where((error) => error.severity == Severity.error)
+        .toList();
+    if (errors.isNotEmpty) {
+      throw 'Code has errors: $errors\n\n${result.content}';
+    }
+  }
+
+  @override
+  void setUp() {
+    super.setUp();
+    writeTestPackageConfig(flutter: true);
+    testPath = convertPath('/home/test/lib/test.dart');
+    otherPath = convertPath('/home/test/lib/other_file.dart');
+  }
+
+  Future<void> test_collectionLiteral_const() async {
+    const testCode = '''
+main() {
+  const colors = [
+    [[COLOR]],
+  ];
+}
+''';
+    await checkAllColors(testCode, onlyConst: true);
+  }
+
+  Future<void> test_collectionLiteral_nonConst() async {
+    const testCode = '''
+main() {
+  final colors = [
+    [[COLOR]],
+  ];
+}
+''';
+    await checkAllColors(testCode);
+  }
+
+  Future<void> test_customClass() async {
+    const testCode = '''
+import 'other_file.dart';
+
+void main() {
+  final a1 = MyTheme.staticWhite;
+  final a2 = MyTheme.staticMaterialRedAccent;
+  const theme = MyTheme();
+  final b1 = theme.instanceWhite;
+  final b2 = theme.instanceMaterialRedAccent;
+}
+''';
+
+    const otherCode = '''
+class MyTheme {
+  static const Color staticWhite = Colors.white;
+  static const MaterialAccentColor staticMaterialRedAccent = Colors.redAccent;
+
+  final Color instanceWhite;
+  final MaterialAccentColor instanceMaterialRedAccent;
+
+  const MyTheme()
+      : instanceWhite = Colors.white,
+        instanceMaterialRedAccent = Colors.redAccent;
+}
+''';
+    await expectColors(
+      testCode,
+      {
+        'MyTheme.staticWhite': 0xFFFFFFFF,
+        'MyTheme.staticMaterialRedAccent': 0xFFFFAA00,
+        'theme.instanceWhite': 0xFFFFFFFF,
+        'theme.instanceMaterialRedAccent': 0xFFFFAA00,
+      },
+      otherCode: otherCode,
+    );
+  }
+
+  Future<void> test_local_const() async {
+    const testCode = '''
+main() {
+  const a = [[COLOR]];
+}
+''';
+    await checkAllColors(testCode, onlyConst: true);
+  }
+
+  Future<void> test_local_nonConst() async {
+    const testCode = '''
+main() {
+  final a = [[COLOR]];
+}
+''';
+    await checkAllColors(testCode);
+  }
+
+  Future<void> test_namedParameter_const() async {
+    const testCode = '''
+main() {
+  const w = Widget(color: [[COLOR]]);
+}
+
+class Widget {
+  final Color? color;
+  const Widget({this.color});
+}
+''';
+    await checkAllColors(testCode, onlyConst: true);
+  }
+
+  Future<void> test_namedParameter_nonConst() async {
+    const testCode = '''
+main() {
+  final w = Widget(color: [[COLOR]]);
+}
+
+class Widget {
+  final Color? color;
+  Widget({this.color});
+}
+''';
+    await checkAllColors(testCode);
+  }
+
+  Future<void> test_nested_const() async {
+    const testCode = '''
+main() {
+  const a = [[COLOR]];
+}
+''';
+    await checkAllColors(testCode, onlyConst: true);
+  }
+
+  Future<void> test_nested_nonConst() async {
+    const testCode = '''
+main() {
+  final a = [[COLOR]];
+}
+''';
+    await checkAllColors(testCode);
+  }
+
+  Future<void> test_topLevel_const() async {
+    const testCode = '''
+const a = [[COLOR]];
+''';
+    await checkAllColors(testCode, onlyConst: true);
+  }
+
+  Future<void> test_topLevel_nonConst() async {
+    const testCode = '''
+final a = [[COLOR]];
+''';
+    await checkAllColors(testCode);
+  }
+
+  String _withCommonImports(String code) => '''
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/painting.dart';
+import 'package:flutter/material.dart';
+
+$code''';
+}
diff --git a/pkg/analysis_server/test/src/computer/test_all.dart b/pkg/analysis_server/test/src/computer/test_all.dart
index 6f67c7a..af15c51 100644
--- a/pkg/analysis_server/test/src/computer/test_all.dart
+++ b/pkg/analysis_server/test/src/computer/test_all.dart
@@ -5,6 +5,7 @@
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'closing_labels_computer_test.dart' as closing_labels_computer;
+import 'color_computer_test.dart' as color_computer;
 import 'folding_computer_test.dart' as folding_computer;
 import 'highlights_computer_test.dart' as highlights_computer;
 import 'import_elements_computer_test.dart' as import_elements_computer;
@@ -15,6 +16,7 @@
 void main() {
   defineReflectiveSuite(() {
     closing_labels_computer.main();
+    color_computer.main();
     folding_computer.main();
     highlights_computer.main();
     import_elements_computer.main();
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index 517eabc..767d937 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -25,7 +25,7 @@
   static const _opaqueClassName = 'Opaque';
   static const _ffiNativeName = 'FfiNative';
 
-  static const List<String> _primitiveIntegerNativeTypes = [
+  static const Set<String> _primitiveIntegerNativeTypes = {
     'Int8',
     'Int16',
     'Int32',
@@ -35,12 +35,12 @@
     'Uint32',
     'Uint64',
     'IntPtr'
-  ];
+  };
 
-  static const List<String> _primitiveDoubleNativeTypes = [
+  static const Set<String> _primitiveDoubleNativeTypes = {
     'Float',
     'Double',
-  ];
+  };
 
   static const _primitiveBoolNativeType = 'Bool';
 
@@ -1516,13 +1516,7 @@
   bool get isCompoundSubtype {
     var element = name.staticElement;
     if (element is ClassElement) {
-      bool isCompound(InterfaceType? type) {
-        return type != null && type.isCompound;
-      }
-
-      return isCompound(element.supertype) ||
-          element.interfaces.any(isCompound) ||
-          element.mixins.any(isCompound);
+      return element.allSupertypes.any((e) => e.isCompound);
     }
     return false;
   }
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index afb6393..44f4d37 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -1030,8 +1030,22 @@
       if (explicitTypeNeeded) {
         var firstNeededType = neededTypes[0];
         if (neededTypes.any((t) => t != firstNeededType)) {
-          throw UnimplementedError(
-              'Different explicit types needed in multi-variable declaration');
+          // Different variables need different types.  We handle this by
+          // introducing casts, which is not great but gets the job done.
+          for (int i = 0; i < node.variables.length; i++) {
+            if (neededTypes[i] != inferredTypes[i]) {
+              // We only have to worry about variables with initializers because
+              // variables without initializers will get the type `dynamic`.
+              var initializer = node.variables[i].initializer;
+              if (initializer != null) {
+                (_fixBuilder._getChange(initializer) as NodeChangeForExpression)
+                    .addExpressionChange(
+                        IntroduceAsChange(neededTypes[i], isDowncast: false),
+                        AtomicEditInfo(
+                            NullabilityFixDescription.otherCastExpression, {}));
+              }
+            }
+          }
         } else {
           (_fixBuilder._getChange(node) as NodeChangeForVariableDeclarationList)
               .addExplicitType = firstNeededType;
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 69a8936..286aa4e 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -8952,6 +8952,24 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  Future<void> test_var_with_different_types_becoming_explicit() async {
+    // When types need to be added to some variables in a declaration but not
+    // others, we handle it by introducing `as` casts.
+    var content = '''
+f(int i, String s) {
+  var x = i, y = s;
+  x = null;
+}
+''';
+    var expected = '''
+f(int i, String s) {
+  var x = i as int?, y = s;
+  x = null;
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   Future<void> test_weak_if_visit_weak_subexpression() async {
     var content = '''
 int f(int x, int/*?*/ y) {
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index 1ae463a..cc93c1e 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -13099,13 +13099,9 @@
   /**
    * Allows access to all custom data attributes (data-*) set on this element.
    *
-   * The keys for the map must follow these rules:
-   *
-   * * The name must not begin with 'xml'.
-   * * The name cannot contain a semi-colon (';').
-   * * The name cannot contain any capital letters.
-   *
-   * Any keys from markup will be converted to camel-cased keys in the map.
+   * Any data attributes in the markup will be converted to camel-cased keys
+   * in the map based on [these conversion
+   * rules](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset).
    *
    * For example, HTML specified as:
    *
@@ -13117,6 +13113,8 @@
    *
    * See also:
    *
+   * * [HTML data-* attributes naming
+       restrictions](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*)
    * * [Custom data
    *   attributes](http://dev.w3.org/html5/spec-preview/global-attributes.html#custom-data-attribute)
    */
diff --git a/tools/VERSION b/tools/VERSION
index 78db584..21d2a58 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 16
 PATCH 0
-PRERELEASE 8
+PRERELEASE 9
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/dom/templates/html/impl/impl_Element.darttemplate b/tools/dom/templates/html/impl/impl_Element.darttemplate
index 8ced8e6..046addf 100644
--- a/tools/dom/templates/html/impl/impl_Element.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Element.darttemplate
@@ -756,13 +756,9 @@
   /**
    * Allows access to all custom data attributes (data-*) set on this element.
    *
-   * The keys for the map must follow these rules:
-   *
-   * * The name must not begin with 'xml'.
-   * * The name cannot contain a semi-colon (';').
-   * * The name cannot contain any capital letters.
-   *
-   * Any keys from markup will be converted to camel-cased keys in the map.
+   * Any data attributes in the markup will be converted to camel-cased keys
+   * in the map based on [these conversion
+   * rules](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset).
    *
    * For example, HTML specified as:
    *
@@ -774,6 +770,8 @@
    *
    * See also:
    *
+   * * [HTML data-* attributes naming
+       restrictions](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*)
    * * [Custom data
    *   attributes](http://dev.w3.org/html5/spec-preview/global-attributes.html#custom-data-attribute)
    */