Some fixes for UI-as-code type inference

Change-Id: I72711aad365e95f2f1b20d7f23d7765a9581ff4f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96022
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 7891289..dbca328 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -3670,6 +3670,8 @@
    */
   final InheritanceManager2 inheritance;
 
+  final AnalysisOptionsImpl _analysisOptions;
+
   /// The object used to resolve the element associated with the current node.
   ElementResolver elementResolver;
 
@@ -3736,13 +3738,14 @@
       {Scope nameScope,
       bool propagateTypes: true,
       reportConstEvaluationErrors: true})
-      : super(definingLibrary, source, typeProvider, errorListener,
+      : _analysisOptions = definingLibrary.context.analysisOptions,
+        super(definingLibrary, source, typeProvider, errorListener,
             nameScope: nameScope) {
-    AnalysisOptions options = definingLibrary.context.analysisOptions;
     this.elementResolver = new ElementResolver(this,
         reportConstEvaluationErrors: reportConstEvaluationErrors);
     this.typeSystem = definingLibrary.context.typeSystem;
     bool strongModeHints = false;
+    AnalysisOptions options = _analysisOptions;
     if (options is AnalysisOptionsImpl) {
       strongModeHints = options.strongModeHints;
     }
@@ -4903,6 +4906,23 @@
             typeProvider.iterableType.instantiate([elementType]);
         _pushCollectionTypesDownToAll(node.elements2,
             elementType: elementType, iterableType: iterableType);
+        if (!_analysisOptions.experimentStatus.spread_collections &&
+            !_analysisOptions.experimentStatus.control_flow_collections &&
+            node.elements2.isEmpty &&
+            node.typeArguments == null &&
+            node.isMap) {
+          // The node is really an empty set literal with no type arguments.
+          // Rewrite the AST.
+          // ignore: deprecated_member_use_from_same_package
+          SetOrMapLiteral setLiteral = new AstFactoryImpl().setLiteral(
+              node.constKeyword,
+              null,
+              node.leftBracket,
+              null,
+              node.rightBracket);
+          NodeReplacer.replace(node, setLiteral);
+          node = setLiteral;
+        }
       } else if (typeArguments.length == 2) {
         DartType keyType = typeArguments[0];
         DartType valueType = typeArguments[1];
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index 66fe369..9b7342a 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -1051,25 +1051,38 @@
 
   @override
   void visitSetOrMapLiteral(SetOrMapLiteral node) {
-    DartType staticType = node.staticType;
-    if (staticType == null) {
-      DartType literalType = _inferSetOrMapLiteralType(node);
-      if (literalType.element == _typeProvider.mapType.element) {
-        (node as SetOrMapLiteralImpl).becomeMap();
-      } else {
-        assert(literalType.element == _typeProvider.setType.element);
-        (node as SetOrMapLiteralImpl).becomeSet();
-      }
-      _resolver.inferenceContext.recordInference(node, literalType);
-      _recordStaticType(node, literalType);
-    } else if (staticType is InterfaceType) {
-      List<DartType> typeArguments = staticType.typeArguments;
+    var typeArguments = node.typeArguments?.arguments;
+
+    // If we have type arguments, use them.
+    // TODO(paulberry): this logic seems redundant with
+    //  ResolverVisitor._fromTypeArguments
+    if (typeArguments != null) {
       if (typeArguments.length == 1) {
         (node as SetOrMapLiteralImpl).becomeSet();
+        var elementType = _getType(typeArguments[0]) ?? _dynamicType;
+        _recordStaticType(
+            node, _typeProvider.setType.instantiate(<DartType>[elementType]));
+        return;
       } else if (typeArguments.length == 2) {
         (node as SetOrMapLiteralImpl).becomeMap();
+        var keyType = _getType(typeArguments[0]) ?? _dynamicType;
+        var valueType = _getType(typeArguments[1]) ?? _dynamicType;
+        _recordStaticType(node,
+            _typeProvider.mapType.instantiate(<DartType>[keyType, valueType]));
+        return;
       }
+      // If we get here, then a nonsense number of type arguments were provided,
+      // so treat it as though no type arguments were provided.
     }
+    DartType literalType = _inferSetOrMapLiteralType(node);
+    if (literalType.element == _typeProvider.mapType.element) {
+      (node as SetOrMapLiteralImpl).becomeMap();
+    } else {
+      assert(literalType.element == _typeProvider.setType.element);
+      (node as SetOrMapLiteralImpl).becomeSet();
+    }
+    _resolver.inferenceContext.recordInference(node, literalType);
+    _recordStaticType(node, literalType);
   }
 
   /**
@@ -1954,9 +1967,6 @@
   DartType _inferSetOrMapLiteralType(SetOrMapLiteral literal) {
     DartType contextType = InferenceContext.getContext(literal);
     NodeList<CollectionElement> elements = literal.elements2;
-    if (elements.length < 2 && contextType != null) {
-      return contextType;
-    }
     List<_InferredCollectionElementTypeInformation> inferredTypes = [];
     bool canBeAMap = true;
     bool mustBeAMap = false;
@@ -1976,11 +1986,20 @@
     } else if (canBeAMap && mustBeAMap) {
       return _toMapType(literal, contextType, inferredTypes);
     }
-    if (contextType == null) {
-      DartType dynamicType = _typeProvider.dynamicType;
-      return _typeProvider.mapType.instantiate([dynamicType, dynamicType]);
+    // TODO(paulberry): the following computations should be based on the
+    // greatest closure of the context type.
+    bool contextIsIterable = contextType != null &&
+        _typeSystem.isSubtypeOf(contextType, _typeProvider.iterableObjectType);
+    bool contextIsMap = contextType != null &&
+        _typeSystem.isSubtypeOf(contextType, _typeProvider.mapObjectObjectType);
+    if (contextIsIterable && !contextIsMap) {
+      return _toSetType(literal, contextType, inferredTypes);
+    } else {
+      if (elements.isNotEmpty && (!contextIsMap || contextIsIterable)) {
+        // Ambiguous.  TODO(paulberry): report an error.
+      }
+      return _toMapType(literal, contextType, inferredTypes);
     }
-    return contextType;
   }
 
   /**
diff --git a/pkg/analyzer/test/src/summary/top_level_inference_test.dart b/pkg/analyzer/test/src/summary/top_level_inference_test.dart
index b2c8847..b4d8961 100644
--- a/pkg/analyzer/test/src/summary/top_level_inference_test.dart
+++ b/pkg/analyzer/test/src/summary/top_level_inference_test.dart
@@ -374,12 +374,6 @@
   @override
   List<String> get enabledExperiments =>
       [EnableString.spread_collections, EnableString.control_flow_collections];
-
-  @failingTest
-  @override
-  test_initializer_untypedMap() async {
-    await super.test_initializer_untypedMap();
-  }
 }
 
 @reflectiveTest