This repository has been archived by the owner on Nov 20, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 169
/
Copy pathoverridden_fields.dart
182 lines (152 loc) · 4.71 KB
/
overridden_fields.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:collection/collection.dart' show IterableExtension;
import '../analyzer.dart';
const _desc = r"Don't override fields.";
const _details = r'''
**DON'T** override fields.
Overriding fields is almost always done unintentionally. Regardless, it is a
bad practice to do so.
**BAD:**
```dart
class Base {
Object field = 'lorem';
Object something = 'change';
}
class Bad1 extends Base {
@override
final field = 'ipsum'; // LINT
}
class Bad2 extends Base {
@override
Object something = 'done'; // LINT
}
```
**GOOD:**
```dart
class Base {
Object field = 'lorem';
Object something = 'change';
}
class Ok extends Base {
Object newField; // OK
final Object newFinal = 'ignore'; // OK
}
```
**GOOD:**
```dart
abstract class BaseLoggingHandler {
Base transformer;
}
class LogPrintHandler implements BaseLoggingHandler {
@override
Derived transformer; // OK
}
```
''';
Iterable<InterfaceType> _findAllSupertypesAndMixins(
InterfaceType? interface, List<InterfaceType> accumulator) {
if (interface == null ||
interface.isDartCoreObject ||
accumulator.contains(interface)) {
return accumulator;
}
accumulator.add(interface);
var superclass = interface.superclass;
var interfaces = <InterfaceType>[];
if (superclass != null) {
interfaces.add(superclass);
}
interfaces
..addAll(interface.element.mixins)
..addAll(_findAllSupertypesAndMixins(superclass, accumulator));
return interfaces.where((i) => i != interface);
}
Iterable<InterfaceType> _findAllSupertypesInMixin(MixinElement mixinElement) {
var supertypes = <InterfaceType>[];
var accumulator = <InterfaceType>[];
for (var type in mixinElement.superclassConstraints) {
supertypes.add(type);
supertypes.addAll(_findAllSupertypesAndMixins(type, accumulator));
}
return supertypes;
}
class OverriddenFields extends LintRule {
static const LintCode code = LintCode(
'overridden_fields', "Field overrides a field inherited from '{0}'.",
correctionMessage:
'Try removing the field, overriding the getter and setter if '
'necessary.');
OverriddenFields()
: super(
name: 'overridden_fields',
description: _desc,
details: _details,
group: Group.style);
@override
LintCode get lintCode => code;
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
var visitor = _Visitor(this);
registry.addFieldDeclaration(this, visitor);
}
}
class _Visitor extends SimpleAstVisitor<void> {
final LintRule rule;
_Visitor(this.rule);
@override
void visitFieldDeclaration(FieldDeclaration node) {
if (node.isStatic) {
return;
}
for (var variable in node.fields.variables) {
var declaredField = variable.declaredElement;
if (declaredField != null) {
var overriddenField = _getOverriddenMember(declaredField);
if (overriddenField != null && !overriddenField.isAbstract) {
rule.reportLintForToken(variable.name,
arguments: [overriddenField.enclosingElement.displayName]);
}
}
}
}
PropertyAccessorElement? _getOverriddenMember(Element member) {
var memberName = member.name;
var library = member.library;
bool isOverriddenMember(PropertyAccessorElement a) {
if (memberName == null || a.isStatic) {
return false;
}
if (a.isSynthetic && a.name == memberName) {
// Ensure that private members are overriding a member of the same library.
if (Identifier.isPrivateName(memberName)) {
return library == a.library;
}
return true;
}
return false;
}
bool containsOverriddenMember(InterfaceType i) =>
i.accessors.any(isOverriddenMember);
var enclosingElement = member.enclosingElement;
if (enclosingElement is! InterfaceElement) {
return null;
}
var classElement = enclosingElement;
Iterable<InterfaceType> interfaces;
if (classElement is MixinElement) {
interfaces = _findAllSupertypesInMixin(classElement);
} else {
interfaces =
_findAllSupertypesAndMixins(classElement.thisType, <InterfaceType>[]);
}
var interface = interfaces.firstWhereOrNull(containsOverriddenMember);
return interface?.accessors.firstWhere(isOverriddenMember);
}
}