Version 3.9.0-313.0.dev

Merge e86b73617262df6fcbd9c082a1e8cade019a62b3 into dev
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 8ad0ee75..5e8202f 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -5409,10 +5409,7 @@
       variables.cast<PatternVariableFragment>();
 }
 
-class LabelElementImpl extends ElementImpl
-    with WrappedElementMixin
-    implements LabelElement {
-  @override
+class LabelElementImpl extends ElementImpl implements LabelElement {
   final LabelFragmentImpl _wrappedElement;
 
   LabelElementImpl(this._wrappedElement);
@@ -5440,6 +5437,9 @@
   bool get isOnSwitchMember => _wrappedElement.isOnSwitchMember;
 
   @override
+  bool get isSynthetic => _wrappedElement.isSynthetic;
+
+  @override
   ElementKind get kind => ElementKind.LABEL;
 
   @override
@@ -5450,6 +5450,9 @@
   LibraryElement get library2 => library;
 
   @override
+  String? get name3 => _wrappedElement.name2;
+
+  @override
   T? accept<T>(ElementVisitor2<T> visitor) {
     return visitor.visitLabelElement(this);
   }
@@ -5459,6 +5462,15 @@
   T? accept2<T>(ElementVisitor2<T> visitor) => accept(visitor);
 
   @override
+  String displayString2({
+    bool multiline = false,
+    bool preferTypeAlias = false,
+  }) => _wrappedElement.getDisplayString(
+    multiline: multiline,
+    preferTypeAlias: preferTypeAlias,
+  );
+
+  @override
   void visitChildren2<T>(ElementVisitor2<T> visitor) {}
 }
 
@@ -6679,9 +6691,7 @@
 }
 
 class LocalFunctionElementImpl extends ExecutableElementImpl
-    with WrappedElementMixin
     implements LocalFunctionElement {
-  @override
   final LocalFunctionFragmentImpl _wrappedElement;
 
   LocalFunctionElementImpl(this._wrappedElement);
@@ -6737,6 +6747,9 @@
   bool get isStatic => _wrappedElement.isStatic;
 
   @override
+  bool get isSynthetic => _wrappedElement.isSynthetic;
+
+  @override
   ElementKind get kind => ElementKind.FUNCTION;
 
   @override
@@ -6747,6 +6760,9 @@
   MetadataImpl get metadata2 => metadata;
 
   @override
+  String? get name3 => _wrappedElement.name2;
+
+  @override
   TypeImpl get returnType => _wrappedElement.returnType;
 
   @override
@@ -6770,6 +6786,15 @@
   @Deprecated('Use accept instead')
   @override
   T? accept2<T>(ElementVisitor2<T> visitor) => accept(visitor);
+
+  @override
+  String displayString2({
+    bool multiline = false,
+    bool preferTypeAlias = false,
+  }) => _wrappedElement.getDisplayString(
+    multiline: multiline,
+    preferTypeAlias: preferTypeAlias,
+  );
 }
 
 /// A concrete implementation of a [LocalFunctionFragment].
@@ -6798,11 +6823,13 @@
 }
 
 class LocalVariableElementImpl extends PromotableElementImpl
-    with WrappedElementMixin, _NonTopLevelVariableOrParameter
+    with _NonTopLevelVariableOrParameter
     implements LocalVariableElement {
-  @override
   final LocalVariableFragmentImpl _wrappedElement;
 
+  @override
+  TypeImpl type = InvalidTypeImpl.instance;
+
   LocalVariableElementImpl(this._wrappedElement);
 
   @override
@@ -6838,6 +6865,9 @@
   bool get isStatic => _wrappedElement.isStatic;
 
   @override
+  bool get isSynthetic => _wrappedElement.isSynthetic;
+
+  @override
   ElementKind get kind => ElementKind.LOCAL_VARIABLE;
 
   @override
@@ -6855,13 +6885,7 @@
   MetadataImpl get metadata2 => metadata;
 
   @override
-  TypeImpl get type => _wrappedElement.type;
-
-  set type(TypeImpl type) => _wrappedElement.type = type;
-
-  LocalVariableFragmentImpl get wrappedElement {
-    return _wrappedElement;
-  }
+  String? get name3 => _wrappedElement.name2;
 
   @override
   FragmentImpl? get _enclosingFunction => _wrappedElement.enclosingElement3;
@@ -6874,6 +6898,19 @@
   @Deprecated('Use accept instead')
   @override
   T? accept2<T>(ElementVisitor2<T> visitor) => accept(visitor);
+
+  @override
+  String displayString2({
+    bool multiline = false,
+    bool preferTypeAlias = false,
+  }) {
+    var builder = ElementDisplayStringBuilder(
+      multiline: multiline,
+      preferTypeAlias: preferTypeAlias,
+    );
+    builder.writeVariableElement2(this);
+    return builder.toString();
+  }
 }
 
 class LocalVariableFragmentImpl extends NonParameterVariableFragmentImpl
@@ -10126,31 +10163,16 @@
     _type = type;
   }
 
+  /// The declared type of this variable.
+  // TODO(scheglov): turn into field
+  TypeImpl? get type2 => _type;
+
   @override
   void appendTo(ElementDisplayStringBuilder builder) {
     builder.writeVariableElement(this);
   }
 }
 
-mixin WrappedElementMixin implements ElementImpl {
-  @override
-  bool get isSynthetic => _wrappedElement.isSynthetic;
-
-  @override
-  String? get name3 => _wrappedElement.name2;
-
-  FragmentImpl get _wrappedElement;
-
-  @override
-  String displayString2({
-    bool multiline = false,
-    bool preferTypeAlias = false,
-  }) => _wrappedElement.getDisplayString(
-    multiline: multiline,
-    preferTypeAlias: preferTypeAlias,
-  );
-}
-
 abstract class _ExistingFragmentImpl extends FragmentImpl
     with _HasLibraryMixin {
   _ExistingFragmentImpl({required super.nameOffset});
diff --git a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
index 5ca8c03..fe3e03c 100644
--- a/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/resolution_visitor.dart
@@ -244,9 +244,10 @@
         fragment.isFinal = true;
         if (exceptionTypeNode == null) {
           fragment.hasImplicitType = true;
-          fragment.type = _typeProvider.objectType;
+          fragment.element.type = _typeProvider.objectType;
         } else {
           fragment.type = exceptionTypeNode.typeOrThrow;
+          fragment.element.type = exceptionTypeNode.typeOrThrow;
         }
 
         fragment.setCodeRange(
@@ -267,7 +268,8 @@
         stackTraceNode.declaredFragment = fragment;
 
         fragment.isFinal = true;
-        fragment.type = _typeProvider.stackTraceType;
+        fragment.hasImplicitType = true;
+        fragment.element.type = _typeProvider.stackTraceType;
 
         fragment.setCodeRange(
           stackTraceNode.name.offset,
@@ -391,12 +393,13 @@
     fragment.isConst = node.isConst;
     fragment.isFinal = node.isFinal;
 
-    if (node.type == null) {
-      fragment.hasImplicitType = true;
-      fragment.type = _dynamicType;
+    if (node.type case var typeNode?) {
+      typeNode.accept(this);
+      fragment.type = typeNode.typeOrThrow;
+      fragment.element.type = typeNode.typeOrThrow;
     } else {
-      node.type!.accept(this);
-      fragment.type = node.type!.typeOrThrow;
+      fragment.hasImplicitType = true;
+      fragment.element.type = _dynamicType;
     }
 
     _setCodeRange(fragment, node);
@@ -418,7 +421,10 @@
     _elementHolder.enclose(fragment);
     _define(fragment.element);
     fragment.hasImplicitType = node.type == null;
-    fragment.type = node.type?.type ?? InvalidTypeImpl.instance;
+    if (node.type case var typeNode?) {
+      fragment.type = typeNode.typeOrThrow;
+      fragment.element.type = typeNode.typeOrThrow;
+    }
     node.declaredFragment = fragment;
 
     var patternContext = node.patternContext;
@@ -1419,13 +1425,19 @@
       fragment = _elementWalker!.getVariable();
       node.declaredFragment = fragment;
     } else {
-      var localElement = node.declaredFragment as LocalVariableFragmentImpl;
-      fragment = localElement;
+      var localFragment = node.declaredFragment as LocalVariableFragmentImpl;
+      fragment = localFragment;
 
       var varList = node.parent as VariableDeclarationListImpl;
-      localElement.hasImplicitType = varList.type == null;
-      localElement.hasInitializer = initializerNode != null;
-      localElement.type = varList.type?.type ?? _dynamicType;
+      localFragment.hasInitializer = initializerNode != null;
+      if (varList.type case var typeNode?) {
+        var type = typeNode.typeOrThrow;
+        localFragment.type = type;
+        localFragment.element.type = type;
+      } else {
+        localFragment.hasImplicitType = true;
+        localFragment.element.type = _dynamicType;
+      }
     }
 
     if (initializerNode != null) {
diff --git a/pkg/analyzer/test/src/dart/resolution/try_statement_test.dart b/pkg/analyzer/test/src/dart/resolution/try_statement_test.dart
index 725dd22..f0df1ba 100644
--- a/pkg/analyzer/test/src/dart/resolution/try_statement_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/try_statement_test.dart
@@ -84,8 +84,8 @@
       stackTraceParameter: CatchClauseParameter
         name: y
         declaredFragment: isFinal isPublic y@30
-          type: StackTrace
-          element: isFinal isPublic
+          type: null
+          element: hasImplicitType isFinal isPublic
             type: StackTrace
       rightParenthesis: )
       body: Block
@@ -128,8 +128,8 @@
       stackTraceParameter: CatchClauseParameter
         name: st
         declaredFragment: isFinal isPublic st@31
-          type: StackTrace
-          element: isFinal isPublic
+          type: null
+          element: hasImplicitType isFinal isPublic
             type: StackTrace
       rightParenthesis: )
       body: Block
@@ -172,8 +172,8 @@
       stackTraceParameter: CatchClauseParameter
         name: st
         declaredFragment: isFinal isPublic st@31
-          type: StackTrace
-          element: isFinal isPublic
+          type: null
+          element: hasImplicitType isFinal isPublic
             type: StackTrace
       rightParenthesis: )
       body: Block
@@ -213,8 +213,8 @@
       stackTraceParameter: CatchClauseParameter
         name: st
         declaredFragment: isFinal isPublic st@30
-          type: StackTrace
-          element: isFinal isPublic
+          type: null
+          element: hasImplicitType isFinal isPublic
             type: StackTrace
       rightParenthesis: )
       body: Block
@@ -259,8 +259,8 @@
       stackTraceParameter: CatchClauseParameter
         name: st
         declaredFragment: isFinal isPublic st@37
-          type: StackTrace
-          element: isFinal isPublic
+          type: null
+          element: hasImplicitType isFinal isPublic
             type: StackTrace
       rightParenthesis: )
       body: Block
diff --git a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
index 2270e90..5a5a214 100644
--- a/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
+++ b/pkg/analyzer/test/src/summary/resolved_ast_printer.dart
@@ -1786,12 +1786,7 @@
     });
 
     _sink.withIndent(() {
-      if (fragment.hasImplicitType) {
-        // TODO(scheglov): eventually we can just write type a below.
-        _sink.writelnWithIndent('type: null');
-      } else {
-        _writeType('type', fragment.type);
-      }
+      _writeType('type', fragment.type2);
 
       var element = fragment.element;
       _sink.writeIndentedLine(() {
diff --git a/pkg/dart2wasm/lib/dry_run.dart b/pkg/dart2wasm/lib/dry_run.dart
index efeaef5..3bdb316 100644
--- a/pkg/dart2wasm/lib/dry_run.dart
+++ b/pkg/dart2wasm/lib/dry_run.dart
@@ -161,7 +161,7 @@
       : _typeEnvironment = TypeEnvironment(coreTypes, hierarchy),
         _jsAnyType = ExtensionType(
             coreTypes.index.getExtensionType('dart:js_interop', 'JSAny'),
-            Nullability.nullable);
+            Nullability.nonNullable);
 
   @override
   void visitLibrary(Library node) {
@@ -181,7 +181,9 @@
   @override
   void visitIsExpression(IsExpression node) {
     final operandStaticType = node.operand.getStaticType(_context);
-    if (_typeEnvironment.isSubtypeOf(operandStaticType, _jsAnyType)) {
+    if (_typeEnvironment.isSubtypeOf(
+        operandStaticType.withDeclaredNullability(Nullability.nonNullable),
+        _jsAnyType)) {
       errors.add(_DryRunError(
           _DryRunErrorCode.isTestValueError,
           'Should not perform an `is` test on a JS value. Use `isA` with a JS '
@@ -189,7 +191,9 @@
           errorSourceUri: _enclosingLibrary?.importUri,
           errorLocation: node.location));
     }
-    if (_typeEnvironment.isSubtypeOf(node.type, _jsAnyType)) {
+    if (_typeEnvironment.isSubtypeOf(
+        node.type.withDeclaredNullability(Nullability.nonNullable),
+        _jsAnyType)) {
       errors.add(_DryRunError(
           _DryRunErrorCode.isTestTypeError,
           'Should not perform an `is` test against a JS value type. '
@@ -211,13 +215,15 @@
     // Check InterfaceType and ExtensionType
     if (type is TypeDeclarationType) {
       final arguments = type.typeArguments;
-      if (arguments.any((e) => _typeEnvironment.isSubtypeOf(e, _jsAnyType))) {
+      if (arguments.any((e) => _typeEnvironment.isSubtypeOf(
+          e.withDeclaredNullability(Nullability.nonNullable), _jsAnyType))) {
         return true;
       }
       return arguments.any(_hasJsTypeArguments);
     } else if (type is RecordType) {
       final fields = type.positional.followedBy(type.named.map((t) => t.type));
-      if (fields.any((e) => _typeEnvironment.isSubtypeOf(e, _jsAnyType))) {
+      if (fields.any((e) => _typeEnvironment.isSubtypeOf(
+          e.withDeclaredNullability(Nullability.nonNullable), _jsAnyType))) {
         return true;
       }
       return fields.any(_hasJsTypeArguments);
diff --git a/pkg/dart2wasm/test/dry_run/dry_run_test.dart b/pkg/dart2wasm/test/dry_run/dry_run_test.dart
index 50b320c..5879991 100644
--- a/pkg/dart2wasm/test/dry_run/dry_run_test.dart
+++ b/pkg/dart2wasm/test/dry_run/dry_run_test.dart
@@ -38,14 +38,15 @@
 }
 
 class TestFinding {
+  final String rawString;
   final int errorCode;
   final String problemMessage;
   final Uri? errorSourceUri;
   final int? errorLine;
   final int? errorColumn;
 
-  TestFinding(this.errorCode, this.problemMessage, this.errorSourceUri,
-      this.errorLine, this.errorColumn);
+  TestFinding(this.rawString, this.errorCode, this.problemMessage,
+      this.errorSourceUri, this.errorLine, this.errorColumn);
 
   factory TestFinding.fromLine(String line) {
     final parts = line.split(' - ');
@@ -60,6 +61,7 @@
     final errorCode =
         int.parse(problemMessage.substring(errorCodeStart + 1, errorCodeEnd));
     return TestFinding(
+        line,
         errorCode,
         problemMessage.substring(0, errorCodeStart).trim(),
         Uri.parse(uri),
@@ -140,7 +142,11 @@
   timer.stop();
   try {
     final exitCode = result.exitCode;
-    Expect.equals(testCase.expectations.isEmpty ? 0 : 254, exitCode);
+    if (testCase.expectations.isEmpty) {
+      Expect.equals(0, exitCode, 'Unexpected findings:\n${result.stdout}');
+    } else {
+      Expect.equals(254, exitCode, 'Expected findings but found none.');
+    }
     final findings = _parseTestFindings(result.stdout);
     _checkFindings(findings, testCase.expectations);
   } catch (e, s) {
@@ -236,7 +242,8 @@
       findings.length,
       expectations.length,
       'Incorrect number of findings. '
-      'Expected: ${expectations.length}, Actual: ${findings.length}');
+      'Expected: ${expectations.length}, Actual: ${findings.length}\n'
+      'Findings:\n${findings.map((e) => e.rawString).join('\n')}}');
   for (final expectation in expectations) {
     final lineNumber = expectation.lineNumber;
     final lineFindings =
diff --git a/pkg/dart2wasm/test/dry_run/testcases/valid_interop_import.dart b/pkg/dart2wasm/test/dry_run/testcases/valid_interop_import.dart
index e836e48..83e91de 100644
--- a/pkg/dart2wasm/test/dry_run/testcases/valid_interop_import.dart
+++ b/pkg/dart2wasm/test/dry_run/testcases/valid_interop_import.dart
@@ -4,6 +4,14 @@
 
 import 'dart:js_interop';
 
+class A<B> {
+  void foo() {
+    if (null is! B) {
+      print('');
+    }
+  }
+}
+
 void main() {
   JSAny? jsValue;
   if (jsValue.isA<JSString>()) {
@@ -11,4 +19,9 @@
   } else if (jsValue.isA<JSArray>()) {
     print(jsValue);
   }
+
+  Object? dartObject;
+  if (dartObject case {'foo': final String foo}) {
+    print(foo);
+  }
 }
diff --git a/tools/VERSION b/tools/VERSION
index 9dc5f38..4328012 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 3
 MINOR 9
 PATCH 0
-PRERELEASE 312
+PRERELEASE 313
 PRERELEASE_PATCH 0