Add SDK constraint checks for the constant update 2018 features

Change-Id: I308a32de5e73a93d8dead38ab45c130f88ef706d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97567
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/dart/element/type.dart b/pkg/analyzer/lib/dart/element/type.dart
index c4beb80..a41dc92 100644
--- a/pkg/analyzer/lib/dart/element/type.dart
+++ b/pkg/analyzer/lib/dart/element/type.dart
@@ -93,6 +93,12 @@
   bool get isDartCoreNull;
 
   /**
+   * Return `true` if this type represents the type 'String' defined in the
+   * dart:core library.
+   */
+  bool get isDartCoreString;
+
+  /**
    * Return `true` if this type represents the type 'dynamic'.
    */
   bool get isDynamic;
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 8642f19..7c86f12 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -337,6 +337,11 @@
   HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER,
   HintCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT,
   HintCode.SDK_VERSION_ASYNC_EXPORTED_FROM_CORE,
+  HintCode.SDK_VERSION_AS_EXPRESSION_IN_CONST_CONTEXT,
+  HintCode.SDK_VERSION_BOOL_OPERATOR,
+  HintCode.SDK_VERSION_EQ_EQ_OPERATOR_IN_CONST_CONTEXT,
+  HintCode.SDK_VERSION_GT_GT_GT_OPERATOR,
+  HintCode.SDK_VERSION_IS_EXPRESSION_IN_CONST_CONTEXT,
   HintCode.SDK_VERSION_SET_LITERAL,
   HintCode.SDK_VERSION_UI_AS_CODE,
   HintCode.STRICT_RAW_TYPE,
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 5fbed4f..e2393ca 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -1541,6 +1541,15 @@
   }
 
   @override
+  bool get isDartCoreString {
+    ClassElement element = this.element;
+    if (element == null) {
+      return false;
+    }
+    return element.name == "String" && element.library.isDartCore;
+  }
+
+  @override
   bool get isObject => element.supertype == null && !element.isMixin;
 
   @override
@@ -2825,6 +2834,9 @@
   bool get isDartCoreNull => false;
 
   @override
+  bool get isDartCoreString => false;
+
+  @override
   bool get isDynamic => false;
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 950e5ea..ef7f16c 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -550,6 +550,61 @@
           "Try either importing 'dart:async' or updating the SDK constraints.");
 
   /**
+   * An as expression being used in a const context is expected to run on
+   * versions of the SDK that did not support them.
+   */
+  static const HintCode SDK_VERSION_AS_EXPRESSION_IN_CONST_CONTEXT =
+      const HintCode(
+          'SDK_VERSION_AS_EXPRESSION_IN_CONST_CONTEXT',
+          "The use of an as expression in a constant expression wasn't "
+          "supported until version 2.2.2, but this code is required to be able "
+          "to run on earlier versions.",
+          correction: "Try updating the SDK constraints.");
+
+  /**
+   * The operator '&', '|' or '^' is being used on boolean values in code that
+   * is expected to run on versions of the SDK that did not support it.
+   */
+  static const HintCode SDK_VERSION_BOOL_OPERATOR = const HintCode(
+      'SDK_VERSION_BOOL_OPERATOR',
+      "Using the operator '{0}' for 'bool's was not supported until version "
+      "2.2.2, but this code is required to be able to run on earlier versions.",
+      correction: "Try updating the SDK constraints.");
+
+  /**
+   * The operator '==' is being used on non-primitive values in code that
+   * is expected to run on versions of the SDK that did not support it.
+   */
+  static const HintCode SDK_VERSION_EQ_EQ_OPERATOR_IN_CONST_CONTEXT = const HintCode(
+      'SDK_VERSION_EQ_EQ_OPERATOR_IN_CONST_CONTEXT',
+      "Using the operator '==' for non-primitive types was not supported until "
+      "version 2.2.2, but this code is required to be able to run on earlier "
+      "versions.",
+      correction: "Try updating the SDK constraints.");
+
+  /**
+   * The operator '>>>' is being used in code that is expected to run on
+   * versions of the SDK that did not support it.
+   */
+  static const HintCode SDK_VERSION_GT_GT_GT_OPERATOR = const HintCode(
+      'SDK_VERSION_GT_GT_GT_OPERATOR',
+      "The operator '>>>' was not supported until version 2.2.2, but this code "
+      "is required to be able to run on earlier versions.",
+      correction: "Try updating the SDK constraints.");
+
+  /**
+   * An is expression being used in a const context is expected to run on
+   * versions of the SDK that did not support them.
+   */
+  static const HintCode SDK_VERSION_IS_EXPRESSION_IN_CONST_CONTEXT =
+      const HintCode(
+          'SDK_VERSION_IS_EXPRESSION_IN_CONST_CONTEXT',
+          "The use of an is expression in a constant expression wasn't "
+          "supported until version 2.2.2, but this code is required to be able "
+          "to run on earlier versions.",
+          correction: "Try updating the SDK constraints.");
+
+  /**
    * A set literal is being used in code that is expected to run on versions of
    * the SDK that did not support them.
    */
diff --git a/pkg/analyzer/lib/src/hint/sdk_constraint_verifier.dart b/pkg/analyzer/lib/src/hint/sdk_constraint_verifier.dart
index b7a9c21..c8b749c 100644
--- a/pkg/analyzer/lib/src/hint/sdk_constraint_verifier.dart
+++ b/pkg/analyzer/lib/src/hint/sdk_constraint_verifier.dart
@@ -3,9 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/resolver/scope.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/resolver.dart';
@@ -26,6 +29,11 @@
   /// The version constraint for the SDK.
   final VersionConstraint _versionConstraint;
 
+  /// A cached flag indicating whether references to the constant-update-2018
+  /// features need to be checked. Use [checkConstantUpdate2018] to access this
+  /// field.
+  bool _checkConstantUpdate2018;
+
   /// A cached flag indicating whether references to Future and Stream need to
   /// be checked. Use [checkFutureAndStream] to access this field.
   bool _checkFutureAndStream;
@@ -64,6 +72,11 @@
   VersionRange get before_2_2_2 =>
       new VersionRange(max: Version.parse('2.2.2'), includeMax: false);
 
+  /// Return `true` if references to the constant-update-2018 features need to
+  /// be checked.
+  bool get checkConstantUpdate2018 => _checkConstantUpdate2018 ??=
+      !before_2_2_2.intersect(_versionConstraint).isEmpty;
+
   /// Return `true` if references to Future and Stream need to be checked.
   bool get checkFutureAndStream => _checkFutureAndStream ??=
       !before_2_1_0.intersect(_versionConstraint).isEmpty;
@@ -78,6 +91,51 @@
       _checkUiAsCode ??= !before_2_2_2.intersect(_versionConstraint).isEmpty;
 
   @override
+  void visitAsExpression(AsExpression node) {
+    if (checkConstantUpdate2018 &&
+        (node as AsExpressionImpl).inConstantContext) {
+      _errorReporter.reportErrorForNode(
+          HintCode.SDK_VERSION_AS_EXPRESSION_IN_CONST_CONTEXT, node);
+    }
+    super.visitAsExpression(node);
+  }
+
+  @override
+  void visitBinaryExpression(BinaryExpression node) {
+    if (checkConstantUpdate2018) {
+      TokenType operatorType = node.operator.type;
+      if (operatorType == TokenType.GT_GT_GT) {
+        _errorReporter.reportErrorForToken(
+            HintCode.SDK_VERSION_GT_GT_GT_OPERATOR, node.operator);
+      } else if (operatorType == TokenType.AMPERSAND ||
+          operatorType == TokenType.BAR ||
+          operatorType == TokenType.CARET) {
+        if (node.leftOperand.staticType.isDartCoreBool) {
+          _errorReporter.reportErrorForToken(HintCode.SDK_VERSION_BOOL_OPERATOR,
+              node.operator, [node.operator.lexeme]);
+        }
+      } else if (operatorType == TokenType.EQ_EQ &&
+          (node as BinaryExpressionImpl).inConstantContext) {
+        bool primitive(Expression node) {
+          DartType type = node.staticType;
+          return type.isDartCoreBool ||
+              type.isDartCoreDouble ||
+              type.isDartCoreInt ||
+              type.isDartCoreNull ||
+              type.isDartCoreString;
+        }
+
+        if (!primitive(node.leftOperand) || !primitive(node.rightOperand)) {
+          _errorReporter.reportErrorForToken(
+              HintCode.SDK_VERSION_EQ_EQ_OPERATOR_IN_CONST_CONTEXT,
+              node.operator);
+        }
+      }
+    }
+    super.visitBinaryExpression(node);
+  }
+
+  @override
   void visitForElement(ForElement node) {
     _validateUiAsCode(node);
     bool wasInUiAsCode = _inUiAsCode;
@@ -101,6 +159,25 @@
   }
 
   @override
+  void visitIsExpression(IsExpression node) {
+    if (checkConstantUpdate2018 &&
+        (node as IsExpressionImpl).inConstantContext) {
+      _errorReporter.reportErrorForNode(
+          HintCode.SDK_VERSION_IS_EXPRESSION_IN_CONST_CONTEXT, node);
+    }
+    super.visitIsExpression(node);
+  }
+
+  @override
+  void visitMethodDeclaration(MethodDeclaration node) {
+    if (checkConstantUpdate2018 && node.isOperator && node.name.name == '>>>') {
+      _errorReporter.reportErrorForNode(
+          HintCode.SDK_VERSION_GT_GT_GT_OPERATOR, node.name);
+    }
+    super.visitMethodDeclaration(node);
+  }
+
+  @override
   void visitSetOrMapLiteral(SetOrMapLiteral node) {
     if (node.isSet && checkSetLiterals && !_inSetLiteral) {
       _errorReporter.reportErrorForNode(HintCode.SDK_VERSION_SET_LITERAL, node);
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index f9828f5..47659a8 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -143,9 +143,12 @@
 
 void print(Object object) {}
 
-class bool extends Object {
+abstract class bool extends Object {
   external const factory bool.fromEnvironment(String name,
       {bool defaultValue: false});
+  bool operator &(bool other);
+  bool operator |(bool other);
+  bool operator ^(bool other);
 }
 
 abstract class Comparable<T> {
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_constraint_verifier_support.dart b/pkg/analyzer/test/src/diagnostics/sdk_constraint_verifier_support.dart
index 81658b9..0624d6a 100644
--- a/pkg/analyzer/test/src/diagnostics/sdk_constraint_verifier_support.dart
+++ b/pkg/analyzer/test/src/diagnostics/sdk_constraint_verifier_support.dart
@@ -7,9 +7,11 @@
 
 import '../dart/resolution/driver_resolution.dart';
 
-/// A base class designed to be used by tests of the hints produced by an
+/// A base class designed to be used by tests of the hints produced by the
 /// SdkConstraintVerifier.
 class SdkConstraintVerifierTest extends DriverResolutionTest {
+  /// Verify that the [errorCodes] are produced if the [source] is analyzed in
+  /// a context that specifies the minimum SDK version to be [version].
   verifyVersion(String version, String source,
       {List<ErrorCode> errorCodes}) async {
     driver.configure(
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_version_as_expression_in_const_context_test.dart b/pkg/analyzer/test/src/diagnostics/sdk_version_as_expression_in_const_context_test.dart
new file mode 100644
index 0000000..b63e481
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/sdk_version_as_expression_in_const_context_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2019, 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:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'sdk_constraint_verifier_support.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(SdkVersionAsExpressionInConstContextTest);
+  });
+}
+
+@reflectiveTest
+class SdkVersionAsExpressionInConstContextTest
+    extends SdkConstraintVerifierTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+    ..enabledExperiments = [EnableString.constant_update_2018];
+
+  test_equals() {
+    verifyVersion('2.2.2', '''
+const dynamic a = 2;
+const c = (a as int) + 2;
+''');
+  }
+
+  test_lessThan() {
+    verifyVersion('2.2.0', '''
+const dynamic a = 2;
+const c = (a as int) + 2;
+''', errorCodes: [HintCode.SDK_VERSION_AS_EXPRESSION_IN_CONST_CONTEXT]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_version_bool_operator_test.dart b/pkg/analyzer/test/src/diagnostics/sdk_version_bool_operator_test.dart
new file mode 100644
index 0000000..241e28f
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/sdk_version_bool_operator_test.dart
@@ -0,0 +1,95 @@
+// Copyright (c) 2019, 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:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'sdk_constraint_verifier_support.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(SdkVersionBoolOperatorTest);
+  });
+}
+
+@reflectiveTest
+class SdkVersionBoolOperatorTest extends SdkConstraintVerifierTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+    ..enabledExperiments = [EnableString.constant_update_2018];
+
+  test_and_const_equals() {
+    verifyVersion('2.2.2', '''
+const c = true & false;
+''');
+  }
+
+  test_and_const_lessThan() {
+    verifyVersion('2.2.0', '''
+const c = true & false;
+''', errorCodes: [HintCode.SDK_VERSION_BOOL_OPERATOR]);
+  }
+
+  test_and_nonConst_equals() {
+    verifyVersion('2.2.2', '''
+var c = true & false;
+''');
+  }
+
+  test_and_nonConst_lessThan() {
+    verifyVersion('2.2.0', '''
+var c = true & false;
+''', errorCodes: [HintCode.SDK_VERSION_BOOL_OPERATOR]);
+  }
+
+  test_or_const_equals() {
+    verifyVersion('2.2.2', '''
+const c = true | false;
+''');
+  }
+
+  test_or_const_lessThan() {
+    verifyVersion('2.2.0', '''
+const c = true | false;
+''', errorCodes: [HintCode.SDK_VERSION_BOOL_OPERATOR]);
+  }
+
+  test_or_nonConst_equals() {
+    verifyVersion('2.2.2', '''
+var c = true | false;
+''');
+  }
+
+  test_or_nonConst_lessThan() {
+    verifyVersion('2.2.0', '''
+var c = true | false;
+''', errorCodes: [HintCode.SDK_VERSION_BOOL_OPERATOR]);
+  }
+
+  test_xor_const_equals() {
+    verifyVersion('2.2.2', '''
+const c = true ^ false;
+''');
+  }
+
+  test_xor_const_lessThan() {
+    verifyVersion('2.2.0', '''
+const c = true ^ false;
+''', errorCodes: [HintCode.SDK_VERSION_BOOL_OPERATOR]);
+  }
+
+  test_xor_nonConst_equals() {
+    verifyVersion('2.2.2', '''
+var c = true ^ false;
+''');
+  }
+
+  test_xor_nonConst_lessThan() {
+    verifyVersion('2.2.0', '''
+var c = true ^ false;
+''', errorCodes: [HintCode.SDK_VERSION_BOOL_OPERATOR]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_version_eq_eq_operator_test.dart b/pkg/analyzer/test/src/diagnostics/sdk_version_eq_eq_operator_test.dart
new file mode 100644
index 0000000..3d6b6ca
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/sdk_version_eq_eq_operator_test.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2019, 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:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'sdk_constraint_verifier_support.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(SdkVersionEqEqOperatorTest);
+  });
+}
+
+@reflectiveTest
+class SdkVersionEqEqOperatorTest extends SdkConstraintVerifierTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+    ..enabledExperiments = [EnableString.constant_update_2018];
+
+  test_left_equals() {
+    verifyVersion('2.2.2', '''
+class A {
+  const A();
+}
+const A a = A();
+const c = a == null;
+''');
+  }
+
+  test_left_lessThan() {
+    verifyVersion('2.2.0', '''
+class A {
+  const A();
+}
+const A a = A();
+const c = a == null;
+''', errorCodes: [HintCode.SDK_VERSION_EQ_EQ_OPERATOR_IN_CONST_CONTEXT]);
+  }
+
+  test_right_equals() {
+    verifyVersion('2.2.2', '''
+class A {
+  const A();
+}
+const A a = A();
+const c = null == a;
+''');
+  }
+
+  test_right_lessThan() {
+    verifyVersion('2.2.0', '''
+class A {
+  const A();
+}
+const A a = A();
+const c = null == a;
+''', errorCodes: [HintCode.SDK_VERSION_EQ_EQ_OPERATOR_IN_CONST_CONTEXT]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_version_gt_gt_gt_operator_test.dart b/pkg/analyzer/test/src/diagnostics/sdk_version_gt_gt_gt_operator_test.dart
new file mode 100644
index 0000000..88ddc4e
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/sdk_version_gt_gt_gt_operator_test.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2019, 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:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'sdk_constraint_verifier_support.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(SdkVersionGtGtGtOperatorTest);
+  });
+}
+
+@reflectiveTest
+class SdkVersionGtGtGtOperatorTest extends SdkConstraintVerifierTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+    ..enabledExperiments = [EnableString.constant_update_2018];
+
+  test_const_equals() {
+    // TODO(brianwilkerson) Add '>>>' to MockSdk and remove the code
+    //  UNDEFINED_OPERATOR when constant update is enabled by default.
+    verifyVersion('2.2.2', '''
+const a = 42 >>> 3;
+''', errorCodes: [StaticTypeWarningCode.UNDEFINED_OPERATOR]);
+  }
+
+  test_const_lessThan() {
+    // TODO(brianwilkerson) Add '>>>' to MockSdk and remove the code
+    //  UNDEFINED_OPERATOR when constant update is enabled by default.
+    verifyVersion('2.2.0', '''
+const a = 42 >>> 3;
+''', errorCodes: [
+      StaticTypeWarningCode.UNDEFINED_OPERATOR,
+      HintCode.SDK_VERSION_GT_GT_GT_OPERATOR
+    ]);
+  }
+
+  test_declaration_equals() {
+    verifyVersion('2.2.2', '''
+class A {
+  A operator >>>(A a) => this;
+}
+''');
+  }
+
+  test_declaration_lessThan() {
+    verifyVersion('2.2.0', '''
+class A {
+  A operator >>>(A a) => this;
+}
+''', errorCodes: [HintCode.SDK_VERSION_GT_GT_GT_OPERATOR]);
+  }
+
+  test_nonConst_equals() {
+    // TODO(brianwilkerson) Add '>>>' to MockSdk and remove the code
+    //  UNDEFINED_OPERATOR when constant update is enabled by default.
+    verifyVersion('2.2.2', '''
+var a = 42 >>> 3;
+''', errorCodes: [StaticTypeWarningCode.UNDEFINED_OPERATOR]);
+  }
+
+  test_nonConst_lessThan() {
+    // TODO(brianwilkerson) Add '>>>' to MockSdk and remove the code
+    //  UNDEFINED_OPERATOR when constant update is enabled by default.
+    verifyVersion('2.2.0', '''
+var a = 42 >>> 3;
+''', errorCodes: [
+      StaticTypeWarningCode.UNDEFINED_OPERATOR,
+      HintCode.SDK_VERSION_GT_GT_GT_OPERATOR
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_version_is_expression_in_const_context_test.dart b/pkg/analyzer/test/src/diagnostics/sdk_version_is_expression_in_const_context_test.dart
new file mode 100644
index 0000000..70778cf
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/sdk_version_is_expression_in_const_context_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2019, 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:analyzer/src/dart/analysis/experiments.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'sdk_constraint_verifier_support.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(SdkVersionIsExpressionInConstContextTest);
+  });
+}
+
+@reflectiveTest
+class SdkVersionIsExpressionInConstContextTest
+    extends SdkConstraintVerifierTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+    ..enabledExperiments = [EnableString.constant_update_2018];
+
+  test_equals() {
+    verifyVersion('2.2.2', '''
+const dynamic a = 2;
+const c = a is int;
+''');
+  }
+
+  test_lessThan() {
+    verifyVersion('2.2.0', '''
+const dynamic a = 2;
+const c = a is int;
+''', errorCodes: [HintCode.SDK_VERSION_IS_EXPRESSION_IN_CONST_CONTEXT]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 357bdaa..6f6fe2a 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -67,8 +67,16 @@
     as non_constant_spread_expression_from_deferred_library;
 import 'not_iterable_spread_test.dart' as not_iterable_spread;
 import 'not_map_spread_test.dart' as not_map_spread;
+import 'sdk_version_as_expression_in_const_context_test.dart'
+    as sdk_version_as_expression_in_const_context;
 import 'sdk_version_async_exported_from_core_test.dart'
     as sdk_version_async_exported_from_core;
+import 'sdk_version_bool_operator_test.dart' as sdk_version_bool_operator;
+import 'sdk_version_eq_eq_operator_test.dart' as sdk_version_eq_eq_operator;
+import 'sdk_version_gt_gt_gt_operator_test.dart'
+    as sdk_version_gt_gt_gt_operator;
+import 'sdk_version_is_expression_in_const_context_test.dart'
+    as sdk_version_is_expression_in_const_context;
 import 'sdk_version_set_literal_test.dart' as sdk_version_set_literal;
 import 'sdk_version_ui_as_code_test.dart' as sdk_version_ui_as_code;
 import 'set_element_type_not_assignable_test.dart'
@@ -152,7 +160,12 @@
     non_constant_spread_expression_from_deferred_library.main();
     not_iterable_spread.main();
     not_map_spread.main();
+    sdk_version_as_expression_in_const_context.main();
     sdk_version_async_exported_from_core.main();
+    sdk_version_bool_operator.main();
+    sdk_version_eq_eq_operator.main();
+    sdk_version_gt_gt_gt_operator.main();
+    sdk_version_is_expression_in_const_context.main();
     sdk_version_set_literal.main();
     sdk_version_ui_as_code.main();
     set_element_type_not_assignable.main();