• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dart-lang / linter / 6444183028

01 Sep 2023 04:26PM UTC coverage: 96.562% (+0.01%) from 96.551%
6444183028

push

github

web-flow
update labels (#4740)

9127 of 9452 relevant lines covered (96.56%)

1.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.03
/lib/src/util/unrelated_types_visitor.dart
1
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2
// for details. All rights reserved. Use of this source code is governed by a
3
// BSD-style license that can be found in the LICENSE file.
4

5
import 'package:analyzer/dart/ast/ast.dart';
6
import 'package:analyzer/dart/ast/visitor.dart';
7
import 'package:analyzer/dart/element/element.dart';
8
import 'package:analyzer/dart/element/type.dart';
9
import 'package:analyzer/dart/element/type_provider.dart';
10

11
import '../analyzer.dart';
12
import '../util/dart_type_utilities.dart';
13

14
/// Base class for visitor used in rules where we want to lint about invoking
15
/// methods on generic classes where the type of the singular argument is
16
/// unrelated to the singular type argument of the class. Extending this
17
/// visitor is as simple as knowing the methods, classes and libraries that
18
/// uniquely define the target, i.e. implement only [methods].
19
abstract class UnrelatedTypesProcessors extends SimpleAstVisitor<void> {
20
  final LintRule rule;
21
  final TypeSystem typeSystem;
22
  final TypeProvider typeProvider;
23

24
  UnrelatedTypesProcessors(this.rule, this.typeSystem, this.typeProvider);
1✔
25

26
  /// The method definitions which this [UnrelatedTypesProcessors] is concerned
27
  /// with.
28
  List<MethodDefinition> get methods;
29

30
  List<MethodDefinition> get indexOperators => [];
×
31

32
  @override
1✔
33
  void visitIndexExpression(IndexExpression node) {
34
    var matchingMethods =
35
        indexOperators.where((method) => '[]' == method.methodName);
5✔
36
    if (matchingMethods.isEmpty) {
1✔
37
      return;
38
    }
39

40
    var targetType = _getTargetType(node, node.realTarget);
2✔
41
    if (targetType is! InterfaceType) {
1✔
42
      return;
43
    }
44

45
    for (var methodDefinition in matchingMethods) {
2✔
46
      var collectionType = methodDefinition.collectionTypeFor(targetType);
1✔
47
      if (collectionType != null) {
48
        _checkMethod(node.index, methodDefinition, collectionType);
2✔
49
        return;
50
      }
51
    }
52
  }
53

54
  @override
1✔
55
  void visitMethodInvocation(MethodInvocation node) {
56
    if (node.argumentList.arguments.length != 1) {
4✔
57
      return;
58
    }
59

60
    var matchingMethods =
61
        methods.where((method) => node.methodName.name == method.methodName);
7✔
62
    if (matchingMethods.isEmpty) {
1✔
63
      return;
64
    }
65

66
    // At this point, we know that [node] is an invocation of a method which
67
    // has the same name as the method that this [UnrelatedTypesProcessors] is
68
    // concerned with, and that the method call has a single argument.
69
    //
70
    // We've completed the "cheap" checks, and must now continue with the
71
    // arduous task of determining whether the method target implements
72
    // [definition].
73
    var targetType = _getTargetType(node, node.realTarget);
2✔
74
    if (targetType is! InterfaceType) {
1✔
75
      return;
76
    }
77

78
    for (var methodDefinition in matchingMethods) {
2✔
79
      var collectionType = methodDefinition.collectionTypeFor(targetType);
1✔
80
      if (collectionType != null) {
81
        _checkMethod(node.argumentList.arguments.first, methodDefinition,
4✔
82
            collectionType);
83
        return;
84
      }
85
    }
86
  }
87

88
  DartType? _getTargetType(Expression node, Expression? target) {
1✔
89
    if (target != null) {
90
      return target.staticType;
1✔
91
    }
92

93
    // Look for an implicit receiver, starting with [node]'s parent's parent.
94
    for (AstNode? parent = node.parent?.parent;
2✔
95
        parent != null;
96
        parent = parent.parent) {
1✔
97
      if (parent is ClassDeclaration) {
1✔
98
        return parent.declaredElement?.thisType;
2✔
99
      } else if (parent is MixinDeclaration) {
1✔
100
        return parent.declaredElement?.thisType;
×
101
      } else if (parent is EnumDeclaration) {
1✔
102
        return parent.declaredElement?.thisType;
2✔
103
      } else if (parent is ExtensionDeclaration) {
1✔
104
        return parent.extendedType.type;
2✔
105
      }
106
    }
107
    return null;
108
  }
109

110
  /// Checks a [MethodInvocation] or [IndexExpression] which has a singular
111
  /// [argument] and matches [methodDefinition], with a target with a static
112
  /// type of [collectionType].
113
  void _checkMethod(Expression argument, MethodDefinition methodDefinition,
1✔
114
      InterfaceType collectionType) {
115
    // Finally, determine whether the type of the argument is related to the
116
    // type of the method target.
117
    var argumentType = argument.staticType;
1✔
118
    if (argumentType == null) return;
119

120
    switch (methodDefinition.expectedArgumentKind) {
1✔
121
      case ExpectedArgumentKind.assignableToCollectionTypeArgument:
1✔
122
        var typeArgument =
123
            collectionType.typeArguments[methodDefinition.typeArgumentIndex];
3✔
124
        if (typesAreUnrelated(typeSystem, argumentType, typeArgument)) {
2✔
125
          rule.reportLint(argument, arguments: [
3✔
126
            argumentType.getDisplayString(withNullability: true),
1✔
127
            typeArgument.getDisplayString(withNullability: true),
1✔
128
          ]);
129
        }
130

131
      case ExpectedArgumentKind.assignableToCollection:
×
132
        if (!typeSystem.isAssignableTo(argumentType, collectionType)) {
×
133
          rule.reportLint(argument, arguments: [
×
134
            argumentType.getDisplayString(withNullability: true),
×
135
            collectionType.getDisplayString(withNullability: true),
×
136
          ]);
137
        }
138

139
      case ExpectedArgumentKind.assignableToIterableOfTypeArgument:
×
140
        var iterableType =
141
            collectionType.asInstanceOf(typeProvider.iterableElement);
×
142
        if (iterableType != null &&
143
            !typeSystem.isAssignableTo(argumentType, iterableType)) {
×
144
          rule.reportLint(argument, arguments: [
×
145
            argumentType.getDisplayString(withNullability: true),
×
146
            iterableType.getDisplayString(withNullability: true),
×
147
          ]);
148
        }
149
    }
150
  }
151
}
152

153
/// A definition of a method and the expected characteristics of the first
154
/// argument to any invocation.
155
abstract class MethodDefinition {
156
  final String methodName;
157

158
  /// The index of the type argument which the method argument should match.
159
  final int typeArgumentIndex;
160

161
  final ExpectedArgumentKind expectedArgumentKind;
162

163
  MethodDefinition(
1✔
164
    this.methodName,
165
    this.expectedArgumentKind, {
166
    this.typeArgumentIndex = 0,
167
  });
168

169
  InterfaceType? collectionTypeFor(InterfaceType targetType);
170
}
171

172
class MethodDefinitionForElement extends MethodDefinition {
173
  /// The element on which this method is declared.
174
  final ClassElement element;
175

176
  MethodDefinitionForElement(
1✔
177
    this.element,
178
    super.methodName,
179
    super.expectedArgumentKind, {
180
    super.typeArgumentIndex = 0,
181
  });
182

183
  @override
1✔
184
  InterfaceType? collectionTypeFor(InterfaceType targetType) =>
185
      targetType.asInstanceOf(element);
2✔
186
}
187

188
class MethodDefinitionForName extends MethodDefinition {
189
  final String libraryName;
190

191
  final String interfaceName;
192

193
  MethodDefinitionForName(
1✔
194
    this.libraryName,
195
    this.interfaceName,
196
    super.methodName,
197
    super.expectedArgumentKind, {
198
    super.typeArgumentIndex = 0,
199
  });
200

201
  @override
1✔
202
  InterfaceType? collectionTypeFor(InterfaceType targetType) {
203
    for (var supertype in [targetType, ...targetType.allSupertypes]) {
3✔
204
      var element = supertype.element;
1✔
205
      if (element.name == interfaceName &&
3✔
206
          element.library.name == libraryName) {
4✔
207
        return targetType.asInstanceOf(element);
1✔
208
      }
209
    }
210
    return null;
211
  }
212
}
213

214
/// The kind of the expected argument.
215
enum ExpectedArgumentKind {
216
  /// An argument is expected to be assignable to a type argument on the
217
  /// collection type.
218
  assignableToCollectionTypeArgument,
219

220
  /// An argument is expected to be assignable to the collection type.
221
  assignableToCollection,
222

223
  /// An argument is expected to be assignable to `Iterable<E>` where `E` is the
224
  /// (only) type argument on the collection type.
225
  assignableToIterableOfTypeArgument,
226
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc