Make gen-l10n error handling independent of logger state (#119644)

* init

* lint

* lint again
diff --git a/packages/flutter_tools/lib/src/localizations/gen_l10n.dart b/packages/flutter_tools/lib/src/localizations/gen_l10n.dart
index 30bb27f..5a31e09 100644
--- a/packages/flutter_tools/lib/src/localizations/gen_l10n.dart
+++ b/packages/flutter_tools/lib/src/localizations/gen_l10n.dart
@@ -579,6 +579,10 @@
   // Whether we want to use escaping for ICU messages.
   bool useEscaping = false;
 
+  /// Whether any errors were caught. This is set after encountering any errors
+  /// from calling [_generateMethod].
+  bool hadErrors = false;
+
   /// The list of all arb path strings in [inputDirectory].
   List<String> get arbPathStrings {
     return _allBundles.bundles.map((AppResourceBundle bundle) => bundle.file.path).toList();
@@ -1093,151 +1097,152 @@
 
   String _generateMethod(Message message, LocaleInfo locale) {
     try {
-    // Determine if we must import intl for date or number formatting.
-    if (message.placeholdersRequireFormatting) {
-      requiresIntlImport = true;
-    }
+      // Determine if we must import intl for date or number formatting.
+      if (message.placeholdersRequireFormatting) {
+        requiresIntlImport = true;
+      }
 
-    final String translationForMessage = message.messages[locale]!;
-    final Node node = message.parsedMessages[locale]!;
-    // If parse tree is only a string, then return a getter method.
-    if (node.children.every((Node child) => child.type == ST.string)) {
-      // Use the parsed translation to handle escaping with the same behavior.
-      return getterTemplate
-        .replaceAll('@(name)', message.resourceId)
-        .replaceAll('@(message)', "'${generateString(node.children.map((Node child) => child.value!).join())}'");
-    }
+      final String translationForMessage = message.messages[locale]!;
+      final Node node = message.parsedMessages[locale]!;
+      // If parse tree is only a string, then return a getter method.
+      if (node.children.every((Node child) => child.type == ST.string)) {
+        // Use the parsed translation to handle escaping with the same behavior.
+        return getterTemplate
+          .replaceAll('@(name)', message.resourceId)
+          .replaceAll('@(message)', "'${generateString(node.children.map((Node child) => child.value!).join())}'");
+      }
 
-    final List<String> tempVariables = <String>[];
-    // Get a unique temporary variable name.
-    int variableCount = 0;
-    String getTempVariableName() {
-      return '_temp${variableCount++}';
-    }
+      final List<String> tempVariables = <String>[];
+      // Get a unique temporary variable name.
+      int variableCount = 0;
+      String getTempVariableName() {
+        return '_temp${variableCount++}';
+      }
 
-    // Do a DFS post order traversal through placeholderExpr, pluralExpr, and selectExpr nodes.
-    // When traversing through a placeholderExpr node, return "$placeholderName".
-    // When traversing through a pluralExpr node, return "$tempVarN" and add variable declaration in "tempVariables".
-    // When traversing through a selectExpr node, return "$tempVarN" and add variable declaration in "tempVariables".
-    // When traversing through a message node, return concatenation of all of "generateVariables(child)" for each child.
-    String generateVariables(Node node, { bool isRoot = false }) {
-      switch (node.type) {
-        case ST.message:
-          final List<String> expressions = node.children.map<String>((Node node) {
-            if (node.type == ST.string) {
-              return node.value!;
+      // Do a DFS post order traversal through placeholderExpr, pluralExpr, and selectExpr nodes.
+      // When traversing through a placeholderExpr node, return "$placeholderName".
+      // When traversing through a pluralExpr node, return "$tempVarN" and add variable declaration in "tempVariables".
+      // When traversing through a selectExpr node, return "$tempVarN" and add variable declaration in "tempVariables".
+      // When traversing through a message node, return concatenation of all of "generateVariables(child)" for each child.
+      String generateVariables(Node node, { bool isRoot = false }) {
+        switch (node.type) {
+          case ST.message:
+            final List<String> expressions = node.children.map<String>((Node node) {
+              if (node.type == ST.string) {
+                return node.value!;
+              }
+              return generateVariables(node);
+            }).toList();
+            return generateReturnExpr(expressions);
+
+          case ST.placeholderExpr:
+            assert(node.children[1].type == ST.identifier);
+            final String identifier = node.children[1].value!;
+            final Placeholder placeholder = message.placeholders[identifier]!;
+            if (placeholder.requiresFormatting) {
+              return '\$${node.children[1].value}String';
             }
-            return generateVariables(node);
-          }).toList();
-          return generateReturnExpr(expressions);
+            return '\$${node.children[1].value}';
 
-        case ST.placeholderExpr:
-          assert(node.children[1].type == ST.identifier);
-          final String identifier = node.children[1].value!;
-          final Placeholder placeholder = message.placeholders[identifier]!;
-          if (placeholder.requiresFormatting) {
-            return '\$${node.children[1].value}String';
-          }
-          return '\$${node.children[1].value}';
+          case ST.pluralExpr:
+            requiresIntlImport = true;
+            final Map<String, String> pluralLogicArgs = <String, String>{};
+            // Recall that pluralExpr are of the form
+            // pluralExpr := "{" ID "," "plural" "," pluralParts "}"
+            assert(node.children[1].type == ST.identifier);
+            assert(node.children[5].type == ST.pluralParts);
 
-        case ST.pluralExpr:
-          requiresIntlImport = true;
-          final Map<String, String> pluralLogicArgs = <String, String>{};
-          // Recall that pluralExpr are of the form
-          // pluralExpr := "{" ID "," "plural" "," pluralParts "}"
-          assert(node.children[1].type == ST.identifier);
-          assert(node.children[5].type == ST.pluralParts);
+            final Node identifier = node.children[1];
+            final Node pluralParts = node.children[5];
 
-          final Node identifier = node.children[1];
-          final Node pluralParts = node.children[5];
-
-          for (final Node pluralPart in pluralParts.children.reversed) {
-            String pluralCase;
-            Node pluralMessage;
-            if (pluralPart.children[0].value == '=') {
-              assert(pluralPart.children[1].type == ST.number);
-              assert(pluralPart.children[3].type == ST.message);
-              pluralCase = pluralPart.children[1].value!;
-              pluralMessage = pluralPart.children[3];
-            } else {
-              assert(pluralPart.children[0].type == ST.identifier || pluralPart.children[0].type == ST.other);
-              assert(pluralPart.children[2].type == ST.message);
-              pluralCase = pluralPart.children[0].value!;
-              pluralMessage = pluralPart.children[2];
-            }
-            if (!pluralLogicArgs.containsKey(pluralCases[pluralCase])) {
-              final String pluralPartExpression = generateVariables(pluralMessage);
-              final String? transformedPluralCase = pluralCases[pluralCase];
-              // A valid plural case is one of "=0", "=1", "=2", "zero", "one", "two", "few", "many", or "other".
-              if (transformedPluralCase == null) {
-                throw L10nParserException(
-                  '''
+            for (final Node pluralPart in pluralParts.children.reversed) {
+              String pluralCase;
+              Node pluralMessage;
+              if (pluralPart.children[0].value == '=') {
+                assert(pluralPart.children[1].type == ST.number);
+                assert(pluralPart.children[3].type == ST.message);
+                pluralCase = pluralPart.children[1].value!;
+                pluralMessage = pluralPart.children[3];
+              } else {
+                assert(pluralPart.children[0].type == ST.identifier || pluralPart.children[0].type == ST.other);
+                assert(pluralPart.children[2].type == ST.message);
+                pluralCase = pluralPart.children[0].value!;
+                pluralMessage = pluralPart.children[2];
+              }
+              if (!pluralLogicArgs.containsKey(pluralCases[pluralCase])) {
+                final String pluralPartExpression = generateVariables(pluralMessage);
+                final String? transformedPluralCase = pluralCases[pluralCase];
+                // A valid plural case is one of "=0", "=1", "=2", "zero", "one", "two", "few", "many", or "other".
+                if (transformedPluralCase == null) {
+                  throw L10nParserException(
+                    '''
 The plural cases must be one of "=0", "=1", "=2", "zero", "one", "two", "few", "many", or "other.
     $pluralCase is not a valid plural case.''',
-                  _inputFileNames[locale]!,
-                  message.resourceId,
-                  translationForMessage,
-                  pluralPart.positionInMessage,
-                );
-              }
-              pluralLogicArgs[transformedPluralCase] = '      ${pluralCases[pluralCase]}: $pluralPartExpression,';
-            } else if (!suppressWarnings) {
-              logger.printWarning('''
+                    _inputFileNames[locale]!,
+                    message.resourceId,
+                    translationForMessage,
+                    pluralPart.positionInMessage,
+                  );
+                }
+                pluralLogicArgs[transformedPluralCase] = '      ${pluralCases[pluralCase]}: $pluralPartExpression,';
+              } else if (!suppressWarnings) {
+                logger.printWarning('''
 [${_inputFileNames[locale]}:${message.resourceId}] ICU Syntax Warning: The plural part specified below is overridden by a later plural part.
     $translationForMessage
     ${Parser.indentForError(pluralPart.positionInMessage)}''');
+              }
             }
-          }
-          final String tempVarName = getTempVariableName();
-          tempVariables.add(pluralVariableTemplate
-            .replaceAll('@(varName)', tempVarName)
-            .replaceAll('@(count)', identifier.value!)
-            .replaceAll('@(pluralLogicArgs)', pluralLogicArgs.values.join('\n'))
-          );
-          return '\$$tempVarName';
+            final String tempVarName = getTempVariableName();
+            tempVariables.add(pluralVariableTemplate
+              .replaceAll('@(varName)', tempVarName)
+              .replaceAll('@(count)', identifier.value!)
+              .replaceAll('@(pluralLogicArgs)', pluralLogicArgs.values.join('\n'))
+            );
+            return '\$$tempVarName';
 
-        case ST.selectExpr:
-          requiresIntlImport = true;
-          // Recall that pluralExpr are of the form
-          // pluralExpr := "{" ID "," "plural" "," pluralParts "}"
-          assert(node.children[1].type == ST.identifier);
-          assert(node.children[5].type == ST.selectParts);
+          case ST.selectExpr:
+            requiresIntlImport = true;
+            // Recall that pluralExpr are of the form
+            // pluralExpr := "{" ID "," "plural" "," pluralParts "}"
+            assert(node.children[1].type == ST.identifier);
+            assert(node.children[5].type == ST.selectParts);
 
-          final Node identifier = node.children[1];
-          final List<String> selectLogicArgs = <String>[];
-          final Node selectParts = node.children[5];
-          for (final Node selectPart in selectParts.children) {
-            assert(selectPart.children[0].type == ST.identifier || selectPart.children[0].type == ST.other);
-            assert(selectPart.children[2].type == ST.message);
-            final String selectCase = selectPart.children[0].value!;
-            final Node selectMessage = selectPart.children[2];
-            final String selectPartExpression = generateVariables(selectMessage);
-            selectLogicArgs.add("        '$selectCase': $selectPartExpression,");
-          }
-          final String tempVarName = getTempVariableName();
-          tempVariables.add(selectVariableTemplate
-            .replaceAll('@(varName)', tempVarName)
-            .replaceAll('@(choice)', identifier.value!)
-            .replaceAll('@(selectCases)', selectLogicArgs.join('\n'))
-          );
-          return '\$$tempVarName';
-        // ignore: no_default_cases
-        default:
-          throw Exception('Cannot call "generateHelperMethod" on node type ${node.type}');
+            final Node identifier = node.children[1];
+            final List<String> selectLogicArgs = <String>[];
+            final Node selectParts = node.children[5];
+            for (final Node selectPart in selectParts.children) {
+              assert(selectPart.children[0].type == ST.identifier || selectPart.children[0].type == ST.other);
+              assert(selectPart.children[2].type == ST.message);
+              final String selectCase = selectPart.children[0].value!;
+              final Node selectMessage = selectPart.children[2];
+              final String selectPartExpression = generateVariables(selectMessage);
+              selectLogicArgs.add("        '$selectCase': $selectPartExpression,");
+            }
+            final String tempVarName = getTempVariableName();
+            tempVariables.add(selectVariableTemplate
+              .replaceAll('@(varName)', tempVarName)
+              .replaceAll('@(choice)', identifier.value!)
+              .replaceAll('@(selectCases)', selectLogicArgs.join('\n'))
+            );
+            return '\$$tempVarName';
+          // ignore: no_default_cases
+          default:
+            throw Exception('Cannot call "generateHelperMethod" on node type ${node.type}');
+        }
       }
-    }
-    final String messageString = generateVariables(node, isRoot: true);
-    final String tempVarLines = tempVariables.isEmpty ? '' : '${tempVariables.join('\n')}\n';
-    return methodTemplate
-              .replaceAll('@(name)', message.resourceId)
-              .replaceAll('@(parameters)', generateMethodParameters(message).join(', '))
-              .replaceAll('@(dateFormatting)', generateDateFormattingLogic(message))
-              .replaceAll('@(numberFormatting)', generateNumberFormattingLogic(message))
-              .replaceAll('@(tempVars)', tempVarLines)
-              .replaceAll('@(message)', messageString)
-              .replaceAll('@(none)\n', '');
+      final String messageString = generateVariables(node, isRoot: true);
+      final String tempVarLines = tempVariables.isEmpty ? '' : '${tempVariables.join('\n')}\n';
+      return methodTemplate
+                .replaceAll('@(name)', message.resourceId)
+                .replaceAll('@(parameters)', generateMethodParameters(message).join(', '))
+                .replaceAll('@(dateFormatting)', generateDateFormattingLogic(message))
+                .replaceAll('@(numberFormatting)', generateNumberFormattingLogic(message))
+                .replaceAll('@(tempVars)', tempVarLines)
+                .replaceAll('@(message)', messageString)
+                .replaceAll('@(none)\n', '');
     } on L10nParserException catch (error) {
       logger.printError(error.toString());
+      hadErrors = true;
       return '';
     }
   }
@@ -1247,7 +1252,7 @@
     final String generatedLocalizationsFile = _generateCode();
 
     // If there were any syntax errors, don't write to files.
-    if (logger.hadErrorOutput) {
+    if (hadErrors) {
       throw L10nException('Found syntax errors.');
     }
 
diff --git a/packages/flutter_tools/test/general.shard/generate_localizations_test.dart b/packages/flutter_tools/test/general.shard/generate_localizations_test.dart
index 604b821..d40174a 100644
--- a/packages/flutter_tools/test/general.shard/generate_localizations_test.dart
+++ b/packages/flutter_tools/test/general.shard/generate_localizations_test.dart
@@ -785,6 +785,28 @@
   });
 
   group('generateLocalizations', () {
+    // Regression test for https://github.com/flutter/flutter/issues/119593
+    testWithoutContext('other logs from flutter_tools does not affect gen-l10n', () async {
+      _standardFlutterDirectoryL10nSetup(fs);
+
+      final Logger logger = BufferLogger.test();
+      logger.printError('An error output from a different tool in flutter_tools');
+
+      // Should run without error.
+      generateLocalizations(
+        fileSystem: fs,
+        options: LocalizationOptions(
+          arbDirectory: Uri.directory(defaultL10nPathString),
+          outputDirectory: Uri.directory(defaultL10nPathString, windows: false),
+          templateArbFile: Uri.file(defaultTemplateArbFileName, windows: false),
+          useSyntheticPackage: false,
+        ),
+        logger: logger,
+        projectDir: fs.currentDirectory,
+        dependenciesDir: fs.currentDirectory,
+      );
+    });
+
     testWithoutContext('forwards arguments correctly', () async {
       _standardFlutterDirectoryL10nSetup(fs);
       final LocalizationOptions options = LocalizationOptions(