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

dart-lang / linter / 3690449303

pending completion
3690449303

push

github

GitHub
Fix `unnecessary_parenthesis` with postfix bang operator. (#3904)

2 of 2 new or added lines in 1 file covered. (100.0%)

8263 of 8640 relevant lines covered (95.64%)

1.47 hits per line

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

79.82
/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
typedef AstNodePredicate = bool Function(AstNode node);
15

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

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

63
  return true;
64
}
65

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

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

100
  var expression1 = rawExpression1.unParenthesized;
1✔
101
  var expression2 = rawExpression2.unParenthesized;
1✔
102

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

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

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

126
  return false;
127
}
128

129
/// Returns whether [leftType] and [rightType] are _definitely_ unrelated.
130
///
131
/// For the purposes of this function, here are some "relation" rules:
132
/// * `dynamic` and `Null` are considered related to any other type.
133
/// * Two types which are equal modulo nullability are considered related,
134
///   e.g. `int` and `int`, `String` and `String?`, `List<String>` and
135
///   `List<String>`, `List<T>` and `List<T>`, and type variables `A` and `A`.
136
/// * Two types such that one is a subtype of the other, modulo nullability,
137
///   such as `List<dynamic>` and `Iterable<dynamic>`, and type variables `A`
138
///   and `B` where `A extends B`, are considered related.
139
/// * Two interface types:
140
///   * are related if they represent the same class, modulo type arguments,
141
///     modulo nullability, and each of their pair-wise type arguments are
142
///     related, e.g. `List<dynamic>` and `List<int>`, and `Future<T>` and
143
///     `Future<S>` where `S extends T`.
144
///   * are unrelated if [leftType]'s supertype is [Object].
145
///   * are related if their supertypes are equal, e.g. `List<dynamic>` and
146
///     `Set<dynamic>`.
147
/// * Two type variables are related if their bounds are related.
148
/// * Otherwise, the 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.isDynamic ||
1✔
157
      rightType == null ||
158
      rightType.isBottom ||
1✔
159
      rightType.isDynamic) {
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
    // In this case, [leftElement] and [rightElement] each represent
171
    // the same class, like `int`, or `Iterable<String>`.
172
    var leftElement = promotedLeftType.element;
1✔
173
    var rightElement = promotedRightType.element;
1✔
174
    if (leftElement == rightElement) {
1✔
175
      // In this case, [leftElement] and [rightElement] represent the same
176
      // class, modulo generics, e.g. `List<int>` and `List<dynamic>`. Now we
177
      // need to check type arguments.
178
      var leftTypeArguments = promotedLeftType.typeArguments;
1✔
179
      var rightTypeArguments = promotedRightType.typeArguments;
1✔
180
      if (leftTypeArguments.length != rightTypeArguments.length) {
3✔
181
        // I cannot think of how we would enter this block, but it guards
182
        // against RangeError below.
183
        return false;
184
      }
185
      for (var i = 0; i < leftTypeArguments.length; i++) {
3✔
186
        // If any of the pair-wise type arguments are unrelated, then
187
        // [leftType] and [rightType] are unrelated.
188
        if (typesAreUnrelated(
1✔
189
            typeSystem, leftTypeArguments[i], rightTypeArguments[i])) {
2✔
190
          return true;
191
        }
192
      }
193
      // Otherwise, they might be related.
194
      return false;
195
    } else {
196
      return (leftElement.supertype?.isDartCoreObject ?? false) ||
2✔
197
          leftElement.supertype != rightElement.supertype;
3✔
198
    }
199
  } else if (promotedLeftType is TypeParameterType &&
1✔
200
      promotedRightType is TypeParameterType) {
1✔
201
    return typesAreUnrelated(typeSystem, promotedLeftType.element.bound,
3✔
202
        promotedRightType.element.bound);
2✔
203
  } else if (promotedLeftType is FunctionType) {
1✔
204
    if (_isFunctionTypeUnrelatedToType(promotedLeftType, promotedRightType)) {
1✔
205
      return true;
206
    }
207
  } else if (promotedRightType is FunctionType) {
×
208
    if (_isFunctionTypeUnrelatedToType(promotedRightType, promotedLeftType)) {
×
209
      return true;
210
    }
211
  }
212
  return false;
213
}
214

215
bool _isFunctionTypeUnrelatedToType(FunctionType type1, DartType type2) {
1✔
216
  if (type2 is FunctionType) {
1✔
217
    return false;
218
  }
219
  if (type2 is InterfaceType) {
1✔
220
    var element2 = type2.element;
1✔
221
    if (element2 is ClassElement &&
1✔
222
        element2.lookUpConcreteMethod('call', element2.library) != null) {
2✔
223
      return false;
224
    }
225
  }
226
  return true;
227
}
228

229
class DartTypeUtilities {
230
  @Deprecated('Replace with `type.extendsClass`')
×
231
  static bool extendsClass(
232
          DartType? type, String? className, String? library) =>
233
      type.extendsClass(className, library!);
×
234

235
  @Deprecated('Replace with `rawNode.canonicalElement`')
×
236
  static Element? getCanonicalElementFromIdentifier(AstNode? rawNode) =>
237
      rawNode.canonicalElement;
×
238

239
  @Deprecated('Replace with `type.implementsInterface`')
×
240
  static bool implementsInterface(
241
          DartType? type, String interface, String library) =>
242
      type.implementsInterface(interface, library);
×
243

244
  // todo(pq): remove and replace w/ an extension (pending internal migration)
245
  @Deprecated('Slated for removal')
×
246
  static bool isClass(DartType? type, String? className, String? library) =>
247
      type is InterfaceType &&
×
248
      type.element.name == className &&
×
249
      type.element.library.name == library;
×
250

251
  @Deprecated('Replace with `expression.isNullLiteral`')
×
252
  static bool isNullLiteral(Expression? expression) => expression.isNullLiteral;
×
253

254
  @Deprecated('Use `argumentsMatchParameters`')
×
255
  static bool matchesArgumentsWithParameters(NodeList<Expression> arguments,
256
          NodeList<FormalParameter> parameters) =>
257
      argumentsMatchParameters(arguments, parameters);
×
258

259
  @Deprecated('Replace with `node.traverseNodesInDFS`')
×
260
  static Iterable<AstNode> traverseNodesInDFS(AstNode node,
261
          {AstNodePredicate? excludeCriteria}) =>
262
      node.traverseNodesInDFS(excludeCriteria: excludeCriteria);
×
263
}
264

265
class InterfaceTypeDefinition {
266
  final String name;
267
  final String library;
268

269
  InterfaceTypeDefinition(this.name, this.library);
1✔
270

271
  @override
1✔
272
  int get hashCode => name.hashCode ^ library.hashCode;
5✔
273

274
  @override
×
275
  bool operator ==(Object other) {
276
    if (identical(this, other)) {
277
      return true;
278
    }
279
    return other is InterfaceTypeDefinition &&
×
280
        name == other.name &&
×
281
        library == other.library;
×
282
  }
283
}
284

285
extension DartTypeExtensions on DartType {
286
  /// Returns the type which should be used when conducting "interface checks"
287
  /// on `this`.
288
  ///
289
  /// If `this` is a type variable, then the type-for-interface-check of its
290
  /// promoted bound or bound is returned. Otherwise, `this` is returned.
291
  // TODO(srawlins): Move to extensions.dart.
292
  DartType get typeForInterfaceCheck {
1✔
293
    var self = this;
294
    if (self is TypeParameterType) {
1✔
295
      if (self is TypeParameterTypeImpl) {
1✔
296
        var promotedType = self.promotedBound;
1✔
297
        if (promotedType != null) {
298
          return promotedType.typeForInterfaceCheck;
1✔
299
        }
300
      }
301
      return self.bound.typeForInterfaceCheck;
2✔
302
    } else {
303
      return self;
304
    }
305
  }
306
}
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