@@ -74,7 +74,7 @@ class UseLateForPrivateFieldsAndVariables extends LintRule {
7474 }
7575}
7676
77- class _Visitor extends UnifyingAstVisitor <void > {
77+ class _Visitor extends RecursiveAstVisitor <void > {
7878 static final lateables =
7979 < CompilationUnitElement , List <VariableDeclaration >> {};
8080
@@ -86,6 +86,24 @@ class _Visitor extends UnifyingAstVisitor<void> {
8686 CompilationUnitElement ? currentUnit;
8787 _Visitor (this .rule, this .context);
8888
89+ @override
90+ void visitAssignmentExpression (AssignmentExpression node) {
91+ var element = node.writeElement? .canonicalElement;
92+ if (element != null ) {
93+ var assignee = node.leftHandSide;
94+ if (assignee is SimpleIdentifier && assignee.inDeclarationContext ()) {
95+ // This is OK.
96+ } else if (node.operator .type == TokenType .EQ &&
97+ DartTypeUtilities .isNonNullable (
98+ context, node.rightHandSide.staticType)) {
99+ // This is OK; non-null access.
100+ } else {
101+ nullableAccess[currentUnit]? .add (element);
102+ }
103+ }
104+ super .visitAssignmentExpression (node);
105+ }
106+
89107 @override
90108 void visitClassDeclaration (ClassDeclaration node) {
91109 // See: https://dart.dev/tools/diagnostic-messages#late_final_field_with_const_constructor
@@ -120,7 +138,7 @@ class _Visitor extends UnifyingAstVisitor<void> {
120138 if (areAllLibraryUnitsVisited) {
121139 _checkAccess (libraryUnitsInContext);
122140
123- // clean up
141+ // Clean up.
124142 for (var unit in libraryUnitsInContext) {
125143 lateables.remove (unit);
126144 nullableAccess.remove (unit);
@@ -136,53 +154,44 @@ class _Visitor extends UnifyingAstVisitor<void> {
136154
137155 @override
138156 void visitFieldDeclaration (FieldDeclaration node) {
139- for (var variable in node.fields.variables) {
140- var parent = node.parent;
141- // see https://github.com/dart-lang/linter/pull/2189#issuecomment-660115569
142- // We could also include public members in private classes but to do that
143- // we'd need to ensure that there are no instances of either the
144- // enclosing class or any subclass of the enclosing class that are ever
145- // accessible outside this library.
146- if (parent != null &&
147- (Identifier .isPrivateName (variable.name.name) ||
148- _isPrivateExtension (parent))) {
149- _visit (variable);
157+ var parent = node.parent;
158+ if (parent != null ) {
159+ var parentIsPrivateExtension = _isPrivateExtension (parent);
160+ for (var variable in node.fields.variables) {
161+ // See
162+ // https://github.com/dart-lang/linter/pull/2189#issuecomment-660115569.
163+ // We could also include public members in private classes but to do
164+ // that we'd need to ensure that there are no instances of either the
165+ // enclosing class or any subclass of the enclosing class that are ever
166+ // accessible outside this library.
167+ if (parentIsPrivateExtension ||
168+ Identifier .isPrivateName (variable.name.name)) {
169+ _visit (variable);
170+ }
150171 }
151172 }
152173 super .visitFieldDeclaration (node);
153174 }
154175
155176 @override
156- void visitNode (AstNode node) {
157- var parent = node.parent;
177+ void visitPrefixedIdentifier (PrefixedIdentifier node) {
178+ var element = node.staticElement? .canonicalElement;
179+ _visitIdentifierOrPropertyAccess (node, element);
180+ super .visitPrefixedIdentifier (node);
181+ }
158182
159- Element ? element;
160- if (parent is AssignmentExpression && parent.leftHandSide == node) {
161- element = parent.writeElement ? .canonicalElement;
162- } else {
163- element = node.canonicalElement ;
164- }
183+ @override
184+ void visitPropertyAccess ( PropertyAccess node) {
185+ var element = node.propertyName.staticElement ? .canonicalElement;
186+ _visitIdentifierOrPropertyAccess (node, element);
187+ super . visitPropertyAccess (node) ;
188+ }
165189
166- if (element != null ) {
167- if (parent is Expression ) {
168- parent = parent.unParenthesized;
169- }
170- if (node is SimpleIdentifier && node.inDeclarationContext ()) {
171- // ok
172- } else if (parent is PostfixExpression &&
173- parent.operand == node &&
174- parent.operator .type == TokenType .BANG ) {
175- // ok non-null access
176- } else if (parent is AssignmentExpression &&
177- parent.operator .type == TokenType .EQ &&
178- DartTypeUtilities .isNonNullable (
179- context, parent.rightHandSide.staticType)) {
180- // ok non-null access
181- } else {
182- nullableAccess[currentUnit]? .add (element);
183- }
184- }
185- super .visitNode (node);
190+ @override
191+ void visitSimpleIdentifier (SimpleIdentifier node) {
192+ var element = node.staticElement? .canonicalElement;
193+ _visitIdentifierOrPropertyAccess (node, element);
194+ super .visitSimpleIdentifier (node);
186195 }
187196
188197 @override
@@ -222,4 +231,26 @@ class _Visitor extends UnifyingAstVisitor<void> {
222231 }
223232 lateables[currentUnit]? .add (variable);
224233 }
234+
235+ /// Checks whether [expression] , which must be an [Identifier] or
236+ /// [PropertyAccess] , and its [canonicalElement] , represent a nullable access.
237+ void _visitIdentifierOrPropertyAccess (
238+ Expression expression, Element ? canonicalElement) {
239+ assert (expression is Identifier || expression is PropertyAccess );
240+ if (canonicalElement != null ) {
241+ var parent = expression.parent;
242+ if (parent is Expression ) {
243+ parent = parent.unParenthesized;
244+ }
245+ if (expression is SimpleIdentifier && expression.inDeclarationContext ()) {
246+ // This is OK.
247+ } else if (parent is PostfixExpression &&
248+ parent.operand == expression &&
249+ parent.operator .type == TokenType .BANG ) {
250+ // This is OK; non-null access.
251+ } else {
252+ nullableAccess[currentUnit]? .add (canonicalElement);
253+ }
254+ }
255+ }
225256}
0 commit comments