@@ -9,21 +9,38 @@ import 'package:analyzer/dart/element/type.dart';
9
9
import 'package:linter/src/analyzer.dart' ;
10
10
import 'package:linter/src/util/dart_type_utilities.dart' ;
11
11
12
+ typedef _InterfaceTypePredicate = bool Function (InterfaceType type);
13
+
14
+ /// Returns a predicate which returns whether a given [InterfaceTypeDefinition]
15
+ /// is equal to [definition] .
12
16
_InterfaceTypePredicate _buildImplementsDefinitionPredicate (
13
17
InterfaceTypeDefinition definition) =>
14
18
(InterfaceType interface ) =>
15
19
interface .name == definition.name &&
16
20
interface .element.library.name == definition.library;
17
21
22
+ /// Returns all implemented interfaces of [type] .
23
+ ///
24
+ /// This flattens all of the super-interfaces of [type] into one list.
18
25
List <InterfaceType > _findImplementedInterfaces (InterfaceType type,
19
- {List <InterfaceType > acc = const []}) =>
20
- acc .contains (type)
21
- ? acc
26
+ {List <InterfaceType > accumulator = const []}) =>
27
+ accumulator .contains (type)
28
+ ? accumulator
22
29
: type.interfaces.fold (
23
30
< InterfaceType > [type],
24
31
(List <InterfaceType > acc, InterfaceType e) => new List .from (acc)
25
- ..addAll (_findImplementedInterfaces (e, acc : acc)));
32
+ ..addAll (_findImplementedInterfaces (e, accumulator : acc)));
26
33
34
+ /// Returns the first type argument on [definition] , as implemented by [type] .
35
+ ///
36
+ /// In the simplest case, [type] is the same class as [definition] . For
37
+ /// example, given the definition `List<E>` and the type `List<int>` ,
38
+ /// this function returns the DartType for `int` .
39
+ ///
40
+ /// In a more complicated case, we must traverse [type] 's interfaces to find
41
+ /// [definition] . For example, given the definition `Set<E>` and the type `A`
42
+ /// where `A implements B<List, String>` and `B<E, F> implements Set<F>, C<E>` ,
43
+ /// this function returns the DartType for `String` .
27
44
DartType _findIterableTypeArgument (
28
45
InterfaceTypeDefinition definition, InterfaceType type,
29
46
{List <InterfaceType > accumulator = const []}) {
@@ -56,20 +73,22 @@ bool _isParameterizedMethodInvocation(
56
73
node.methodName.name == methodName &&
57
74
node.argumentList.arguments.length == 1 ;
58
75
59
- typedef bool _InterfaceTypePredicate (InterfaceType type);
60
-
61
76
/// Base class for visitor used in rules where we want to lint about invoking
62
- /// methods on generic classes where the parameter is unrelated to the parameter
63
- /// type of the class. Extending this visitor is as simple as knowing the method,
64
- /// class and library that uniquely define the target, i.e. implement only
65
- /// [definition] and [methodName] .
77
+ /// methods on generic classes where the type of the singular argument is
78
+ /// unrelated to the singular type argument of the class. Extending this
79
+ /// visitor is as simple as knowing the method, class and library that uniquely
80
+ /// define the target, i.e. implement only [definition] and [methodName] .
66
81
abstract class UnrelatedTypesProcessors extends SimpleAstVisitor <void > {
67
82
final LintRule rule;
68
83
69
84
UnrelatedTypesProcessors (this .rule);
70
85
86
+ /// The type definition which this [UnrelatedTypesProcessors] is concerned
87
+ /// with.
71
88
InterfaceTypeDefinition get definition;
72
89
90
+ /// The name of the method which this [UnrelatedTypesProcessors] is concerned
91
+ /// with.
73
92
String get methodName;
74
93
75
94
@override
@@ -78,28 +97,39 @@ abstract class UnrelatedTypesProcessors extends SimpleAstVisitor<void> {
78
97
return ;
79
98
}
80
99
81
- DartType type;
100
+ // At this point, we know that [node] is an invocation of a method which
101
+ // has the same name as the method that this UnrelatedTypesProcessors] is
102
+ // concerned with, and that the method has a single parameter.
103
+ //
104
+ // We've completed the "cheap" checks, and must now continue with the
105
+ // arduous task of determining whether the method target implements
106
+ // [definition].
107
+
108
+ DartType targetType;
82
109
if (node.target != null ) {
83
- type = node.target.staticType;
110
+ targetType = node.target.staticType;
84
111
} else {
85
- var classDeclaration =
112
+ final classDeclaration =
86
113
node.thisOrAncestorOfType <ClassOrMixinDeclaration >();
87
114
if (classDeclaration == null ) {
88
- type = null ;
115
+ targetType = null ;
89
116
} else if (classDeclaration is ClassDeclaration ) {
90
- type = resolutionMap
117
+ targetType = resolutionMap
91
118
.elementDeclaredByClassDeclaration (classDeclaration)
92
119
? .type;
93
120
} else if (classDeclaration is MixinDeclaration ) {
94
- type = resolutionMap
121
+ targetType = resolutionMap
95
122
.elementDeclaredByMixinDeclaration (classDeclaration)
96
123
? .type;
97
124
}
98
125
}
99
126
Expression argument = node.argumentList.arguments.first;
100
- if (type is InterfaceType &&
101
- DartTypeUtilities .unrelatedTypes (
102
- argument.staticType, _findIterableTypeArgument (definition, type))) {
127
+
128
+ // Finally, determine whether the type of the argument is related to the
129
+ // type of the method target.
130
+ if (targetType is InterfaceType &&
131
+ DartTypeUtilities .unrelatedTypes (argument.staticType,
132
+ _findIterableTypeArgument (definition, targetType))) {
103
133
rule.reportLint (node);
104
134
}
105
135
}
0 commit comments