• 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.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
        break;
131

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

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

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

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

163
  final ExpectedArgumentKind expectedArgumentKind;
164

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

171
  InterfaceType? collectionTypeFor(InterfaceType targetType);
172
}
173

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

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

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

190
class MethodDefinitionForName extends MethodDefinition {
191
  final String libraryName;
192

193
  final String interfaceName;
194

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

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

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

222
  /// An argument is expected to be assignable to the collection type.
223
  assignableToCollection,
224

225
  /// An argument is expected to be assignable to `Iterable<E>` where `E` is the
226
  /// (only) type argument on the collection type.
227
  assignableToIterableOfTypeArgument,
228
}
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