analyzer: Move RuleContext and RuleContextUnit to public API

Change-Id: I9cca5ee27e623bfa5b878bb0277f5e12e84a745b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/431982
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 0819700..e082c28 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -17,6 +17,7 @@
 import 'package:analysis_server_plugin/edit/fix/fix.dart';
 import 'package:analysis_server_plugin/src/correction/dart_change_workspace.dart';
 import 'package:analysis_server_plugin/src/correction/fix_generators.dart';
+import 'package:analyzer/analysis_rule/rule_context.dart';
 import 'package:analyzer/dart/analysis/analysis_context.dart';
 import 'package:analyzer/dart/analysis/analysis_options.dart';
 import 'package:analyzer/dart/analysis/results.dart';
diff --git a/pkg/analysis_server_plugin/lib/src/plugin_server.dart b/pkg/analysis_server_plugin/lib/src/plugin_server.dart
index 93f2f13..0a0eee2 100644
--- a/pkg/analysis_server_plugin/lib/src/plugin_server.dart
+++ b/pkg/analysis_server_plugin/lib/src/plugin_server.dart
@@ -13,6 +13,7 @@
 import 'package:analysis_server_plugin/src/correction/dart_change_workspace.dart';
 import 'package:analysis_server_plugin/src/correction/fix_processor.dart';
 import 'package:analysis_server_plugin/src/registry.dart';
+import 'package:analyzer/analysis_rule/rule_context.dart';
 import 'package:analyzer/dart/analysis/analysis_context.dart';
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/results.dart';
diff --git a/pkg/analyzer/api.txt b/pkg/analyzer/api.txt
index 6269a79..511e20f 100644
--- a/pkg/analyzer/api.txt
+++ b/pkg/analyzer/api.txt
@@ -1,3 +1,22 @@
+package:analyzer/analysis_rule/rule_context.dart:
+  RuleContext (class extends Object):
+    new (constructor: RuleContext Function())
+    allUnits (getter: List<RuleContextUnit>)
+    currentUnit (getter: RuleContextUnit?)
+    definingUnit (getter: RuleContextUnit)
+    isInLibDir (getter: bool)
+    isInTestDirectory (getter: bool)
+    libraryElement (getter: LibraryElement?)
+    package (getter: WorkspacePackage?)
+    typeProvider (getter: TypeProvider)
+    typeSystem (getter: TypeSystem)
+    isFeatureEnabled (method: bool Function(Feature))
+  RuleContextUnit (class extends Object):
+    new (constructor: RuleContextUnit Function({required String content, required ErrorReporter errorReporter, required File file, required CompilationUnit unit}))
+    content (getter: String)
+    errorReporter (getter: ErrorReporter)
+    file (getter: File)
+    unit (getter: CompilationUnit)
 package:analyzer/analysis_rule/rule_state.dart:
   dart2_12 (static getter: Version)
   dart3 (static getter: Version)
diff --git a/pkg/analyzer/lib/analysis_rule/rule_context.dart b/pkg/analyzer/lib/analysis_rule/rule_context.dart
new file mode 100644
index 0000000..edd940b
--- /dev/null
+++ b/pkg/analyzer/lib/analysis_rule/rule_context.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2025, 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/analysis/features.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type_provider.dart';
+import 'package:analyzer/dart/element/type_system.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/workspace/workspace.dart';
+
+/// Provides access to information needed by analysis rules that is not
+/// available from AST nodes or the element model.
+abstract class RuleContext {
+  /// The list of all compilation units that make up the library under analysis,
+  /// including the defining compilation unit, all parts, and all augmentations.
+  List<RuleContextUnit> get allUnits;
+
+  /// The compilation unit being analyzed.
+  ///
+  /// `null` when a unit is not currently being analyzed (for example when node
+  /// processors are being registered).
+  RuleContextUnit? get currentUnit;
+
+  /// The defining compilation unit of the library under analysis.
+  RuleContextUnit get definingUnit;
+
+  /// Whether the [definingUnit]'s location is in a package's top-level 'lib'
+  /// directory, including locations deeply nested, and locations in the
+  /// package-implementation directory, 'lib/src'.
+  bool get isInLibDir;
+
+  /// Whether the [definingUnit] is in a [package]'s "test" directory.
+  bool get isInTestDirectory;
+
+  /// The library element representing the library that contains the compilation
+  /// unit being analyzed.
+  LibraryElement? get libraryElement;
+
+  /// The package in which the library being analyzed lives, or `null` if it
+  /// does not live in a package.
+  WorkspacePackage? get package;
+
+  TypeProvider get typeProvider;
+
+  TypeSystem get typeSystem;
+
+  /// Whether the given [feature] is enabled in this rule context.
+  bool isFeatureEnabled(Feature feature);
+}
+
+/// Provides access to information needed by analysis rules that is not
+/// available from AST nodes or the element model.
+class RuleContextUnit {
+  final File file;
+  final String content;
+  final ErrorReporter errorReporter;
+  final CompilationUnit unit;
+
+  RuleContextUnit({
+    required this.file,
+    required this.content,
+    required this.errorReporter,
+    required this.unit,
+  });
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 51ae44a..23b0741 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -2,6 +2,7 @@
 // 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/analysis_rule/rule_context.dart';
 import 'package:analyzer/dart/analysis/declared_variables.dart';
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/diagnostic/diagnostic.dart';
diff --git a/pkg/analyzer/lib/src/lint/linter.dart b/pkg/analyzer/lib/src/lint/linter.dart
index df63b14..0111c4a 100644
--- a/pkg/analyzer/lib/src/lint/linter.dart
+++ b/pkg/analyzer/lib/src/lint/linter.dart
@@ -2,6 +2,7 @@
 // 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/analysis_rule/rule_context.dart';
 import 'package:analyzer/analysis_rule/rule_state.dart';
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/analysis/results.dart';
@@ -13,16 +14,22 @@
 import 'package:analyzer/diagnostic/diagnostic.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/error/listener.dart';
-import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/lint/linter_visitor.dart' show RuleVisitorRegistry;
 import 'package:analyzer/src/lint/pub.dart';
 import 'package:analyzer/workspace/workspace.dart';
-import 'package:meta/meta.dart';
 
 export 'package:analyzer/analysis_rule/rule_state.dart'
     show dart2_12, dart3, dart3_3, RuleState;
 export 'package:analyzer/src/lint/linter_visitor.dart' show NodeLintRegistry;
 
+/// Returns whether [filePath] is in the top-level `lib` directory in [package].
+bool _isInLibDir(String? filePath, WorkspacePackage? package) {
+  if (package == null) return false;
+  if (filePath == null) return false;
+  var libDir = package.root.getChildAssumingFolder('lib');
+  return libDir.contains(filePath);
+}
+
 /// A soon-to-be deprecated alias for [RuleContext].
 typedef LinterContext = RuleContext;
 
@@ -290,70 +297,6 @@
   );
 }
 
-/// Provides access to information needed by analysis rules that is not
-/// available from AST nodes or the element model.
-abstract class RuleContext {
-  /// The list of all compilation units that make up the library under analysis,
-  /// including the defining compilation unit, all parts, and all augmentations.
-  List<RuleContextUnit> get allUnits;
-
-  /// The compilation unit being analyzed.
-  ///
-  /// `null` when a unit is not currently being analyzed (for example when node
-  /// processors are being registered).
-  RuleContextUnit? get currentUnit;
-
-  /// The defining compilation unit of the library under analysis.
-  RuleContextUnit get definingUnit;
-
-  /// Whether the [definingUnit]'s location is in a package's top-level 'lib'
-  /// directory, including locations deeply nested, and locations in the
-  /// package-implementation directory, 'lib/src'.
-  bool get isInLibDir;
-
-  /// Whether the [definingUnit] is in a [package]'s "test" directory.
-  bool get isInTestDirectory;
-
-  /// The library element representing the library that contains the compilation
-  /// unit being analyzed.
-  @experimental
-  LibraryElement? get libraryElement2;
-
-  /// The package in which the library being analyzed lives, or `null` if it
-  /// does not live in a package.
-  WorkspacePackage? get package;
-
-  TypeProvider get typeProvider;
-
-  TypeSystem get typeSystem;
-
-  /// Whether the given [feature] is enabled in this rule context.
-  bool isFeatureEnabled(Feature feature);
-
-  static bool _isInLibDir(String? filePath, WorkspacePackage? package) {
-    if (package == null) return false;
-    if (filePath == null) return false;
-    var libDir = package.root.getChildAssumingFolder('lib');
-    return libDir.contains(filePath);
-  }
-}
-
-/// Provides access to information needed by analysis rules that is not
-/// available from AST nodes or the element model.
-class RuleContextUnit {
-  final File _file;
-  final String content;
-  final ErrorReporter errorReporter;
-  final CompilationUnit unit;
-
-  RuleContextUnit({
-    required File file,
-    required this.content,
-    required this.errorReporter,
-    required this.unit,
-  }) : _file = file;
-}
-
 /// A [RuleContext] for a library, parsed into [ParsedUnitResult]s.
 ///
 /// This is available for analysis rules that can operate on parsed,
@@ -371,17 +314,14 @@
   RuleContextWithParsedResults(this.allUnits, this.definingUnit);
 
   @override
-  bool get isInLibDir => RuleContext._isInLibDir(
-    definingUnit.unit.declaredFragment?.source.fullName,
-    package,
-  );
+  bool get isInLibDir =>
+      _isInLibDir(definingUnit.unit.declaredFragment?.source.fullName, package);
 
   @override
   bool get isInTestDirectory => false;
 
-  @experimental
   @override
-  LibraryElement get libraryElement2 =>
+  LibraryElement get libraryElement =>
       throw UnsupportedError(
         'RuleContext with parsed results does not include a LibraryElement',
       );
@@ -437,26 +377,23 @@
   );
 
   @override
-  bool get isInLibDir => RuleContext._isInLibDir(
-    definingUnit.unit.declaredFragment?.source.fullName,
-    package,
-  );
+  bool get isInLibDir =>
+      _isInLibDir(definingUnit.unit.declaredFragment?.source.fullName, package);
 
   @override
   bool get isInTestDirectory {
     if (package case var package?) {
-      var file = definingUnit._file;
+      var file = definingUnit.file;
       return package.isInTestDirectory(file);
     }
     return false;
   }
 
-  @experimental
   @override
-  LibraryElement get libraryElement2 =>
+  LibraryElement get libraryElement =>
       definingUnit.unit.declaredFragment!.element;
 
   @override
   bool isFeatureEnabled(Feature feature) =>
-      libraryElement2.featureSet.isEnabled(feature);
+      libraryElement.featureSet.isEnabled(feature);
 }
diff --git a/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart b/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart
index e11266e..13878ec 100644
--- a/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart
+++ b/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart
@@ -2,6 +2,7 @@
 // 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/analysis_rule/rule_context.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/lint/constants.dart';
diff --git a/pkg/linter/lib/src/rules/implementation_imports.dart b/pkg/linter/lib/src/rules/implementation_imports.dart
index 01ce403..d215a1f 100644
--- a/pkg/linter/lib/src/rules/implementation_imports.dart
+++ b/pkg/linter/lib/src/rules/implementation_imports.dart
@@ -24,7 +24,7 @@
     NodeLintRegistry registry,
     LinterContext context,
   ) {
-    var libraryUri = context.libraryElement2?.uri;
+    var libraryUri = context.libraryElement?.uri;
     if (libraryUri == null) return;
 
     // If the source URI is not a `package` URI, bail out.
diff --git a/pkg/linter/lib/src/rules/no_wildcard_variable_uses.dart b/pkg/linter/lib/src/rules/no_wildcard_variable_uses.dart
index df6d65a..b5fb15c 100644
--- a/pkg/linter/lib/src/rules/no_wildcard_variable_uses.dart
+++ b/pkg/linter/lib/src/rules/no_wildcard_variable_uses.dart
@@ -25,7 +25,7 @@
     NodeLintRegistry registry,
     LinterContext context,
   ) {
-    if (context.libraryElement2.hasWildcardVariablesFeatureEnabled) return;
+    if (context.libraryElement.hasWildcardVariablesFeatureEnabled) return;
 
     var visitor = _Visitor(this);
     registry.addSimpleIdentifier(this, visitor);
diff --git a/pkg/linter/lib/src/rules/prefer_relative_imports.dart b/pkg/linter/lib/src/rules/prefer_relative_imports.dart
index 8bbc788..f8aef87 100644
--- a/pkg/linter/lib/src/rules/prefer_relative_imports.dart
+++ b/pkg/linter/lib/src/rules/prefer_relative_imports.dart
@@ -32,7 +32,7 @@
   ) {
     if (!context.isInLibDir) return;
 
-    var sourceUri = context.libraryElement2?.uri;
+    var sourceUri = context.libraryElement?.uri;
     if (sourceUri == null) return;
 
     var visitor = _Visitor(this, sourceUri, context);
diff --git a/pkg/linter/lib/src/rules/require_trailing_commas.dart b/pkg/linter/lib/src/rules/require_trailing_commas.dart
index a0983bb..b81204b 100644
--- a/pkg/linter/lib/src/rules/require_trailing_commas.dart
+++ b/pkg/linter/lib/src/rules/require_trailing_commas.dart
@@ -29,7 +29,7 @@
     LinterContext context,
   ) {
     // Don't report if tall-style is enforced by the formatter.
-    var languageVersion = context.libraryElement2?.languageVersion.effective;
+    var languageVersion = context.libraryElement?.languageVersion.effective;
     if (languageVersion != null && languageVersion >= language37) return;
 
     var visitor = _Visitor(this);