3
3
// BSD-style license that can be found in the LICENSE file.
4
4
5
5
import 'package:analyzer/dart/ast/ast.dart' ;
6
+ import 'package:analyzer/dart/ast/token.dart' ;
6
7
import 'package:analyzer/dart/ast/visitor.dart' ;
7
8
8
9
import '../analyzer.dart' ;
@@ -67,6 +68,10 @@ class PreferFinalLocals extends LintRule {
67
68
void registerNodeProcessors (
68
69
NodeLintRegistry registry, LinterContext context) {
69
70
var visitor = _Visitor (this );
71
+ registry.addListPattern (this , visitor);
72
+ registry.addMapPattern (this , visitor);
73
+ registry.addObjectPattern (this , visitor);
74
+ registry.addRecordPattern (this , visitor);
70
75
registry.addVariableDeclarationList (this , visitor);
71
76
}
72
77
}
@@ -76,6 +81,101 @@ class _Visitor extends SimpleAstVisitor<void> {
76
81
77
82
_Visitor (this .rule);
78
83
84
+ void checkPatternFields (DartPattern node) {
85
+ var function = node.thisOrAncestorOfType <FunctionBody >();
86
+ if (function == null ) return ;
87
+
88
+ late NodeList <PatternField > fields;
89
+ if (node is RecordPattern ) fields = node.fields;
90
+ if (node is ObjectPattern ) fields = node.fields;
91
+
92
+ var parent = node.unParenthesized.parent;
93
+ var inPatternVariableDeclaration = false ;
94
+ if (parent is PatternVariableDeclaration ) {
95
+ if (parent.keyword.keyword == Keyword .FINAL ) return ;
96
+ inPatternVariableDeclaration = true ;
97
+ }
98
+
99
+ for (var field in fields) {
100
+ var pattern = field.pattern.declaredVariablePattern;
101
+ if (pattern is DeclaredVariablePattern ) {
102
+ var element = pattern.declaredElement;
103
+ if (element == null ) continue ;
104
+ if (function.isPotentiallyMutatedInScope (element)) {
105
+ if (inPatternVariableDeclaration) {
106
+ return ;
107
+ } else {
108
+ continue ;
109
+ }
110
+ }
111
+ if (inPatternVariableDeclaration) {
112
+ rule.reportLint ((parent! as PatternVariableDeclaration ).expression);
113
+ return ;
114
+ } else {
115
+ if (! pattern.keyword.isFinal) {
116
+ rule.reportLintForToken (pattern.name);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ bool isDeclaredFinal (AstNode node) {
124
+ var declaration = node.thisOrAncestorOfType <PatternVariableDeclaration >();
125
+ if (declaration == null ) return false ; // To be safe.
126
+ return declaration.keyword.isFinal;
127
+ }
128
+
129
+ bool isPotentiallyMutated (AstNode pattern, FunctionBody function) {
130
+ if (pattern is DeclaredVariablePattern ) {
131
+ var element = pattern.declaredElement;
132
+ if (element == null || function.isPotentiallyMutatedInScope (element)) {
133
+ return true ;
134
+ }
135
+ }
136
+ return false ;
137
+ }
138
+
139
+ @override
140
+ void visitListPattern (ListPattern node) {
141
+ if (isDeclaredFinal (node)) return ;
142
+
143
+ var function = node.thisOrAncestorOfType <FunctionBody >();
144
+ if (function == null ) return ;
145
+
146
+ for (var element in node.elements) {
147
+ if (isPotentiallyMutated (element, function)) return ;
148
+ }
149
+
150
+ rule.reportLint (node);
151
+ }
152
+
153
+ @override
154
+ void visitMapPattern (MapPattern node) {
155
+ if (isDeclaredFinal (node)) return ;
156
+
157
+ var function = node.thisOrAncestorOfType <FunctionBody >();
158
+ if (function == null ) return ;
159
+
160
+ for (var element in node.elements) {
161
+ if (element is MapPatternEntry ) {
162
+ if (isPotentiallyMutated (element.value, function)) return ;
163
+ }
164
+ }
165
+
166
+ rule.reportLint (node);
167
+ }
168
+
169
+ @override
170
+ void visitObjectPattern (ObjectPattern node) {
171
+ checkPatternFields (node);
172
+ }
173
+
174
+ @override
175
+ void visitRecordPattern (RecordPattern node) {
176
+ checkPatternFields (node);
177
+ }
178
+
79
179
@override
80
180
void visitVariableDeclarationList (VariableDeclarationList node) {
81
181
if (node.isConst || node.isFinal) return ;
@@ -100,3 +200,23 @@ class _Visitor extends SimpleAstVisitor<void> {
100
200
}
101
201
}
102
202
}
203
+
204
+ extension on Token ? {
205
+ bool get isFinal {
206
+ var self = this ;
207
+ if (self == null ) return false ;
208
+ return self.keyword == Keyword .FINAL ;
209
+ }
210
+ }
211
+
212
+ extension on DartPattern {
213
+ DeclaredVariablePattern ? get declaredVariablePattern {
214
+ var self = this ;
215
+ if (self is DeclaredVariablePattern ) return self;
216
+ // todo(pq): more cases?
217
+ if (self is LogicalAndPattern ) {
218
+ return self.rightOperand.declaredVariablePattern;
219
+ }
220
+ return null ;
221
+ }
222
+ }
0 commit comments