[flutter_tools] migrate project-validate to analyze --suggestions (#106149)

diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index 205ca86..9ea7631 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -51,6 +51,7 @@
 import 'src/isolated/mustache_template.dart';
 import 'src/isolated/resident_web_runner.dart';
 import 'src/pre_run_validator.dart';
+import 'src/project_validator.dart';
 import 'src/resident_runner.dart';
 import 'src/runner/flutter_command.dart';
 import 'src/web/web_runner.dart';
@@ -141,6 +142,7 @@
     logger: globals.logger,
     terminal: globals.terminal,
     artifacts: globals.artifacts!,
+    allProjectValidators: <ProjectValidator>[],
   ),
   AssembleCommand(verboseHelp: verboseHelp, buildSystem: globals.buildSystem),
   AttachCommand(verboseHelp: verboseHelp),
diff --git a/packages/flutter_tools/lib/src/commands/analyze.dart b/packages/flutter_tools/lib/src/commands/analyze.dart
index 928c5b2..62c109d 100644
--- a/packages/flutter_tools/lib/src/commands/analyze.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze.dart
@@ -5,13 +5,17 @@
 import 'package:process/process.dart';
 
 import '../artifacts.dart';
+import '../base/common.dart';
 import '../base/file_system.dart';
 import '../base/logger.dart';
 import '../base/platform.dart';
 import '../base/terminal.dart';
+import '../project_validator.dart';
 import '../runner/flutter_command.dart';
+import 'analyze_base.dart';
 import 'analyze_continuously.dart';
 import 'analyze_once.dart';
+import 'validate_project.dart';
 
 class AnalyzeCommand extends FlutterCommand {
   AnalyzeCommand({
@@ -23,11 +27,13 @@
     required Logger logger,
     required ProcessManager processManager,
     required Artifacts artifacts,
+    required List<ProjectValidator> allProjectValidators,
   }) : _artifacts = artifacts,
        _fileSystem = fileSystem,
        _processManager = processManager,
        _logger = logger,
        _terminal = terminal,
+       _allProjectValidators = allProjectValidators,
        _platform = platform {
     argParser.addFlag('flutter-repo',
         negatable: false,
@@ -56,6 +62,9 @@
         help: 'The path to write the request and response protocol. This is '
               'only intended to be used for debugging the tooling.',
         hide: !verboseHelp);
+    argParser.addFlag('suggestions',
+        help: 'Show suggestions about the current flutter project.'
+    );
 
     // Hidden option to enable a benchmarking mode.
     argParser.addFlag('benchmark',
@@ -92,6 +101,7 @@
   final Terminal _terminal;
   final ProcessManager _processManager;
   final Platform _platform;
+  final List<ProjectValidator> _allProjectValidators;
 
   @override
   String get name => 'analyze';
@@ -119,7 +129,29 @@
 
   @override
   Future<FlutterCommandResult> runCommand() async {
-    if (boolArgDeprecated('watch')) {
+    final bool? suggestionFlag = boolArg('suggestions');
+    if (suggestionFlag != null && suggestionFlag == true) {
+      final String directoryPath;
+      final bool? watchFlag = boolArg('watch');
+      if (watchFlag != null && watchFlag) {
+        throwToolExit('flag --watch is not compatible with --suggestions');
+      }
+      if (workingDirectory == null) {
+        final Set<String> items = findDirectories(argResults!, _fileSystem);
+        if (items.isEmpty || items.length > 1) {
+          throwToolExit('The suggestions flags needs one directory path');
+        }
+        directoryPath = items.first;
+      } else {
+        directoryPath = workingDirectory!.path;
+      }
+      return ValidateProject(
+        fileSystem: _fileSystem,
+        logger: _logger,
+        allProjectValidators: _allProjectValidators,
+        userPath: directoryPath,
+      ).run();
+    } else if (boolArgDeprecated('watch')) {
       await AnalyzeContinuously(
         argResults!,
         runner!.getRepoRoots(),
diff --git a/packages/flutter_tools/lib/src/commands/analyze_base.dart b/packages/flutter_tools/lib/src/commands/analyze_base.dart
index dbb55c9..54a9ff6 100644
--- a/packages/flutter_tools/lib/src/commands/analyze_base.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze_base.dart
@@ -286,3 +286,20 @@
     return result;
   }
 }
+
+/// Find directories or files from argResults.rest.
+Set<String> findDirectories(ArgResults argResults, FileSystem fileSystem) {
+  final Set<String> items = Set<String>.of(argResults.rest
+      .map<String>((String path) => fileSystem.path.canonicalize(path)));
+  if (items.isNotEmpty) {
+    for (final String item in items) {
+      final FileSystemEntityType type = fileSystem.typeSync(item);
+
+      if (type == FileSystemEntityType.notFound) {
+        throwToolExit("You provided the path '$item', however it does not exist on disk");
+      }
+    }
+  }
+
+  return items;
+}
diff --git a/packages/flutter_tools/lib/src/commands/analyze_once.dart b/packages/flutter_tools/lib/src/commands/analyze_once.dart
index 5af2000..35808c8 100644
--- a/packages/flutter_tools/lib/src/commands/analyze_once.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze_once.dart
@@ -35,19 +35,7 @@
   Future<void> analyze() async {
     final String currentDirectory =
         (workingDirectory ?? fileSystem.currentDirectory).path;
-
-    // find directories or files from argResults.rest
-    final Set<String> items = Set<String>.of(argResults.rest
-        .map<String>((String path) => fileSystem.path.canonicalize(path)));
-    if (items.isNotEmpty) {
-      for (final String item in items) {
-        final FileSystemEntityType type = fileSystem.typeSync(item);
-
-        if (type == FileSystemEntityType.notFound) {
-          throwToolExit("'$item' does not exist");
-        }
-      }
-    }
+    final Set<String> items = findDirectories(argResults, fileSystem);
 
     if (isFlutterRepo) {
       // check for conflicting dependencies
diff --git a/packages/flutter_tools/lib/src/commands/validate_project.dart b/packages/flutter_tools/lib/src/commands/validate_project.dart
index 0f7569f..9117f7c 100644
--- a/packages/flutter_tools/lib/src/commands/validate_project.dart
+++ b/packages/flutter_tools/lib/src/commands/validate_project.dart
@@ -9,31 +9,22 @@
 import '../project_validator_result.dart';
 import '../runner/flutter_command.dart';
 
-class ValidateProjectCommand extends FlutterCommand {
-  ValidateProjectCommand({
+class ValidateProject {
+  ValidateProject({
     required this.fileSystem,
     required this.logger,
     required this.allProjectValidators,
+    required this.userPath,
     this.verbose = false
   });
 
   final FileSystem fileSystem;
   final Logger logger;
   final bool verbose;
+  final String userPath;
   final List<ProjectValidator> allProjectValidators;
 
-  @override
-  final String name = 'validate-project';
-
-  @override
-  final String description = 'Show information about the current project.';
-
-  @override
-  final String category = FlutterCommandCategory.project;
-
-  @override
-  Future<FlutterCommandResult> runCommand() async {
-    final String userPath = getUserPath();
+  Future<FlutterCommandResult> run() async {
     final Directory workingDirectory = userPath.isEmpty ? fileSystem.currentDirectory : fileSystem.directory(userPath);
 
     final FlutterProject project =  FlutterProject.fromDirectory(workingDirectory);
@@ -94,8 +85,4 @@
 
     return '$icon $result';
   }
-
-  String getUserPath(){
-    return (argResults == null || argResults!.rest.isEmpty) ? '' : argResults!.rest[0];
-  }
 }
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart
index 87cd6dc..0205a9a 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart
@@ -19,6 +19,7 @@
 import 'package:flutter_tools/src/dart/analysis.dart';
 import 'package:flutter_tools/src/dart/pub.dart';
 import 'package:flutter_tools/src/globals.dart' as globals;
+import 'package:flutter_tools/src/project_validator.dart';
 import 'package:process/process.dart';
 
 import '../../src/common.dart';
@@ -199,6 +200,7 @@
       platform: FakePlatform(),
       fileSystem: MemoryFileSystem.test(),
       processManager: processManager,
+      allProjectValidators: <ProjectValidator>[],
     );
 
     final TestFlutterCommandRunner commandRunner = TestFlutterCommandRunner();
@@ -246,6 +248,7 @@
       platform: FakePlatform(),
       fileSystem: fileSystem,
       processManager: processManager,
+      allProjectValidators: <ProjectValidator>[],
     );
 
     await FakeAsync().run((FakeAsync time) async {
@@ -298,6 +301,7 @@
       platform: FakePlatform(),
       fileSystem: MemoryFileSystem.test(),
       processManager: processManager,
+      allProjectValidators: <ProjectValidator>[],
     );
 
     await FakeAsync().run((FakeAsync time) async {
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/project_validator_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/analyze_suggestion_test.dart
similarity index 66%
rename from packages/flutter_tools/test/commands.shard/hermetic/project_validator_test.dart
rename to packages/flutter_tools/test/commands.shard/hermetic/analyze_suggestion_test.dart
index bc387e5..1411060 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/project_validator_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/analyze_suggestion_test.dart
@@ -6,13 +6,17 @@
 
 import 'package:args/command_runner.dart';
 import 'package:file/memory.dart';
+import 'package:flutter_tools/src/artifacts.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/logger.dart';
-import 'package:flutter_tools/src/commands/validate_project.dart';
+import 'package:flutter_tools/src/base/platform.dart';
+import 'package:flutter_tools/src/base/terminal.dart';
+import 'package:flutter_tools/src/commands/analyze.dart';
 import 'package:flutter_tools/src/project.dart';
 import 'package:flutter_tools/src/project_validator.dart';
 import 'package:flutter_tools/src/project_validator_result.dart';
 
+import '../../src/common.dart';
 import '../../src/context.dart';
 import '../../src/test_flutter_command_runner.dart';
 
@@ -70,26 +74,36 @@
 
 void main() {
   FileSystem fileSystem;
+  Terminal terminal;
+  ProcessManager processManager;
+  Platform platform;
 
-  group('analyze project command', () {
+  group('analyze --suggestions command', () {
 
     setUp(() {
       fileSystem = MemoryFileSystem.test();
+      terminal = Terminal.test();
+      processManager = FakeProcessManager.empty();
+      platform = FakePlatform();
     });
 
     testUsingContext('success, error and warning', () async {
       final BufferLogger loggerTest = BufferLogger.test();
-      final ValidateProjectCommand command = ValidateProjectCommand(
-          fileSystem: fileSystem,
-          logger: loggerTest,
-          allProjectValidators: <ProjectValidator>[
-            ProjectValidatorDummy(),
-            ProjectValidatorSecondDummy()
-          ]
+      final AnalyzeCommand command = AnalyzeCommand(
+        artifacts: Artifacts.test(),
+        fileSystem: fileSystem,
+        logger: loggerTest,
+        platform: platform,
+        terminal: terminal,
+        processManager: processManager,
+        allProjectValidators: <ProjectValidator>[
+          ProjectValidatorDummy(),
+          ProjectValidatorSecondDummy()
+        ]
       );
       final CommandRunner<void> runner = createTestCommandRunner(command);
 
-      await runner.run(<String>['validate-project']);
+      await runner.run(<String>['analyze', '--suggestions', './']);
 
       const String expected = '\n'
           '┌──────────────────────────────────────────┐\n'
@@ -107,18 +121,41 @@
 
     testUsingContext('crash', () async {
       final BufferLogger loggerTest = BufferLogger.test();
-      final ValidateProjectCommand command = ValidateProjectCommand(
+      final AnalyzeCommand command = AnalyzeCommand(
+          artifacts: Artifacts.test(),
           fileSystem: fileSystem,
           logger: loggerTest,
-          allProjectValidators: <ProjectValidator>[ProjectValidatorCrash()]
+          platform: platform,
+          terminal: terminal,
+          processManager: processManager,
+          allProjectValidators: <ProjectValidator>[
+            ProjectValidatorCrash(),
+          ]
       );
       final CommandRunner<void> runner = createTestCommandRunner(command);
 
-      await runner.run(<String>['validate-project']);
+      await runner.run(<String>['analyze', '--suggestions', './']);
 
       const String expected = '[☠] Exception: my exception: #0      ProjectValidatorCrash.start';
 
       expect(loggerTest.statusText, contains(expected));
     });
+
+    testUsingContext('--watch and --suggestions not compatible together', () async {
+      final BufferLogger loggerTest = BufferLogger.test();
+      final AnalyzeCommand command = AnalyzeCommand(
+        artifacts: Artifacts.test(),
+        fileSystem: fileSystem,
+        logger: loggerTest,
+        platform: platform,
+        terminal: terminal,
+        processManager: processManager,
+        allProjectValidators: <ProjectValidator>[]
+      );
+      final CommandRunner<void> runner = createTestCommandRunner(command);
+      Future<void> result () => runner.run(<String>['analyze', '--suggestions', '--watch']);
+
+      expect(result, throwsToolExit(message: 'flag --watch is not compatible with --suggestions'));
+    });
   });
 }
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/analyze_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/analyze_test.dart
index 88a9aa3..b842491 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/analyze_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/analyze_test.dart
@@ -15,6 +15,7 @@
 import 'package:flutter_tools/src/commands/analyze.dart';
 import 'package:flutter_tools/src/commands/analyze_base.dart';
 import 'package:flutter_tools/src/dart/analysis.dart';
+import 'package:flutter_tools/src/project_validator.dart';
 
 import '../../src/common.dart';
 import '../../src/context.dart';
@@ -70,6 +71,7 @@
         platform: platform,
         processManager: processManager,
         terminal: terminal,
+        allProjectValidators: <ProjectValidator>[],
       );
       runner = createTestCommandRunner(command);
 
diff --git a/packages/flutter_tools/test/integration.shard/analyze_once_test.dart b/packages/flutter_tools/test/integration.shard/analyze_once_test.dart
index 7fa3a66..3691058 100644
--- a/packages/flutter_tools/test/integration.shard/analyze_once_test.dart
+++ b/packages/flutter_tools/test/integration.shard/analyze_once_test.dart
@@ -160,7 +160,7 @@
   testWithoutContext('file not found', () async {
     await runCommand(
         arguments: <String>['analyze', '--no-pub', 'not_found.abc'],
-        exitMessageContains: "not_found.abc' does not exist",
+        exitMessageContains: "not_found.abc', however it does not exist on disk",
         exitCode: 1
     );
   });
diff --git a/packages/flutter_tools/test/integration.shard/project_validator_integration_test.dart b/packages/flutter_tools/test/integration.shard/analyze_suggestions_integration_test.dart
similarity index 79%
rename from packages/flutter_tools/test/integration.shard/project_validator_integration_test.dart
rename to packages/flutter_tools/test/integration.shard/analyze_suggestions_integration_test.dart
index 33a3727..52cbb17 100644
--- a/packages/flutter_tools/test/integration.shard/project_validator_integration_test.dart
+++ b/packages/flutter_tools/test/integration.shard/analyze_suggestions_integration_test.dart
@@ -5,7 +5,7 @@
 import 'package:args/command_runner.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/logger.dart';
-import 'package:flutter_tools/src/commands/validate_project.dart';
+import 'package:flutter_tools/src/commands/analyze.dart';
 import 'package:flutter_tools/src/globals.dart' as globals;
 import 'package:flutter_tools/src/project_validator.dart';
 
@@ -15,7 +15,7 @@
 void main() {
   late FileSystem fileSystem;
 
-  group('analyze project command', () {
+  group('analyze --suggestions command integration', () {
 
     setUp(() {
       fileSystem = globals.localFileSystem;
@@ -23,14 +23,24 @@
 
     testUsingContext('General Info Project Validator', () async {
       final BufferLogger loggerTest = BufferLogger.test();
-      final ValidateProjectCommand command = ValidateProjectCommand(
+      final AnalyzeCommand command = AnalyzeCommand(
+          artifacts: globals.artifacts!,
           fileSystem: fileSystem,
           logger: loggerTest,
-          allProjectValidators: <ProjectValidator>[GeneralInfoProjectValidator()]
+          platform: globals.platform,
+          terminal: globals.terminal,
+          processManager: globals.processManager,
+          allProjectValidators: <ProjectValidator>[GeneralInfoProjectValidator()],
       );
       final CommandRunner<void> runner = createTestCommandRunner(command);
 
-      await runner.run(<String>['validate-project', '../../dev/integration_tests/flutter_gallery']);
+      await runner.run(<String>[
+        'analyze',
+        '--no-pub',
+        '--no-current-package',
+        '--suggestions',
+        '../../dev/integration_tests/flutter_gallery',
+      ]);
 
       const String expected = '\n'
       '┌────────────────────────────────────────────────────────────────────────────┐\n'