Skip to content

Commit

Permalink
Preserve encoding during DocumentState updates (#77354)
Browse files Browse the repository at this point in the history
Fixes dotnet/sdk#46780

This issue was introduced by #74623, which changed
`Document.WithSyntaxRoot` to always use the encoding specified by the
new syntax root. APIs like SyntaxRewriter do not preserve the input
encoding during node rewrites, so `Formatter.Format` would overwrite the
document syntax root with a node that did not specify any encoding. The
fix _allows_ `WithSyntaxRoot` to specify a new encoding, but for cases
where the encoding of the new syntax root is not known, we fall back to
the original behavior of preserving the encoding from the previous root
and/or document text.
  • Loading branch information
sharwell authored Feb 27, 2025
2 parents 9e6dca8 + 908cf87 commit 94cbd0b
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 4 deletions.
14 changes: 14 additions & 0 deletions src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,20 @@ internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode)

// use the encoding that we get from the new root
var encoding = newRoot.SyntaxTree.Encoding;
if (encoding is null)
{
// The new tree doesn't specify an encoding. For these cases, continue to use the previous encoding of the
// document.
if (TryGetSyntaxTree(out var priorTree))
{
// this is most likely available since UpdateTree is normally called after modifying the existing tree.
encoding = priorTree.Encoding;
}
else if (TryGetText(out var priorText))
{
encoding = priorText.Encoding;
}
}

var syntaxTreeFactory = LanguageServices.GetRequiredService<ISyntaxTreeFactoryService>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2898,13 +2898,14 @@ class C { }";
Assert.Equal(encoding.EncodingName, text.Encoding.EncodingName);
Assert.Equal(fileContent, text.ToString());

// update root blindly again, after observing encoding, see that encoding is overridden to null
// update root blindly again, after observing encoding, see that encoding is preserved
// 🐉 Tools rely on encoding preservation; see https://github.com/dotnet/sdk/issues/46780
var doc3 = document.WithSyntaxRoot(gen.CompilationUnit()); // empty CU
var doc3text = await doc3.GetTextAsync();
Assert.Null(doc3text.Encoding);
Assert.Same(text.Encoding, doc3text.Encoding);
var doc3tree = await doc3.GetSyntaxTreeAsync();
Assert.Null(doc3tree.Encoding);
Assert.Null(doc3tree.GetText().Encoding);
Assert.Same(text.Encoding, doc3tree.Encoding);
Assert.Same(text.Encoding, doc3tree.GetText().Encoding);

// change doc to have no encoding, still succeeds at writing to disk with old encoding
var root = await document.GetSyntaxRootAsync();
Expand Down

0 comments on commit 94cbd0b

Please sign in to comment.