Revert "linter: Refactor prefer_collection_literals to use context type more"

This reverts commit cd8a3370e7d854ded526329178eb8c046917ddf7.

Reason for revert: lint starts barking at the wrong tree: b/298917960

Original change's description:
> linter: Refactor prefer_collection_literals to use context type more
>
> There is a basic premise in this rule which we cannot satisfy exactly:
> we need to disallow `LinkedHashSet()` unless the context type requires
> the developer to use `LinkedHashSet`. But the context type is long
> gone when the lint rule is run.
>
> This CL adds some logic to try to attempt figuring out the context
> type in the cases where users have filed bugs, but it will never be
> super accurate.
>
> Fixes https://github.com/dart-lang/linter/issues/4736
> Fixes https://github.com/dart-lang/linter/issues/3057
> Fixes https://github.com/dart-lang/linter/issues/1649
> Fixes https://github.com/dart-lang/linter/issues/2795
>
> Change-Id: I3e6c6de81084dca2825488c89830ab3e7ea63186
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/323680
> Reviewed-by: Phil Quitslund <pquitslund@google.com>
> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
> Commit-Queue: Samuel Rawlins <srawlins@google.com>

Change-Id: I980053dd51ffd4447721e0ad7436b07ca704b554
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/324021
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: Alexander Thomas <athom@google.com>
Commit-Queue: Ilya Yanok <yanok@google.com>
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_map_literal_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_map_literal_test.dart
index f29794a..3b1ea9f 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_map_literal_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_map_literal_test.dart
@@ -87,4 +87,26 @@
 var m = <String, int>{};
 ''');
   }
+
+  Future<void> test_typedef() async {
+    await resolveTestCode('''
+typedef M = Map<String, int>;
+var m = M();
+''');
+    await assertHasFix('''
+typedef M = Map<String, int>;
+var m = <String, int>{};
+''');
+  }
+
+  Future<void> test_typedef_declaredType() async {
+    await resolveTestCode('''
+typedef M = Map<String, int>;
+Map<String, int> m = M();
+''');
+    await assertHasFix('''
+typedef M = Map<String, int>;
+Map<String, int> m = {};
+''');
+  }
 }
diff --git a/pkg/linter/lib/src/rules/prefer_collection_literals.dart b/pkg/linter/lib/src/rules/prefer_collection_literals.dart
index e9ce1b1..8533ea3 100644
--- a/pkg/linter/lib/src/rules/prefer_collection_literals.dart
+++ b/pkg/linter/lib/src/rules/prefer_collection_literals.dart
@@ -4,11 +4,7 @@
 
 import 'package:analyzer/dart/ast/ast.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/dart/element/type_provider.dart';
-// ignore: implementation_imports
-import 'package:analyzer/src/dart/element/type.dart' show InvalidTypeImpl;
 
 import '../analyzer.dart';
 import '../extensions.dart';
@@ -36,8 +32,8 @@
 
 **EXCEPTIONS:**
 
-When a `LinkedHashSet` or `LinkedHashMap` is expected, a collection literal is
-not preferred (or allowed).
+There are cases with `LinkedHashSet` or `LinkedHashMap` where a literal constructor
+will trigger a type error so those will be excluded from the lint.
 
 ```dart
 void main() {
@@ -76,7 +72,7 @@
   @override
   void registerNodeProcessors(
       NodeLintRegistry registry, LinterContext context) {
-    var visitor = _Visitor(this, context.typeProvider);
+    var visitor = _Visitor(this);
     registry.addInstanceCreationExpression(this, visitor);
     registry.addMethodInvocation(this, visitor);
   }
@@ -84,25 +80,17 @@
 
 class _Visitor extends SimpleAstVisitor<void> {
   final LintRule rule;
-  final TypeProvider typeProvider;
-  _Visitor(this.rule, this.typeProvider);
+  _Visitor(this.rule);
 
   @override
   void visitInstanceCreationExpression(InstanceCreationExpression node) {
     var constructorName = node.constructorName.name?.name;
 
-    if (node.constructorName.type.element is TypeAliasElement) {
-      // Allow the use of typedef constructors.
-      return;
-    }
-
     // Maps.
-    if (node.isHashMap) {
-      var approximateContextType = _approximateContextType(node);
-      if (approximateContextType is InvalidType) return;
-      if (approximateContextType.isTypeHashMap) return;
-    }
-    if (node.isMap || node.isHashMap) {
+    if (_isMap(node) || _isHashMap(node)) {
+      if (_shouldSkipLinkedHashLint(node, _isTypeHashMap)) {
+        return;
+      }
       if (constructorName == null && node.argumentList.arguments.isEmpty) {
         rule.reportLint(node);
       }
@@ -110,15 +98,14 @@
     }
 
     // Sets.
-    if (node.isHashSet) {
-      var approximateContextType = _approximateContextType(node);
-      if (approximateContextType is InvalidType) return;
-      if (approximateContextType.isTypeHashSet) return;
-    }
-    if (node.isSet || node.isHashSet) {
+    if (_isSet(node) || _isHashSet(node)) {
+      if (_shouldSkipLinkedHashLint(node, _isTypeHashSet)) {
+        return;
+      }
+
       var args = node.argumentList.arguments;
       if (constructorName == null) {
-        // Allow `LinkedHashSet(equals: (a, b) => false, hashCode: (o) => 13)`.
+        // Skip: LinkedHashSet(equals: (a, b) => false, hashCode: (o) => 13)
         if (args.isEmpty) {
           rule.reportLint(node);
         }
@@ -135,7 +122,7 @@
 
   @override
   void visitMethodInvocation(MethodInvocation node) {
-    // Something like `['foo', 'bar', 'baz'].toSet()`.
+    // ['foo', 'bar', 'baz'].toSet();
     if (node.methodName.name != 'toSet') {
       return;
     }
@@ -144,130 +131,64 @@
     }
   }
 
-  /// A very, very rough approximation of the context type of [node].
-  ///
-  /// This approximation will never be accurate for some expressions.
-  DartType? _approximateContextType(Expression node) {
-    var ancestor = node.parent;
-    var ancestorChild = node;
-    while (ancestor != null) {
-      if (ancestor is ParenthesizedExpression) {
-        ancestorChild = ancestor;
-        ancestor = ancestor.parent;
-      } else if (ancestor is CascadeExpression &&
-          ancestorChild == ancestor.target) {
-        ancestorChild = ancestor;
-        ancestor = ancestor.parent;
-      } else {
-        break;
+  bool _isHashMap(Expression expression) =>
+      _isTypeHashMap(expression.staticType);
+
+  bool _isHashSet(Expression expression) =>
+      _isTypeHashSet(expression.staticType);
+
+  bool _isMap(Expression expression) =>
+      expression.staticType?.isDartCoreMap ?? false;
+
+  bool _isSet(Expression expression) =>
+      expression.staticType?.isDartCoreSet ?? false;
+
+  bool _isTypeHashMap(DartType? type) =>
+      type.isSameAs('LinkedHashMap', 'dart.collection');
+
+  bool _isTypeHashSet(DartType? type) =>
+      type.isSameAs('LinkedHashSet', 'dart.collection');
+
+  bool _shouldSkipLinkedHashLint(
+      InstanceCreationExpression node, bool Function(DartType node) typeCheck) {
+    if (_isHashMap(node) || _isHashSet(node)) {
+      // Skip: LinkedHashSet<int> s =  ...; or LinkedHashMap<int> s =  ...;
+      var parent = node.parent;
+      if (parent is VariableDeclaration) {
+        var parent2 = parent.parent;
+        if (parent2 is VariableDeclarationList) {
+          var assignmentType = parent2.type?.type;
+          if (assignmentType != null && typeCheck(assignmentType)) {
+            return true;
+          }
+        }
+      }
+      // Skip: function(LinkedHashSet()); when function(LinkedHashSet mySet) or
+      // function(LinkedHashMap()); when function(LinkedHashMap myMap)
+      if (parent is ArgumentList) {
+        var paramType = node.staticParameterElement?.type;
+        if (paramType == null || typeCheck(paramType)) {
+          return true;
+        }
+      }
+
+      // Skip: void f({required LinkedHashSet<Foo> s})
+      if (parent is NamedExpression) {
+        var paramType = parent.staticParameterElement?.type;
+        if (paramType != null && typeCheck(paramType)) {
+          return true;
+        }
+      }
+
+      // Skip: <int, LinkedHashSet>{}.putIfAbsent(3, () => LinkedHashSet());
+      // or <int, LinkedHashMap>{}.putIfAbsent(3, () => LinkedHashMap());
+      if (parent is ExpressionFunctionBody) {
+        var expressionType = parent.expression.staticType;
+        if (expressionType != null && typeCheck(expressionType)) {
+          return true;
+        }
       }
     }
-
-    switch (ancestor) {
-      // TODO(srawlins): Handle [AwaitExpression], [BinaryExpression],
-      // [CascadeExpression], [ConditionalExpression], [SwitchExpressionCase],
-      // likely others. Or move everything here to an analysis phase which
-      // has the actual context type.
-      case ArgumentList():
-        // Allow `function(LinkedHashSet())` for `function(LinkedHashSet mySet)`
-        // and `function(LinkedHashMap())` for `function(LinkedHashMap myMap)`.
-        return node.staticParameterElement?.type ?? InvalidTypeImpl.instance;
-      case AssignmentExpression():
-        // Allow `x = LinkedHashMap()`.
-        return ancestor.staticType;
-      case ExpressionFunctionBody(parent: var function)
-          when function is FunctionExpression:
-        // Allow `<int, LinkedHashSet>{}.putIfAbsent(3, () => LinkedHashSet())`
-        // and `<int, LinkedHashMap>{}.putIfAbsent(3, () => LinkedHashMap())`.
-        var functionType = _approximateContextType(function);
-        return functionType is FunctionType ? functionType.returnType : null;
-      case NamedExpression():
-        // Allow `void f({required LinkedHashSet<Foo> s})`.
-        return ancestor.staticParameterElement?.type ??
-            InvalidTypeImpl.instance;
-      case ReturnStatement():
-        return _expectedReturnType(
-          ancestor.thisOrAncestorOfType<FunctionBody>(),
-        );
-      case VariableDeclaration(parent: VariableDeclarationList(:var type)):
-        // Allow `LinkedHashSet<int> s = node` and
-        // `LinkedHashMap<int> s = node`.
-        return type?.type;
-      case YieldStatement():
-        return _expectedReturnType(
-          ancestor.thisOrAncestorOfType<FunctionBody>(),
-        );
-    }
-
-    return null;
+    return false;
   }
-
-  /// Extracts the expected type for return statements or yield statements.
-  ///
-  /// For example, for an asynchronous [body] in a function with a declared
-  /// [returnType] of `Future<int>`, this returns `int`. (Note: it would be more
-  /// accurate to use `FutureOr<int>` and an assignability check, but `int` is
-  /// an approximation that works for now; this should probably be revisited.)
-  DartType? _expectedReturnableOrYieldableType(
-    DartType? returnType,
-    FunctionBody body,
-  ) {
-    if (returnType == null || returnType is InvalidType) return null;
-    if (body.isAsynchronous) {
-      if (!body.isGenerator && returnType.isDartAsyncFuture) {
-        var typeArgs = (returnType as InterfaceType).typeArguments;
-        return typeArgs.isEmpty ? null : typeArgs.first;
-      }
-      if (body.isGenerator && returnType.isDartAsyncStream) {
-        var typeArgs = (returnType as InterfaceType).typeArguments;
-        return typeArgs.isEmpty ? null : typeArgs.first;
-      }
-    } else {
-      if (body.isGenerator && returnType.isDartCoreIterable) {
-        var typeArgs = (returnType as InterfaceType).typeArguments;
-        return typeArgs.isEmpty ? null : typeArgs.first;
-      }
-    }
-    return returnType;
-  }
-
-  /// Attempts to calculate the expected return type of the function represented
-  /// by [body], accounting for an approximation of the function's context type,
-  /// in the case of a function literal.
-  DartType? _expectedReturnType(FunctionBody? body) {
-    if (body == null) return null;
-    var parent = body.parent;
-    if (parent is FunctionExpression) {
-      var grandparent = parent.parent;
-      if (grandparent is FunctionDeclaration) {
-        var returnType = grandparent.declaredElement?.returnType;
-        return _expectedReturnableOrYieldableType(returnType, body);
-      }
-      var functionType = _approximateContextType(parent);
-      if (functionType is! FunctionType) return null;
-      var returnType = functionType.returnType;
-      return _expectedReturnableOrYieldableType(returnType, body);
-    }
-    if (parent is MethodDeclaration) {
-      var returnType = parent.declaredElement?.returnType;
-      return _expectedReturnableOrYieldableType(returnType, body);
-    }
-    return null;
-  }
-}
-
-extension on Expression {
-  bool get isHashMap => staticType.isTypeHashMap;
-
-  bool get isHashSet => staticType.isTypeHashSet;
-
-  bool get isMap => staticType?.isDartCoreMap ?? false;
-
-  bool get isSet => staticType?.isDartCoreSet ?? false;
-}
-
-extension on DartType? {
-  bool get isTypeHashMap => isSameAs('LinkedHashMap', 'dart.collection');
-
-  bool get isTypeHashSet => isSameAs('LinkedHashSet', 'dart.collection');
 }
diff --git a/pkg/linter/test/rules/prefer_collection_literals_test.dart b/pkg/linter/test/rules/prefer_collection_literals_test.dart
index 40da7be..80fb78d 100644
--- a/pkg/linter/test/rules/prefer_collection_literals_test.dart
+++ b/pkg/linter/test/rules/prefer_collection_literals_test.dart
@@ -17,36 +17,7 @@
   @override
   String get lintRule => 'prefer_collection_literals';
 
-  test_assignment() async {
-    await assertNoDiagnostics(r'''
-import 'dart:collection';
-
-void f(LinkedHashSet<int> s) {
-  s = LinkedHashSet();
-}
-''');
-  }
-
-  test_assignment_withCascade() async {
-    await assertNoDiagnostics(r'''
-import 'dart:collection';
-
-void f(LinkedHashSet<int> s) {
-  s = LinkedHashSet()..addAll([1, 2, 3]);
-}
-''');
-  }
-
-  test_assignment_withParentheses() async {
-    await assertNoDiagnostics(r'''
-import 'dart:collection';
-
-void f(LinkedHashSet<int> s) {
-  s = ((LinkedHashSet()));
-}
-''');
-  }
-
+  @failingTest
   test_closure_returns_linkedHashSet() async {
     await assertDiagnostics(r'''
 import 'dart:collection';
@@ -122,80 +93,6 @@
     ]);
   }
 
-  test_returnStatement_async() async {
-    await assertNoDiagnostics(r'''
-import 'dart:collection';
-
-Future<LinkedHashSet<int>> f() async {
-  return LinkedHashSet();
-}
-''');
-  }
-
-  test_returnStatement_asyncStar() async {
-    await assertNoDiagnostics(r'''
-import 'dart:collection';
-
-Stream<LinkedHashSet<int>> f() async* {
-  yield LinkedHashSet();
-}
-''');
-  }
-
-  test_returnStatement_functionDeclaration() async {
-    await assertNoDiagnostics(r'''
-import 'dart:collection';
-
-LinkedHashSet<int> f() {
-  return LinkedHashSet();
-}
-''');
-  }
-
-  test_returnStatement_functionExpression() async {
-    await assertNoDiagnostics(r'''
-import 'dart:collection';
-
-void f() {
-  g(() {
-    return LinkedHashSet();
-  });
-}
-
-void g(LinkedHashSet<int> Function()) {}
-''');
-  }
-
-  test_returnStatement_methodDeclaration() async {
-    await assertNoDiagnostics(r'''
-import 'dart:collection';
-
-class C {
-  LinkedHashSet<int> f() {
-    return LinkedHashSet();
-  }
-}
-''');
-  }
-
-  test_returnStatement_syncStar() async {
-    await assertNoDiagnostics(r'''
-import 'dart:collection';
-
-Iterable<LinkedHashSet<int>> f() sync* {
-  yield LinkedHashSet();
-}
-''');
-  }
-
-  test_typedefConstruction() async {
-    await assertNoDiagnostics(r'''
-typedef MyMap = Map<int, int>;
-
-var x = MyMap();
-''');
-  }
-
   test_undefinedFunction() async {
     await assertDiagnostics(r'''
 import 'dart:collection';
diff --git a/pkg/linter/test_data/rules/prefer_collection_literals.dart b/pkg/linter/test_data/rules/prefer_collection_literals.dart
index ec01589f..ba92825 100644
--- a/pkg/linter/test_data/rules/prefer_collection_literals.dart
+++ b/pkg/linter/test_data/rules/prefer_collection_literals.dart
@@ -58,16 +58,14 @@
   printIndentedHashSet(0, LinkedHashSet<int>()); // OK
 
   Set<int> ss8 = LinkedHashSet.from([1, 2, 3]); // LINT
-  LinkedHashSet<int> ss9 = LinkedHashSet.from([1, 2, 3]); // OK
+  LinkedHashSet<int> ss9 =  LinkedHashSet.from([1, 2, 3]); // OK
 
   Iterable iter = Iterable.empty(); // OK
   var sss = Set.from(iter); // OK
 
-  LinkedHashSet<String> sss1 = <int, LinkedHashSet<String>>{}
-      .putIfAbsent(3, () => LinkedHashSet<String>()); // OK
+  LinkedHashSet<String> sss1 = <int, LinkedHashSet<String>>{}.putIfAbsent(3, () => LinkedHashSet<String>()); // OK
 
-  var lhs = LinkedHashSet(equals: (a, b) => false, hashCode: (o) => 13)
-    ..addAll({}); // OK
+  var lhs = LinkedHashSet(equals: (a, b) => false, hashCode: (o) => 13)..addAll({}); // OK
 
   LinkedHashMap hashMap = LinkedHashMap(); // OK
   Object hashMap2 = LinkedHashMap(); // LINT
@@ -78,8 +76,7 @@
   printMap(LinkedHashMap<int, int>()); // LINT
   printHashMap(LinkedHashMap<int, int>()); // OK
 
-  LinkedHashMap<String, String> lhm = <int, LinkedHashMap<String, String>>{}
-      .putIfAbsent(3, () => LinkedHashMap<String, String>()); // OK
+  LinkedHashMap<String, String> lhm = <int, LinkedHashMap<String,String>>{}.putIfAbsent(3, () => LinkedHashMap<String, String>()); // OK
 }
 
 void printObject(Object o) => print('$o');
diff --git a/pkg/linter/tool/machine/rules.json b/pkg/linter/tool/machine/rules.json
index 4933826..8581e7f 100644
--- a/pkg/linter/tool/machine/rules.json
+++ b/pkg/linter/tool/machine/rules.json
@@ -1661,7 +1661,7 @@
       "flutter"
     ],
     "fixStatus": "hasFix",
-    "details": "**DO** use collection literals when possible.\n\n**BAD:**\n```dart\nvar addresses = Map<String, String>();\nvar uniqueNames = Set<String>();\nvar ids = LinkedHashSet<int>();\nvar coordinates = LinkedHashMap<int, int>();\n```\n\n**GOOD:**\n```dart\nvar addresses = <String, String>{};\nvar uniqueNames = <String>{};\nvar ids = <int>{};\nvar coordinates = <int, int>{};\n```\n\n**EXCEPTIONS:**\n\nWhen a `LinkedHashSet` or `LinkedHashMap` is expected, a collection literal is\nnot preferred (or allowed).\n\n```dart\nvoid main() {\n  LinkedHashSet<int> linkedHashSet =  LinkedHashSet.from([1, 2, 3]); // OK\n  LinkedHashMap linkedHashMap = LinkedHashMap(); // OK\n  \n  printSet(LinkedHashSet<int>()); // LINT\n  printHashSet(LinkedHashSet<int>()); // OK\n\n  printMap(LinkedHashMap<int, int>()); // LINT\n  printHashMap(LinkedHashMap<int, int>()); // OK\n}\n\nvoid printSet(Set<int> ids) => print('$ids!');\nvoid printHashSet(LinkedHashSet<int> ids) => printSet(ids);\nvoid printMap(Map map) => print('$map!');\nvoid printHashMap(LinkedHashMap map) => printMap(map);\n```\n",
+    "details": "**DO** use collection literals when possible.\n\n**BAD:**\n```dart\nvar addresses = Map<String, String>();\nvar uniqueNames = Set<String>();\nvar ids = LinkedHashSet<int>();\nvar coordinates = LinkedHashMap<int, int>();\n```\n\n**GOOD:**\n```dart\nvar addresses = <String, String>{};\nvar uniqueNames = <String>{};\nvar ids = <int>{};\nvar coordinates = <int, int>{};\n```\n\n**EXCEPTIONS:**\n\nThere are cases with `LinkedHashSet` or `LinkedHashMap` where a literal constructor\nwill trigger a type error so those will be excluded from the lint.\n\n```dart\nvoid main() {\n  LinkedHashSet<int> linkedHashSet =  LinkedHashSet.from([1, 2, 3]); // OK\n  LinkedHashMap linkedHashMap = LinkedHashMap(); // OK\n  \n  printSet(LinkedHashSet<int>()); // LINT\n  printHashSet(LinkedHashSet<int>()); // OK\n\n  printMap(LinkedHashMap<int, int>()); // LINT\n  printHashMap(LinkedHashMap<int, int>()); // OK\n}\n\nvoid printSet(Set<int> ids) => print('$ids!');\nvoid printHashSet(LinkedHashSet<int> ids) => printSet(ids);\nvoid printMap(Map map) => print('$map!');\nvoid printHashMap(LinkedHashMap map) => printMap(map);\n```\n",
     "sinceDartSdk": "2.0.0"
   },
   {