diff --git a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Performance/CSharpUseSearchValues.Fixer.cs b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Performance/CSharpUseSearchValues.Fixer.cs index 7cc58e7efb..4e59a07c85 100644 --- a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Performance/CSharpUseSearchValues.Fixer.cs +++ b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Performance/CSharpUseSearchValues.Fixer.cs @@ -89,6 +89,19 @@ protected override SyntaxNode GetDeclaratorInitializer(SyntaxNode syntax) values.Count <= 128 && // Arbitrary limit to avoid emitting huge literals !ContainsAnyComments(creationSyntax)) // Avoid removing potentially valuable comments { + if (isByte) + { + foreach (char c in values) + { + if (c > 127) + { + // We shouldn't turn non-ASCII byte values into Utf8StringLiterals as that may change behavior. + // e.g. (byte)'ÿ' means 0xFF, but "ÿ"u8 is two bytes (0xC3, 0xBF) that encode that character in UTF8. + return null; + } + } + } + string valuesString = string.Concat(values); string stringLiteral = SymbolDisplay.FormatLiteral(valuesString, quote: true); diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Performance/UseSearchValuesTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Performance/UseSearchValuesTests.cs index 7dc9d17b1a..5740b84eb1 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Performance/UseSearchValuesTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Performance/UseSearchValuesTests.cs @@ -771,6 +771,42 @@ private void TestMethod(string text) await VerifyCodeFixAsync(LanguageVersion.CSharp7_3, source, expected); } + [Fact] + public async Task TestCodeFixerDoesNotUseUtf8StringLiteralsForNonAsciiChars() + { + string source = + """ + using System; + using System.Buffers; + + internal sealed class Test + { + private void TestMethod(ReadOnlySpan span) + { + _ = span.IndexOfAny([|new[] { (byte)'ÿ', (byte)'a', (byte)'e', (byte)'i', (byte)'o', (byte)'u' }|]); + } + } + """; + + string expected = + """ + using System; + using System.Buffers; + + internal sealed class Test + { + private static readonly SearchValues s_myBytes = SearchValues.Create(new[] { (byte)'ÿ', (byte)'a', (byte)'e', (byte)'i', (byte)'o', (byte)'u' }); + + private void TestMethod(ReadOnlySpan span) + { + _ = span.IndexOfAny(s_myBytes); + } + } + """; + + await VerifyCodeFixAsync(LanguageVersion.CSharp11, source, expected); + } + [Fact] public static async Task TestCodeFixerDoesNotRemoveTheOriginalMemberIfPublic() {