diff --git a/Assets/Tests/Editor/PausePointCompiledLineMapWarningTests.cs b/Assets/Tests/Editor/PausePointCompiledLineMapWarningTests.cs
index 668da781cd..d253c97be3 100644
--- a/Assets/Tests/Editor/PausePointCompiledLineMapWarningTests.cs
+++ b/Assets/Tests/Editor/PausePointCompiledLineMapWarningTests.cs
@@ -932,6 +932,291 @@ public void AppendRequestedLineCandidateCompiledLinesToDriftWarningOrUnchanged_W
+ " Candidate: the text at --line 107 in the edited file appears at lines 1, 3, 4 (first 3 matches) in the last compiled source."));
}
+ ///
+ /// What: one compiled-source match for the requested --line text is appended to a
+ /// resolve-failure Message.
+ ///
+ [Test]
+ public void AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged_WhenOneLineMatches_AppendsCandidate()
+ {
+ const string message =
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'.";
+ string[] compiledLines = new string[110];
+ for (int index = 0; index < compiledLines.Length; index++)
+ {
+ compiledLines[index] = " return 0;";
+ }
+
+ compiledLines[109] = " return 2;";
+
+ string result = PausePointEnableWarnings.AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged(
+ message,
+ 116,
+ " return 2; ",
+ compiledLines);
+
+ Assert.That(
+ result,
+ Is.EqualTo(
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'."
+ + " Candidate: the text at --line 116 in the edited file appears at line 110 in the last compiled source."));
+ }
+
+ ///
+ /// What: two compiled-source matches for the requested --line text append both line numbers.
+ ///
+ [Test]
+ public void AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged_WhenTwoLinesMatch_AppendsBothCandidates()
+ {
+ const string message =
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'.";
+ string[] compiledLines =
+ {
+ "class Sample",
+ " return 2;",
+ " return 1;",
+ " return 2;"
+ };
+
+ string result = PausePointEnableWarnings.AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged(
+ message,
+ 116,
+ "return 2;",
+ compiledLines);
+
+ Assert.That(
+ result,
+ Is.EqualTo(
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'."
+ + " Candidate: the text at --line 116 in the edited file appears at lines 2, 4 in the last compiled source."));
+ }
+
+ ///
+ /// What: more than three compiled-source matches for a resolve-failure Message cap at the
+ /// first three.
+ ///
+ [Test]
+ public void AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged_WhenMoreThanThreeLinesMatch_CapsAtFirstThree()
+ {
+ const string message =
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'.";
+ string[] compiledLines =
+ {
+ " return 2;",
+ " return 1;",
+ " return 2;",
+ " return 2;",
+ " return 2;"
+ };
+
+ string result = PausePointEnableWarnings.AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged(
+ message,
+ 116,
+ "return 2;",
+ compiledLines);
+
+ Assert.That(
+ result,
+ Is.EqualTo(
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'."
+ + " Candidate: the text at --line 116 in the edited file appears at lines 1, 3, 4 (first 3 matches) in the last compiled source."));
+ }
+
+ ///
+ /// What: a resolve-failure Message stays unchanged when compiled source has no matching line.
+ ///
+ [Test]
+ public void AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged_WhenNoLineMatches_ReturnsUnchanged()
+ {
+ const string message =
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'.";
+ string[] compiledLines =
+ {
+ " return 0;"
+ };
+
+ string result = PausePointEnableWarnings.AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged(
+ message,
+ 116,
+ "return 2;",
+ compiledLines);
+
+ Assert.That(result, Is.EqualTo(message));
+ }
+
+ ///
+ /// What: a resolve-failure Message stays unchanged when the edited line text is blank.
+ ///
+ [Test]
+ public void AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged_WhenEditedTextEmpty_ReturnsUnchanged()
+ {
+ const string message =
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'.";
+ string[] compiledLines =
+ {
+ " return 2;"
+ };
+
+ string result = PausePointEnableWarnings.AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged(
+ message,
+ 116,
+ " ",
+ compiledLines);
+
+ Assert.That(result, Is.EqualTo(message));
+ }
+
+ ///
+ /// What: a resolve-failure Message stays unchanged when compiled source lines are null.
+ ///
+ [Test]
+ public void AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged_WhenCompiledSourceLinesNull_ReturnsUnchanged()
+ {
+ const string message =
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'.";
+
+ string result = PausePointEnableWarnings.AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged(
+ message,
+ 116,
+ "return 2;",
+ null);
+
+ Assert.That(result, Is.EqualTo(message));
+ }
+
+ ///
+ /// What: an active hot-reload gate plus a compiled-source match appends Candidate after
+ /// Nearby methods on a resolve-failure Message.
+ ///
+ [Test]
+ public void BuildResolveFailureMessage_WhenHotReloadGateTrueAndLineMatches_AppendsNearbyThenCandidate()
+ {
+ const string errorMessage =
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'.";
+ SourcePausePointNearbyCompiledMethod[] nearby =
+ {
+ new SourcePausePointNearbyCompiledMethod("Enemy.Update", 100, 120)
+ };
+ string[] compiledLines =
+ {
+ " return 2;"
+ };
+
+ string result = PausePointEnableWarnings.BuildResolveFailureMessage(
+ errorMessage,
+ nearby,
+ true,
+ 116,
+ true,
+ "return 2;",
+ compiledLines);
+
+ Assert.That(
+ result,
+ Is.EqualTo(
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'."
+ + " Nearby methods in the last compiled source: 'Enemy.Update' spans lines 100-120."
+ + " Candidate: the text at --line 116 in the edited file appears at line 1 in the last compiled source."));
+ }
+
+ ///
+ /// What: the same matching inputs without the hot-reload gate keep Nearby methods and omit
+ /// Candidate.
+ ///
+ [Test]
+ public void BuildResolveFailureMessage_WhenHotReloadGateFalseAndLineMatches_AppendsNearbyOnly()
+ {
+ const string errorMessage =
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'.";
+ SourcePausePointNearbyCompiledMethod[] nearby =
+ {
+ new SourcePausePointNearbyCompiledMethod("Enemy.Update", 100, 120)
+ };
+ string[] compiledLines =
+ {
+ " return 2;"
+ };
+
+ string result = PausePointEnableWarnings.BuildResolveFailureMessage(
+ errorMessage,
+ nearby,
+ false,
+ 116,
+ true,
+ "return 2;",
+ compiledLines);
+
+ Assert.That(
+ result,
+ Is.EqualTo(
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'."
+ + " Nearby methods in the last compiled source: 'Enemy.Update' spans lines 100-120."));
+ }
+
+ ///
+ /// What: a null compiled-source list under an active hot-reload gate keeps Nearby methods
+ /// and omits Candidate.
+ ///
+ [Test]
+ public void BuildResolveFailureMessage_WhenCompiledSourceLinesNull_AppendsNearbyOnly()
+ {
+ const string errorMessage =
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'.";
+ SourcePausePointNearbyCompiledMethod[] nearby =
+ {
+ new SourcePausePointNearbyCompiledMethod("Enemy.Update", 100, 120)
+ };
+
+ string result = PausePointEnableWarnings.BuildResolveFailureMessage(
+ errorMessage,
+ nearby,
+ true,
+ 116,
+ true,
+ "return 2;",
+ null);
+
+ Assert.That(
+ result,
+ Is.EqualTo(
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'."
+ + " Nearby methods in the last compiled source: 'Enemy.Update' spans lines 100-120."));
+ }
+
+ ///
+ /// What: a failed edited-line read under an active hot-reload gate keeps Nearby methods and
+ /// omits Candidate even when compiled source would match.
+ ///
+ [Test]
+ public void BuildResolveFailureMessage_WhenRequestedLineReadFails_AppendsNearbyOnly()
+ {
+ const string errorMessage =
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'.";
+ SourcePausePointNearbyCompiledMethod[] nearby =
+ {
+ new SourcePausePointNearbyCompiledMethod("Enemy.Update", 100, 120)
+ };
+ string[] compiledLines =
+ {
+ " return 2;"
+ };
+
+ string result = PausePointEnableWarnings.BuildResolveFailureMessage(
+ errorMessage,
+ nearby,
+ true,
+ 116,
+ false,
+ "return 2;",
+ compiledLines);
+
+ Assert.That(
+ result,
+ Is.EqualTo(
+ "No sequence point found on or after line 116 in 'Assets/Scripts/Enemy.cs'."
+ + " Nearby methods in the last compiled source: 'Enemy.Update' spans lines 100-120."));
+ }
+
///
/// What: retarget warning interpolates resolved method, requested line, and edited span.
///
diff --git a/Packages/src/Editor/FirstPartyTools/PausePoint/PausePointEnableWarnings.cs b/Packages/src/Editor/FirstPartyTools/PausePoint/PausePointEnableWarnings.cs
index d93bcd2e62..df3f3fd4c1 100644
--- a/Packages/src/Editor/FirstPartyTools/PausePoint/PausePointEnableWarnings.cs
+++ b/Packages/src/Editor/FirstPartyTools/PausePoint/PausePointEnableWarnings.cs
@@ -231,6 +231,76 @@ internal static string AppendRequestedLineCandidateCompiledLinesToDriftWarningOr
truncated);
}
+ ///
+ /// Appends compiled-line Candidate text to a resolve-failure Message when the edited
+ /// --line text appears in the last compiled source.
+ ///
+ internal static string AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged(
+ string message,
+ int requestedLine,
+ string requestedLineEditedText,
+ IReadOnlyList compiledSourceLines)
+ {
+ if (string.IsNullOrEmpty(message) || requestedLine <= 0)
+ {
+ return message ?? string.Empty;
+ }
+
+ if (string.IsNullOrEmpty(requestedLineEditedText) || compiledSourceLines == null)
+ {
+ return message;
+ }
+
+ string editedTrimmed = requestedLineEditedText.Trim();
+ if (editedTrimmed.Length == 0)
+ {
+ return message;
+ }
+
+ (List matches, bool truncated) = CollectCandidateCompiledLineNumbers(
+ editedTrimmed,
+ compiledSourceLines);
+ if (matches.Count == 0)
+ {
+ return message;
+ }
+
+ // Why no drift-warning gate: resolve-failure Messages have no drift warning, and
+ // Candidate is the only compiled-line number the caller can retry with.
+ return message + FormatRequestedLineCandidateCompiledLinesSuffix(
+ requestedLine,
+ matches,
+ truncated);
+ }
+
+ ///
+ /// Builds a resolve-failure Message: Nearby methods, then Candidate when hot-reload
+ /// patches are active and the edited --line text was read.
+ ///
+ internal static string BuildResolveFailureMessage(
+ string errorMessage,
+ IReadOnlyList nearbyCompiledMethods,
+ bool hasActiveHotReloadPatches,
+ int requestedLine,
+ bool requestedLineReadOk,
+ string requestedLineEditedText,
+ IReadOnlyList compiledSourceLinesOrNull)
+ {
+ string message = AppendNearbyCompiledMethodsSuffix(errorMessage, nearbyCompiledMethods);
+ // Why skip Candidate when the edited line was not read: the Candidate sentence names
+ // the text at --line N in the edited file, which is false if that read failed.
+ if (!hasActiveHotReloadPatches || !requestedLineReadOk)
+ {
+ return message;
+ }
+
+ return AppendResolveFailureRequestedLineCandidateSuffixOrUnchanged(
+ message,
+ requestedLine,
+ requestedLineEditedText,
+ compiledSourceLinesOrNull);
+ }
+
private static (List matches, bool truncated) CollectCandidateCompiledLineNumbers(
string editedTrimmed,
IReadOnlyList compiledSourceLines)
diff --git a/Packages/src/Editor/FirstPartyTools/PausePoint/PausePointUseCase.cs b/Packages/src/Editor/FirstPartyTools/PausePoint/PausePointUseCase.cs
index 92d52535b1..f582dabf8c 100644
--- a/Packages/src/Editor/FirstPartyTools/PausePoint/PausePointUseCase.cs
+++ b/Packages/src/Editor/FirstPartyTools/PausePoint/PausePointUseCase.cs
@@ -273,10 +273,34 @@ private static PausePointResponse EnableBySourceLocation(EnablePausePointSchema
string recommendedNextAction = hasActiveHotReloadPatches
? SourcePausePointConstants.HotReloadCompiledLineMapResolveFailureNextAction
: SourcePausePointConstants.ResolveFailedRecommendedNextAction;
+ IReadOnlyList compiledSourceLinesOrNull = null;
+ bool requestedLineReadOk = false;
+ string requestedLineEditedText = string.Empty;
+ // Why skip snapshot/edited-line IO without patches: Candidate is omitted on that
+ // path, so those reads would change the historical no-patch failure for no gain.
+ if (hasActiveHotReloadPatches)
+ {
+ string compiledSnapshotSource = LoadCompiledSnapshotSourceOrEmpty(parameters.File);
+ compiledSourceLinesOrNull = string.IsNullOrEmpty(compiledSnapshotSource)
+ ? null
+ : SourcePausePointSourceLineReader.SplitSourceLines(compiledSnapshotSource);
+ (requestedLineReadOk, requestedLineEditedText) =
+ PausePointCompiledLineComparisonWarnings.ReadEditedLineText(
+ parameters.File,
+ parameters.Line);
+ }
+
+ string message = PausePointEnableWarnings.BuildResolveFailureMessage(
+ resolveResult.ErrorMessage,
+ resolveResult.NearbyCompiledMethods,
+ hasActiveHotReloadPatches,
+ parameters.Line,
+ requestedLineReadOk,
+ requestedLineEditedText,
+ compiledSourceLinesOrNull);
+
PausePointResponse response = CreateValidationFailure(
- PausePointEnableWarnings.AppendNearbyCompiledMethodsSuffix(
- resolveResult.ErrorMessage,
- resolveResult.NearbyCompiledMethods),
+ message,
SourcePausePointConstants.ErrorCodeResolveFailed,
recommendedNextAction);
response.Warning = PausePointEnableWarnings.ChooseCompiledLineMapWarning(