CQ. Add WorkspacePackage.isInTestDirectory(File), use in linter.

Change-Id: I87948ade3aeb0599b1bab30feff7d1ee297611f7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/374846
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Phil Quitslund <pquitslund@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 1b88512..bf0b5cb 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
@@ -538,8 +538,14 @@
               errorListener,
               StringSource(parsedUnit.content, null),
             );
-            allUnits.add(LintRuleUnitContext(
-                parsedUnit.content, parsedUnit.unit, errorReporter));
+            allUnits.add(
+              LintRuleUnitContext(
+                file: parsedUnit.file,
+                content: parsedUnit.content,
+                errorReporter: errorReporter,
+                unit: parsedUnit.unit,
+              ),
+            );
           }
           for (var linterUnit in allUnits) {
             _computeParsedResultLint(linterUnit, allUnits);
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 22f9bed..96bfecb 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -353,9 +353,10 @@
     WorkspacePackage? workspacePackage;
     for (var unitAnalysis in _libraryUnits.values) {
       var linterContextUnit = LintRuleUnitContext(
-        unitAnalysis.file.content,
-        unitAnalysis.unit,
-        unitAnalysis.errorReporter,
+        file: unitAnalysis.file.resource,
+        content: unitAnalysis.file.content,
+        unit: unitAnalysis.unit,
+        errorReporter: unitAnalysis.errorReporter,
       );
       analysesToContextUnits[unitAnalysis] = linterContextUnit;
       if (unitAnalysis.unit.declaredElement == definingUnit) {
diff --git a/pkg/analyzer/lib/src/lint/linter.dart b/pkg/analyzer/lib/src/lint/linter.dart
index 44185a7..fcbd7cb 100644
--- a/pkg/analyzer/lib/src/lint/linter.dart
+++ b/pkg/analyzer/lib/src/lint/linter.dart
@@ -11,6 +11,7 @@
 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/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/ast/token.dart';
 import 'package:analyzer/src/dart/constant/compute.dart';
@@ -107,6 +108,9 @@
   /// package-implementation directory, 'lib/src'.
   bool get isInLibDir;
 
+  /// Whether the [definingUnit] is in a [package]'s "test" directory.
+  bool get isInTestDirectory;
+
   LibraryElement? get libraryElement;
 
   /// The package in which the library being analyzed lives, or `null` if it
@@ -143,6 +147,9 @@
       definingUnit.unit.declaredElement?.source.fullName, package);
 
   @override
+  bool get isInTestDirectory => false;
+
+  @override
   LibraryElement get libraryElement => throw UnsupportedError(
       'LinterContext with parsed results does not include a LibraryElement');
 
@@ -168,6 +175,7 @@
 
   @override
   final WorkspacePackage? package;
+
   @override
   final TypeProvider typeProvider;
 
@@ -191,6 +199,15 @@
       definingUnit.unit.declaredElement?.source.fullName, package);
 
   @override
+  bool get isInTestDirectory {
+    if (package case var package?) {
+      var file = definingUnit.file;
+      return package.isInTestDirectory(file);
+    }
+    return false;
+  }
+
+  @override
   LibraryElement get libraryElement =>
       definingUnit.unit.declaredElement!.library;
 }
@@ -355,13 +372,17 @@
 /// Provides access to information needed by lint rules that is not available
 /// from AST nodes or the element model.
 class LintRuleUnitContext {
+  final File file;
   final String content;
-
+  final ErrorReporter errorReporter;
   final CompilationUnit unit;
 
-  final ErrorReporter errorReporter;
-
-  LintRuleUnitContext(this.content, this.unit, this.errorReporter);
+  LintRuleUnitContext({
+    required this.file,
+    required this.content,
+    required this.errorReporter,
+    required this.unit,
+  });
 }
 
 /// An error listener that only records whether any constant related errors have
diff --git a/pkg/analyzer/lib/src/workspace/blaze.dart b/pkg/analyzer/lib/src/workspace/blaze.dart
index 2b9dae0..a774f5e 100644
--- a/pkg/analyzer/lib/src/workspace/blaze.dart
+++ b/pkg/analyzer/lib/src/workspace/blaze.dart
@@ -601,6 +601,13 @@
   }
 
   @override
+  bool isInTestDirectory(File file) {
+    var resourceProvider = workspace.provider;
+    var packageRoot = resourceProvider.getFolder(root);
+    return packageRoot.getChildAssumingFolder('test').contains(file.path);
+  }
+
+  @override
   // TODO(brianwilkerson): Implement this by looking in the BUILD file for 'deps'
   //  lists.
   Packages packagesAvailableTo(String libraryPath) => Packages.empty;
diff --git a/pkg/analyzer/lib/src/workspace/pub.dart b/pkg/analyzer/lib/src/workspace/pub.dart
index 5188cea..a71f157 100644
--- a/pkg/analyzer/lib/src/workspace/pub.dart
+++ b/pkg/analyzer/lib/src/workspace/pub.dart
@@ -458,7 +458,7 @@
     return false;
   }
 
-  /// Whether [file] is in the `test` directory of this package.
+  @override
   bool isInTestDirectory(File file) {
     var resourceProvider = workspace.provider;
     var packageRoot = resourceProvider.getFolder(root);
diff --git a/pkg/analyzer/lib/src/workspace/workspace.dart b/pkg/analyzer/lib/src/workspace/workspace.dart
index dce40d4..883c285 100644
--- a/pkg/analyzer/lib/src/workspace/workspace.dart
+++ b/pkg/analyzer/lib/src/workspace/workspace.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/file_system/file_system.dart';
 import 'package:analyzer/source/source.dart';
 import 'package:analyzer/src/context/packages.dart';
 import 'package:analyzer/src/generated/sdk.dart';
@@ -107,6 +108,11 @@
     }
   }
 
+  /// Whether [file] is in a "test" directory of this package.
+  bool isInTestDirectory(File file) {
+    return false;
+  }
+
   /// Return a map from the names of packages to the absolute and normalized
   /// path of the root of those packages for all of the packages that could
   /// validly be imported by the library with the given [libraryPath].
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 ffb1104..1e80289 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
@@ -34,8 +34,12 @@
       RecordingErrorListener(),
       StringSource(result.content, null),
     );
-    var contextUnit =
-        LintRuleUnitContext(result.content, result.unit, errorReporter);
+    var contextUnit = LintRuleUnitContext(
+      file: result.file,
+      content: result.content,
+      errorReporter: errorReporter,
+      unit: result.unit,
+    );
 
     var libraryElement = result.libraryElement;
     var analysisContext = libraryElement.session.analysisContext;
diff --git a/pkg/analyzer/test/src/workspace/blaze_test.dart b/pkg/analyzer/test/src/workspace/blaze_test.dart
index 6487927..aedf648 100644
--- a/pkg/analyzer/test/src/workspace/blaze_test.dart
+++ b/pkg/analyzer/test/src/workspace/blaze_test.dart
@@ -771,6 +771,24 @@
     expect(package?.workspace, equals(workspace));
   }
 
+  test_isInTestDirectory() {
+    _setUpPackage();
+
+    expect(
+      package!.isInTestDirectory(
+        getFile('/ws/some/code/lib/a.dart'),
+      ),
+      isFalse,
+    );
+
+    expect(
+      package!.isInTestDirectory(
+        getFile('/ws/some/code/test/a.dart'),
+      ),
+      isTrue,
+    );
+  }
+
   void test_packagesAvailableTo() {
     _setUpPackage();
     var path = convertPath('/ws/some/code/lib/code.dart');
diff --git a/pkg/linter/lib/src/rules/always_declare_return_types.dart b/pkg/linter/lib/src/rules/always_declare_return_types.dart
index fa549d10f..07236b6 100644
--- a/pkg/linter/lib/src/rules/always_declare_return_types.dart
+++ b/pkg/linter/lib/src/rules/always_declare_return_types.dart
@@ -107,7 +107,7 @@
     if (node.isSetter) return;
     if (node.name.type == TokenType.INDEX_EQ) return;
 
-    if (_isInTestDirectory()) {
+    if (context.isInTestDirectory) {
       if (node.name.lexeme.startsWith('test_') ||
           node.name.lexeme.startsWith('solo_test_')) {
         return;
@@ -120,16 +120,4 @@
       errorCode: AlwaysDeclareReturnTypes.methodCode,
     );
   }
-
-  bool _isInTestDirectory() {
-    if (context.package case PubPackage pubPackage) {
-      var packageRoot = pubPackage.pubspecFile.parent;
-      var filePath = context.libraryElement?.source.fullName;
-      if (filePath != null) {
-        var file = packageRoot.provider.getFile(filePath);
-        return pubPackage.isInTestDirectory(file);
-      }
-    }
-    return false;
-  }
 }