Skip to content
This repository was archived by the owner on Nov 20, 2024. It is now read-only.

Commit 0c9c8ed

Browse files
authored
Document and tidy UnrelatedTypesProcessors (#1458)
1 parent 6663779 commit 0c9c8ed

File tree

2 files changed

+63
-22
lines changed

2 files changed

+63
-22
lines changed

lib/src/util/unrelated_types_visitor.dart

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,38 @@ import 'package:analyzer/dart/element/type.dart';
99
import 'package:linter/src/analyzer.dart';
1010
import 'package:linter/src/util/dart_type_utilities.dart';
1111

12+
typedef _InterfaceTypePredicate = bool Function(InterfaceType type);
13+
14+
/// Returns a predicate which returns whether a given [InterfaceTypeDefinition]
15+
/// is equal to [definition].
1216
_InterfaceTypePredicate _buildImplementsDefinitionPredicate(
1317
InterfaceTypeDefinition definition) =>
1418
(InterfaceType interface) =>
1519
interface.name == definition.name &&
1620
interface.element.library.name == definition.library;
1721

22+
/// Returns all implemented interfaces of [type].
23+
///
24+
/// This flattens all of the super-interfaces of [type] into one list.
1825
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
2229
: type.interfaces.fold(
2330
<InterfaceType>[type],
2431
(List<InterfaceType> acc, InterfaceType e) => new List.from(acc)
25-
..addAll(_findImplementedInterfaces(e, acc: acc)));
32+
..addAll(_findImplementedInterfaces(e, accumulator: acc)));
2633

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`.
2744
DartType _findIterableTypeArgument(
2845
InterfaceTypeDefinition definition, InterfaceType type,
2946
{List<InterfaceType> accumulator = const []}) {
@@ -56,20 +73,22 @@ bool _isParameterizedMethodInvocation(
5673
node.methodName.name == methodName &&
5774
node.argumentList.arguments.length == 1;
5875

59-
typedef bool _InterfaceTypePredicate(InterfaceType type);
60-
6176
/// 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].
6681
abstract class UnrelatedTypesProcessors extends SimpleAstVisitor<void> {
6782
final LintRule rule;
6883

6984
UnrelatedTypesProcessors(this.rule);
7085

86+
/// The type definition which this [UnrelatedTypesProcessors] is concerned
87+
/// with.
7188
InterfaceTypeDefinition get definition;
7289

90+
/// The name of the method which this [UnrelatedTypesProcessors] is concerned
91+
/// with.
7392
String get methodName;
7493

7594
@override
@@ -78,28 +97,39 @@ abstract class UnrelatedTypesProcessors extends SimpleAstVisitor<void> {
7897
return;
7998
}
8099

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;
82109
if (node.target != null) {
83-
type = node.target.staticType;
110+
targetType = node.target.staticType;
84111
} else {
85-
var classDeclaration =
112+
final classDeclaration =
86113
node.thisOrAncestorOfType<ClassOrMixinDeclaration>();
87114
if (classDeclaration == null) {
88-
type = null;
115+
targetType = null;
89116
} else if (classDeclaration is ClassDeclaration) {
90-
type = resolutionMap
117+
targetType = resolutionMap
91118
.elementDeclaredByClassDeclaration(classDeclaration)
92119
?.type;
93120
} else if (classDeclaration is MixinDeclaration) {
94-
type = resolutionMap
121+
targetType = resolutionMap
95122
.elementDeclaredByMixinDeclaration(classDeclaration)
96123
?.type;
97124
}
98125
}
99126
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))) {
103133
rule.reportLint(node);
104134
}
105135
}

test/rules/iterable_contains_unrelated_type.dart

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ void someFunction4() {
2626

2727
void someFunction4_1() {
2828
List list;
29-
if(list.contains(null)) print('someFucntion4_1');
29+
if (list.contains(null)) print('someFucntion4_1');
3030
}
3131

3232
void someFunction5_1() {
@@ -97,7 +97,8 @@ void someFunction12() {
9797
}
9898

9999
void bug_267(list) {
100-
if (list.contains('1')) print('someFunction'); // https://github.com/dart-lang/linter/issues/267
100+
if (list.contains('1')) // https://github.com/dart-lang/linter/issues/267
101+
print('someFunction');
101102
}
102103

103104
abstract class ClassBase {}
@@ -133,22 +134,32 @@ bool takesIterable2(Iterable<String> iterable) => iterable.contains('a'); // OK
133134
bool takesIterable3(Iterable iterable) => iterable.contains('a'); // OK
134135

135136
abstract class A implements Iterable<int> {}
137+
136138
abstract class B extends A {}
139+
137140
bool takesB(B b) => b.contains('a'); // LINT
138141

139142
abstract class A1 implements Iterable<String> {}
143+
140144
abstract class B1 extends A1 {}
145+
141146
bool takesB1(B1 b) => b.contains('a'); // OK
142147

143148
abstract class A3 implements Iterable {}
149+
144150
abstract class B3 extends A3 {}
151+
145152
bool takesB3(B3 b) => b.contains('a'); // OK
146153

147154
abstract class A2 implements Iterable<String> {}
155+
148156
abstract class B2 extends A2 {}
157+
149158
bool takesB2(B2 b) => b.contains('a'); // OK
150159

151-
abstract class SomeIterable<E> implements Iterable<E> {}
160+
abstract class AddlInterface {}
161+
162+
abstract class SomeIterable<E> implements Iterable<E>, AddlInterface {}
152163

153164
abstract class MyClass implements SomeIterable<int> {
154165
bool badMethod(String thing) => this.contains(thing); // LINT

0 commit comments

Comments
 (0)