linter: Allow lint rules to specify afterLibrary actions
Add `NodeLintRegistry.afterLibrary` API which allows a lint rule to
register a callback to be called after the last CompilationUnit is
visited.
Change-Id: I77bf279412617e6f2c825b6dbeaf34849e515156
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368524
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index e13a22a..fdb0b87 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -51,6 +51,8 @@
import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer/src/utilities/extensions/version.dart';
import 'package:analyzer/src/workspace/pub.dart';
+import 'package:analyzer/src/workspace/workspace.dart';
+import 'package:collection/collection.dart';
class AnalysisForCompletionResult {
final FileState fileState;
@@ -318,19 +320,7 @@
}
if (_analysisOptions.lint) {
- var analysesToContextUnits = {
- for (var unitAnalysis in _libraryUnits.values)
- unitAnalysis: LinterContextUnit(
- unitAnalysis.file.content,
- unitAnalysis.unit,
- unitAnalysis.errorReporter,
- ),
- };
- var allUnits = analysesToContextUnits.values.toList();
- for (var MapEntry(key: unitAnalysis, value: currentUnit)
- in analysesToContextUnits.entries) {
- _computeLints(unitAnalysis, allUnits, currentUnit);
- }
+ _computeLints();
}
_checkForInconsistentLanguageVersionOverride();
@@ -342,52 +332,77 @@
unitAnalysis.errorReporter,
unitAnalysis.errorListener.errors,
unitAnalysis.ignoreInfo,
- unitAnalysis.lineInfo,
+ unitAnalysis.unit.lineInfo,
_analysisOptions.unignorableNames,
).reportErrors();
}
}
- void _computeLints(
- UnitAnalysis unitAnalysis,
- List<LinterContextUnit> allUnits,
- LinterContextUnit currentUnit,
- ) {
- // Skip computing lints on macro generated augmentations.
- // See: https://github.com/dart-lang/sdk/issues/54875
- if (unitAnalysis.file.isMacroAugmentation) return;
+ void _computeLints() {
+ var definingUnit = _libraryElement.definingCompilationUnit;
+ var analysesToContextUnits = <UnitAnalysis, LinterContextUnit>{};
+ LinterContextUnit? definingContextUnit;
+ WorkspacePackage? workspacePackage;
+ for (var unitAnalysis in _libraryUnits.values) {
+ var linterContextUnit = LinterContextUnit(
+ unitAnalysis.file.content,
+ unitAnalysis.unit,
+ unitAnalysis.errorReporter,
+ );
+ analysesToContextUnits[unitAnalysis] = linterContextUnit;
+ if (unitAnalysis.unit.declaredElement == definingUnit) {
+ definingContextUnit = linterContextUnit;
+ workspacePackage = unitAnalysis.file.workspacePackage;
+ }
+ }
- var unit = currentUnit.unit;
- var errorReporter = currentUnit.errorReporter;
+ var allUnits = analysesToContextUnits.values.toList();
+ definingContextUnit ??= allUnits.first;
var enableTiming = _analysisOptions.enableTiming;
var nodeRegistry = NodeLintRegistry(enableTiming);
-
var context = LinterContextImpl(
allUnits,
- currentUnit,
+ definingContextUnit,
_typeProvider,
_typeSystem,
_inheritance,
- unitAnalysis.file.workspacePackage,
+ workspacePackage,
);
+
for (var linter in _analysisOptions.lintRules) {
- linter.reporter = errorReporter;
var timer = enableTiming ? lintRuleTimers.getTimer(linter) : null;
timer?.start();
linter.registerNodeProcessors(nodeRegistry, context);
timer?.stop();
}
- // Run lints that handle specific node types.
- unit.accept(
- LinterVisitor(
- nodeRegistry,
- LinterExceptionHandler(
- propagateExceptions: _analysisOptions.propagateLinterExceptions,
- ).logException,
- ),
- );
+ var logException = LinterExceptionHandler(
+ propagateExceptions: _analysisOptions.propagateLinterExceptions,
+ ).logException;
+
+ for (var MapEntry(key: unitAnalysis, value: currentUnit)
+ in analysesToContextUnits.entries) {
+ // Skip computing lints on macro generated augmentations.
+ // See: https://github.com/dart-lang/sdk/issues/54875
+ if (unitAnalysis.file.isMacroAugmentation) return;
+
+ var unit = currentUnit.unit;
+ var errorReporter = currentUnit.errorReporter;
+
+ for (var linter in _analysisOptions.lintRules) {
+ linter.reporter = errorReporter;
+ }
+
+ // Run lint rules that handle specific node types.
+ unit.accept(
+ LinterVisitor(nodeRegistry, logException),
+ );
+ }
+
+ // Now that all lint rules have visited the code in each of the compilation
+ // units, we can accept each lint rule's `afterLibrary` hook.
+ LinterVisitor(nodeRegistry, logException).afterLibrary();
}
void _computeVerifyErrors(UnitAnalysis unitAnalysis) {
@@ -567,10 +582,7 @@
var result = UnitAnalysis(
file: file,
errorListener: errorListener,
- errorReporter: ErrorReporter(errorListener, file.source),
unit: unit,
- lineInfo: unit.lineInfo,
- ignoreInfo: IgnoreInfo.forDart(unit, file.content),
);
_libraryUnits[file] = result;
return result;
diff --git a/pkg/analyzer/lib/src/dart/analysis/unit_analysis.dart b/pkg/analyzer/lib/src/dart/analysis/unit_analysis.dart
index 3470580..a7a0636 100644
--- a/pkg/analyzer/lib/src/dart/analysis/unit_analysis.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/unit_analysis.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/error/listener.dart';
-import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
@@ -15,7 +14,6 @@
final RecordingErrorListener errorListener;
final ErrorReporter errorReporter;
final CompilationUnitImpl unit;
- final LineInfo lineInfo;
final IgnoreInfo ignoreInfo;
late final CompilationUnitElementImpl element;
@@ -23,9 +21,7 @@
UnitAnalysis({
required this.file,
required this.errorListener,
- required this.errorReporter,
required this.unit,
- required this.lineInfo,
- required this.ignoreInfo,
- });
+ }) : errorReporter = ErrorReporter(errorListener, file.source),
+ ignoreInfo = IgnoreInfo.forDart(unit, file.content);
}
diff --git a/pkg/analyzer/lib/src/lint/linter.dart b/pkg/analyzer/lib/src/lint/linter.dart
index 0a4fb8f5..35c9c7b 100644
--- a/pkg/analyzer/lib/src/lint/linter.dart
+++ b/pkg/analyzer/lib/src/lint/linter.dart
@@ -4,7 +4,6 @@
import 'dart:io';
-import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
@@ -184,18 +183,27 @@
abstract class LinterContext {
List<LinterContextUnit> get allUnits;
- LinterContextUnit get currentUnit;
+ LinterContextUnit get definingUnit;
InheritanceManager3 get inheritanceManager;
+ /// Whether the [definingUnit] is in a package's top-level `lib` directory.
+ bool get isInLibDir;
+
+ LibraryElement? get libraryElement;
+
WorkspacePackage? get package;
TypeProvider get typeProvider;
TypeSystem get typeSystem;
- /// Returns whether the [feature] is enabled in the library being linted.
- bool isEnabled(Feature feature);
+ static bool _isInLibDir(String? path, WorkspacePackage? package) {
+ if (package == null) return false;
+ if (path == null) return false;
+ var libDir = p.join(package.root, 'lib');
+ return p.isWithin(libDir, path);
+ }
}
class LinterContextImpl implements LinterContext {
@@ -203,7 +211,7 @@
final List<LinterContextUnit> allUnits;
@override
- final LinterContextUnit currentUnit;
+ final LinterContextUnit definingUnit;
@override
final WorkspacePackage? package;
@@ -218,7 +226,7 @@
LinterContextImpl(
this.allUnits,
- this.currentUnit,
+ this.definingUnit,
this.typeProvider,
this.typeSystem,
this.inheritanceManager,
@@ -226,10 +234,12 @@
);
@override
- bool isEnabled(Feature feature) {
- var unitElement = currentUnit.unit.declaredElement!;
- return unitElement.library.featureSet.isEnabled(feature);
- }
+ bool get isInLibDir => LinterContext._isInLibDir(
+ definingUnit.unit.declaredElement?.source.fullName, package);
+
+ @override
+ LibraryElement get libraryElement =>
+ definingUnit.unit.declaredElement!.library;
}
class LinterContextParsedImpl implements LinterContext {
@@ -237,7 +247,7 @@
final List<LinterContextUnit> allUnits;
@override
- final LinterContextUnit currentUnit;
+ final LinterContextUnit definingUnit;
@override
final WorkspacePackage? package = null;
@@ -247,20 +257,26 @@
LinterContextParsedImpl(
this.allUnits,
- this.currentUnit,
+ this.definingUnit,
);
@override
+ bool get isInLibDir {
+ return LinterContext._isInLibDir(
+ definingUnit.unit.declaredElement?.source.fullName, package);
+ }
+
+ @override
+ LibraryElement get libraryElement =>
+ throw UnsupportedError('LinterContext with parsed results');
+
+ @override
TypeProvider get typeProvider =>
throw UnsupportedError('LinterContext with parsed results');
@override
TypeSystem get typeSystem =>
throw UnsupportedError('LinterContext with parsed results');
-
- @override
- bool isEnabled(Feature feature) =>
- throw UnsupportedError('LinterContext with parsed results');
}
class LinterContextUnit {
diff --git a/pkg/analyzer/lib/src/lint/linter_visitor.dart b/pkg/analyzer/lib/src/lint/linter_visitor.dart
index d8a65dd..afa4892 100644
--- a/pkg/analyzer/lib/src/lint/linter_visitor.dart
+++ b/pkg/analyzer/lib/src/lint/linter_visitor.dart
@@ -23,6 +23,10 @@
: exceptionHandler = exceptionHandler ??
LinterExceptionHandler(propagateExceptions: true).logException;
+ void afterLibrary() {
+ _runAfterLibrarySubscriptions(registry._afterLibrary);
+ }
+
@override
void visitAdjacentStrings(AdjacentStrings node) {
_runSubscriptions(node, registry._forAdjacentStrings);
@@ -1073,10 +1077,19 @@
node.visitChildren(this);
}
+ void _runAfterLibrarySubscriptions(
+ List<_AfterLibrarySubscription> subscriptions) {
+ for (var subscription in subscriptions) {
+ var timer = subscription.timer;
+ timer?.start();
+ subscription.callback();
+ timer?.stop();
+ }
+ }
+
void _runSubscriptions<T extends AstNode>(
T node, List<_Subscription<T>> subscriptions) {
- for (int i = 0; i < subscriptions.length; i++) {
- var subscription = subscriptions[i];
+ for (var subscription in subscriptions) {
var timer = subscription.timer;
timer?.start();
try {
@@ -1095,6 +1108,7 @@
/// The container to register visitors for separate AST node types.
class NodeLintRegistry {
final bool enableTiming;
+ final List<_AfterLibrarySubscription> _afterLibrary = [];
final List<_Subscription<AdjacentStrings>> _forAdjacentStrings = [];
final List<_Subscription<Annotation>> _forAnnotation = [];
final List<_Subscription<ArgumentList>> _forArgumentList = [];
@@ -2067,6 +2081,11 @@
_forYieldStatement.add(_Subscription(linter, visitor, _getTimer(linter)));
}
+ void afterLibrary(LintRule linter, void Function() callback) {
+ _afterLibrary
+ .add(_AfterLibrarySubscription(linter, callback, _getTimer(linter)));
+ }
+
/// Get the timer associated with the given [linter].
Stopwatch? _getTimer(LintRule linter) {
if (enableTiming) {
@@ -2077,6 +2096,14 @@
}
}
+class _AfterLibrarySubscription {
+ final LintRule linter;
+ final void Function() callback;
+ final Stopwatch? timer;
+
+ _AfterLibrarySubscription(this.linter, this.callback, this.timer);
+}
+
/// A single subscription for a node type, by the specified [linter].
class _Subscription<T> {
final LintRule linter;
diff --git a/pkg/linter/lib/src/rules/always_use_package_imports.dart b/pkg/linter/lib/src/rules/always_use_package_imports.dart
index b802b33..6b2886a 100644
--- a/pkg/linter/lib/src/rules/always_use_package_imports.dart
+++ b/pkg/linter/lib/src/rules/always_use_package_imports.dart
@@ -6,7 +6,6 @@
import 'package:analyzer/dart/ast/visitor.dart';
import '../analyzer.dart';
-import '../ast.dart';
const _desc = r'Avoid relative imports for files in `lib/`.';
@@ -68,10 +67,8 @@
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
// Relative paths from outside of the lib folder are handled by the
- // `avoid_relative_lib_imports` lint.
- if (!isInLibDir(context.currentUnit.unit, context.package)) {
- return;
- }
+ // `avoid_relative_lib_imports` lint rule.
+ if (!context.isInLibDir) return;
var visitor = _Visitor(this);
registry.addImportDirective(this, visitor);
diff --git a/pkg/linter/lib/src/rules/avoid_renaming_method_parameters.dart b/pkg/linter/lib/src/rules/avoid_renaming_method_parameters.dart
index b11677f..ef169d1 100644
--- a/pkg/linter/lib/src/rules/avoid_renaming_method_parameters.dart
+++ b/pkg/linter/lib/src/rules/avoid_renaming_method_parameters.dart
@@ -9,7 +9,6 @@
import 'package:analyzer/dart/element/element.dart';
import '../analyzer.dart';
-import '../ast.dart';
import '../extensions.dart';
const _desc = r"Don't rename parameters of overridden methods.";
@@ -66,9 +65,7 @@
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
- if (!isInLibDir(context.currentUnit.unit, context.package)) {
- return;
- }
+ if (!context.isInLibDir) return;
var visitor = _Visitor(this);
registry.addMethodDeclaration(this, visitor);
diff --git a/pkg/linter/lib/src/rules/avoid_web_libraries_in_flutter.dart b/pkg/linter/lib/src/rules/avoid_web_libraries_in_flutter.dart
index 65c94c2..ab745f5 100644
--- a/pkg/linter/lib/src/rules/avoid_web_libraries_in_flutter.dart
+++ b/pkg/linter/lib/src/rules/avoid_web_libraries_in_flutter.dart
@@ -98,7 +98,7 @@
if (hasFlutter == null) {
// Clear the previous cache.
clearCache();
- var pubspecFile = locatePubspecFile(context.currentUnit.unit);
+ var pubspecFile = locatePubspecFile(context.definingUnit.unit);
hasFlutter = hasFlutterDep(pubspecFile);
_rootHasFlutterCache[root] = hasFlutter;
}
diff --git a/pkg/linter/lib/src/rules/document_ignores.dart b/pkg/linter/lib/src/rules/document_ignores.dart
index 279c195..4450f1e 100644
--- a/pkg/linter/lib/src/rules/document_ignores.dart
+++ b/pkg/linter/lib/src/rules/document_ignores.dart
@@ -52,7 +52,7 @@
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
- var visitor = _Visitor(this, context);
+ var visitor = _Visitor(this);
registry.addCompilationUnit(this, visitor);
}
}
@@ -60,13 +60,11 @@
class _Visitor extends SimpleAstVisitor<void> {
final LintRule rule;
- final String content;
-
- _Visitor(this.rule, LinterContext context)
- : content = context.currentUnit.content;
+ _Visitor(this.rule);
@override
void visitCompilationUnit(CompilationUnit node) {
+ var content = node.declaredElement?.source.contents.data;
for (var comment in node.ignoreComments) {
var ignoredElements = comment.ignoredElements;
if (ignoredElements.isEmpty) {
@@ -84,7 +82,8 @@
// first line.
var previousLineOffset =
node.lineInfo.getOffsetOfLine(ignoreCommentLine - 2);
- if (_startsWithEndOfLineComment(previousLineOffset)) {
+ if (content != null &&
+ _startsWithEndOfLineComment(content, previousLineOffset)) {
// A preceding comment, which may be attached to a different token,
// documents this/these ignore(s). For example in:
//
@@ -102,7 +101,7 @@
/// Returns whether [content] at [offset_] represents starts with optional
/// whitespace and then an end-of-line comment (two slashes).
- bool _startsWithEndOfLineComment(int offset_) {
+ bool _startsWithEndOfLineComment(String content, int offset_) {
var offset = offset_;
var length = content.length;
while (offset < length) {
diff --git a/pkg/linter/lib/src/rules/eol_at_end_of_file.dart b/pkg/linter/lib/src/rules/eol_at_end_of_file.dart
index 36180ef..4eddee0 100644
--- a/pkg/linter/lib/src/rules/eol_at_end_of_file.dart
+++ b/pkg/linter/lib/src/rules/eol_at_end_of_file.dart
@@ -57,8 +57,11 @@
@override
void visitCompilationUnit(CompilationUnit node) {
- var content = context.currentUnit.content;
- if (content.isNotEmpty &&
+ var content = node.declaredElement?.source.contents.data;
+ if (content != null &&
+ content.isNotEmpty &&
+ // TODO(srawlins): Re-implement this check without iterating over
+ // various lists of strings.
(!content.endsWithNewline || content.endsWithMultipleNewlines)) {
rule.reportLintForOffset(content.trimRight().length, 1);
}
diff --git a/pkg/linter/lib/src/rules/lines_longer_than_80_chars.dart b/pkg/linter/lib/src/rules/lines_longer_than_80_chars.dart
index e429da3..196b45f 100644
--- a/pkg/linter/lib/src/rules/lines_longer_than_80_chars.dart
+++ b/pkg/linter/lib/src/rules/lines_longer_than_80_chars.dart
@@ -195,8 +195,10 @@
end = lineInfo.getOffsetOfLine(i + 1) - 1;
var length = end - start;
if (length > 80) {
- if (context.currentUnit.content[end] == _lf &&
- context.currentUnit.content[end - 1] == _cr) {
+ var content = node.declaredElement?.source.contents.data;
+ if (content != null &&
+ content[end] == _lf &&
+ content[end - 1] == _cr) {
end--;
}
}
diff --git a/pkg/linter/lib/src/rules/prefer_relative_imports.dart b/pkg/linter/lib/src/rules/prefer_relative_imports.dart
index d6e59bd..36b0663 100644
--- a/pkg/linter/lib/src/rules/prefer_relative_imports.dart
+++ b/pkg/linter/lib/src/rules/prefer_relative_imports.dart
@@ -8,7 +8,6 @@
import 'package:path/path.dart' as path;
import '../analyzer.dart';
-import '../ast.dart';
import 'implementation_imports.dart' show samePackage;
const _desc = r'Prefer relative imports for files in `lib/`.';
@@ -54,9 +53,7 @@
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
- if (!isInLibDir(context.currentUnit.unit, context.package)) {
- return;
- }
+ if (!context.isInLibDir) return;
var visitor = _Visitor(this, context);
registry.addImportDirective(this, visitor);
diff --git a/pkg/linter/lib/src/rules/pub/depend_on_referenced_packages.dart b/pkg/linter/lib/src/rules/pub/depend_on_referenced_packages.dart
index 07ae793..6c15bb4 100644
--- a/pkg/linter/lib/src/rules/pub/depend_on_referenced_packages.dart
+++ b/pkg/linter/lib/src/rules/pub/depend_on_referenced_packages.dart
@@ -79,7 +79,7 @@
for (var dep in dependencies)
if (dep.name?.text != null) dep.name!.text!,
if (devDependencies != null &&
- !isInPublicDir(context.currentUnit.unit, context.package))
+ !isInPublicDir(context.definingUnit.unit, context.package))
for (var dep in devDependencies)
if (dep.name?.text != null) dep.name!.text!,
];
diff --git a/pkg/linter/lib/src/rules/public_member_api_docs.dart b/pkg/linter/lib/src/rules/public_member_api_docs.dart
index d79a3e1..b0efd72 100644
--- a/pkg/linter/lib/src/rules/public_member_api_docs.dart
+++ b/pkg/linter/lib/src/rules/public_member_api_docs.dart
@@ -87,9 +87,7 @@
if (package != null && !package.canHavePublicApi) {
return;
}
- if (!isInLibDir(context.currentUnit.unit, context.package)) {
- return;
- }
+ if (!context.isInLibDir) return;
var visitor = _Visitor(this, context);
registry.addClassDeclaration(this, visitor);
diff --git a/pkg/linter/lib/src/rules/unnecessary_breaks.dart b/pkg/linter/lib/src/rules/unnecessary_breaks.dart
index 941b5f2..4eeee8f 100644
--- a/pkg/linter/lib/src/rules/unnecessary_breaks.dart
+++ b/pkg/linter/lib/src/rules/unnecessary_breaks.dart
@@ -78,7 +78,7 @@
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
- if (!context.isEnabled(Feature.patterns)) return;
+ if (!context.libraryElement!.featureSet.isEnabled(Feature.patterns)) return;
var visitor = _Visitor(this);
registry.addBreakStatement(this, visitor);
}
diff --git a/pkg/linter/lib/src/rules/unnecessary_lambdas.dart b/pkg/linter/lib/src/rules/unnecessary_lambdas.dart
index acae0cf..d901638 100644
--- a/pkg/linter/lib/src/rules/unnecessary_lambdas.dart
+++ b/pkg/linter/lib/src/rules/unnecessary_lambdas.dart
@@ -116,8 +116,8 @@
final TypeSystem typeSystem;
_Visitor(this.rule, LinterContext context)
- : constructorTearOffsEnabled =
- context.isEnabled(Feature.constructor_tearoffs),
+ : constructorTearOffsEnabled = context.libraryElement!.featureSet
+ .isEnabled(Feature.constructor_tearoffs),
typeSystem = context.typeSystem;
@override
diff --git a/pkg/linter/lib/src/rules/unnecessary_library_name.dart b/pkg/linter/lib/src/rules/unnecessary_library_name.dart
index fa4a1fe..b021de54 100644
--- a/pkg/linter/lib/src/rules/unnecessary_library_name.dart
+++ b/pkg/linter/lib/src/rules/unnecessary_library_name.dart
@@ -66,7 +66,7 @@
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
- if (!context.currentUnit.unit.featureSet
+ if (!context.libraryElement!.featureSet
.isEnabled(Feature.unnamedLibraries)) {
return;
}
diff --git a/pkg/linter/lib/src/rules/use_build_context_synchronously.dart b/pkg/linter/lib/src/rules/use_build_context_synchronously.dart
index 6d5f08b..1cf7e18 100644
--- a/pkg/linter/lib/src/rules/use_build_context_synchronously.dart
+++ b/pkg/linter/lib/src/rules/use_build_context_synchronously.dart
@@ -985,7 +985,7 @@
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
- var unit = context.currentUnit.unit;
+ var unit = context.definingUnit.unit;
if (!unit.inTestDir) {
var visitor = _Visitor(this);
registry.addMethodInvocation(this, visitor);
diff --git a/pkg/linter/lib/src/rules/use_enums.dart b/pkg/linter/lib/src/rules/use_enums.dart
index bb74b27..f23d5d7 100644
--- a/pkg/linter/lib/src/rules/use_enums.dart
+++ b/pkg/linter/lib/src/rules/use_enums.dart
@@ -78,7 +78,9 @@
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
- if (!context.isEnabled(Feature.enhanced_enums)) return;
+ if (!context.libraryElement!.featureSet.isEnabled(Feature.enhanced_enums)) {
+ return;
+ }
var visitor = _Visitor(this, context);
registry.addClassDeclaration(this, visitor);
diff --git a/pkg/linter/lib/src/rules/use_late_for_private_fields_and_variables.dart b/pkg/linter/lib/src/rules/use_late_for_private_fields_and_variables.dart
index 2359cff..09d2aaf 100644
--- a/pkg/linter/lib/src/rules/use_late_for_private_fields_and_variables.dart
+++ b/pkg/linter/lib/src/rules/use_late_for_private_fields_and_variables.dart
@@ -76,21 +76,40 @@
NodeLintRegistry registry, LinterContext context) {
var visitor = _Visitor(this, context);
registry.addCompilationUnit(this, visitor);
+ registry.afterLibrary(this, () => visitor.afterLibrary());
}
}
class _Visitor extends RecursiveAstVisitor<void> {
- static final lateables =
- <CompilationUnitElement, List<VariableDeclaration>>{};
+ final lateables = <CompilationUnitElement, List<VariableDeclaration>>{};
- static final nullableAccess = <CompilationUnitElement, Set<Element>>{};
+ final nullableAccess = <Element>{};
+
final LintRule rule;
-
final LinterContext context;
- CompilationUnitElement? currentUnit;
+ /// The "current" [CompilationUnitElement], which is set by
+ /// [visitCompilationUnit].
+ late CompilationUnitElement currentUnit;
+
_Visitor(this.rule, this.context);
+ void afterLibrary() {
+ for (var contextUnit in context.allUnits) {
+ var unit = contextUnit.unit.declaredElement;
+ var variables = lateables[unit];
+ if (variables == null) continue;
+ for (var variable in variables) {
+ if (!nullableAccess.contains(variable.declaredElement)) {
+ var contextUnit = context.allUnits
+ .firstWhereOrNull((u) => u.unit.declaredElement == unit);
+ if (contextUnit == null) continue;
+ contextUnit.errorReporter.atNode(variable, rule.lintCode);
+ }
+ }
+ }
+ }
+
@override
void visitAssignmentExpression(AssignmentExpression node) {
var element = node.writeElement?.canonicalElement;
@@ -104,7 +123,7 @@
context.typeSystem.isNonNullable(rhsType)) {
// This is OK; non-null access.
} else {
- nullableAccess[currentUnit]?.add(element);
+ nullableAccess.add(element);
}
}
super.visitAssignmentExpression(node);
@@ -125,30 +144,10 @@
@override
void visitCompilationUnit(CompilationUnit node) {
var declaredElement = node.declaredElement;
- if (declaredElement == null) {
- return;
- }
- lateables.putIfAbsent(declaredElement, () => []);
- nullableAccess.putIfAbsent(declaredElement, () => {});
+ if (declaredElement == null) return;
currentUnit = declaredElement;
super.visitCompilationUnit(node);
-
- var unitsInContext =
- context.allUnits.map((e) => e.unit.declaredElement).toSet();
- var libraryUnitsInContext =
- declaredElement.library.units.where(unitsInContext.contains).toSet();
- var areAllLibraryUnitsVisited =
- libraryUnitsInContext.every(lateables.containsKey);
- if (areAllLibraryUnitsVisited) {
- _checkAccess(libraryUnitsInContext);
-
- // Clean up.
- for (var unit in libraryUnitsInContext) {
- lateables.remove(unit);
- nullableAccess.remove(unit);
- }
- }
}
@override
@@ -209,34 +208,15 @@
super.visitTopLevelVariableDeclaration(node);
}
- void _checkAccess(Iterable<CompilationUnitElement> units) {
- var allNullableAccess =
- units.expand((unit) => nullableAccess[unit] ?? const {}).toSet();
- for (var unit in units) {
- for (var variable in lateables[unit] ?? const <VariableDeclaration>[]) {
- if (!allNullableAccess.contains(variable.declaredElement)) {
- var contextUnit = context.allUnits
- .firstWhereOrNull((u) => u.unit.declaredElement == unit);
- if (contextUnit == null) continue;
- contextUnit.errorReporter.atNode(variable, rule.lintCode);
- }
- }
- }
- }
-
void _visit(VariableDeclaration variable) {
- if (variable.isLate) {
- return;
- }
- if (variable.isSynthetic) {
- return;
- }
+ if (variable.isLate) return;
+ if (variable.isSynthetic) return;
var declaredElement = variable.declaredElement;
if (declaredElement == null ||
context.typeSystem.isNonNullable(declaredElement.type)) {
return;
}
- lateables[currentUnit]?.add(variable);
+ lateables.putIfAbsent(currentUnit, () => []).add(variable);
}
/// Checks whether [expression], which must be an [Identifier] or
@@ -244,20 +224,20 @@
void _visitIdentifierOrPropertyAccess(
Expression expression, Element? canonicalElement) {
assert(expression is Identifier || expression is PropertyAccess);
- if (canonicalElement != null) {
- var parent = expression.parent;
- if (parent is Expression) {
- parent = parent.unParenthesized;
- }
- if (expression is SimpleIdentifier && expression.inDeclarationContext()) {
- // This is OK.
- } else if (parent is PostfixExpression &&
- parent.operand == expression &&
- parent.operator.type == TokenType.BANG) {
- // This is OK; non-null access.
- } else {
- nullableAccess[currentUnit]?.add(canonicalElement);
- }
+ if (canonicalElement == null) return;
+
+ var parent = expression.parent;
+ if (parent is Expression) {
+ parent = parent.unParenthesized;
+ }
+ if (expression is SimpleIdentifier && expression.inDeclarationContext()) {
+ // This is OK.
+ } else if (parent is PostfixExpression &&
+ parent.operand == expression &&
+ parent.operator.type == TokenType.BANG) {
+ // This is OK; non-null access.
+ } else {
+ nullableAccess.add(canonicalElement);
}
}
}
diff --git a/pkg/linter/lib/src/rules/use_super_parameters.dart b/pkg/linter/lib/src/rules/use_super_parameters.dart
index 1b1e0c1..d2ccc0a 100644
--- a/pkg/linter/lib/src/rules/use_super_parameters.dart
+++ b/pkg/linter/lib/src/rules/use_super_parameters.dart
@@ -68,7 +68,10 @@
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
- if (!context.isEnabled(Feature.super_parameters)) return;
+ if (!context.libraryElement!.featureSet
+ .isEnabled(Feature.super_parameters)) {
+ return;
+ }
var visitor = _Visitor(this, context);
registry.addConstructorDeclaration(this, visitor);
diff --git a/pkg/linter/tool/checks/rules/no_solo_tests.dart b/pkg/linter/tool/checks/rules/no_solo_tests.dart
index a36b154..5328bdf 100644
--- a/pkg/linter/tool/checks/rules/no_solo_tests.dart
+++ b/pkg/linter/tool/checks/rules/no_solo_tests.dart
@@ -39,7 +39,7 @@
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
- if (context.currentUnit.unit.inTestDir) {
+ if (context.definingUnit.unit.inTestDir) {
var visitor = _Visitor(this);
registry.addMethodDeclaration(this, visitor);
}