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

dart-lang / linter / 6444183028

01 Sep 2023 04:26PM CUT 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

81.9
/lib/src/util/dart_type_utilities.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/element/element.dart';
7
import 'package:analyzer/dart/element/type.dart';
8
import 'package:analyzer/src/dart/element/type.dart'; // ignore: implementation_imports
9

10
import '../analyzer.dart';
11
import '../ast.dart';
12
import '../extensions.dart';
13

14
bool argumentsMatchParameters(
1✔
15
    NodeList<Expression> arguments, NodeList<FormalParameter> parameters) {
16
  var namedParameters = <String, Element?>{};
1✔
17
  var namedArguments = <String, Element>{};
1✔
18
  var positionalParameters = <Element?>[];
1✔
19
  var positionalArguments = <Element>[];
1✔
20
  for (var parameter in parameters) {
2✔
21
    var identifier = parameter.name;
1✔
22
    if (identifier != null) {
23
      if (parameter.isNamed) {
1✔
24
        namedParameters[identifier.lexeme] = parameter.declaredElement;
3✔
25
      } else {
26
        positionalParameters.add(parameter.declaredElement);
2✔
27
      }
28
    }
29
  }
30
  for (var argument in arguments) {
2✔
31
    if (argument is NamedExpression) {
1✔
32
      var element = argument.expression.canonicalElement;
2✔
33
      if (element == null) {
34
        return false;
35
      }
36
      namedArguments[argument.name.label.name] = element;
4✔
37
    } else {
38
      var element = argument.canonicalElement;
1✔
39
      if (element == null) {
40
        return false;
41
      }
42
      positionalArguments.add(element);
1✔
43
    }
44
  }
45
  if (positionalParameters.length != positionalArguments.length ||
3✔
46
      namedParameters.keys.length != namedArguments.keys.length) {
5✔
47
    return false;
48
  }
49
  for (var i = 0; i < positionalArguments.length; i++) {
3✔
50
    if (positionalArguments[i] != positionalParameters[i]) {
3✔
51
      return false;
52
    }
53
  }
54

55
  for (var key in namedParameters.keys) {
2✔
56
    if (namedParameters[key] != namedArguments[key]) {
3✔
57
      return false;
58
    }
59
  }
60

61
  return true;
62
}
63

64
/// Returns whether the canonical elements of [element1] and [element2] are
65
/// equal.
66
bool canonicalElementsAreEqual(Element? element1, Element? element2) =>
1✔
67
    element1?.canonicalElement == element2?.canonicalElement;
3✔
68

69
/// Returns whether the canonical elements from two nodes are equal.
70
///
71
/// As in, [NullableAstNodeExtension.canonicalElement], the two nodes must be
72
/// [Expression]s in order to be compared (otherwise `false` is returned).
73
///
74
/// The two nodes must both be a [SimpleIdentifier], [PrefixedIdentifier], or
75
/// [PropertyAccess] (otherwise `false` is returned).
76
///
77
/// If the two nodes are PrefixedIdentifiers, or PropertyAccess nodes, then
78
/// `true` is returned only if their canonical elements are equal, in
79
/// addition to their prefixes' and targets' (respectfully) canonical
80
/// elements.
81
///
82
/// There is an inherent assumption about pure getters. For example:
83
///
84
///     A a1 = ...
85
///     A a2 = ...
86
///     a1.b.c; // statement 1
87
///     a2.b.c; // statement 2
88
///     a1.b.c; // statement 3
89
///
90
/// The canonical elements from statements 1 and 2 are different, because a1
91
/// is not the same element as a2.  The canonical elements from statements 1
92
/// and 3 are considered to be equal, even though `A.b` may have side effects
93
/// which alter the returned value.
94
bool canonicalElementsFromIdentifiersAreEqual(
1✔
95
    Expression? rawExpression1, Expression? rawExpression2) {
96
  if (rawExpression1 == null || rawExpression2 == null) return false;
97

98
  var expression1 = rawExpression1.unParenthesized;
1✔
99
  var expression2 = rawExpression2.unParenthesized;
1✔
100

101
  if (expression1 is SimpleIdentifier) {
1✔
102
    return expression2 is SimpleIdentifier &&
1✔
103
        canonicalElementsAreEqual(getWriteOrReadElement(expression1),
2✔
104
            getWriteOrReadElement(expression2));
1✔
105
  }
106

107
  if (expression1 is PrefixedIdentifier) {
1✔
108
    return expression2 is PrefixedIdentifier &&
1✔
109
        canonicalElementsAreEqual(expression1.prefix.staticElement,
3✔
110
            expression2.prefix.staticElement) &&
2✔
111
        canonicalElementsAreEqual(getWriteOrReadElement(expression1.identifier),
3✔
112
            getWriteOrReadElement(expression2.identifier));
2✔
113
  }
114

115
  if (expression1 is PropertyAccess && expression2 is PropertyAccess) {
2✔
116
    var target1 = expression1.target;
1✔
117
    var target2 = expression2.target;
1✔
118
    return canonicalElementsFromIdentifiersAreEqual(target1, target2) &&
1✔
119
        canonicalElementsAreEqual(
1✔
120
            getWriteOrReadElement(expression1.propertyName),
2✔
121
            getWriteOrReadElement(expression2.propertyName));
2✔
122
  }
123

124
  return false;
125
}
126

127
/// Returns whether [leftType] and [rightType] are _definitely_ unrelated.
128
///
129
/// For the purposes of this function, here are some "relation" rules:
130
/// * `dynamic` and `Null` are considered related to any other type.
131
/// * Two types which are equal modulo nullability are considered related,
132
///   e.g. `int` and `int`, `String` and `String?`, `List<String>` and
133
///   `List<String>`, `List<T>` and `List<T>`, and type variables `A` and `A`.
134
/// * Two types such that one is a subtype of the other, modulo nullability,
135
///   such as `List<dynamic>` and `Iterable<dynamic>`, and type variables `A`
136
///   and `B` where `A extends B`, are considered related.
137
/// * Two interface types:
138
///   * are related if they represent the same class, modulo type arguments,
139
///     modulo nullability, and each of their pair-wise type arguments are
140
///     related, e.g. `List<dynamic>` and `List<int>`, and `Future<T>` and
141
///     `Future<S>` where `S extends T`.
142
///   * are unrelated if [leftType]'s supertype is [Object].
143
///   * are related if their supertypes are equal, e.g. `List<dynamic>` and
144
///     `Set<dynamic>`.
145
/// * Two type variables are related if their bounds are related.
146
/// * A record type is unrelated to any other type except a record type of
147
///   the same shape.
148
/// * Otherwise, any two types are related.
149
// TODO(srawlins): typedefs and functions in general.
150
bool typesAreUnrelated(
1✔
151
    TypeSystem typeSystem, DartType? leftType, DartType? rightType) {
152
  // If we don't have enough information, or can't really compare the types,
153
  // return false as they _might_ be related.
154
  if (leftType == null ||
155
      leftType.isBottom ||
1✔
156
      leftType is DynamicType ||
1✔
157
      rightType == null ||
158
      rightType.isBottom ||
1✔
159
      rightType is DynamicType) {
1✔
160
    return false;
161
  }
162
  var promotedLeftType = typeSystem.promoteToNonNull(leftType);
1✔
163
  var promotedRightType = typeSystem.promoteToNonNull(rightType);
1✔
164
  if (promotedLeftType == promotedRightType ||
1✔
165
      typeSystem.isSubtypeOf(promotedLeftType, promotedRightType) ||
1✔
166
      typeSystem.isSubtypeOf(promotedRightType, promotedLeftType)) {
1✔
167
    return false;
168
  }
169
  if (promotedLeftType is InterfaceType && promotedRightType is InterfaceType) {
2✔
170
    return typeSystem.interfaceTypesAreUnrelated(
1✔
171
        promotedLeftType, promotedRightType);
172
  } else if (promotedLeftType is TypeParameterType &&
1✔
173
      promotedRightType is TypeParameterType) {
1✔
174
    return typesAreUnrelated(typeSystem, promotedLeftType.element.bound,
3✔
175
        promotedRightType.element.bound);
2✔
176
  } else if (promotedLeftType is FunctionType) {
1✔
177
    if (_isFunctionTypeUnrelatedToType(promotedLeftType, promotedRightType)) {
1✔
178
      return true;
179
    }
180
  } else if (promotedRightType is FunctionType) {
1✔
181
    if (_isFunctionTypeUnrelatedToType(promotedRightType, promotedLeftType)) {
×
182
      return true;
183
    }
184
  } else if (promotedLeftType is RecordType ||
1✔
185
      promotedRightType is RecordType) {
1✔
186
    return !typeSystem.isAssignableTo(promotedLeftType, promotedRightType) &&
1✔
187
        !typeSystem.isAssignableTo(promotedRightType, promotedLeftType);
1✔
188
  }
189
  return false;
190
}
191

192
bool _isFunctionTypeUnrelatedToType(FunctionType type1, DartType type2) {
1✔
193
  if (type2 is FunctionType) {
1✔
194
    return false;
195
  }
196
  if (type2 is InterfaceType) {
1✔
197
    var element2 = type2.element;
1✔
198
    if (element2 is ClassElement &&
1✔
199
        element2.lookUpConcreteMethod('call', element2.library) != null) {
2✔
200
      return false;
201
    }
202
  }
203
  return true;
204
}
205

206
typedef AstNodePredicate = bool Function(AstNode node);
207

208
class DartTypeUtilities {
209
  @Deprecated('Replace with `type.extendsClass`')
×
210
  static bool extendsClass(
211
          DartType? type, String? className, String? library) =>
212
      type.extendsClass(className, library!);
×
213

214
  @Deprecated('Replace with `rawNode.canonicalElement`')
×
215
  static Element? getCanonicalElementFromIdentifier(AstNode? rawNode) =>
216
      rawNode.canonicalElement;
×
217

218
  @Deprecated('Replace with `type.implementsInterface`')
×
219
  static bool implementsInterface(
220
          DartType? type, String interface, String library) =>
221
      type.implementsInterface(interface, library);
×
222

223
  // todo(pq): remove and replace w/ an extension (pending internal migration)
224
  @Deprecated('Slated for removal')
×
225
  static bool isClass(DartType? type, String? className, String? library) =>
226
      type is InterfaceType &&
×
227
      type.element.name == className &&
×
228
      type.element.library.name == library;
×
229

230
  @Deprecated('Replace with `expression.isNullLiteral`')
×
231
  static bool isNullLiteral(Expression? expression) => expression.isNullLiteral;
×
232

233
  @Deprecated('Use `argumentsMatchParameters`')
×
234
  static bool matchesArgumentsWithParameters(NodeList<Expression> arguments,
235
          NodeList<FormalParameter> parameters) =>
236
      argumentsMatchParameters(arguments, parameters);
×
237

238
  @Deprecated('Replace with `node.traverseNodesInDFS`')
×
239
  static Iterable<AstNode> traverseNodesInDFS(AstNode node,
240
          {AstNodePredicate? excludeCriteria}) =>
241
      node.traverseNodesInDFS(excludeCriteria: excludeCriteria);
×
242
}
243

244
class InterfaceTypeDefinition {
245
  final String name;
246
  final String library;
247

248
  InterfaceTypeDefinition(this.name, this.library);
1✔
249

250
  @override
1✔
251
  int get hashCode => name.hashCode ^ library.hashCode;
5✔
252

253
  @override
×
254
  bool operator ==(Object other) {
255
    if (identical(this, other)) {
256
      return true;
257
    }
258
    return other is InterfaceTypeDefinition &&
×
259
        name == other.name &&
×
260
        library == other.library;
×
261
  }
262
}
263

264
extension on TypeSystem {
265
  bool interfaceTypesAreUnrelated(
1✔
266
      InterfaceType leftType, InterfaceType rightType) {
267
    var leftElement = leftType.element;
1✔
268
    var rightElement = rightType.element;
1✔
269
    if (leftElement == rightElement) {
1✔
270
      // In this case, [leftElement] and [rightElement] represent the same
271
      // class, modulo generics, e.g. `List<int>` and `List<dynamic>`. Now we
272
      // need to check type arguments.
273
      var leftTypeArguments = leftType.typeArguments;
1✔
274
      var rightTypeArguments = rightType.typeArguments;
1✔
275
      if (leftTypeArguments.length != rightTypeArguments.length) {
3✔
276
        // I cannot think of how we would enter this block, but it guards
277
        // against RangeError below.
278
        return false;
279
      }
280
      for (var i = 0; i < leftTypeArguments.length; i++) {
3✔
281
        // If any of the pair-wise type arguments are unrelated, then
282
        // [leftType] and [rightType] are unrelated.
283
        if (typesAreUnrelated(
1✔
284
            this, leftTypeArguments[i], rightTypeArguments[i])) {
2✔
285
          return true;
286
        }
287
      }
288
      // Otherwise, they might be related.
289
      return false;
290
    } else {
291
      var sameSupertypes = leftElement.supertype == rightElement.supertype;
3✔
292

293
      // Unrelated Enums have the same supertype, but they are not the same element, so
294
      // they are unrelated.
295
      if (sameSupertypes && leftElement is EnumElement) {
1✔
296
        return true;
297
      }
298

299
      return (leftElement.supertype?.isDartCoreObject ?? false) ||
2✔
300
          !sameSupertypes;
301
    }
302
  }
303
}
304

305
extension DartTypeExtensions on DartType {
306
  /// Returns the type which should be used when conducting "interface checks"
307
  /// on `this`.
308
  ///
309
  /// If `this` is a type variable, then the type-for-interface-check of its
310
  /// promoted bound or bound is returned. Otherwise, `this` is returned.
311
  // TODO(srawlins): Move to extensions.dart.
312
  DartType get typeForInterfaceCheck {
1✔
313
    var self = this;
314
    if (self is TypeParameterType) {
1✔
315
      if (self is TypeParameterTypeImpl) {
1✔
316
        var promotedType = self.promotedBound;
1✔
317
        if (promotedType != null) {
318
          return promotedType.typeForInterfaceCheck;
1✔
319
        }
320
      }
321
      return self.bound.typeForInterfaceCheck;
2✔
322
    } else {
323
      return self;
324
    }
325
  }
326
}
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