From f4f5a45f2515407c6225dbe1e78ebfe434c1ed9d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:00:06 +0000 Subject: [PATCH 1/6] Initial plan From e5d9a56123ea0d7437f4c07b35d27986e4e5d9a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:05:58 +0000 Subject: [PATCH 2/6] Add With* convenience methods to SourceSpan and mark as readonly struct Agent-Logs-Url: https://github.com/dotnet/razor/sessions/0fe1bde6-bf65-4d15-b842-d85d7ca68e41 Co-authored-by: chsienki <16246502+chsienki@users.noreply.github.com> --- ...HelperResolutionPhase.ComponentTagHelperResolver.cs | 2 +- .../src/Language/SourceSpan.cs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs index 9b0007092b3..93a7f2465a3 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs @@ -991,7 +991,7 @@ private static void CopyAsTagHelperAttributeValues(HtmlAttributeIntermediateNode var totalLength = source.Children[^1] is HtmlAttributeValueIntermediateNode lastValue && lastValue.Source is { } ls ? (ls.AbsoluteIndex + ls.Length) - fs.AbsoluteIndex : mergedText.Length; - spanSource = new SourceSpan(fs.FilePath, fs.AbsoluteIndex, fs.LineIndex, fs.CharacterIndex, totalLength, fs.LineCount, fs.EndCharacterIndex); + spanSource = fs.WithLength(totalLength); } mergedContent.Source = spanSource; diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/SourceSpan.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/SourceSpan.cs index 6a929b7faa8..e5ec9e35fb4 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/SourceSpan.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/SourceSpan.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Razor.Language; -public struct SourceSpan : IEquatable +public readonly struct SourceSpan : IEquatable { public static readonly SourceSpan Undefined = new SourceSpan(SourceLocation.Undefined, 0); @@ -126,4 +126,12 @@ public readonly int CompareByStartThenLength(SourceSpan other) return Length.CompareTo(other.Length); } + + public readonly SourceSpan WithFilePath(string filePath) => new(filePath, AbsoluteIndex, LineIndex, CharacterIndex, Length, LineCount, EndCharacterIndex); + public readonly SourceSpan WithAbsoluteIndex(int absoluteIndex) => new(FilePath, absoluteIndex, LineIndex, CharacterIndex, Length, LineCount, EndCharacterIndex); + public readonly SourceSpan WithLineIndex(int lineIndex) => new(FilePath, AbsoluteIndex, lineIndex, CharacterIndex, Length, LineCount, EndCharacterIndex); + public readonly SourceSpan WithCharacterIndex(int characterIndex) => new(FilePath, AbsoluteIndex, LineIndex, characterIndex, Length, LineCount, EndCharacterIndex); + public readonly SourceSpan WithLength(int length) => new(FilePath, AbsoluteIndex, LineIndex, CharacterIndex, length, LineCount, EndCharacterIndex); + public readonly SourceSpan WithLineCount(int lineCount) => new(FilePath, AbsoluteIndex, LineIndex, CharacterIndex, Length, lineCount, EndCharacterIndex); + public readonly SourceSpan WithEndCharacterIndex(int endCharacterIndex) => new(FilePath, AbsoluteIndex, LineIndex, CharacterIndex, Length, LineCount, endCharacterIndex); } From e4ad9b9dee7a2e04c3158a99905ae632b53b0c86 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:16:27 +0000 Subject: [PATCH 3/6] Use With* chaining for all derived SourceSpan constructions in DefaultTagHelperResolutionPhase files Agent-Logs-Url: https://github.com/dotnet/razor/sessions/aee1b7aa-43a0-4cd8-848f-b844edb970a0 Co-authored-by: chsienki <16246502+chsienki@users.noreply.github.com> --- ...olutionPhase.ComponentTagHelperResolver.cs | 51 +++----------- ...ResolutionPhase.LegacyTagHelperResolver.cs | 66 +++++-------------- .../DefaultTagHelperResolutionPhase.cs | 26 ++------ 3 files changed, 30 insertions(+), 113 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs index 93a7f2465a3..484e044eed2 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs @@ -195,7 +195,7 @@ private void ConvertToUnresolvedDirectiveAttribute( var directiveNameSpan = nameSpan; if (directiveNameSpan is SourceSpan ns && attributeName.StartsWith('@')) { - directiveNameSpan = new SourceSpan(ns.FilePath, ns.AbsoluteIndex + 1, ns.LineIndex, ns.CharacterIndex + 1, ns.Length - 1, ns.LineCount, ns.EndCharacterIndex); + directiveNameSpan = ns.WithAbsoluteIndex(ns.AbsoluteIndex + 1).WithCharacterIndex(ns.CharacterIndex + 1).WithLength(ns.Length - 1); } // Strip parameter suffix from OriginalAttributeSpan for parameter matches @@ -203,7 +203,7 @@ private void ConvertToUnresolvedDirectiveAttribute( if (match.IsParameterMatch && directiveAttributeName.HasParameter && parameterOriginalSpan is SourceSpan ps) { var nameWithoutParamLen = directiveAttributeName.TextWithoutParameter.Length; - parameterOriginalSpan = new SourceSpan(ps.FilePath, ps.AbsoluteIndex, ps.LineIndex, ps.CharacterIndex, nameWithoutParamLen, ps.LineCount, ps.CharacterIndex + nameWithoutParamLen); + parameterOriginalSpan = ps.WithLength(nameWithoutParamLen).WithEndCharacterIndex(ps.CharacterIndex + nameWithoutParamLen); } IntermediateNode directiveNode = match.IsParameterMatch && directiveAttributeName.HasParameter @@ -581,14 +581,7 @@ private static void MergeAdjacentCSharpTokens(IntermediateNode node) if (firstSpan is { } first && lastSpan is { } last) { var endAbsolute = last.AbsoluteIndex + last.Length; - mergedSpan = new SourceSpan( - first.FilePath, - first.AbsoluteIndex, - first.LineIndex, - first.CharacterIndex, - endAbsolute - first.AbsoluteIndex, - last.LineIndex - first.LineIndex + 1, - last.EndCharacterIndex); + mergedSpan = first.WithLength(endAbsolute - first.AbsoluteIndex).WithLineCount(last.LineIndex - first.LineIndex + 1).WithEndCharacterIndex(last.EndCharacterIndex); } var content = sb.ToString(); @@ -877,14 +870,7 @@ private static void ConvertComponentAttributeToTagHelper( var directiveNameSpan = attributeNameSpan; if (directiveNameSpan is SourceSpan nameSpan && attributeName.StartsWith('@')) { - directiveNameSpan = new SourceSpan( - nameSpan.FilePath, - nameSpan.AbsoluteIndex + 1, - nameSpan.LineIndex, - nameSpan.CharacterIndex + 1, - nameSpan.Length - 1, - nameSpan.LineCount, - nameSpan.EndCharacterIndex); + directiveNameSpan = nameSpan.WithAbsoluteIndex(nameSpan.AbsoluteIndex + 1).WithCharacterIndex(nameSpan.CharacterIndex + 1).WithLength(nameSpan.Length - 1); } IntermediateNode directiveNode = match.IsParameterMatch && directiveAttributeName.HasParameter @@ -1052,14 +1038,7 @@ private static void CopyAsTagHelperAttributeValues(HtmlAttributeIntermediateNode var nameCharIndex = attrSource.CharacterIndex + nameIndex; - return new SourceSpan( - attrSource.FilePath, - attrSource.AbsoluteIndex + nameIndex, - attrSource.LineIndex, - nameCharIndex, - nameLength, - 0, - nameCharIndex + nameLength); + return attrSource.WithAbsoluteIndex(attrSource.AbsoluteIndex + nameIndex).WithCharacterIndex(nameCharIndex).WithLength(nameLength).WithLineCount(0).WithEndCharacterIndex(nameCharIndex + nameLength); } private static SourceSpan? ComputeAttributeValueSpan(HtmlAttributeIntermediateNode htmlAttr) @@ -1080,16 +1059,11 @@ private static void CopyAsTagHelperAttributeValues(HtmlAttributeIntermediateNode var endIndex = lastSource.AbsoluteIndex + lastSource.Length; var length = endIndex - childSource.AbsoluteIndex; var endCharIndex = lastSource.CharacterIndex + lastSource.Length; - return new SourceSpan( - childSource.FilePath, - childSource.AbsoluteIndex, - childSource.LineIndex, - childSource.CharacterIndex, - length, + return childSource.WithLength(length) // Note: does not incorporate lastSource.LineCount; attribute values // spanning multiple lines are uncommon and the old pipeline had the same limitation. - lastSource.LineIndex - childSource.LineIndex, - endCharIndex); + .WithLineCount(lastSource.LineIndex - childSource.LineIndex) + .WithEndCharacterIndex(endCharIndex); } return childSource; @@ -1113,14 +1087,7 @@ private static void CopyAsTagHelperAttributeValues(HtmlAttributeIntermediateNode var valueCharIndex = attrSource.CharacterIndex + valueStart; - return new SourceSpan( - attrSource.FilePath, - attrSource.AbsoluteIndex + valueStart, - attrSource.LineIndex, - valueCharIndex, - valueLength, - 0, - valueCharIndex + valueLength); + return attrSource.WithAbsoluteIndex(attrSource.AbsoluteIndex + valueStart).WithCharacterIndex(valueCharIndex).WithLength(valueLength).WithLineCount(0).WithEndCharacterIndex(valueCharIndex + valueLength); } /// diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs index 742bb3ce48a..d8effcf34d3 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs @@ -600,9 +600,7 @@ private static void ConvertMixedLiteralAndExpressionValue(IntermediateNode targe // Compute value source span with correct line/char positions. var valueCharIndex = attrSource.CharacterIndex + prefix.Length + (attrSource.AbsoluteIndex + prefix.Length < valueStart ? 1 : 0); var valueEndCharIndex = attrSource.EndCharacterIndex - suffix.Length; - var valueSource = new SourceSpan( - attrSource.FilePath, valueStart, attrSource.LineIndex, valueCharIndex, - valueLength, attrSource.LineCount, valueEndCharIndex); + var valueSource = attrSource.WithAbsoluteIndex(valueStart).WithCharacterIndex(valueCharIndex).WithLength(valueLength).WithEndCharacterIndex(valueEndCharIndex); targetNode.Children.Add(new CSharpIntermediateToken(sourceText, valueSource)); } } @@ -678,10 +676,10 @@ private static void ConvertEscapedAtExpressionValue(IntermediateNode targetNode, var transAbsIdx = atSrc.AbsoluteIndex + 2; var transCharIdx = atSrc.CharacterIndex + 2; // Empty token (MarkupEphemeralTextLiteral equivalent) - var emptySource = new SourceSpan(atSrc.FilePath, transAbsIdx, atSrc.LineIndex, transCharIdx, 0, 0, transCharIdx); + var emptySource = atSrc.WithAbsoluteIndex(transAbsIdx).WithCharacterIndex(transCharIdx).WithLength(0).WithLineCount(0).WithEndCharacterIndex(transCharIdx); targetNode.Children.Add(new CSharpIntermediateToken(string.Empty, emptySource)); // @ transition token - var transSource = new SourceSpan(atSrc.FilePath, transAbsIdx, atSrc.LineIndex, transCharIdx, 1, 0, transCharIdx + 1); + var transSource = atSrc.WithAbsoluteIndex(transAbsIdx).WithCharacterIndex(transCharIdx).WithLength(1).WithLineCount(0).WithEndCharacterIndex(transCharIdx + 1); targetNode.Children.Add(new CSharpIntermediateToken("@", transSource)); } @@ -693,7 +691,7 @@ private static void ConvertEscapedAtExpressionValue(IntermediateNode targetNode, { var openAbsIndex = innerSource.AbsoluteIndex - 1; var openCharIndex = innerSource.CharacterIndex - 1; - var openSource = new SourceSpan(innerSource.FilePath, openAbsIndex, innerSource.LineIndex, openCharIndex, 1, 0, openCharIndex + 1); + var openSource = innerSource.WithAbsoluteIndex(openAbsIndex).WithCharacterIndex(openCharIndex).WithLength(1).WithLineCount(0).WithEndCharacterIndex(openCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken("(", openSource)); foreach (var innerChild in csharpExprValue.Children) @@ -706,7 +704,7 @@ private static void ConvertEscapedAtExpressionValue(IntermediateNode targetNode, { var closeAbsIndex = lastSource.AbsoluteIndex + lastSource.Length; var closeCharIndex = lastSource.EndCharacterIndex; - var closeSource = new SourceSpan(lastSource.FilePath, closeAbsIndex, lastSource.LineIndex, closeCharIndex, 1, 0, closeCharIndex + 1); + var closeSource = lastSource.WithAbsoluteIndex(closeAbsIndex).WithCharacterIndex(closeCharIndex).WithLength(1).WithLineCount(0).WithEndCharacterIndex(closeCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken(")", closeSource)); } } @@ -742,9 +740,7 @@ private static void ConvertPureCSharpExpressionValue(IntermediateNode targetNode // to match baseline's separate token structure. var openParenAbsIndex = valueStart + 1; // after @ var openParenCharIndex = attrSource.CharacterIndex + prefix.Length + 1; - var openParenSource = new SourceSpan( - attrSource.FilePath, openParenAbsIndex, attrSource.LineIndex, openParenCharIndex, - 1, 0, openParenCharIndex + 1); + var openParenSource = attrSource.WithAbsoluteIndex(openParenAbsIndex).WithCharacterIndex(openParenCharIndex).WithLength(1).WithLineCount(0).WithEndCharacterIndex(openParenCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken("(", openParenSource)); // Inner expression content from the CSharpExpressionAttributeValueIntermediateNode @@ -768,9 +764,7 @@ private static void ConvertPureCSharpExpressionValue(IntermediateNode targetNode var closeParenAbsIndex = valueStart + valueLength - 1; // last char var closeParenCharIndex = attrSource.CharacterIndex + prefix.Length + valueLength - 1; - var closeParenSource = new SourceSpan( - attrSource.FilePath, closeParenAbsIndex, attrSource.LineIndex, closeParenCharIndex, - 1, 0, closeParenCharIndex + 1); + var closeParenSource = attrSource.WithAbsoluteIndex(closeParenAbsIndex).WithCharacterIndex(closeParenCharIndex).WithLength(1).WithLineCount(0).WithEndCharacterIndex(closeParenCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken(")", closeParenSource)); } else @@ -875,14 +869,7 @@ private static void UnwrapCSharpAttributeValue( var valueAbsIndex = attrSource.AbsoluteIndex + prefix.Length; var valueCharIndex = attrSource.CharacterIndex + prefix.Length; - return new SourceSpan( - attrSource.FilePath, - valueAbsIndex, - attrSource.LineIndex, - valueCharIndex, - length: 0, - lineCount: 0, - endCharacterIndex: valueCharIndex); + return attrSource.WithAbsoluteIndex(valueAbsIndex).WithCharacterIndex(valueCharIndex).WithLength(0).WithLineCount(0).WithEndCharacterIndex(valueCharIndex); } /// @@ -1148,14 +1135,7 @@ private static void MergeAdjacentHtmlContent(IntermediateNode parent, int index, current.Children.AddRange(next.Children); if (current.Source is SourceSpan currentSource && next.Source is SourceSpan nextSource) { - current.Source = new SourceSpan( - currentSource.FilePath, - currentSource.AbsoluteIndex, - currentSource.LineIndex, - currentSource.CharacterIndex, - (nextSource.AbsoluteIndex + nextSource.Length) - currentSource.AbsoluteIndex, - nextSource.LineCount, - nextSource.EndCharacterIndex); + current.Source = currentSource.WithLength((nextSource.AbsoluteIndex + nextSource.Length) - currentSource.AbsoluteIndex).WithLineCount(nextSource.LineCount).WithEndCharacterIndex(nextSource.EndCharacterIndex); } else if (current.Source == null) { @@ -1340,20 +1320,13 @@ private static void LowerImplicitExpressionAttribute_Legacy( var openLoc = sourceDocument.Text.Lines.GetLinePosition(contentStart); var closeLoc = sourceDocument.Text.Lines.GetLinePosition(contentStart + contentLength - 1); - expr.Source = new SourceSpan(exprSource.FilePath, contentStart, openLoc.Line, openLoc.Character, contentLength, 0, closeLoc.Character + 1); + expr.Source = exprSource.WithAbsoluteIndex(contentStart).WithLineIndex(openLoc.Line).WithCharacterIndex(openLoc.Character).WithLength(contentLength).WithLineCount(0).WithEndCharacterIndex(closeLoc.Character + 1); } else { // Implicit expression: single token. var contentLocation = sourceDocument.Text.Lines.GetLinePosition(contentStart); - var contentSpan = new SourceSpan( - exprSource.FilePath, - contentStart, - contentLocation.Line, - contentLocation.Character, - contentLength, - 0, - contentLocation.Character + contentLength); + var contentSpan = exprSource.WithAbsoluteIndex(contentStart).WithLineIndex(contentLocation.Line).WithCharacterIndex(contentLocation.Character).WithLength(contentLength).WithLineCount(0).WithEndCharacterIndex(contentLocation.Character + contentLength); expr.Children.Add(new CSharpIntermediateToken( LazyContent.Create(text, static s => s), contentSpan)); expr.Source = contentSpan; @@ -1383,8 +1356,7 @@ private static void LowerImplicitExpressionAttribute_Legacy( var mergedContent = sb.ToString(); var tokenSpan = firstSpan is { } f && lastSpan is { } l - ? new SourceSpan(f.FilePath, f.AbsoluteIndex, f.LineIndex, f.CharacterIndex, - (l.AbsoluteIndex + l.Length) - f.AbsoluteIndex, l.LineIndex - f.LineIndex, l.EndCharacterIndex) + ? f.WithLength((l.AbsoluteIndex + l.Length) - f.AbsoluteIndex).WithLineCount(l.LineIndex - f.LineIndex).WithEndCharacterIndex(l.EndCharacterIndex) : firstSpan; expr.Children.Add(new CSharpIntermediateToken( LazyContent.Create(mergedContent, static s => s), tokenSpan)); @@ -1458,7 +1430,7 @@ private static void LowerMixedContentFromChildren_Legacy( if (i + 1 < htmlAttr.Children.Count && htmlAttr.Children[i + 1].Source is { } nextSrc) { var loc = sourceDocument.Text.Lines.GetLinePosition(nextSrc.AbsoluteIndex); - emptySpan = new SourceSpan(nextSrc.FilePath, nextSrc.AbsoluteIndex, loc.Line, loc.Character, 0, 0, loc.Character); + emptySpan = nextSrc.WithLineIndex(loc.Line).WithCharacterIndex(loc.Character).WithLength(0).WithLineCount(0).WithEndCharacterIndex(loc.Character); } target.Children.Add(CreateEmptyCSharpToken(emptySpan)); @@ -1606,8 +1578,7 @@ private static void FlushPendingLiterals( if (pendingFirstSpan is { } f && pendingLastSpan is { } l) { - htmlContent.Source = new SourceSpan(f.FilePath, f.AbsoluteIndex, f.LineIndex, f.CharacterIndex, - (l.AbsoluteIndex + l.Length) - f.AbsoluteIndex, l.LineIndex - f.LineIndex + 1, l.EndCharacterIndex); + htmlContent.Source = f.WithLength((l.AbsoluteIndex + l.Length) - f.AbsoluteIndex).WithLineCount(l.LineIndex - f.LineIndex + 1).WithEndCharacterIndex(l.EndCharacterIndex); } target.Children.Add(htmlContent); @@ -1638,14 +1609,7 @@ private static void TryAddCSharpInDeclarationDiagnostic( var diagSource = exprChild.Source ?? elementNode.Source ?? SourceSpan.Undefined; if (diagSource.AbsoluteIndex > 0) { - diagSource = new SourceSpan( - diagSource.FilePath, - diagSource.AbsoluteIndex - 1, - diagSource.LineIndex, - diagSource.CharacterIndex - 1, - diagSource.Length + 1, - diagSource.LineCount, - diagSource.EndCharacterIndex); + diagSource = diagSource.WithAbsoluteIndex(diagSource.AbsoluteIndex - 1).WithCharacterIndex(diagSource.CharacterIndex - 1).WithLength(diagSource.Length + 1); } tagHelperNode.AddDiagnostic( diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.cs index 74daa974dad..47ce7a12bdc 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.cs @@ -394,8 +394,7 @@ private static void MergeAdjacentHtmlContent(IntermediateNode parent) // Adjacent nodes are sequential, so next always ends after current. var end = ns.AbsoluteIndex + ns.Length; var lineCount = (ns.LineIndex + ns.LineCount) - cs.LineIndex; - current.Source = new SourceSpan(cs.FilePath, cs.AbsoluteIndex, cs.LineIndex, cs.CharacterIndex, - end - cs.AbsoluteIndex, lineCount, ns.EndCharacterIndex); + current.Source = cs.WithLength(end - cs.AbsoluteIndex).WithLineCount(lineCount).WithEndCharacterIndex(ns.EndCharacterIndex); } else if (current.Source == null) { @@ -545,8 +544,7 @@ private static void ConvertUnresolvedValuesToBasicForm(HtmlAttributeIntermediate var lastSrc = htmlContent.Children[^1].Source; if (firstSrc is { } fs && lastSrc is { } ls) { - htmlContent.Source = new SourceSpan(fs.FilePath, fs.AbsoluteIndex, fs.LineIndex, fs.CharacterIndex, - (ls.AbsoluteIndex + ls.Length) - fs.AbsoluteIndex, ls.LineIndex - fs.LineIndex, ls.EndCharacterIndex); + htmlContent.Source = fs.WithLength((ls.AbsoluteIndex + ls.Length) - fs.AbsoluteIndex).WithLineCount(ls.LineIndex - fs.LineIndex).WithEndCharacterIndex(ls.EndCharacterIndex); } } @@ -779,8 +777,7 @@ private static SourceSpan MergeSpans(SourceSpan a, SourceSpan b) var first = a.AbsoluteIndex <= b.AbsoluteIndex ? a : b; var last = a.AbsoluteIndex + a.Length >= b.AbsoluteIndex + b.Length ? a : b; var lineCount = (last.LineIndex + last.LineCount) - first.LineIndex; - return new SourceSpan(first.FilePath, start, first.LineIndex, first.CharacterIndex, - end - start, lineCount, last.EndCharacterIndex); + return first.WithAbsoluteIndex(start).WithLength(end - start).WithLineCount(lineCount).WithEndCharacterIndex(last.EndCharacterIndex); } /// @@ -873,9 +870,7 @@ private static void EmitEscapedAtCSharpExpression( EmitExplicitExpressionTokens(expr, expressionSource.AbsoluteIndex, expressionSource.Length, sourceDocument); var exprLoc = sourceDocument.Text.Lines.GetLinePosition(expressionSource.AbsoluteIndex); - expr.Source = new SourceSpan( - expressionSource.FilePath, expressionSource.AbsoluteIndex, - exprLoc.Line, exprLoc.Character, expressionSource.Length, 0, exprLoc.Character + expressionSource.Length); + expr.Source = expressionSource.WithLineIndex(exprLoc.Line).WithCharacterIndex(exprLoc.Character).WithLineCount(0).WithEndCharacterIndex(exprLoc.Character + expressionSource.Length); target.Children.Add(expr); } @@ -957,14 +952,7 @@ private static bool AreAllChildrenOfType(IntermediateNodeCollection children) return null; } - return new SourceSpan( - s.FilePath, - s.AbsoluteIndex - prefixLength, - s.LineIndex, - s.CharacterIndex - prefixLength, - s.Length + prefixLength, - s.LineCount, - s.EndCharacterIndex); + return s.WithAbsoluteIndex(s.AbsoluteIndex - prefixLength).WithCharacterIndex(s.CharacterIndex - prefixLength).WithLength(s.Length + prefixLength); } /// @@ -1057,9 +1045,7 @@ private static void TryAddMalformedEndTagDiagnostic( var diagSource = elementNode.Source; if (elementNode.EndTagSpan is SourceSpan ets) { - diagSource = new SourceSpan( - ets.FilePath, ets.AbsoluteIndex + 2, ets.LineIndex, ets.CharacterIndex + 2, - tagName.Length, 0, ets.CharacterIndex + 2 + tagName.Length); + diagSource = ets.WithAbsoluteIndex(ets.AbsoluteIndex + 2).WithCharacterIndex(ets.CharacterIndex + 2).WithLength(tagName.Length).WithLineCount(0).WithEndCharacterIndex(ets.CharacterIndex + 2 + tagName.Length); } if (diagSource is SourceSpan ds) From 55b61657e45741921deb833874fd75dcebbd7f8e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:26:34 +0000 Subject: [PATCH 4/6] Break With* chains onto multiple lines for readability Agent-Logs-Url: https://github.com/dotnet/razor/sessions/97b3a1f3-564f-4a27-84ab-64ad0fa5ccf5 Co-authored-by: chsienki <16246502+chsienki@users.noreply.github.com> --- ...olutionPhase.ComponentTagHelperResolver.cs | 27 ++++-- ...ResolutionPhase.LegacyTagHelperResolver.cs | 83 +++++++++++++++---- .../DefaultTagHelperResolutionPhase.cs | 28 +++++-- 3 files changed, 111 insertions(+), 27 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs index 484e044eed2..9145c80a9ed 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.ComponentTagHelperResolver.cs @@ -195,7 +195,9 @@ private void ConvertToUnresolvedDirectiveAttribute( var directiveNameSpan = nameSpan; if (directiveNameSpan is SourceSpan ns && attributeName.StartsWith('@')) { - directiveNameSpan = ns.WithAbsoluteIndex(ns.AbsoluteIndex + 1).WithCharacterIndex(ns.CharacterIndex + 1).WithLength(ns.Length - 1); + directiveNameSpan = ns.WithAbsoluteIndex(ns.AbsoluteIndex + 1) + .WithCharacterIndex(ns.CharacterIndex + 1) + .WithLength(ns.Length - 1); } // Strip parameter suffix from OriginalAttributeSpan for parameter matches @@ -203,7 +205,8 @@ private void ConvertToUnresolvedDirectiveAttribute( if (match.IsParameterMatch && directiveAttributeName.HasParameter && parameterOriginalSpan is SourceSpan ps) { var nameWithoutParamLen = directiveAttributeName.TextWithoutParameter.Length; - parameterOriginalSpan = ps.WithLength(nameWithoutParamLen).WithEndCharacterIndex(ps.CharacterIndex + nameWithoutParamLen); + parameterOriginalSpan = ps.WithLength(nameWithoutParamLen) + .WithEndCharacterIndex(ps.CharacterIndex + nameWithoutParamLen); } IntermediateNode directiveNode = match.IsParameterMatch && directiveAttributeName.HasParameter @@ -581,7 +584,9 @@ private static void MergeAdjacentCSharpTokens(IntermediateNode node) if (firstSpan is { } first && lastSpan is { } last) { var endAbsolute = last.AbsoluteIndex + last.Length; - mergedSpan = first.WithLength(endAbsolute - first.AbsoluteIndex).WithLineCount(last.LineIndex - first.LineIndex + 1).WithEndCharacterIndex(last.EndCharacterIndex); + mergedSpan = first.WithLength(endAbsolute - first.AbsoluteIndex) + .WithLineCount(last.LineIndex - first.LineIndex + 1) + .WithEndCharacterIndex(last.EndCharacterIndex); } var content = sb.ToString(); @@ -870,7 +875,9 @@ private static void ConvertComponentAttributeToTagHelper( var directiveNameSpan = attributeNameSpan; if (directiveNameSpan is SourceSpan nameSpan && attributeName.StartsWith('@')) { - directiveNameSpan = nameSpan.WithAbsoluteIndex(nameSpan.AbsoluteIndex + 1).WithCharacterIndex(nameSpan.CharacterIndex + 1).WithLength(nameSpan.Length - 1); + directiveNameSpan = nameSpan.WithAbsoluteIndex(nameSpan.AbsoluteIndex + 1) + .WithCharacterIndex(nameSpan.CharacterIndex + 1) + .WithLength(nameSpan.Length - 1); } IntermediateNode directiveNode = match.IsParameterMatch && directiveAttributeName.HasParameter @@ -1038,7 +1045,11 @@ private static void CopyAsTagHelperAttributeValues(HtmlAttributeIntermediateNode var nameCharIndex = attrSource.CharacterIndex + nameIndex; - return attrSource.WithAbsoluteIndex(attrSource.AbsoluteIndex + nameIndex).WithCharacterIndex(nameCharIndex).WithLength(nameLength).WithLineCount(0).WithEndCharacterIndex(nameCharIndex + nameLength); + return attrSource.WithAbsoluteIndex(attrSource.AbsoluteIndex + nameIndex) + .WithCharacterIndex(nameCharIndex) + .WithLength(nameLength) + .WithLineCount(0) + .WithEndCharacterIndex(nameCharIndex + nameLength); } private static SourceSpan? ComputeAttributeValueSpan(HtmlAttributeIntermediateNode htmlAttr) @@ -1087,7 +1098,11 @@ private static void CopyAsTagHelperAttributeValues(HtmlAttributeIntermediateNode var valueCharIndex = attrSource.CharacterIndex + valueStart; - return attrSource.WithAbsoluteIndex(attrSource.AbsoluteIndex + valueStart).WithCharacterIndex(valueCharIndex).WithLength(valueLength).WithLineCount(0).WithEndCharacterIndex(valueCharIndex + valueLength); + return attrSource.WithAbsoluteIndex(attrSource.AbsoluteIndex + valueStart) + .WithCharacterIndex(valueCharIndex) + .WithLength(valueLength) + .WithLineCount(0) + .WithEndCharacterIndex(valueCharIndex + valueLength); } /// diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs index d8effcf34d3..54624b13f70 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs @@ -600,7 +600,10 @@ private static void ConvertMixedLiteralAndExpressionValue(IntermediateNode targe // Compute value source span with correct line/char positions. var valueCharIndex = attrSource.CharacterIndex + prefix.Length + (attrSource.AbsoluteIndex + prefix.Length < valueStart ? 1 : 0); var valueEndCharIndex = attrSource.EndCharacterIndex - suffix.Length; - var valueSource = attrSource.WithAbsoluteIndex(valueStart).WithCharacterIndex(valueCharIndex).WithLength(valueLength).WithEndCharacterIndex(valueEndCharIndex); + var valueSource = attrSource.WithAbsoluteIndex(valueStart) + .WithCharacterIndex(valueCharIndex) + .WithLength(valueLength) + .WithEndCharacterIndex(valueEndCharIndex); targetNode.Children.Add(new CSharpIntermediateToken(sourceText, valueSource)); } } @@ -676,10 +679,18 @@ private static void ConvertEscapedAtExpressionValue(IntermediateNode targetNode, var transAbsIdx = atSrc.AbsoluteIndex + 2; var transCharIdx = atSrc.CharacterIndex + 2; // Empty token (MarkupEphemeralTextLiteral equivalent) - var emptySource = atSrc.WithAbsoluteIndex(transAbsIdx).WithCharacterIndex(transCharIdx).WithLength(0).WithLineCount(0).WithEndCharacterIndex(transCharIdx); + var emptySource = atSrc.WithAbsoluteIndex(transAbsIdx) + .WithCharacterIndex(transCharIdx) + .WithLength(0) + .WithLineCount(0) + .WithEndCharacterIndex(transCharIdx); targetNode.Children.Add(new CSharpIntermediateToken(string.Empty, emptySource)); // @ transition token - var transSource = atSrc.WithAbsoluteIndex(transAbsIdx).WithCharacterIndex(transCharIdx).WithLength(1).WithLineCount(0).WithEndCharacterIndex(transCharIdx + 1); + var transSource = atSrc.WithAbsoluteIndex(transAbsIdx) + .WithCharacterIndex(transCharIdx) + .WithLength(1) + .WithLineCount(0) + .WithEndCharacterIndex(transCharIdx + 1); targetNode.Children.Add(new CSharpIntermediateToken("@", transSource)); } @@ -691,7 +702,11 @@ private static void ConvertEscapedAtExpressionValue(IntermediateNode targetNode, { var openAbsIndex = innerSource.AbsoluteIndex - 1; var openCharIndex = innerSource.CharacterIndex - 1; - var openSource = innerSource.WithAbsoluteIndex(openAbsIndex).WithCharacterIndex(openCharIndex).WithLength(1).WithLineCount(0).WithEndCharacterIndex(openCharIndex + 1); + var openSource = innerSource.WithAbsoluteIndex(openAbsIndex) + .WithCharacterIndex(openCharIndex) + .WithLength(1) + .WithLineCount(0) + .WithEndCharacterIndex(openCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken("(", openSource)); foreach (var innerChild in csharpExprValue.Children) @@ -704,7 +719,11 @@ private static void ConvertEscapedAtExpressionValue(IntermediateNode targetNode, { var closeAbsIndex = lastSource.AbsoluteIndex + lastSource.Length; var closeCharIndex = lastSource.EndCharacterIndex; - var closeSource = lastSource.WithAbsoluteIndex(closeAbsIndex).WithCharacterIndex(closeCharIndex).WithLength(1).WithLineCount(0).WithEndCharacterIndex(closeCharIndex + 1); + var closeSource = lastSource.WithAbsoluteIndex(closeAbsIndex) + .WithCharacterIndex(closeCharIndex) + .WithLength(1) + .WithLineCount(0) + .WithEndCharacterIndex(closeCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken(")", closeSource)); } } @@ -740,7 +759,11 @@ private static void ConvertPureCSharpExpressionValue(IntermediateNode targetNode // to match baseline's separate token structure. var openParenAbsIndex = valueStart + 1; // after @ var openParenCharIndex = attrSource.CharacterIndex + prefix.Length + 1; - var openParenSource = attrSource.WithAbsoluteIndex(openParenAbsIndex).WithCharacterIndex(openParenCharIndex).WithLength(1).WithLineCount(0).WithEndCharacterIndex(openParenCharIndex + 1); + var openParenSource = attrSource.WithAbsoluteIndex(openParenAbsIndex) + .WithCharacterIndex(openParenCharIndex) + .WithLength(1) + .WithLineCount(0) + .WithEndCharacterIndex(openParenCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken("(", openParenSource)); // Inner expression content from the CSharpExpressionAttributeValueIntermediateNode @@ -764,7 +787,11 @@ private static void ConvertPureCSharpExpressionValue(IntermediateNode targetNode var closeParenAbsIndex = valueStart + valueLength - 1; // last char var closeParenCharIndex = attrSource.CharacterIndex + prefix.Length + valueLength - 1; - var closeParenSource = attrSource.WithAbsoluteIndex(closeParenAbsIndex).WithCharacterIndex(closeParenCharIndex).WithLength(1).WithLineCount(0).WithEndCharacterIndex(closeParenCharIndex + 1); + var closeParenSource = attrSource.WithAbsoluteIndex(closeParenAbsIndex) + .WithCharacterIndex(closeParenCharIndex) + .WithLength(1) + .WithLineCount(0) + .WithEndCharacterIndex(closeParenCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken(")", closeParenSource)); } else @@ -869,7 +896,11 @@ private static void UnwrapCSharpAttributeValue( var valueAbsIndex = attrSource.AbsoluteIndex + prefix.Length; var valueCharIndex = attrSource.CharacterIndex + prefix.Length; - return attrSource.WithAbsoluteIndex(valueAbsIndex).WithCharacterIndex(valueCharIndex).WithLength(0).WithLineCount(0).WithEndCharacterIndex(valueCharIndex); + return attrSource.WithAbsoluteIndex(valueAbsIndex) + .WithCharacterIndex(valueCharIndex) + .WithLength(0) + .WithLineCount(0) + .WithEndCharacterIndex(valueCharIndex); } /// @@ -1135,7 +1166,9 @@ private static void MergeAdjacentHtmlContent(IntermediateNode parent, int index, current.Children.AddRange(next.Children); if (current.Source is SourceSpan currentSource && next.Source is SourceSpan nextSource) { - current.Source = currentSource.WithLength((nextSource.AbsoluteIndex + nextSource.Length) - currentSource.AbsoluteIndex).WithLineCount(nextSource.LineCount).WithEndCharacterIndex(nextSource.EndCharacterIndex); + current.Source = currentSource.WithLength((nextSource.AbsoluteIndex + nextSource.Length) - currentSource.AbsoluteIndex) + .WithLineCount(nextSource.LineCount) + .WithEndCharacterIndex(nextSource.EndCharacterIndex); } else if (current.Source == null) { @@ -1320,13 +1353,23 @@ private static void LowerImplicitExpressionAttribute_Legacy( var openLoc = sourceDocument.Text.Lines.GetLinePosition(contentStart); var closeLoc = sourceDocument.Text.Lines.GetLinePosition(contentStart + contentLength - 1); - expr.Source = exprSource.WithAbsoluteIndex(contentStart).WithLineIndex(openLoc.Line).WithCharacterIndex(openLoc.Character).WithLength(contentLength).WithLineCount(0).WithEndCharacterIndex(closeLoc.Character + 1); + expr.Source = exprSource.WithAbsoluteIndex(contentStart) + .WithLineIndex(openLoc.Line) + .WithCharacterIndex(openLoc.Character) + .WithLength(contentLength) + .WithLineCount(0) + .WithEndCharacterIndex(closeLoc.Character + 1); } else { // Implicit expression: single token. var contentLocation = sourceDocument.Text.Lines.GetLinePosition(contentStart); - var contentSpan = exprSource.WithAbsoluteIndex(contentStart).WithLineIndex(contentLocation.Line).WithCharacterIndex(contentLocation.Character).WithLength(contentLength).WithLineCount(0).WithEndCharacterIndex(contentLocation.Character + contentLength); + var contentSpan = exprSource.WithAbsoluteIndex(contentStart) + .WithLineIndex(contentLocation.Line) + .WithCharacterIndex(contentLocation.Character) + .WithLength(contentLength) + .WithLineCount(0) + .WithEndCharacterIndex(contentLocation.Character + contentLength); expr.Children.Add(new CSharpIntermediateToken( LazyContent.Create(text, static s => s), contentSpan)); expr.Source = contentSpan; @@ -1356,7 +1399,9 @@ private static void LowerImplicitExpressionAttribute_Legacy( var mergedContent = sb.ToString(); var tokenSpan = firstSpan is { } f && lastSpan is { } l - ? f.WithLength((l.AbsoluteIndex + l.Length) - f.AbsoluteIndex).WithLineCount(l.LineIndex - f.LineIndex).WithEndCharacterIndex(l.EndCharacterIndex) + ? f.WithLength((l.AbsoluteIndex + l.Length) - f.AbsoluteIndex) + .WithLineCount(l.LineIndex - f.LineIndex) + .WithEndCharacterIndex(l.EndCharacterIndex) : firstSpan; expr.Children.Add(new CSharpIntermediateToken( LazyContent.Create(mergedContent, static s => s), tokenSpan)); @@ -1430,7 +1475,11 @@ private static void LowerMixedContentFromChildren_Legacy( if (i + 1 < htmlAttr.Children.Count && htmlAttr.Children[i + 1].Source is { } nextSrc) { var loc = sourceDocument.Text.Lines.GetLinePosition(nextSrc.AbsoluteIndex); - emptySpan = nextSrc.WithLineIndex(loc.Line).WithCharacterIndex(loc.Character).WithLength(0).WithLineCount(0).WithEndCharacterIndex(loc.Character); + emptySpan = nextSrc.WithLineIndex(loc.Line) + .WithCharacterIndex(loc.Character) + .WithLength(0) + .WithLineCount(0) + .WithEndCharacterIndex(loc.Character); } target.Children.Add(CreateEmptyCSharpToken(emptySpan)); @@ -1578,7 +1627,9 @@ private static void FlushPendingLiterals( if (pendingFirstSpan is { } f && pendingLastSpan is { } l) { - htmlContent.Source = f.WithLength((l.AbsoluteIndex + l.Length) - f.AbsoluteIndex).WithLineCount(l.LineIndex - f.LineIndex + 1).WithEndCharacterIndex(l.EndCharacterIndex); + htmlContent.Source = f.WithLength((l.AbsoluteIndex + l.Length) - f.AbsoluteIndex) + .WithLineCount(l.LineIndex - f.LineIndex + 1) + .WithEndCharacterIndex(l.EndCharacterIndex); } target.Children.Add(htmlContent); @@ -1609,7 +1660,9 @@ private static void TryAddCSharpInDeclarationDiagnostic( var diagSource = exprChild.Source ?? elementNode.Source ?? SourceSpan.Undefined; if (diagSource.AbsoluteIndex > 0) { - diagSource = diagSource.WithAbsoluteIndex(diagSource.AbsoluteIndex - 1).WithCharacterIndex(diagSource.CharacterIndex - 1).WithLength(diagSource.Length + 1); + diagSource = diagSource.WithAbsoluteIndex(diagSource.AbsoluteIndex - 1) + .WithCharacterIndex(diagSource.CharacterIndex - 1) + .WithLength(diagSource.Length + 1); } tagHelperNode.AddDiagnostic( diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.cs index 47ce7a12bdc..49837f9cd2e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.cs @@ -394,7 +394,9 @@ private static void MergeAdjacentHtmlContent(IntermediateNode parent) // Adjacent nodes are sequential, so next always ends after current. var end = ns.AbsoluteIndex + ns.Length; var lineCount = (ns.LineIndex + ns.LineCount) - cs.LineIndex; - current.Source = cs.WithLength(end - cs.AbsoluteIndex).WithLineCount(lineCount).WithEndCharacterIndex(ns.EndCharacterIndex); + current.Source = cs.WithLength(end - cs.AbsoluteIndex) + .WithLineCount(lineCount) + .WithEndCharacterIndex(ns.EndCharacterIndex); } else if (current.Source == null) { @@ -544,7 +546,9 @@ private static void ConvertUnresolvedValuesToBasicForm(HtmlAttributeIntermediate var lastSrc = htmlContent.Children[^1].Source; if (firstSrc is { } fs && lastSrc is { } ls) { - htmlContent.Source = fs.WithLength((ls.AbsoluteIndex + ls.Length) - fs.AbsoluteIndex).WithLineCount(ls.LineIndex - fs.LineIndex).WithEndCharacterIndex(ls.EndCharacterIndex); + htmlContent.Source = fs.WithLength((ls.AbsoluteIndex + ls.Length) - fs.AbsoluteIndex) + .WithLineCount(ls.LineIndex - fs.LineIndex) + .WithEndCharacterIndex(ls.EndCharacterIndex); } } @@ -777,7 +781,10 @@ private static SourceSpan MergeSpans(SourceSpan a, SourceSpan b) var first = a.AbsoluteIndex <= b.AbsoluteIndex ? a : b; var last = a.AbsoluteIndex + a.Length >= b.AbsoluteIndex + b.Length ? a : b; var lineCount = (last.LineIndex + last.LineCount) - first.LineIndex; - return first.WithAbsoluteIndex(start).WithLength(end - start).WithLineCount(lineCount).WithEndCharacterIndex(last.EndCharacterIndex); + return first.WithAbsoluteIndex(start) + .WithLength(end - start) + .WithLineCount(lineCount) + .WithEndCharacterIndex(last.EndCharacterIndex); } /// @@ -870,7 +877,10 @@ private static void EmitEscapedAtCSharpExpression( EmitExplicitExpressionTokens(expr, expressionSource.AbsoluteIndex, expressionSource.Length, sourceDocument); var exprLoc = sourceDocument.Text.Lines.GetLinePosition(expressionSource.AbsoluteIndex); - expr.Source = expressionSource.WithLineIndex(exprLoc.Line).WithCharacterIndex(exprLoc.Character).WithLineCount(0).WithEndCharacterIndex(exprLoc.Character + expressionSource.Length); + expr.Source = expressionSource.WithLineIndex(exprLoc.Line) + .WithCharacterIndex(exprLoc.Character) + .WithLineCount(0) + .WithEndCharacterIndex(exprLoc.Character + expressionSource.Length); target.Children.Add(expr); } @@ -952,7 +962,9 @@ private static bool AreAllChildrenOfType(IntermediateNodeCollection children) return null; } - return s.WithAbsoluteIndex(s.AbsoluteIndex - prefixLength).WithCharacterIndex(s.CharacterIndex - prefixLength).WithLength(s.Length + prefixLength); + return s.WithAbsoluteIndex(s.AbsoluteIndex - prefixLength) + .WithCharacterIndex(s.CharacterIndex - prefixLength) + .WithLength(s.Length + prefixLength); } /// @@ -1045,7 +1057,11 @@ private static void TryAddMalformedEndTagDiagnostic( var diagSource = elementNode.Source; if (elementNode.EndTagSpan is SourceSpan ets) { - diagSource = ets.WithAbsoluteIndex(ets.AbsoluteIndex + 2).WithCharacterIndex(ets.CharacterIndex + 2).WithLength(tagName.Length).WithLineCount(0).WithEndCharacterIndex(ets.CharacterIndex + 2 + tagName.Length); + diagSource = ets.WithAbsoluteIndex(ets.AbsoluteIndex + 2) + .WithCharacterIndex(ets.CharacterIndex + 2) + .WithLength(tagName.Length) + .WithLineCount(0) + .WithEndCharacterIndex(ets.CharacterIndex + 2 + tagName.Length); } if (diagSource is SourceSpan ds) From 46e8e5be1c61a8ae312601b8fb3149144ca841c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 21:30:08 +0000 Subject: [PATCH 5/6] Revert With* chains in LegacyTagHelperResolver.cs to new SourceSpan(...) constructors Agent-Logs-Url: https://github.com/dotnet/razor/sessions/41e03d10-22e7-4198-91ac-8127387721ae Co-authored-by: chsienki <16246502+chsienki@users.noreply.github.com> --- ...ResolutionPhase.LegacyTagHelperResolver.cs | 83 ++++--------------- 1 file changed, 15 insertions(+), 68 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs index 54624b13f70..e802ce00a4c 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs @@ -600,10 +600,7 @@ private static void ConvertMixedLiteralAndExpressionValue(IntermediateNode targe // Compute value source span with correct line/char positions. var valueCharIndex = attrSource.CharacterIndex + prefix.Length + (attrSource.AbsoluteIndex + prefix.Length < valueStart ? 1 : 0); var valueEndCharIndex = attrSource.EndCharacterIndex - suffix.Length; - var valueSource = attrSource.WithAbsoluteIndex(valueStart) - .WithCharacterIndex(valueCharIndex) - .WithLength(valueLength) - .WithEndCharacterIndex(valueEndCharIndex); + var valueSource = new SourceSpan(attrSource.FilePath, valueStart, attrSource.LineIndex, valueCharIndex, valueLength, attrSource.LineCount, valueEndCharIndex); targetNode.Children.Add(new CSharpIntermediateToken(sourceText, valueSource)); } } @@ -679,18 +676,10 @@ private static void ConvertEscapedAtExpressionValue(IntermediateNode targetNode, var transAbsIdx = atSrc.AbsoluteIndex + 2; var transCharIdx = atSrc.CharacterIndex + 2; // Empty token (MarkupEphemeralTextLiteral equivalent) - var emptySource = atSrc.WithAbsoluteIndex(transAbsIdx) - .WithCharacterIndex(transCharIdx) - .WithLength(0) - .WithLineCount(0) - .WithEndCharacterIndex(transCharIdx); + var emptySource = new SourceSpan(atSrc.FilePath, transAbsIdx, atSrc.LineIndex, transCharIdx, 0, 0, transCharIdx); targetNode.Children.Add(new CSharpIntermediateToken(string.Empty, emptySource)); // @ transition token - var transSource = atSrc.WithAbsoluteIndex(transAbsIdx) - .WithCharacterIndex(transCharIdx) - .WithLength(1) - .WithLineCount(0) - .WithEndCharacterIndex(transCharIdx + 1); + var transSource = new SourceSpan(atSrc.FilePath, transAbsIdx, atSrc.LineIndex, transCharIdx, 1, 0, transCharIdx + 1); targetNode.Children.Add(new CSharpIntermediateToken("@", transSource)); } @@ -702,11 +691,7 @@ private static void ConvertEscapedAtExpressionValue(IntermediateNode targetNode, { var openAbsIndex = innerSource.AbsoluteIndex - 1; var openCharIndex = innerSource.CharacterIndex - 1; - var openSource = innerSource.WithAbsoluteIndex(openAbsIndex) - .WithCharacterIndex(openCharIndex) - .WithLength(1) - .WithLineCount(0) - .WithEndCharacterIndex(openCharIndex + 1); + var openSource = new SourceSpan(innerSource.FilePath, openAbsIndex, innerSource.LineIndex, openCharIndex, 1, 0, openCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken("(", openSource)); foreach (var innerChild in csharpExprValue.Children) @@ -719,11 +704,7 @@ private static void ConvertEscapedAtExpressionValue(IntermediateNode targetNode, { var closeAbsIndex = lastSource.AbsoluteIndex + lastSource.Length; var closeCharIndex = lastSource.EndCharacterIndex; - var closeSource = lastSource.WithAbsoluteIndex(closeAbsIndex) - .WithCharacterIndex(closeCharIndex) - .WithLength(1) - .WithLineCount(0) - .WithEndCharacterIndex(closeCharIndex + 1); + var closeSource = new SourceSpan(lastSource.FilePath, closeAbsIndex, lastSource.LineIndex, closeCharIndex, 1, 0, closeCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken(")", closeSource)); } } @@ -759,11 +740,7 @@ private static void ConvertPureCSharpExpressionValue(IntermediateNode targetNode // to match baseline's separate token structure. var openParenAbsIndex = valueStart + 1; // after @ var openParenCharIndex = attrSource.CharacterIndex + prefix.Length + 1; - var openParenSource = attrSource.WithAbsoluteIndex(openParenAbsIndex) - .WithCharacterIndex(openParenCharIndex) - .WithLength(1) - .WithLineCount(0) - .WithEndCharacterIndex(openParenCharIndex + 1); + var openParenSource = new SourceSpan(attrSource.FilePath, openParenAbsIndex, attrSource.LineIndex, openParenCharIndex, 1, 0, openParenCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken("(", openParenSource)); // Inner expression content from the CSharpExpressionAttributeValueIntermediateNode @@ -787,11 +764,7 @@ private static void ConvertPureCSharpExpressionValue(IntermediateNode targetNode var closeParenAbsIndex = valueStart + valueLength - 1; // last char var closeParenCharIndex = attrSource.CharacterIndex + prefix.Length + valueLength - 1; - var closeParenSource = attrSource.WithAbsoluteIndex(closeParenAbsIndex) - .WithCharacterIndex(closeParenCharIndex) - .WithLength(1) - .WithLineCount(0) - .WithEndCharacterIndex(closeParenCharIndex + 1); + var closeParenSource = new SourceSpan(attrSource.FilePath, closeParenAbsIndex, attrSource.LineIndex, closeParenCharIndex, 1, 0, closeParenCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken(")", closeParenSource)); } else @@ -896,11 +869,7 @@ private static void UnwrapCSharpAttributeValue( var valueAbsIndex = attrSource.AbsoluteIndex + prefix.Length; var valueCharIndex = attrSource.CharacterIndex + prefix.Length; - return attrSource.WithAbsoluteIndex(valueAbsIndex) - .WithCharacterIndex(valueCharIndex) - .WithLength(0) - .WithLineCount(0) - .WithEndCharacterIndex(valueCharIndex); + return new SourceSpan(attrSource.FilePath, valueAbsIndex, attrSource.LineIndex, valueCharIndex, 0, 0, valueCharIndex); } /// @@ -1166,9 +1135,7 @@ private static void MergeAdjacentHtmlContent(IntermediateNode parent, int index, current.Children.AddRange(next.Children); if (current.Source is SourceSpan currentSource && next.Source is SourceSpan nextSource) { - current.Source = currentSource.WithLength((nextSource.AbsoluteIndex + nextSource.Length) - currentSource.AbsoluteIndex) - .WithLineCount(nextSource.LineCount) - .WithEndCharacterIndex(nextSource.EndCharacterIndex); + current.Source = new SourceSpan(currentSource.FilePath, currentSource.AbsoluteIndex, currentSource.LineIndex, currentSource.CharacterIndex, (nextSource.AbsoluteIndex + nextSource.Length) - currentSource.AbsoluteIndex, nextSource.LineCount, nextSource.EndCharacterIndex); } else if (current.Source == null) { @@ -1353,23 +1320,13 @@ private static void LowerImplicitExpressionAttribute_Legacy( var openLoc = sourceDocument.Text.Lines.GetLinePosition(contentStart); var closeLoc = sourceDocument.Text.Lines.GetLinePosition(contentStart + contentLength - 1); - expr.Source = exprSource.WithAbsoluteIndex(contentStart) - .WithLineIndex(openLoc.Line) - .WithCharacterIndex(openLoc.Character) - .WithLength(contentLength) - .WithLineCount(0) - .WithEndCharacterIndex(closeLoc.Character + 1); + expr.Source = new SourceSpan(exprSource.FilePath, contentStart, openLoc.Line, openLoc.Character, contentLength, 0, closeLoc.Character + 1); } else { // Implicit expression: single token. var contentLocation = sourceDocument.Text.Lines.GetLinePosition(contentStart); - var contentSpan = exprSource.WithAbsoluteIndex(contentStart) - .WithLineIndex(contentLocation.Line) - .WithCharacterIndex(contentLocation.Character) - .WithLength(contentLength) - .WithLineCount(0) - .WithEndCharacterIndex(contentLocation.Character + contentLength); + var contentSpan = new SourceSpan(exprSource.FilePath, contentStart, contentLocation.Line, contentLocation.Character, contentLength, 0, contentLocation.Character + contentLength); expr.Children.Add(new CSharpIntermediateToken( LazyContent.Create(text, static s => s), contentSpan)); expr.Source = contentSpan; @@ -1399,9 +1356,7 @@ private static void LowerImplicitExpressionAttribute_Legacy( var mergedContent = sb.ToString(); var tokenSpan = firstSpan is { } f && lastSpan is { } l - ? f.WithLength((l.AbsoluteIndex + l.Length) - f.AbsoluteIndex) - .WithLineCount(l.LineIndex - f.LineIndex) - .WithEndCharacterIndex(l.EndCharacterIndex) + ? new SourceSpan(f.FilePath, f.AbsoluteIndex, f.LineIndex, f.CharacterIndex, (l.AbsoluteIndex + l.Length) - f.AbsoluteIndex, l.LineIndex - f.LineIndex, l.EndCharacterIndex) : firstSpan; expr.Children.Add(new CSharpIntermediateToken( LazyContent.Create(mergedContent, static s => s), tokenSpan)); @@ -1475,11 +1430,7 @@ private static void LowerMixedContentFromChildren_Legacy( if (i + 1 < htmlAttr.Children.Count && htmlAttr.Children[i + 1].Source is { } nextSrc) { var loc = sourceDocument.Text.Lines.GetLinePosition(nextSrc.AbsoluteIndex); - emptySpan = nextSrc.WithLineIndex(loc.Line) - .WithCharacterIndex(loc.Character) - .WithLength(0) - .WithLineCount(0) - .WithEndCharacterIndex(loc.Character); + emptySpan = new SourceSpan(nextSrc.FilePath, nextSrc.AbsoluteIndex, loc.Line, loc.Character, 0, 0, loc.Character); } target.Children.Add(CreateEmptyCSharpToken(emptySpan)); @@ -1627,9 +1578,7 @@ private static void FlushPendingLiterals( if (pendingFirstSpan is { } f && pendingLastSpan is { } l) { - htmlContent.Source = f.WithLength((l.AbsoluteIndex + l.Length) - f.AbsoluteIndex) - .WithLineCount(l.LineIndex - f.LineIndex + 1) - .WithEndCharacterIndex(l.EndCharacterIndex); + htmlContent.Source = new SourceSpan(f.FilePath, f.AbsoluteIndex, f.LineIndex, f.CharacterIndex, (l.AbsoluteIndex + l.Length) - f.AbsoluteIndex, l.LineIndex - f.LineIndex + 1, l.EndCharacterIndex); } target.Children.Add(htmlContent); @@ -1660,9 +1609,7 @@ private static void TryAddCSharpInDeclarationDiagnostic( var diagSource = exprChild.Source ?? elementNode.Source ?? SourceSpan.Undefined; if (diagSource.AbsoluteIndex > 0) { - diagSource = diagSource.WithAbsoluteIndex(diagSource.AbsoluteIndex - 1) - .WithCharacterIndex(diagSource.CharacterIndex - 1) - .WithLength(diagSource.Length + 1); + diagSource = new SourceSpan(diagSource.FilePath, diagSource.AbsoluteIndex - 1, diagSource.LineIndex, diagSource.CharacterIndex - 1, diagSource.Length + 1, diagSource.LineCount, diagSource.EndCharacterIndex); } tagHelperNode.AddDiagnostic( From c7b066fe2337dfdd431f1eba42a0d575262b5ae8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 03:53:15 +0000 Subject: [PATCH 6/6] Restore original multi-line formatting for reverted new SourceSpan(...) calls Agent-Logs-Url: https://github.com/dotnet/razor/sessions/d7f252db-e4ba-4bae-81d4-4145549e627d Co-authored-by: chsienki <16246502+chsienki@users.noreply.github.com> --- ...ResolutionPhase.LegacyTagHelperResolver.cs | 54 +++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs index e802ce00a4c..742bb3ce48a 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultTagHelperResolutionPhase.LegacyTagHelperResolver.cs @@ -600,7 +600,9 @@ private static void ConvertMixedLiteralAndExpressionValue(IntermediateNode targe // Compute value source span with correct line/char positions. var valueCharIndex = attrSource.CharacterIndex + prefix.Length + (attrSource.AbsoluteIndex + prefix.Length < valueStart ? 1 : 0); var valueEndCharIndex = attrSource.EndCharacterIndex - suffix.Length; - var valueSource = new SourceSpan(attrSource.FilePath, valueStart, attrSource.LineIndex, valueCharIndex, valueLength, attrSource.LineCount, valueEndCharIndex); + var valueSource = new SourceSpan( + attrSource.FilePath, valueStart, attrSource.LineIndex, valueCharIndex, + valueLength, attrSource.LineCount, valueEndCharIndex); targetNode.Children.Add(new CSharpIntermediateToken(sourceText, valueSource)); } } @@ -740,7 +742,9 @@ private static void ConvertPureCSharpExpressionValue(IntermediateNode targetNode // to match baseline's separate token structure. var openParenAbsIndex = valueStart + 1; // after @ var openParenCharIndex = attrSource.CharacterIndex + prefix.Length + 1; - var openParenSource = new SourceSpan(attrSource.FilePath, openParenAbsIndex, attrSource.LineIndex, openParenCharIndex, 1, 0, openParenCharIndex + 1); + var openParenSource = new SourceSpan( + attrSource.FilePath, openParenAbsIndex, attrSource.LineIndex, openParenCharIndex, + 1, 0, openParenCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken("(", openParenSource)); // Inner expression content from the CSharpExpressionAttributeValueIntermediateNode @@ -764,7 +768,9 @@ private static void ConvertPureCSharpExpressionValue(IntermediateNode targetNode var closeParenAbsIndex = valueStart + valueLength - 1; // last char var closeParenCharIndex = attrSource.CharacterIndex + prefix.Length + valueLength - 1; - var closeParenSource = new SourceSpan(attrSource.FilePath, closeParenAbsIndex, attrSource.LineIndex, closeParenCharIndex, 1, 0, closeParenCharIndex + 1); + var closeParenSource = new SourceSpan( + attrSource.FilePath, closeParenAbsIndex, attrSource.LineIndex, closeParenCharIndex, + 1, 0, closeParenCharIndex + 1); targetNode.Children.Add(new CSharpIntermediateToken(")", closeParenSource)); } else @@ -869,7 +875,14 @@ private static void UnwrapCSharpAttributeValue( var valueAbsIndex = attrSource.AbsoluteIndex + prefix.Length; var valueCharIndex = attrSource.CharacterIndex + prefix.Length; - return new SourceSpan(attrSource.FilePath, valueAbsIndex, attrSource.LineIndex, valueCharIndex, 0, 0, valueCharIndex); + return new SourceSpan( + attrSource.FilePath, + valueAbsIndex, + attrSource.LineIndex, + valueCharIndex, + length: 0, + lineCount: 0, + endCharacterIndex: valueCharIndex); } /// @@ -1135,7 +1148,14 @@ private static void MergeAdjacentHtmlContent(IntermediateNode parent, int index, current.Children.AddRange(next.Children); if (current.Source is SourceSpan currentSource && next.Source is SourceSpan nextSource) { - current.Source = new SourceSpan(currentSource.FilePath, currentSource.AbsoluteIndex, currentSource.LineIndex, currentSource.CharacterIndex, (nextSource.AbsoluteIndex + nextSource.Length) - currentSource.AbsoluteIndex, nextSource.LineCount, nextSource.EndCharacterIndex); + current.Source = new SourceSpan( + currentSource.FilePath, + currentSource.AbsoluteIndex, + currentSource.LineIndex, + currentSource.CharacterIndex, + (nextSource.AbsoluteIndex + nextSource.Length) - currentSource.AbsoluteIndex, + nextSource.LineCount, + nextSource.EndCharacterIndex); } else if (current.Source == null) { @@ -1326,7 +1346,14 @@ private static void LowerImplicitExpressionAttribute_Legacy( { // Implicit expression: single token. var contentLocation = sourceDocument.Text.Lines.GetLinePosition(contentStart); - var contentSpan = new SourceSpan(exprSource.FilePath, contentStart, contentLocation.Line, contentLocation.Character, contentLength, 0, contentLocation.Character + contentLength); + var contentSpan = new SourceSpan( + exprSource.FilePath, + contentStart, + contentLocation.Line, + contentLocation.Character, + contentLength, + 0, + contentLocation.Character + contentLength); expr.Children.Add(new CSharpIntermediateToken( LazyContent.Create(text, static s => s), contentSpan)); expr.Source = contentSpan; @@ -1356,7 +1383,8 @@ private static void LowerImplicitExpressionAttribute_Legacy( var mergedContent = sb.ToString(); var tokenSpan = firstSpan is { } f && lastSpan is { } l - ? new SourceSpan(f.FilePath, f.AbsoluteIndex, f.LineIndex, f.CharacterIndex, (l.AbsoluteIndex + l.Length) - f.AbsoluteIndex, l.LineIndex - f.LineIndex, l.EndCharacterIndex) + ? new SourceSpan(f.FilePath, f.AbsoluteIndex, f.LineIndex, f.CharacterIndex, + (l.AbsoluteIndex + l.Length) - f.AbsoluteIndex, l.LineIndex - f.LineIndex, l.EndCharacterIndex) : firstSpan; expr.Children.Add(new CSharpIntermediateToken( LazyContent.Create(mergedContent, static s => s), tokenSpan)); @@ -1578,7 +1606,8 @@ private static void FlushPendingLiterals( if (pendingFirstSpan is { } f && pendingLastSpan is { } l) { - htmlContent.Source = new SourceSpan(f.FilePath, f.AbsoluteIndex, f.LineIndex, f.CharacterIndex, (l.AbsoluteIndex + l.Length) - f.AbsoluteIndex, l.LineIndex - f.LineIndex + 1, l.EndCharacterIndex); + htmlContent.Source = new SourceSpan(f.FilePath, f.AbsoluteIndex, f.LineIndex, f.CharacterIndex, + (l.AbsoluteIndex + l.Length) - f.AbsoluteIndex, l.LineIndex - f.LineIndex + 1, l.EndCharacterIndex); } target.Children.Add(htmlContent); @@ -1609,7 +1638,14 @@ private static void TryAddCSharpInDeclarationDiagnostic( var diagSource = exprChild.Source ?? elementNode.Source ?? SourceSpan.Undefined; if (diagSource.AbsoluteIndex > 0) { - diagSource = new SourceSpan(diagSource.FilePath, diagSource.AbsoluteIndex - 1, diagSource.LineIndex, diagSource.CharacterIndex - 1, diagSource.Length + 1, diagSource.LineCount, diagSource.EndCharacterIndex); + diagSource = new SourceSpan( + diagSource.FilePath, + diagSource.AbsoluteIndex - 1, + diagSource.LineIndex, + diagSource.CharacterIndex - 1, + diagSource.Length + 1, + diagSource.LineCount, + diagSource.EndCharacterIndex); } tagHelperNode.AddDiagnostic(