• 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

84.66
/lib/src/extensions.dart
1
// Copyright (c) 2022, 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/token.dart';
7
import 'package:analyzer/dart/constant/value.dart';
8
import 'package:analyzer/dart/element/element.dart';
9
import 'package:analyzer/dart/element/type.dart';
10
import 'package:analyzer/file_system/physical_file_system.dart';
11
import 'package:analyzer/src/dart/element/member.dart'; // ignore: implementation_imports
12
import 'package:collection/collection.dart';
13

14
import 'analyzer.dart';
15
import 'util/dart_type_utilities.dart';
16

17
class EnumLikeClassDescription {
18
  final Map<DartObject, Set<FieldElement>> _enumConstants;
19
  EnumLikeClassDescription(this._enumConstants);
1✔
20

21
  /// Returns a fresh map of the class's enum-like constant values.
22
  Map<DartObject, Set<FieldElement>> get enumConstants => {..._enumConstants};
3✔
23
}
24

25
extension AstNodeExtension on AstNode {
26
  Iterable<AstNode> get childNodes => childEntities.whereType<AstNode>();
3✔
27

28
  bool get isEffectivelyPrivate {
1✔
29
    var node = this;
30
    if (node.isInternal) return true;
1✔
31
    if (node is ClassDeclaration) {
1✔
32
      var classElement = node.declaredElement;
1✔
33
      if (classElement != null) {
34
        if (classElement.isSealed) return true;
1✔
35
        if (classElement.isAbstract) {
1✔
36
          if (classElement.isFinal) return true;
1✔
37
          if (classElement.isInterface) return true;
1✔
38
        }
39
      }
40
    }
41
    return false;
42
  }
43

44
  bool get isInternal {
1✔
45
    var parent = thisOrAncestorOfType<CompilationUnitMember>();
1✔
46
    if (parent == null) return false;
47

48
    var element = parent.declaredElement;
1✔
49
    return element != null && element.hasInternal;
1✔
50
  }
51

52
  /// Builds the list resulting from traversing the node in DFS and does not
53
  /// include the node itself.
54
  ///
55
  /// It excludes the nodes for which the [excludeCriteria] returns true. If
56
  /// [excludeCriteria] is not provided, all nodes are included.
57
  @Deprecated(
1✔
58
      'This approach is slow and slated for removal. Traversal via a standard visitor is preferred.')
59
  Iterable<AstNode> traverseNodesInDFS({AstNodePredicate? excludeCriteria}) {
60
    var nodes = <AstNode>{};
61
    var nodesToVisit = List.of(childNodes);
2✔
62
    if (excludeCriteria == null) {
63
      while (nodesToVisit.isNotEmpty) {
1✔
64
        var node = nodesToVisit.removeAt(0);
1✔
65
        nodes.add(node);
1✔
66
        nodesToVisit.insertAll(0, node.childNodes);
2✔
67
      }
68
    } else {
69
      while (nodesToVisit.isNotEmpty) {
×
70
        var node = nodesToVisit.removeAt(0);
×
71
        if (excludeCriteria(node)) continue;
×
72
        nodes.add(node);
×
73
        nodesToVisit.insertAll(0, node.childNodes);
×
74
      }
75
    }
76

77
    return nodes;
78
  }
79
}
80

81
extension AstNodeNullableExtension on AstNode? {
82
  bool get isFieldNameShortcut {
1✔
83
    var node = this;
84
    if (node is NullCheckPattern) node = node.parent;
2✔
85
    if (node is NullAssertPattern) node = node.parent;
2✔
86
    return node is PatternField && node.name != null && node.name?.name == null;
4✔
87
  }
88

89
  /// Return `true` if the expression is null aware, or if one of its recursive
90
  /// targets is null aware.
91
  bool containsNullAwareInvocationInChain() {
1✔
92
    var node = this;
93
    if (node is PropertyAccess) {
1✔
94
      if (node.isNullAware) return true;
1✔
95
      return node.target.containsNullAwareInvocationInChain();
2✔
96
    } else if (node is MethodInvocation) {
1✔
97
      if (node.isNullAware) return true;
1✔
98
      return node.target.containsNullAwareInvocationInChain();
2✔
99
    } else if (node is IndexExpression) {
1✔
100
      if (node.isNullAware) return true;
1✔
101
      return node.target.containsNullAwareInvocationInChain();
2✔
102
    }
103
    return false;
104
  }
105
}
106

107
extension BlockExtension on Block {
108
  /// Returns the last statement of this block, or `null` if this is empty.
109
  ///
110
  /// If the last immediate statement of this block is a [Block], recurses into
111
  /// it to find the last statement.
112
  Statement? get lastStatement {
×
113
    if (statements.isEmpty) {
×
114
      return null;
115
    }
116
    var lastStatement = statements.last;
×
117
    if (lastStatement is Block) {
×
118
      return lastStatement.lastStatement;
×
119
    }
120
    return lastStatement;
121
  }
122
}
123

124
extension ClassElementExtension on ClassElement {
125
  /// Returns an [EnumLikeClassDescription] for this if the latter is a valid
126
  /// "enum-like" class.
127
  ///
128
  /// An enum-like class must meet the following requirements:
129
  ///
130
  /// * is concrete,
131
  /// * has no public constructors,
132
  /// * has no factory constructors,
133
  /// * has two or more static const fields with the same type as the class,
134
  /// * has no subclasses declared in the defining library.
135
  ///
136
  /// The returned [EnumLikeClassDescription]'s `enumConstantNames` contains all
137
  /// of the static const fields with the same type as the class, with one
138
  /// exception; any static const field which is marked `@Deprecated` and is
139
  /// equal to another static const field with the same type as the class is not
140
  /// included. Such a field is assumed to be deprecated in favor of the field
141
  /// with equal value.
142
  EnumLikeClassDescription? get asEnumLikeClass {
1✔
143
    // See discussion: https://github.com/dart-lang/linter/issues/2083.
144

145
    // Must be concrete.
146
    if (isAbstract) {
1✔
147
      return null;
148
    }
149

150
    // With only private non-factory constructors.
151
    for (var constructor in constructors) {
2✔
152
      if (!constructor.isPrivate || constructor.isFactory) {
2✔
153
        return null;
154
      }
155
    }
156

157
    var type = thisType;
1✔
158

159
    // And 2 or more static const fields whose type is the enclosing class.
160
    var enumConstantCount = 0;
161
    var enumConstants = <DartObject, Set<FieldElement>>{};
1✔
162
    for (var field in fields) {
2✔
163
      // Ensure static const.
164
      if (field.isSynthetic || !field.isConst || !field.isStatic) {
3✔
165
        continue;
166
      }
167
      // Check for type equality.
168
      if (field.type != type) {
2✔
169
        continue;
170
      }
171
      var fieldValue = field.computeConstantValue();
1✔
172
      if (fieldValue == null) {
173
        continue;
174
      }
175
      enumConstantCount++;
1✔
176
      enumConstants.putIfAbsent(fieldValue, () => {}).add(field);
3✔
177
    }
178
    if (enumConstantCount < 2) {
1✔
179
      return null;
180
    }
181

182
    // And no subclasses in the defining library.
183
    if (hasSubclassInDefiningCompilationUnit) return null;
1✔
184

185
    return EnumLikeClassDescription(enumConstants);
1✔
186
  }
187

188
  bool get hasSubclassInDefiningCompilationUnit {
1✔
189
    var compilationUnit = library.definingCompilationUnit;
2✔
190
    for (var cls in compilationUnit.classes) {
2✔
191
      InterfaceType? classType = cls.thisType;
1✔
192
      do {
193
        classType = classType?.superclass;
1✔
194
        if (classType == thisType) {
2✔
195
          return true;
196
        }
197
      } while (classType != null && !classType.isDartCoreObject);
1✔
198
    }
199
    return false;
200
  }
201

202
  bool get isEnumLikeClass => asEnumLikeClass != null;
2✔
203

204
  /// Returns whether this class is exactly [otherName] declared in
205
  /// [otherLibrary].
206
  bool isClass(String otherName, String otherLibrary) =>
×
207
      name == otherName && library.name == otherLibrary;
×
208
}
209

210
extension ClassMemberListExtension on List<ClassMember> {
211
  MethodDeclaration? getMethod(String name) => whereType<MethodDeclaration>()
2✔
212
      .firstWhereOrNull((node) => node.name.lexeme == name);
5✔
213
}
214

215
extension ConstructorElementExtension on ConstructorElement {
216
  /// Returns whether `this` is the same element as the [className] constructor
217
  /// named [constructorName] declared in [uri].
218
  bool isSameAs({
1✔
219
    required String uri,
220
    required String className,
221
    required String constructorName,
222
  }) =>
223
      library.name == uri &&
3✔
224
      enclosingElement.name == className &&
3✔
225
      name == constructorName;
2✔
226
}
227

228
extension DartTypeExtension on DartType? {
229
  bool extendsClass(String? className, String library) {
1✔
230
    var self = this;
231
    if (self is InterfaceType) {
1✔
232
      return _extendsClass(self, <InterfaceElement>{}, className, library);
1✔
233
    }
234
    return false;
235
  }
236

237
  bool implementsAnyInterface(Iterable<InterfaceTypeDefinition> definitions) {
1✔
238
    bool isAnyInterface(InterfaceType i) =>
1✔
239
        definitions.any((d) => i.isSameAs(d.name, d.library));
5✔
240

241
    var typeToCheck = this;
242
    if (typeToCheck is TypeParameterType) {
1✔
243
      typeToCheck = typeToCheck.typeForInterfaceCheck;
1✔
244
    }
245
    if (typeToCheck is InterfaceType) {
1✔
246
      return isAnyInterface(typeToCheck) ||
1✔
247
          !typeToCheck.element.isSynthetic &&
2✔
248
              typeToCheck.element.allSupertypes.any(isAnyInterface);
3✔
249
    } else {
250
      return false;
251
    }
252
  }
253

254
  bool implementsInterface(String interface, String library) {
1✔
255
    var self = this;
256
    if (self is! InterfaceType) {
1✔
257
      return false;
258
    }
259
    bool predicate(InterfaceType i) => i.isSameAs(interface, library);
2✔
260
    var element = self.element;
1✔
261
    return predicate(self) ||
1✔
262
        !element.isSynthetic && element.allSupertypes.any(predicate);
3✔
263
  }
264

265
  /// Returns whether `this` is the same element as [interface], declared in
266
  /// [library].
267
  bool isSameAs(String? interface, String? library) {
1✔
268
    var self = this;
269
    return self is InterfaceType &&
1✔
270
        self.element.name == interface &&
3✔
271
        self.element.library.name == library;
4✔
272
  }
273

274
  static bool _extendsClass(
1✔
275
          InterfaceType? type,
276
          Set<InterfaceElement> seenElements,
277
          String? className,
278
          String? library) =>
279
      type != null &&
280
      seenElements.add(type.element) &&
2✔
281
      (type.isSameAs(className, library) ||
1✔
282
          _extendsClass(type.superclass, seenElements, className, library));
2✔
283
}
284

285
extension ElementExtension on Element {
286
  Element get canonicalElement {
1✔
287
    var self = this;
288
    if (self is PropertyAccessorElement) {
1✔
289
      var variable = self.variable;
1✔
290
      if (variable is FieldMember) {
1✔
291
        // A field element defined in a parameterized type where the values of
292
        // the type parameters are known.
293
        //
294
        // This concept should be invisible when comparing FieldElements, but a
295
        // bug in the analyzer causes FieldElements to not evaluate as
296
        // equivalent to equivalent FieldMembers. See
297
        // https://github.com/dart-lang/sdk/issues/35343.
298
        return variable.declaration;
1✔
299
      } else {
300
        return variable;
301
      }
302
    } else {
303
      return self;
304
    }
305
  }
306
}
307

308
extension ExpressionExtension on Expression? {
309
  bool get isNullLiteral => this?.unParenthesized is NullLiteral;
3✔
310
}
311

312
extension FieldDeclarationExtension on FieldDeclaration {
313
  bool get isInvalidExtensionTypeField =>
1✔
314
      !isStatic && parent is ExtensionTypeDeclaration;
3✔
315
}
316

317
extension InhertanceManager3Extension on InheritanceManager3 {
318
  /// Returns the class member that is overridden by [member], if there is one,
319
  /// as defined by [getInherited].
320
  ExecutableElement? overriddenMember(Element? member) {
1✔
321
    if (member == null) {
322
      return null;
323
    }
324

325
    var interfaceElement = member.thisOrAncestorOfType<InterfaceElement>();
1✔
326
    if (interfaceElement == null) {
327
      return null;
328
    }
329
    var name = member.name;
1✔
330
    if (name == null) {
331
      return null;
332
    }
333

334
    var libraryUri = interfaceElement.library.source.uri;
3✔
335
    return getInherited(interfaceElement.thisType, Name(libraryUri, name));
3✔
336
  }
337
}
338

339
extension InterfaceElementExtension on InterfaceElement {
340
  /// Returns whether this element is exactly [otherName] declared in
341
  /// [otherLibrary].
342
  bool isClass(String otherName, String otherLibrary) =>
1✔
343
      name == otherName && library.name == otherLibrary;
2✔
344
}
345

346
extension InterfaceTypeExtension on InterfaceType {
347
  /// Returns the collection of all interfaces that this type implements,
348
  /// including itself.
349
  Iterable<InterfaceType> get implementedInterfaces {
1✔
350
    void searchSupertypes(
1✔
351
        InterfaceType? type,
352
        Set<InterfaceElement> alreadyVisited,
353
        List<InterfaceType> interfaceTypes) {
354
      if (type == null || !alreadyVisited.add(type.element)) {
2✔
355
        return;
356
      }
357
      interfaceTypes.add(type);
1✔
358
      searchSupertypes(type.superclass, alreadyVisited, interfaceTypes);
2✔
359
      for (var interface in type.interfaces) {
2✔
360
        searchSupertypes(interface, alreadyVisited, interfaceTypes);
1✔
361
      }
362
      for (var mixin in type.mixins) {
1✔
363
        searchSupertypes(mixin, alreadyVisited, interfaceTypes);
×
364
      }
365
    }
366

367
    var interfaceTypes = <InterfaceType>[];
1✔
368
    searchSupertypes(this, {}, interfaceTypes);
1✔
369
    return interfaceTypes;
370
  }
371
}
372

373
extension MethodDeclarationExtension on MethodDeclaration {
374
  bool get hasInheritedMethod => lookUpInheritedMethod() != null;
2✔
375

376
  /// Returns whether this method is an override of a method in any supertype.
377
  bool get isOverride {
1✔
378
    var name = declaredElement?.name;
2✔
379
    if (name == null) {
380
      return false;
381
    }
382
    var parentElement = declaredElement?.enclosingElement;
2✔
383
    if (parentElement is! InterfaceElement) {
1✔
384
      return false;
385
    }
386
    var parentLibrary = parentElement.library;
1✔
387

388
    if (isGetter) {
1✔
389
      // Search supertypes for a getter of the same name.
390
      return parentElement.allSupertypes
1✔
391
          .any((t) => t.lookUpGetter2(name, parentLibrary) != null);
3✔
392
    } else if (isSetter) {
1✔
393
      // Search supertypes for a setter of the same name.
394
      return parentElement.allSupertypes
×
395
          .any((t) => t.lookUpSetter2(name, parentLibrary) != null);
×
396
    } else {
397
      // Search supertypes for a method of the same name.
398
      return parentElement.allSupertypes
1✔
399
          .any((t) => t.lookUpMethod2(name, parentLibrary) != null);
3✔
400
    }
401
  }
402

403
  PropertyAccessorElement? lookUpGetter() {
1✔
404
    var declaredElement = this.declaredElement;
1✔
405
    if (declaredElement == null) {
406
      return null;
407
    }
408
    var parent = declaredElement.enclosingElement;
1✔
409
    if (parent is InterfaceElement) {
1✔
410
      return parent.lookUpGetter(name.lexeme, declaredElement.library);
4✔
411
    }
412
    if (parent is ExtensionElement) {
×
413
      return parent.getGetter(name.lexeme);
×
414
    }
415
    return null;
416
  }
417

418
  PropertyAccessorElement? lookUpInheritedConcreteGetter() {
×
419
    var declaredElement = this.declaredElement;
×
420
    if (declaredElement == null) {
421
      return null;
422
    }
423
    var parent = declaredElement.enclosingElement;
×
424
    if (parent is InterfaceElement) {
×
425
      return parent.lookUpInheritedConcreteGetter(
×
426
          name.lexeme, declaredElement.library);
×
427
    }
428
    // Extensions don't inherit.
429
    return null;
430
  }
431

432
  MethodElement? lookUpInheritedConcreteMethod() {
×
433
    var declaredElement = this.declaredElement;
×
434
    if (declaredElement != null) {
435
      var parent = declaredElement.enclosingElement;
×
436
      if (parent is InterfaceElement) {
×
437
        return parent.lookUpInheritedConcreteMethod(
×
438
            name.lexeme, declaredElement.library);
×
439
      }
440
    }
441
    // Extensions don't inherit.
442
    return null;
443
  }
444

445
  PropertyAccessorElement? lookUpInheritedConcreteSetter() {
1✔
446
    var declaredElement = this.declaredElement;
1✔
447
    if (declaredElement != null) {
448
      var parent = declaredElement.enclosingElement;
1✔
449
      if (parent is InterfaceElement) {
1✔
450
        return parent.lookUpInheritedConcreteSetter(
1✔
451
            name.lexeme, declaredElement.library);
3✔
452
      }
453
    }
454
    // Extensions don't inherit.
455
    return null;
456
  }
457

458
  MethodElement? lookUpInheritedMethod() {
1✔
459
    var declaredElement = this.declaredElement;
1✔
460
    if (declaredElement != null) {
461
      var parent = declaredElement.enclosingElement;
1✔
462
      if (parent is InterfaceElement) {
1✔
463
        return parent.lookUpInheritedMethod(
1✔
464
            name.lexeme, declaredElement.library);
3✔
465
      }
466
    }
467
    return null;
468
  }
469
}
470

471
extension NullableAstNodeExtension on AstNode? {
472
  Element? get canonicalElement {
1✔
473
    var self = this;
474
    if (self is Expression) {
1✔
475
      var node = self.unParenthesized;
1✔
476
      if (node is Identifier) {
1✔
477
        return node.staticElement?.canonicalElement;
2✔
478
      } else if (node is PropertyAccess) {
1✔
479
        return node.propertyName.staticElement?.canonicalElement;
3✔
480
      }
481
    }
482
    return null;
483
  }
484
}
485

486
extension StringExtension on String {
487
  String toAbsoluteNormalizedPath() {
1✔
488
    var pathContext = PhysicalResourceProvider.INSTANCE.pathContext;
2✔
489
    return pathContext.normalize(pathContext.absolute(this));
2✔
490
  }
491
}
492

493
extension TokenExtension on Token? {
494
  bool get isFinal => this?.keyword == Keyword.FINAL;
3✔
495
}
496

497
extension TokenTypeExtension on TokenType {
498
  TokenType get inverted => switch (this) {
1✔
499
        TokenType.LT_EQ => TokenType.GT_EQ,
1✔
500
        TokenType.LT => TokenType.GT,
1✔
501
        TokenType.GT => TokenType.LT,
1✔
502
        TokenType.GT_EQ => TokenType.LT_EQ,
1✔
503
        _ => this
504
      };
505
}
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