From 74aadfb37847e403faa2943d091a42d1922dec11 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 29 May 2026 15:56:59 +0000 Subject: [PATCH 1/2] Initial plan From a9b1fec5c3adb337ab81024149796891b94cd478 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 29 May 2026 16:23:30 +0000 Subject: [PATCH 2/2] Fix preprocessor endless loop when UDC replacement produces identical output Add no-op detection in doProcessCommands() and doProcessTranslates() to break the processing loop when a UDC rule replacement produces tokens textually identical to its input. This prevents infinite loops with rules like `#xcommand NOTHING [<*any*>] => NOTHING` or `#xcommand ENDSEQUENCE [<*any*>] => ENDSEQUENCE` where the output matches the input pattern. Applied to both Compiler and MacroCompiler preprocessor implementations. --- .../Preprocessor/XSharpPreprocessor.cs | 28 +++++++++++++++++++ .../Preprocessor/XSharpPreprocessor.cs | 28 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/Compiler/src/Compiler/XSharpCodeAnalysis/Preprocessor/XSharpPreprocessor.cs b/src/Compiler/src/Compiler/XSharpCodeAnalysis/Preprocessor/XSharpPreprocessor.cs index d9a3bd9050..a24b1c08a6 100644 --- a/src/Compiler/src/Compiler/XSharpCodeAnalysis/Preprocessor/XSharpPreprocessor.cs +++ b/src/Compiler/src/Compiler/XSharpCodeAnalysis/Preprocessor/XSharpPreprocessor.cs @@ -2620,7 +2620,15 @@ private bool doProcessTranslates(IList line, out IList var rule = _transRules.FindMatchingRule(temp, out var matchInfo); if (rule != null) { + var prevTemp = temp; temp = doReplace(temp, rule, matchInfo); + if (isResultUnchanged(prevTemp, temp)) + { + // the replacement did not change the tokens, so stop to prevent an endless loop + result.AddRange(temp); + temp.Clear(); + break; + } if (usedRules.HasRecursion(rule, temp)) { // duplicate, so exit now @@ -2686,7 +2694,13 @@ private bool doProcessCommands(IList line, out IList r // nothing to do, so exit. Leave changed the way it is. This does not have to be the first iteration break; } + var prevResult = result; result = doReplace(result, rule, matchInfo); + if (isResultUnchanged(prevResult, result)) + { + // the replacement did not change the tokens, so stop to prevent an endless loop + break; + } if (usedRules.HasRecursion(rule, result)) { // duplicate so exit now @@ -2796,5 +2810,19 @@ private List doReplace(IList line, PPRule rule, PPMatc #endif return result; } + + private static bool isResultUnchanged(IList input, IList output) + { + if (input.Count != output.Count) + return false; + for (int i = 0; i < input.Count; i++) + { + if (!string.Equals(input[i].Text, output[i].Text, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + } + return true; + } } } diff --git a/src/Runtime/MacroCompiler/Preprocessor/XSharpPreprocessor.cs b/src/Runtime/MacroCompiler/Preprocessor/XSharpPreprocessor.cs index 5d5a1083dc..393928f54f 100644 --- a/src/Runtime/MacroCompiler/Preprocessor/XSharpPreprocessor.cs +++ b/src/Runtime/MacroCompiler/Preprocessor/XSharpPreprocessor.cs @@ -2540,7 +2540,15 @@ private bool doProcessTranslates(IList line, out IList var rule = _transRules.FindMatchingRule(temp, out var matchInfo); if (rule != null) { + var prevTemp = temp; temp = doReplace(temp, rule, matchInfo); + if (isResultUnchanged(prevTemp, temp)) + { + // the replacement did not change the tokens, so stop to prevent an endless loop + result.AddRange(temp); + temp.Clear(); + break; + } if (usedRules.HasRecursion(rule, temp)) { // duplicate, so exit now @@ -2606,7 +2614,13 @@ private bool doProcessCommands(IList line, out IList r // nothing to do, so exit. Leave changed the way it is. This does not have to be the first iteration break; } + var prevResult = result; result = doReplace(result, rule, matchInfo); + if (isResultUnchanged(prevResult, result)) + { + // the replacement did not change the tokens, so stop to prevent an endless loop + break; + } if (usedRules.HasRecursion(rule, result)) { // duplicate so exit now @@ -2716,5 +2730,19 @@ private List doReplace(IList line, PPRule rule, PPMatc #endif return result; } + + private static bool isResultUnchanged(IList input, IList output) + { + if (input.Count != output.Count) + return false; + for (int i = 0; i < input.Count; i++) + { + if (!string.Equals(input[i].Text, output[i].Text, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + } + return true; + } } }