diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems
index 60ed46390e5cd..ae97995d350bd 100644
--- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems
+++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems
@@ -125,6 +125,7 @@
+
diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx
index ebbc461b8bf34..929b1b576d6ef 100644
--- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx
+++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx
@@ -365,4 +365,10 @@
Convert to top-level statements
+
+ Convert to UTF-8 string literal
+
+
+ Use UTF-8 string literal
+
\ No newline at end of file
diff --git a/src/Analyzers/CSharp/Analyzers/UseUTF8StringLiteral/UseUTF8StringLiteralDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseUTF8StringLiteral/UseUTF8StringLiteralDiagnosticAnalyzer.cs
new file mode 100644
index 0000000000000..392cb91c74b0a
--- /dev/null
+++ b/src/Analyzers/CSharp/Analyzers/UseUTF8StringLiteral/UseUTF8StringLiteralDiagnosticAnalyzer.cs
@@ -0,0 +1,199 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis.CodeStyle;
+using Microsoft.CodeAnalysis.CSharp.CodeStyle;
+using Microsoft.CodeAnalysis.CSharp.EmbeddedLanguages.VirtualChars;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Shared.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Operations;
+using Microsoft.CodeAnalysis.Shared.Collections;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.CSharp.UseUTF8StringLiteral
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ internal sealed class UseUTF8StringLiteralDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer
+ {
+ public enum ArrayCreationOperationLocation
+ {
+ Ancestors,
+ Descendants,
+ Current
+ }
+
+ public UseUTF8StringLiteralDiagnosticAnalyzer()
+ : base(IDEDiagnosticIds.UseUTF8StringLiteralDiagnosticId,
+ EnforceOnBuildValues.UseUTF8StringLiteral,
+ CSharpCodeStyleOptions.PreferUTF8StringLiterals,
+ LanguageNames.CSharp,
+ new LocalizableResourceString(nameof(CSharpAnalyzersResources.Convert_to_UTF8_string_literal), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)),
+ new LocalizableResourceString(nameof(CSharpAnalyzersResources.Use_UTF8_string_literal), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)))
+ {
+ }
+
+ public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
+ => DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
+
+ protected override void InitializeWorker(AnalysisContext context)
+ => context.RegisterCompilationStartAction(context =>
+ {
+ if (!context.Compilation.LanguageVersion().IsCSharp11OrAbove())
+ return;
+
+ var expressionType = context.Compilation.GetTypeByMetadataName(typeof(System.Linq.Expressions.Expression<>).FullName!);
+
+ context.RegisterOperationAction(c => AnalyzeOperation(c, expressionType), OperationKind.ArrayCreation);
+ });
+
+ private void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol? expressionType)
+ {
+ var arrayCreationOperation = (IArrayCreationOperation)context.Operation;
+
+ // Don't offer if the user doesn't want it
+ var option = context.GetOption(CSharpCodeStyleOptions.PreferUTF8StringLiterals);
+ if (!option.Value)
+ return;
+
+ // Only replace arrays with initializers
+ if (arrayCreationOperation.Initializer is null)
+ return;
+
+ // Using UTF8 string literals as nested array initializers is invalid
+ if (arrayCreationOperation.DimensionSizes.Length > 1)
+ return;
+
+ // Must be a byte array
+ if (arrayCreationOperation.Type is not IArrayTypeSymbol { ElementType.SpecialType: SpecialType.System_Byte })
+ return;
+
+ // UTF8 strings are not valid to use in attributes
+ if (arrayCreationOperation.Syntax.Ancestors().OfType().Any())
+ return;
+
+ // Can't use a UTF8 string inside an expression tree.
+ var semanticModel = context.Operation.SemanticModel;
+ Contract.ThrowIfNull(semanticModel);
+ if (arrayCreationOperation.Syntax.IsInExpressionTree(semanticModel, expressionType, context.CancellationToken))
+ return;
+
+ var elements = arrayCreationOperation.Initializer.ElementValues;
+
+ // If the compiler has constructed this array creation, then we don't want to do anything
+ // if there aren't any elements, as we could just end up inserting ""u8 somewhere.
+ if (arrayCreationOperation.IsImplicit && elements.Length == 0)
+ return;
+
+ if (!TryConvertToUTF8String(builder: null, elements))
+ return;
+
+ if (arrayCreationOperation.Syntax is ImplicitArrayCreationExpressionSyntax or ArrayCreationExpressionSyntax)
+ {
+ ReportArrayCreationDiagnostic(context, arrayCreationOperation.Syntax, option.Notification.Severity);
+ }
+ else if (elements.Length > 0 && elements[0].Syntax.Parent is ArgumentSyntax)
+ {
+ // For regular parameter arrays the code fix will need to search down
+ ReportParameterArrayDiagnostic(context, arrayCreationOperation.Syntax, elements, option.Notification.Severity, ArrayCreationOperationLocation.Descendants);
+ }
+ else if (elements.Length > 0 && elements[0].Syntax.Parent.IsKind(SyntaxKind.CollectionInitializerExpression))
+ {
+ // For collection initializers where the Add method takes a parameter array, the code fix
+ // will have to search up
+ ReportParameterArrayDiagnostic(context, arrayCreationOperation.Syntax, elements, option.Notification.Severity, ArrayCreationOperationLocation.Ancestors);
+ }
+ }
+
+ private void ReportParameterArrayDiagnostic(OperationAnalysisContext context, SyntaxNode syntaxNode, ImmutableArray elements, ReportDiagnostic severity, ArrayCreationOperationLocation operationLocation)
+ {
+ // When the first elements parent is as argument, or an edge case for collection
+ // initializers where the Add method takes a param array, it means we have a parameter array.
+ // We raise the diagnostic on all of the parameters that make up the array. We could do just
+ // the first element, but that might be odd seeing: M(1, 2, [|3|], 4, 5)
+ var span = TextSpan.FromBounds(elements[0].Syntax.SpanStart, elements[^1].Syntax.Span.End);
+ var location = Location.Create(syntaxNode.SyntaxTree, span);
+
+ ReportDiagnostic(context, syntaxNode, severity, location, operationLocation);
+ }
+
+ private void ReportArrayCreationDiagnostic(OperationAnalysisContext context, SyntaxNode syntaxNode, ReportDiagnostic severity)
+ {
+ // When the user writes the array creation we raise the diagnostic on the first token, which will be the "new" keyword
+ var location = syntaxNode.GetFirstToken().GetLocation();
+
+ ReportDiagnostic(context, syntaxNode, severity, location, ArrayCreationOperationLocation.Current);
+ }
+
+ private void ReportDiagnostic(OperationAnalysisContext context, SyntaxNode syntaxNode, ReportDiagnostic severity, Location location, ArrayCreationOperationLocation operationLocation)
+ {
+ // Store the original syntax location so the code fix can find the operation again
+ var additionalLocations = ImmutableArray.Create(syntaxNode.GetLocation());
+
+ // Also let the code fix where to look to find the operation that originally trigger this diagnostic
+ var properties = ImmutableDictionary.Empty.Add(nameof(ArrayCreationOperationLocation), operationLocation.ToString());
+
+ context.ReportDiagnostic(
+ DiagnosticHelper.Create(Descriptor, location, severity, additionalLocations, properties));
+ }
+
+ internal static bool TryConvertToUTF8String(StringBuilder? builder, ImmutableArray arrayCreationElements)
+ {
+ for (var i = 0; i < arrayCreationElements.Length;)
+ {
+ // Need to call a method to do the actual rune decoding as it uses stackalloc, and stackalloc
+ // in a loop is a bad idea
+ if (!TryGetNextRune(arrayCreationElements, i, out var rune, out var bytesConsumed))
+ return false;
+
+ i += bytesConsumed;
+
+ if (builder is not null)
+ {
+ if (rune.TryGetEscapeCharacter(out var escapeChar))
+ {
+ builder.Append('\\');
+ builder.Append(escapeChar);
+ }
+ else
+ {
+ builder.Append(rune.ToString());
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private static bool TryGetNextRune(ImmutableArray arrayCreationElements, int startIndex, out Rune rune, out int bytesConsumed)
+ {
+ rune = default;
+ bytesConsumed = 0;
+
+ // We only need max 4 elements for a single Rune
+ var length = Math.Min(arrayCreationElements.Length - startIndex, 4);
+
+ Span array = stackalloc byte[length];
+ for (var i = 0; i < length; i++)
+ {
+ var element = arrayCreationElements[startIndex + i];
+
+ // First basic check is that the array element is actually a byte
+ if (element.ConstantValue.Value is not byte b)
+ return false;
+
+ array[i] = b;
+ }
+
+ // If we can't decode a rune from the array then it can't be represented as a string
+ return Rune.DecodeFromUtf8(array, out rune, out bytesConsumed) == System.Buffers.OperationStatus.Done;
+ }
+ }
+}
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf
index 32d9a3f07a6bd..b5b10229c5dca 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
Převést na namespace pro celý blok
@@ -182,6 +187,11 @@
Byl zjištěn nedosažitelný kód.
+
+
+ Use UTF-8 string literal
+
+
Pro přístupové objekty používat text bloku
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf
index 86583906cdf71..a28cacb0510f3 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
In namespace eines Blockbereichs konvertieren
@@ -182,6 +187,11 @@
Unerreichbarer Code wurde entdeckt.
+
+
+ Use UTF-8 string literal
+
+
Blocktextkörper für Accessoren verwenden
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf
index 6265461c5ed10..c97a81c78a541 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
Convertir en namespace con ámbito de bloque
@@ -182,6 +187,11 @@
Se detectó código inaccesible
+
+
+ Use UTF-8 string literal
+
+
Usar cuerpo del bloque para los descriptores de acceso
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf
index 015d7d7cc8e64..1bd9aeb7142cb 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
Convertir en namespace bloc inclus dans l'étendue
@@ -182,6 +187,11 @@
Code inaccessible détecté
+
+
+ Use UTF-8 string literal
+
+
Utiliser un corps de bloc pour les accesseurs
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf
index d597d6e592152..53b1b9e7efc0a 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
Converti in namespace con ambito blocco
@@ -182,6 +187,11 @@
È stato rilevato codice non raggiungibile
+
+
+ Use UTF-8 string literal
+
+
Usa il corpo del blocco per le funzioni di accesso
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf
index 859990b35b94e..a5c1239359af5 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
範囲指定されたブロックが設定された namespace に変換
@@ -182,6 +187,11 @@
到達できないコードが検出されました
+
+
+ Use UTF-8 string literal
+
+
アクセサーにブロック本体を使用する
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf
index e3f20a9fcb56a..6df5c0250e218 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
블록 범위 namespace 스로 변환
@@ -182,6 +187,11 @@
접근할 수 없는 코드가 있습니다.
+
+
+ Use UTF-8 string literal
+
+
접근자에 블록 본문 사용
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf
index 80c3c3a016753..927b421c87b7e 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
Konwertuj do zakresu bloku elementu namespace
@@ -182,6 +187,11 @@
Wykryto nieosiągalny kod
+
+
+ Use UTF-8 string literal
+
+
Użyj treści bloku dla metod dostępu
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf
index 90da74c11e533..54898d392cb3a 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
Converter para bloquear o namespace com escopo
@@ -182,6 +187,11 @@
Código inacessível detectado
+
+
+ Use UTF-8 string literal
+
+
Usar o corpo do bloco para acessadores
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf
index 7a02ac0433fbd..bdfc04162fee9 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
Преобразовать в namespace с заданной областью видимости блока
@@ -182,6 +187,11 @@
Обнаружен недостижимый код
+
+
+ Use UTF-8 string literal
+
+
Использовать тело блока для методов доступа
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf
index 78a4e05c95dcb..7cd153c66da3e 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
Kapsamlı namespace öğesini engellemek için dönüştür
@@ -182,6 +187,11 @@
Ulaşılamayan kod algılandı
+
+
+ Use UTF-8 string literal
+
+
Erişimciler için blok gövdesi kullan
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf
index 045fd1f6cd680..c818368b72919 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
转换为块范围限定的 namespace
@@ -182,6 +187,11 @@
检测到无法访问的代码
+
+
+ Use UTF-8 string literal
+
+
使用访问器的程序块主体
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf
index a879fda099827..8dbe6d1e9dbee 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf
@@ -32,6 +32,11 @@
Convert to 'Program.Main' style program
{Locked="Program.Main"} this is the C# syntax we are going to generate
+
+
+ Convert to UTF-8 string literal
+
+
轉換為已設定區塊範圍的 namespace
@@ -182,6 +187,11 @@
偵測到執行不到的程式碼
+
+
+ Use UTF-8 string literal
+
+
使用存取子的區塊主體
diff --git a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems
index f5c4b7a5242ae..f3ba41afcb066 100644
--- a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems
+++ b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems
@@ -99,6 +99,7 @@
+
diff --git a/src/Analyzers/CSharp/CodeFixes/UseUTF8StringLiteral/UseUTF8StringLiteralCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseUTF8StringLiteral/UseUTF8StringLiteralCodeFixProvider.cs
new file mode 100644
index 0000000000000..a0909cbff1fbe
--- /dev/null
+++ b/src/Analyzers/CSharp/CodeFixes/UseUTF8StringLiteral/UseUTF8StringLiteralCodeFixProvider.cs
@@ -0,0 +1,195 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Immutable;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Operations;
+using Microsoft.CodeAnalysis.PooledObjects;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.CSharp.UseUTF8StringLiteral
+{
+ [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseUTF8StringLiteral), Shared]
+ internal sealed class UseUTF8StringLiteralCodeFixProvider : SyntaxEditorBasedCodeFixProvider
+ {
+ private const char QuoteCharacter = '"';
+ private const string Suffix = "u8";
+
+ [ImportingConstructor]
+ [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+ public UseUTF8StringLiteralCodeFixProvider()
+ {
+ }
+
+ public override ImmutableArray FixableDiagnosticIds { get; } =
+ ImmutableArray.Create(IDEDiagnosticIds.UseUTF8StringLiteralDiagnosticId);
+
+ public override Task RegisterCodeFixesAsync(CodeFixContext context)
+ {
+ RegisterCodeFix(context, CSharpAnalyzersResources.Use_UTF8_string_literal, nameof(CSharpAnalyzersResources.Use_UTF8_string_literal));
+ return Task.CompletedTask;
+ }
+
+ protected override async Task FixAllAsync(
+ Document document, ImmutableArray diagnostics,
+ SyntaxEditor editor, CodeActionOptionsProvider options, CancellationToken cancellationToken)
+ {
+ var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+
+ foreach (var diagnostic in diagnostics)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var node = diagnostic.Location.FindNode(getInnermostNodeForTie: true, cancellationToken);
+ var stringValue = GetUTF8StringValueForDiagnostic(semanticModel, diagnostic, cancellationToken);
+
+ // If we're replacing a byte array that is passed to a parameter array, not and an explicit array creation
+ // then node will be the ArgumentListSyntax that the implicit array creation is just a part of, so we have
+ // to handle that separately, as we can't just replace node with a string literal
+ //
+ // eg given a method:
+ // M(string x, params byte[] b)
+ // our diagnostic would be reported on:
+ // M("hi", [|1, 2, 3, 4|]);
+ // but node will point to:
+ // M([|"hi", 1, 2, 3, 4|]);
+
+ if (node is BaseArgumentListSyntax argumentList)
+ {
+ editor.ReplaceNode(node, CreateArgumentListWithUTF8String(argumentList, diagnostic.Location, stringValue));
+ }
+ else
+ {
+ editor.ReplaceNode(node, CreateUTF8String(node, stringValue));
+ }
+ }
+ }
+
+ private static string GetUTF8StringValueForDiagnostic(SemanticModel semanticModel, Diagnostic diagnostic, CancellationToken cancellationToken)
+ {
+ // For computing the UTF8 string we need the original location of the array creation
+ // operation, which is stored in additional locations.
+ var location = diagnostic.AdditionalLocations[0];
+ var node = location.FindNode(getInnermostNodeForTie: true, cancellationToken);
+
+ var operation = semanticModel.GetRequiredOperation(node, cancellationToken);
+
+ var operationLocationString = diagnostic.Properties[nameof(UseUTF8StringLiteralDiagnosticAnalyzer.ArrayCreationOperationLocation)];
+ if (!Enum.TryParse(operationLocationString, out UseUTF8StringLiteralDiagnosticAnalyzer.ArrayCreationOperationLocation operationLocation))
+ throw ExceptionUtilities.Unreachable;
+
+ IArrayCreationOperation arrayOp;
+
+ // Because we get the location from an IOperation.Syntax, sometimes we have to look a
+ // little harder to get back from syntax to the operation that triggered the diagnostic
+ if (operationLocation == UseUTF8StringLiteralDiagnosticAnalyzer.ArrayCreationOperationLocation.Ancestors)
+ {
+ // For collection initializers where the Add method takes a param array, and the array creation
+ // will be a parent of the operation
+ arrayOp = FindArrayCreationOperationAncestor(operation);
+ }
+ else if (operationLocation == UseUTF8StringLiteralDiagnosticAnalyzer.ArrayCreationOperationLocation.Descendants)
+ {
+ // Otherwise, we must have an implicit array creation for a parameter array, so the location
+ // will be the invocation, or similar, that has the argument, and we need to descend child
+ // nodes to find the one we are interested in. To make sure we're finding the right one,
+ // we can use the diagnostic location for that, since the analyzer raises it on the first element.
+ arrayOp = operation.DescendantsAndSelf()
+ .OfType()
+ .Where(a => a.Initializer?.ElementValues.FirstOrDefault()?.Syntax.SpanStart == diagnostic.Location.SourceSpan.Start)
+ .First();
+ }
+ else
+ {
+ arrayOp = (IArrayCreationOperation)operation;
+ }
+
+ Contract.ThrowIfNull(arrayOp.Initializer);
+
+ // Get our list of bytes from the array elements
+ using var _ = PooledStringBuilder.GetInstance(out var builder);
+ builder.Capacity = arrayOp.Initializer.ElementValues.Length;
+ if (!UseUTF8StringLiteralDiagnosticAnalyzer.TryConvertToUTF8String(builder, arrayOp.Initializer.ElementValues))
+ {
+ // We shouldn't get here, because the code fix shouldn't ask for a string value
+ // if the analyzer couldn't convert it
+ throw ExceptionUtilities.Unreachable;
+ }
+
+ return builder.ToString();
+
+ static IArrayCreationOperation FindArrayCreationOperationAncestor(IOperation operation)
+ {
+ while (operation is not null)
+ {
+ if (operation is IArrayCreationOperation arrayOperation)
+ return arrayOperation;
+
+ operation = operation.Parent!;
+ }
+
+ throw ExceptionUtilities.Unreachable;
+ }
+ }
+
+ private static SyntaxNode CreateArgumentListWithUTF8String(BaseArgumentListSyntax argumentList, Location location, string stringValue)
+ {
+ // To construct our new argument list we add any existing tokens before the location
+ // and then once we hit the location, we add our string literal
+ // We can't just loop through the arguments, as we want to preserve trivia on the
+ // comma tokens, if any.
+ using var _ = ArrayBuilder.GetInstance(out var arguments);
+ foreach (var argument in argumentList.ChildNodesAndTokens())
+ {
+ // Skip the open paren, its a child token but not an argument
+ if (argument.Kind() is SyntaxKind.OpenParenToken or SyntaxKind.OpenBracketToken)
+ {
+ continue;
+ }
+
+ // See if we found our first argument
+ if (argument.Span.Start == location.SourceSpan.Start)
+ {
+ // We don't need to worry about leading trivia here, because anything before the current
+ // argument will have been trailing trivia on the previous comma.
+ var stringLiteral = CreateUTF8String(SyntaxTriviaList.Empty, stringValue, argumentList.Arguments.Last().GetTrailingTrivia());
+ arguments.Add(SyntaxFactory.Argument(stringLiteral));
+ break;
+ }
+
+ arguments.Add(argument);
+ }
+
+ return argumentList.WithArguments(SyntaxFactory.SeparatedList(arguments));
+ }
+
+ private static LiteralExpressionSyntax CreateUTF8String(SyntaxNode nodeToTakeTriviaFrom, string stringValue)
+ {
+ return CreateUTF8String(nodeToTakeTriviaFrom.GetLeadingTrivia(), stringValue, nodeToTakeTriviaFrom.GetTrailingTrivia());
+ }
+
+ private static LiteralExpressionSyntax CreateUTF8String(SyntaxTriviaList leadingTrivia, string stringValue, SyntaxTriviaList trailingTrivia)
+ {
+ var literal = SyntaxFactory.Token(
+ leading: leadingTrivia,
+ kind: SyntaxKind.UTF8StringLiteralToken,
+ text: QuoteCharacter + stringValue + QuoteCharacter + Suffix,
+ valueText: "",
+ trailing: trailingTrivia);
+
+ return SyntaxFactory.LiteralExpression(SyntaxKind.UTF8StringLiteralExpression, literal);
+ }
+ }
+}
diff --git a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems
index 5e1f5d689eb10..f518510827bf6 100644
--- a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems
+++ b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems
@@ -123,6 +123,7 @@
+
diff --git a/src/Analyzers/CSharp/Tests/UseUTF8StringLiteral/UseUTF8StringLiteralTests.cs b/src/Analyzers/CSharp/Tests/UseUTF8StringLiteral/UseUTF8StringLiteralTests.cs
new file mode 100644
index 0000000000000..191a6c7eea3fc
--- /dev/null
+++ b/src/Analyzers/CSharp/Tests/UseUTF8StringLiteral/UseUTF8StringLiteralTests.cs
@@ -0,0 +1,1406 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.UseUTF8StringLiteral;
+using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
+using Microsoft.CodeAnalysis.Test.Utilities;
+using Microsoft.CodeAnalysis.Testing;
+using Xunit;
+
+namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseUTF8StringLiteral
+{
+ using VerifyCS = CSharpCodeFixVerifier<
+ UseUTF8StringLiteralDiagnosticAnalyzer,
+ UseUTF8StringLiteralCodeFixProvider>;
+
+ public class UseUTF8StringLiteralTests
+ {
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestNotInAttribute()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class MyAttribute : System.Attribute
+{
+ public MyAttribute(byte[] data)
+ {
+ }
+}
+
+public class C
+{
+ [MyAttribute(new byte[] { 65, 66, 67 })]
+ public void M()
+ {
+ }
+}",
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestNotInCSharp10()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = new byte[] { 65, 66, 67 };
+ }
+}",
+ LanguageVersion = LanguageVersion.CSharp10
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestNotWithoutInitializer()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = new byte[10];
+ }
+}",
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestNotInExpressionTree()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+using System;
+using System.Linq.Expressions;
+
+public class C
+{
+ public void M()
+ {
+ N(() => new byte[] { 65, 66, 67 });
+ }
+
+ public void N(Expression> f)
+ {
+ }
+}",
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestNotWhenNotByteArray()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = new int[] { 65, 66, 67 };
+ }
+}",
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestNotWhenOptionNotSet()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = new byte[] { 65, 66, 67 };
+ }
+}",
+ EditorConfig = @"
+[*.cs]
+csharp_style_prefer_utf8_string_literals = false
+",
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestNotWhenNonLiteralElement()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = new byte[] { 65, GetB(), 67 };
+ }
+
+ public byte GetB() => 66;
+}",
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestNotWhenMultidimensionalArray()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = new byte[,] { { 65, 66 }, { 67, 68 }, { 69, 70 } };
+ }
+}",
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestSimpleByteArray()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = [|new|] byte[] { 65, 66, 67 };
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = ""ABC""u8;
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestConstant()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ private const byte B = 66;
+ public void M()
+ {
+ var x = [|new|] byte[] { 65, B, 67 };
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ private const byte B = 66;
+ public void M()
+ {
+ var x = ""ABC""u8;
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestImplicitArray()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = [|new|] [] { (byte)65, (byte)66, (byte)67 };
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = ""ABC""u8;
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestExplicitCast()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = [|new|] byte[] { 65, (byte)'B', 67 };
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = ""ABC""u8;
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestHexLiteral()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = [|new|] byte[] { 0x41, 0x42, 0x43 };
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = ""ABC""u8;
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestBinaryExpression()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = [|new|] byte[] { 60 + 5, 60 + 6, 60 + 7 };
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = ""ABC""u8;
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestEmptyArray()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = [|new|] byte[] { };
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = """"u8;
+ }
+}",
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestTrivia1()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = [|new|] byte[] { 65, 66, 67 }; // I wish this byte array was easier to read
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = ""ABC""u8; // I wish this byte array was easier to read
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestTrivia2()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(byte[] b)
+ {
+ M(/* arrays are */ [|new|] byte[] { 65, 66, 67 } /* cool */);
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M(byte[] b)
+ {
+ M(/* arrays are */ ""ABC""u8 /* cool */);
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestMultiple()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = [|new|] byte[] { 0x41, 0x42, 0x43 };
+ var y = [|new|] byte[] { 0x44, 0x45, 0x46 };
+ var z = [|new|] byte[] { 0x47, 0x48, 0x49 };
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = ""ABC""u8;
+ var y = ""DEF""u8;
+ var z = ""GHI""u8;
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestEscapeChars()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = [|new|] byte[] { 34, 92, 0, 7, 8, 12, 10, 13, 9, 11 };
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = ""\""\\\0\a\b\f\n\r\t\v""u8;
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestEmoji()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = [|new|] byte[] { 240, 159, 152, 128 };
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = ""😀""u8;
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestHalfEmoji1()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = new byte[] { 240, 159 };
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestHalfEmoji2()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = new byte[] { 152, 128 };
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestHalfEmoji3()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = new byte[] { 65, 152, 128, 66 };
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestUnicodeReplacementChar()
+ {
+ // The unicode replacement character is what is returned when, for example, an unpaired
+ // surrogate is converted to a UTF8 string. This test just ensures that the presence of
+ // that character isn't being used to detect a failure state of some kind.
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = [|new|] byte[] { 239, 191, 189 };
+ }
+}",
+ FixedCode =
+@"
+public class C
+{
+ public void M()
+ {
+ var x = ""�""u8;
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestCollectionInitializer()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+using System.Collections;
+using System.Collections.Generic;
+
+class C : IEnumerable
+{
+ void M(C c)
+ {
+ // Each literal of the three is a separate IArrayCreationOperation
+ // Lowered code is similar to:
+ /*
+ C c = new C();
+ c.Add(new byte[] { 65 });
+ c.Add(new byte[] { 66 });
+ c.Add(new byte[] { 67 });
+ */
+ c = new() { [|65|], [|66|], [|67|] };
+ }
+
+ public void Add(params byte[] bytes)
+ {
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ throw new System.NotImplementedException();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ throw new System.NotImplementedException();
+ }
+}",
+ FixedCode =
+@"
+using System.Collections;
+using System.Collections.Generic;
+
+class C : IEnumerable
+{
+ void M(C c)
+ {
+ // Each literal of the three is a separate IArrayCreationOperation
+ // Lowered code is similar to:
+ /*
+ C c = new C();
+ c.Add(new byte[] { 65 });
+ c.Add(new byte[] { 66 });
+ c.Add(new byte[] { 67 });
+ */
+ c = new() { ""A""u8, ""B""u8, ""C""u8 };
+ }
+
+ public void Add(params byte[] bytes)
+ {
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ throw new System.NotImplementedException();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ throw new System.NotImplementedException();
+ }
+}",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestUsingWithParamArray()
+ {
+ // From: https://github.com/dotnet/roslyn/blob/0c7c0b33f0871fc4308eb2d75d77b87fc9293290/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IUsingStatement.cs#L1189-L1194
+ // There is an array creation operation for the param array
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+class C
+{
+ public static void M1()
+ {
+ using(var s = new S())
+ {
+ }
+ }
+}
+ref struct S
+{
+ public void Dispose(int a = 1, bool b = true, params byte[] others) { }
+}",
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Theory, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ // Various cases copied from https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs
+ [InlineData(new byte[] { 0x37, 0x02, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65 }, "7translate")]
+ [InlineData(new byte[] { 0x3f, 0x01 }, "?")]
+ public async Task TestValidUTF8Strings(byte[] bytes, string stringValue)
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+$@"
+public class C
+{{
+ private static readonly byte[] _bytes = [|new|] byte[] {{ {string.Join(", ", bytes)} }};
+}}
+",
+ FixedCode =
+$@"
+public class C
+{{
+ private static readonly byte[] _bytes = ""{stringValue}""u8;
+}}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+
+ // Lets make sure there aren't any false positives here, and make sure the byte array actually
+ // correctly round-trips via UTF8
+ var newStringValue = Encoding.UTF8.GetString(bytes);
+ Assert.Equal(stringValue, newStringValue);
+ var newBytes = Encoding.UTF8.GetBytes(stringValue);
+ Assert.Equal(bytes, newBytes);
+ }
+
+ [Theory, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ // Various cases copied from https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http2/HuffmanDecodingTests.cs
+ [InlineData(new byte[] { 0xff, 0xcf })]
+ [InlineData(new byte[] { 0b100111_00, 0b101_10100, 0b0_101000_0, 0b0111_1111 })]
+ [InlineData(new byte[] { 0xb6, 0xb9, 0xac, 0x1c, 0x85, 0x58, 0xd5, 0x20, 0xa4, 0xb6, 0xc2, 0xad, 0x61, 0x7b, 0x5a, 0x54, 0x25, 0x1f })]
+ [InlineData(new byte[] { 0xfe, 0x53 })]
+ [InlineData(new byte[] { 0xff, 0xff, 0xf6, 0xff, 0xff, 0xfd, 0x68 })]
+ [InlineData(new byte[] { 0xff, 0xff, 0xf9, 0xff, 0xff, 0xfd, 0x86 })]
+ // _headerNameHuffmanBytes from https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs
+ [InlineData(new byte[] { 0xa8, 0xbe, 0x16, 0x9c, 0xa3, 0x90, 0xb6, 0x7f })]
+ public async Task TestInvalidUTF8Strings(byte[] bytes)
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+$@"
+public class C
+{{
+ private static readonly byte[] _bytes = new byte[] {{ {string.Join(", ", bytes)} }};
+}}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+
+ // Lets make sure there aren't any false negatives here, and see if the byte array would actually
+ // correctly round-trip via UTF8
+ var stringValue = Encoding.UTF8.GetString(bytes);
+ var newBytes = Encoding.UTF8.GetBytes(stringValue);
+ Assert.NotEqual(bytes, newBytes);
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray1()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(params byte[] b)
+ {
+ M([|new|] byte[] { 65, 66, 67 });
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(params byte[] b)
+ {
+ M(""ABC""u8);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray2()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(int i, params byte[] b)
+ {
+ M(1, [|new|] byte[] { 65, 66, 67 });
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(int i, params byte[] b)
+ {
+ M(1, ""ABC""u8);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray3()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(params byte[] b)
+ {
+ M([|65|]);
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(params byte[] b)
+ {
+ M(""A""u8);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray4()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(params byte[] b)
+ {
+ M(/* hi */ [|65|] /* there */);
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(params byte[] b)
+ {
+ M(/* hi */ ""A""u8 /* there */);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray5()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(params byte[] b)
+ {
+ M([|65, 66, 67|]);
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(params byte[] b)
+ {
+ M(""ABC""u8);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray6()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(params byte[] b)
+ {
+ M(/* hi */ [|65, 66, 67|] /* there */);
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(params byte[] b)
+ {
+ M(/* hi */ ""ABC""u8 /* there */);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray7()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(int x, params byte[] b)
+ {
+ M(1);
+ }
+}
+",
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray8()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(int x, params byte[] b)
+ {
+ M(1, [|65, 66, 67|]);
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(int x, params byte[] b)
+ {
+ M(1, ""ABC""u8);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray9()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(int x, params byte[] b)
+ {
+ M(1, /* hi */ [|65|] /* there */);
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(int x, params byte[] b)
+ {
+ M(1, /* hi */ ""A""u8 /* there */);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray10()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(int x, params byte[] b)
+ {
+ M(1, /* hi */ [|65, 66, 67|] /* there */);
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(int x, params byte[] b)
+ {
+ M(1, /* hi */ ""ABC""u8 /* there */);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray11()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(int x, int y, int z, params byte[] b)
+ {
+ M( /* b1 */ 1 /* a1 */, /* b2 */ 2 /* a2 */, /* b3 */ 3 /* a3 */, /* b4 */ [|65, /* x1 */ 66, /* x2 */ 67|] /* a4 */);
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(int x, int y, int z, params byte[] b)
+ {
+ M( /* b1 */ 1 /* a1 */, /* b2 */ 2 /* a2 */, /* b3 */ 3 /* a3 */, /* b4 */ ""ABC""u8 /* a4 */);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray12()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public C(params byte[] b)
+ {
+ new C([|65, 66, 67|]);
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public C(params byte[] b)
+ {
+ new C(""ABC""u8);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray13()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public int this[params byte[] bytes]
+ {
+ get => 0;
+ }
+
+ public void M()
+ {
+ _ = this[[|65, 66, 67|]];
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public int this[params byte[] bytes]
+ {
+ get => 0;
+ }
+
+ public void M()
+ {
+ _ = this[""ABC""u8];
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray14()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public record C1(int x) : B([|65, 66, 67|]);
+
+public record C2(params byte[] Bytes) : B(Bytes);
+
+public record B(params byte[] Bytes)
+{
+ public void M()
+ {
+ new C1(1);
+ new C2([|65, 66, 67|]);
+ new B([|65, 66, 67|]);
+ }
+}
+namespace System.Runtime.CompilerServices
+{
+ public sealed class IsExternalInit
+ {
+ }
+}
+",
+ FixedCode =
+@"
+public record C1(int x) : B(""ABC""u8);
+
+public record C2(params byte[] Bytes) : B(Bytes);
+
+public record B(params byte[] Bytes)
+{
+ public void M()
+ {
+ new C1(1);
+ new C2(""ABC""u8);
+ new B(""ABC""u8);
+ }
+}
+namespace System.Runtime.CompilerServices
+{
+ public sealed class IsExternalInit
+ {
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray15()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C1 : B
+{
+ public C1(int x)
+ : base([|65, 66, 67|])
+ {
+ }
+}
+
+public class C2 : B
+{
+ public C2(params byte[] Bytes)
+ : base(Bytes)
+ {
+ }
+}
+
+public class B
+{
+ public B(string x, params byte[] bytes)
+ : this(bytes)
+ {
+ }
+
+ public B(int x)
+ : this([|65, 66, 67|])
+ {
+ }
+
+ public B(params byte[] bytes)
+ {
+ new C1(1);
+ new C2([|65, 66, 67|]);
+ new B([|65, 66, 67|]);
+ new B(""a"", [|65, 66, 67|]);
+ }
+}
+",
+ FixedCode =
+@"
+public class C1 : B
+{
+ public C1(int x)
+ : base(""ABC""u8)
+ {
+ }
+}
+
+public class C2 : B
+{
+ public C2(params byte[] Bytes)
+ : base(Bytes)
+ {
+ }
+}
+
+public class B
+{
+ public B(string x, params byte[] bytes)
+ : this(bytes)
+ {
+ }
+
+ public B(int x)
+ : this(""ABC""u8)
+ {
+ }
+
+ public B(params byte[] bytes)
+ {
+ new C1(1);
+ new C2(""ABC""u8);
+ new B(""ABC""u8);
+ new B(""a"", ""ABC""u8);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray16()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(int[] i, byte[] b)
+ {
+ M(new int[] { 1 }, [|new|] byte[] { 65, 66, 67 });
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(int[] i, byte[] b)
+ {
+ M(new int[] { 1 }, ""ABC""u8);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestParamArray17()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(int[] i, params byte[] b)
+ {
+ M(new int[] { 1 }, [|65, 66, 67|]);
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(int[] i, params byte[] b)
+ {
+ M(new int[] { 1 }, ""ABC""u8);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
+ public async Task TestMultidimensionalArray()
+ {
+ await new VerifyCS.Test
+ {
+ TestCode =
+@"
+public class C
+{
+ public void M(byte[][] i, byte[] b)
+ {
+ M(new byte[][] { [|new|] byte[] { 65, 66, 67 }, [|new|] byte[] { 65, 66, 67 } }, [|new|] byte[] { 65, 66, 67 });
+ }
+}
+",
+ FixedCode =
+@"
+public class C
+{
+ public void M(byte[][] i, byte[] b)
+ {
+ M(new byte[][] { ""ABC""u8, ""ABC""u8 }, ""ABC""u8);
+ }
+}
+",
+ CodeActionValidationMode = CodeActionValidationMode.None,
+ LanguageVersion = LanguageVersion.Preview
+ }.RunAsync();
+ }
+ }
+}
diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs
index 3b4584133e91b..f0c2b7d0e03f8 100644
--- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs
+++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs
@@ -99,6 +99,7 @@ internal static class EnforceOnBuildValues
public const EnforceOnBuild UseTopLevelStatements = /*IDE0210*/ EnforceOnBuild.WhenExplicitlyEnabled;
public const EnforceOnBuild UseProgramMain = /*IDE0211*/ EnforceOnBuild.WhenExplicitlyEnabled;
public const EnforceOnBuild ForEachCast = /*IDE0220*/ EnforceOnBuild.WhenExplicitlyEnabled;
+ public const EnforceOnBuild UseUTF8StringLiteral = /*IDE0230*/ EnforceOnBuild.WhenExplicitlyEnabled;
public const EnforceOnBuild MultipleBlankLines = /*IDE2000*/ EnforceOnBuild.WhenExplicitlyEnabled;
public const EnforceOnBuild EmbeddedStatementPlacement = /*IDE2001*/ EnforceOnBuild.WhenExplicitlyEnabled;
public const EnforceOnBuild ConsecutiveBracePlacement = /*IDE2002*/ EnforceOnBuild.WhenExplicitlyEnabled;
diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs
index bcdec71665046..b6127e0153f05 100644
--- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs
+++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs
@@ -176,6 +176,8 @@ internal static class IDEDiagnosticIds
public const string ForEachCastDiagnosticId = "IDE0220";
+ public const string UseUTF8StringLiteralDiagnosticId = "IDE0230";
+
// Analyzer error Ids
public const string AnalyzerChangedId = "IDE1001";
public const string AnalyzerDependencyConflictId = "IDE1002";
diff --git a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs
index 8ccb223fa4fec..da49261278a69 100644
--- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs
+++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs
@@ -157,5 +157,6 @@ internal static class PredefinedCodeFixProviderNames
public const string UseSystemHashCode = nameof(UseSystemHashCode);
public const string UseThrowExpression = nameof(UseThrowExpression);
public const string UseTupleSwap = nameof(UseTupleSwap);
+ public const string UseUTF8StringLiteral = nameof(UseUTF8StringLiteral);
}
}
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index 2eaab7ce3ea2b..2640bf46255ad 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -7080,13 +7080,13 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
The operator '{0}' requires a matching non-checked version of the operator to also be defined
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
- Utf8 String Literals
+ UTF-8 string literals
- An expression tree may not contain UTF8 string conversion or literal.
+ An expression tree may not contain UTF-8 string conversion or literal.
unsigned right shift
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
index 7ed4a5f26ecca..aee8d92be6a47 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
index 2b6ed4bc49504..5e017286f6b78 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
index 51a5cf47791c0..69325f77ca71b 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
index 6f472f3725ac8..e52fbcd8b6031 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
index e7a9d833f7608..4a0285271f1a6 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
index c4f04db2569dd..92cf8d2ca6e8f 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
index e9c9e50442396..f8e8d56c302b6 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
index a212f153f34e8..eade8c0332949 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
index c39963cf2495c..4c2c413af0111 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
index 6a5261ee66764..57ad39b81ed9b 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
index 8beca3a5c9133..d9f63bb2ef77d 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
index 83d32bce4da53..438c92a30702b 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
index 8bf1e6acadddf..450f5e51e2485 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
@@ -223,8 +223,8 @@
-
- The input string cannot be converted into the equivalent UTF8 byte representation. {0}
+
+ The input string cannot be converted into the equivalent UTF-8 byte representation. {0}
@@ -503,8 +503,8 @@
-
- An expression tree may not contain UTF8 string conversion or literal.
+
+ An expression tree may not contain UTF-8 string conversion or literal.
@@ -1563,8 +1563,8 @@
-
- Utf8 String Literals
+
+ UTF-8 string literals
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs
index 11a295a5ac308..a683f5a7998a4 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs
@@ -129,15 +129,15 @@ .maxstack 2
comp = CreateCompilation(source + HelpersSource, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (13,30): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (13,30): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static byte[] Test1() => "hello";
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""hello""").WithArguments("Utf8 String Literals").WithLocation(13, 30),
- // (14,34): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""hello""").WithArguments("UTF-8 string literals").WithLocation(13, 30),
+ // (14,34): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static Span Test2() => "dog";
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""dog""").WithArguments("Utf8 String Literals").WithLocation(14, 34),
- // (15,42): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""dog""").WithArguments("UTF-8 string literals").WithLocation(14, 34),
+ // (15,42): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static ReadOnlySpan Test3() => "cat";
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""cat""").WithArguments("Utf8 String Literals").WithLocation(15, 42)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""cat""").WithArguments("UTF-8 string literals").WithLocation(15, 42)
);
}
@@ -226,28 +226,28 @@ .maxstack 2
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""
hello
""""""").WithArguments("raw string literals").WithLocation(13, 30),
- // (13,30): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (13,30): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static byte[] Test1() => "hello";
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""
hello
- """"""").WithArguments("Utf8 String Literals").WithLocation(13, 30),
+ """"""").WithArguments("UTF-8 string literals").WithLocation(13, 30),
// (16,34): error CS8652: The feature 'raw string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static Span Test2() => """dog""";
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""dog""""""").WithArguments("raw string literals").WithLocation(16, 34),
- // (16,34): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (16,34): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static Span Test2() => "dog";
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""dog""""""").WithArguments("Utf8 String Literals").WithLocation(16, 34),
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""dog""""""").WithArguments("UTF-8 string literals").WithLocation(16, 34),
// (17,42): error CS8652: The feature 'raw string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static ReadOnlySpan Test3() => """
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""
cat
""""""").WithArguments("raw string literals").WithLocation(17, 42),
- // (17,42): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (17,42): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static ReadOnlySpan Test3() => "cat";
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""
cat
-""""""").WithArguments("Utf8 String Literals").WithLocation(17, 42)
+""""""").WithArguments("UTF-8 string literals").WithLocation(17, 42)
);
}
@@ -283,12 +283,12 @@ static void Main()
comp = CreateCompilation(source + HelpersSource, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (7,49): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (7,49): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// (byte[] b, (byte[] d, string e) c) a = ("hello", ("dog", "cat"));
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""hello""").WithArguments("Utf8 String Literals").WithLocation(7, 49),
- // (7,59): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""hello""").WithArguments("UTF-8 string literals").WithLocation(7, 49),
+ // (7,59): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// (byte[] b, (byte[] d, string e) c) a = ("hello", ("dog", "cat"));
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""dog""").WithArguments("Utf8 String Literals").WithLocation(7, 59)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""dog""").WithArguments("UTF-8 string literals").WithLocation(7, 59)
);
}
@@ -324,12 +324,12 @@ static void Main()
comp = CreateCompilation(source + HelpersSource, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (7,45): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (7,45): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// (byte[] a, (byte[] b, string c)) = ("hello", ("dog", "cat"));
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""hello""").WithArguments("Utf8 String Literals").WithLocation(7, 45),
- // (7,55): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""hello""").WithArguments("UTF-8 string literals").WithLocation(7, 45),
+ // (7,55): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// (byte[] a, (byte[] b, string c)) = ("hello", ("dog", "cat"));
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""dog""").WithArguments("Utf8 String Literals").WithLocation(7, 55)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""dog""").WithArguments("UTF-8 string literals").WithLocation(7, 55)
);
}
@@ -371,15 +371,15 @@ static void Main()
comp = CreateCompilation(source + HelpersSource, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (7,21): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (7,21): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// var array = (byte[])"hello";
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"(byte[])""hello""").WithArguments("Utf8 String Literals").WithLocation(7, 21),
- // (8,20): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"(byte[])""hello""").WithArguments("UTF-8 string literals").WithLocation(7, 21),
+ // (8,20): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// var span = (Span)"dog";
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"(Span)""dog""").WithArguments("Utf8 String Literals").WithLocation(8, 20),
- // (9,28): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"(Span)""dog""").WithArguments("UTF-8 string literals").WithLocation(8, 20),
+ // (9,28): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// var readonlySpan = (ReadOnlySpan)"cat";
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"(ReadOnlySpan)""cat""").WithArguments("Utf8 String Literals").WithLocation(9, 28)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"(ReadOnlySpan)""cat""").WithArguments("UTF-8 string literals").WithLocation(9, 28)
);
}
@@ -415,12 +415,12 @@ static void Main()
comp = CreateCompilation(source + HelpersSource, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (7,54): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (7,54): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// var a = ((byte[] b, (byte[] d, string e) c))("hello", ("dog", "cat"));
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""hello""").WithArguments("Utf8 String Literals").WithLocation(7, 54),
- // (7,64): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""hello""").WithArguments("UTF-8 string literals").WithLocation(7, 54),
+ // (7,64): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// var a = ((byte[] b, (byte[] d, string e) c))("hello", ("dog", "cat"));
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""dog""").WithArguments("Utf8 String Literals").WithLocation(7, 64)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""dog""").WithArguments("UTF-8 string literals").WithLocation(7, 64)
);
}
@@ -706,15 +706,15 @@ .locals init (System.ReadOnlySpan V_0)
comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (15,30): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (15,30): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static byte[] Test1() => nullValue;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, "nullValue").WithArguments("Utf8 String Literals").WithLocation(15, 30),
- // (16,34): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, "nullValue").WithArguments("UTF-8 string literals").WithLocation(15, 30),
+ // (16,34): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static Span Test2() => nullValue;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, "nullValue").WithArguments("Utf8 String Literals").WithLocation(16, 34),
- // (17,42): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, "nullValue").WithArguments("UTF-8 string literals").WithLocation(16, 34),
+ // (17,42): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static ReadOnlySpan Test3() => nullValue;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, "nullValue").WithArguments("Utf8 String Literals").WithLocation(17, 42)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, "nullValue").WithArguments("UTF-8 string literals").WithLocation(17, 42)
);
}
@@ -757,15 +757,15 @@ static void Main()
comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (8,21): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (8,21): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// var array = (byte[])nullValue;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, "(byte[])nullValue").WithArguments("Utf8 String Literals").WithLocation(8, 21),
- // (9,20): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, "(byte[])nullValue").WithArguments("UTF-8 string literals").WithLocation(8, 21),
+ // (9,20): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// var span = (Span)nullValue;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, "(Span)nullValue").WithArguments("Utf8 String Literals").WithLocation(9, 20),
- // (10,28): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, "(Span)nullValue").WithArguments("UTF-8 string literals").WithLocation(9, 20),
+ // (10,28): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// var readOnlySpan = (ReadOnlySpan)nullValue;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, "(ReadOnlySpan)nullValue").WithArguments("Utf8 String Literals").WithLocation(10, 28)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, "(ReadOnlySpan)nullValue").WithArguments("UTF-8 string literals").WithLocation(10, 28)
);
}
@@ -2205,9 +2205,9 @@ static void Main()
comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (7,39): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (7,39): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// System.Console.WriteLine(Test("s", (int)1));
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""s""").WithArguments("Utf8 String Literals").WithLocation(7, 39)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""s""").WithArguments("UTF-8 string literals").WithLocation(7, 39)
);
}
@@ -2357,9 +2357,9 @@ static class E
comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (9,31): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (9,31): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// Console.WriteLine(p.M(""));
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""").WithArguments("Utf8 String Literals").WithLocation(9, 31)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""").WithArguments("UTF-8 string literals").WithLocation(9, 31)
);
}
@@ -2539,15 +2539,15 @@ .maxstack 2
comp = CreateCompilation(source + HelpersSource, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (13,30): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (13,30): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static byte[] Test1() => "hello"u8;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""hello""" + suffix).WithArguments("Utf8 String Literals").WithLocation(13, 30),
- // (14,34): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""hello""" + suffix).WithArguments("UTF-8 string literals").WithLocation(13, 30),
+ // (14,34): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static Span Test2() => "dog"u8;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""dog""" + suffix).WithArguments("Utf8 String Literals").WithLocation(14, 34),
- // (15,42): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""dog""" + suffix).WithArguments("UTF-8 string literals").WithLocation(14, 34),
+ // (15,42): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static ReadOnlySpan Test3() => "cat"u8;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""cat""" + suffix).WithArguments("Utf8 String Literals").WithLocation(15, 42)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""cat""" + suffix).WithArguments("UTF-8 string literals").WithLocation(15, 42)
);
}
@@ -2629,15 +2629,15 @@ .maxstack 2
comp = CreateCompilation(source + HelpersSource, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (13,30): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (13,30): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static byte[] Test1() => "hello"u8;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"@""hello""" + suffix).WithArguments("Utf8 String Literals").WithLocation(13, 30),
- // (14,34): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"@""hello""" + suffix).WithArguments("UTF-8 string literals").WithLocation(13, 30),
+ // (14,34): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static Span Test2() => "dog"u8;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"@""dog""" + suffix).WithArguments("Utf8 String Literals").WithLocation(14, 34),
- // (15,42): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"@""dog""" + suffix).WithArguments("UTF-8 string literals").WithLocation(14, 34),
+ // (15,42): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static ReadOnlySpan Test3() => "cat"u8;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"@""cat""" + suffix).WithArguments("Utf8 String Literals").WithLocation(15, 42)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"@""cat""" + suffix).WithArguments("UTF-8 string literals").WithLocation(15, 42)
);
}
@@ -2722,21 +2722,21 @@ .maxstack 2
// (13,30): error CS8652: The feature 'raw string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static byte[] Test1() => """hello"""u8;
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""hello""""""" + suffix).WithArguments("raw string literals").WithLocation(13, 30),
- // (13,30): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (13,30): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static byte[] Test1() => """hello"""u8;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""hello""""""" + suffix).WithArguments("Utf8 String Literals").WithLocation(13, 30),
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""hello""""""" + suffix).WithArguments("UTF-8 string literals").WithLocation(13, 30),
// (14,34): error CS8652: The feature 'raw string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static Span Test2() => """dog"""u8;
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""dog""""""" + suffix).WithArguments("raw string literals").WithLocation(14, 34),
- // (14,34): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (14,34): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static Span Test2() => """dog"""u8;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""dog""""""" + suffix).WithArguments("Utf8 String Literals").WithLocation(14, 34),
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""dog""""""" + suffix).WithArguments("UTF-8 string literals").WithLocation(14, 34),
// (15,42): error CS8652: The feature 'raw string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static ReadOnlySpan Test3() => """cat"""u8;
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""cat""""""" + suffix).WithArguments("raw string literals").WithLocation(15, 42),
- // (15,42): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (15,42): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static ReadOnlySpan Test3() => """cat"""u8;
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""cat""""""" + suffix).WithArguments("Utf8 String Literals").WithLocation(15, 42)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""cat""""""" + suffix).WithArguments("UTF-8 string literals").WithLocation(15, 42)
);
}
@@ -2829,31 +2829,31 @@ .maxstack 2
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""
hello
""""""" + suffix).WithArguments("raw string literals").WithLocation(13, 30),
- // (13,30): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (13,30): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static byte[] Test1() => """
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""
hello
- """"""" + suffix).WithArguments("Utf8 String Literals").WithLocation(13, 30),
+ """"""" + suffix).WithArguments("UTF-8 string literals").WithLocation(13, 30),
// (16,34): error CS8652: The feature 'raw string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static Span Test2() => """
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""
dog
""""""" + suffix).WithArguments("raw string literals").WithLocation(16, 34),
- // (16,34): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (16,34): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static Span Test2() => """
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""
dog
- """"""" + suffix).WithArguments("Utf8 String Literals").WithLocation(16, 34),
+ """"""" + suffix).WithArguments("UTF-8 string literals").WithLocation(16, 34),
// (19,42): error CS8652: The feature 'raw string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static ReadOnlySpan Test3() => """
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""
cat
""""""" + suffix).WithArguments("raw string literals").WithLocation(19, 42),
- // (19,42): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (19,42): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// static ReadOnlySpan Test3() => """
Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""""""
cat
- """"""" + suffix).WithArguments("Utf8 String Literals").WithLocation(19, 42)
+ """"""" + suffix).WithArguments("UTF-8 string literals").WithLocation(19, 42)
);
}
@@ -3406,9 +3406,9 @@ static void Main()
comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
- // (6,40): error CS8652: The feature 'Utf8 String Literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
+ // (6,40): error CS8652: The feature 'UTF-8 string literals' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// System.Console.WriteLine(Test(("s", 1)));
- Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""s""").WithArguments("Utf8 String Literals").WithLocation(6, 40)
+ Diagnostic(ErrorCode.ERR_FeatureInPreview, @"""s""").WithArguments("UTF-8 string literals").WithLocation(6, 40)
);
}
diff --git a/src/Compilers/Test/Core/Traits/Traits.cs b/src/Compilers/Test/Core/Traits/Traits.cs
index 942c762f2247a..586fa4ae36379 100644
--- a/src/Compilers/Test/Core/Traits/Traits.cs
+++ b/src/Compilers/Test/Core/Traits/Traits.cs
@@ -208,6 +208,7 @@ public static class Features
public const string CodeActionsUseSimpleUsingStatement = "CodeActions.UseSimpleUsingStatement";
public const string CodeActionsUseSystemHashCode = "CodeActions.UseSystemHashCode";
public const string CodeActionsUseThrowExpression = "CodeActions.UseThrowExpression";
+ public const string CodeActionsUseUTF8StringLiteral = "CodeActions.CodeActionsUseUTF8StringLiteral";
public const string CodeActionsWrapping = "CodeActions.Wrapping";
public const string CodeCleanup = nameof(CodeCleanup);
public const string CodeDefinitionWindow = nameof(CodeDefinitionWindow);
diff --git a/src/EditorFeatures/CSharp/CodeCleanup/CSharpCodeCleanupService.cs b/src/EditorFeatures/CSharp/CodeCleanup/CSharpCodeCleanupService.cs
index f55f01f233fb3..eee69d201f5b1 100644
--- a/src/EditorFeatures/CSharp/CodeCleanup/CSharpCodeCleanupService.cs
+++ b/src/EditorFeatures/CSharp/CodeCleanup/CSharpCodeCleanupService.cs
@@ -241,6 +241,10 @@ internal class CSharpCodeCleanupService : AbstractCodeCleanupService
new DiagnosticSet(CSharpFeaturesResources.Apply_throw_expression_preferences,
IDEDiagnosticIds.UseThrowExpressionDiagnosticId),
+ // csharp_style_prefer_utf8_string_literals
+ new DiagnosticSet(CSharpFeaturesResources.Apply_utf8_string_literal_preferences,
+ IDEDiagnosticIds.UseUTF8StringLiteralDiagnosticId),
+
// csharp_style_unused_value_assignment_preference
// csharp_style_unused_value_expression_statement_preference
new DiagnosticSet(FeaturesResources.Apply_unused_value_preferences,
diff --git a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs
index 7c211cae86a4a..3dd3a6319fc83 100644
--- a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs
+++ b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs
@@ -557,7 +557,7 @@ private void Method()
[Theory]
[Trait(Traits.Feature, Traits.Features.CodeCleanup)]
[InlineData(LanguageNames.CSharp, 35)]
- [InlineData(LanguageNames.VisualBasic, 70)]
+ [InlineData(LanguageNames.VisualBasic, 71)]
public void VerifyAllCodeStyleFixersAreSupportedByCodeCleanup(string language, int expectedNumberOfUnsupportedDiagnosticIds)
{
var supportedDiagnostics = GetSupportedDiagnosticIdsForCodeCleanupService(language);
diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs
index d37a122129bcf..42b5bf0e1a21f 100644
--- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs
+++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs
@@ -438,6 +438,9 @@ public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable()
# IDE0220
dotnet_diagnostic.IDE0220.severity = %value%
+# IDE0230
+dotnet_diagnostic.IDE0230.severity = %value%
+
# IDE1005
dotnet_diagnostic.IDE1005.severity = %value%
@@ -1055,6 +1058,9 @@ No editorconfig based code style option
# IDE0220, ForEachExplicitCastInSource
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
+# IDE0230, PreferUTF8StringLiterals
+csharp_style_prefer_utf8_string_literals = true
+
# IDE1005, PreferConditionalDelegateCall
csharp_style_conditional_delegate_call = true
diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx
index ab0ce0622bb8d..88e584a3c32d3 100644
--- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx
+++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx
@@ -624,4 +624,7 @@
Global 'using' directives
+
+ Apply UTF-8 string literal preferences
+
\ No newline at end of file
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf
index a452e4a3d5a7e..c8d18056a4fdc 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf
@@ -117,6 +117,11 @@
Použít předvolby příkazu using
+
+
+ Apply UTF-8 string literal preferences
+
+
Použít předvolby var
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf
index 2adc01512c8e5..c73a8149745f5 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf
@@ -117,6 +117,11 @@
Verwendunganweisungseinstellungen anwenden
+
+
+ Apply UTF-8 string literal preferences
+
+
„var“-Einstellungen anwenden
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf
index 94796d4cfb3c5..5c465cf487a68 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf
@@ -117,6 +117,11 @@
Aplicar preferencias de instrucción using
+
+
+ Apply UTF-8 string literal preferences
+
+
Aplicar preferencias \"var\"
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf
index 0bbca808db5e8..c9d360a5ea75a 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf
@@ -117,6 +117,11 @@
Appliquer les préférences d’instruction using
+
+
+ Apply UTF-8 string literal preferences
+
+
Appliquer les préférences 'var'
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf
index 2a00ed9d3f68a..87555e7660a2b 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf
@@ -117,6 +117,11 @@
Applica preferenze dell'istruzione using
+
+
+ Apply UTF-8 string literal preferences
+
+
Applica preferenze 'var'
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf
index ea233e502b782..a4f93c1ea1bcf 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf
@@ -117,6 +117,11 @@
using ステートメントの基本設定を適用する
+
+
+ Apply UTF-8 string literal preferences
+
+
'var' の基本設定を適用する:
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf
index 943be3487fb8f..df2d82a06750b 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf
@@ -117,6 +117,11 @@
Using 문 적용 기본 설정
+
+
+ Apply UTF-8 string literal preferences
+
+
'var' 적용 기본 설정
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf
index a9e710994802c..bb9bde014fa93 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf
@@ -117,6 +117,11 @@
Zastosuj preferencje instrukcji użycia
+
+
+ Apply UTF-8 string literal preferences
+
+
Zastosuj preferencje „var”
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf
index 2a12ca888048f..1f808fc338950 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf
@@ -117,6 +117,11 @@
Aplicar preferências de uso de instrução
+
+
+ Apply UTF-8 string literal preferences
+
+
Aplicar preferências de “var”:
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf
index 339102c114ab0..4bd0a6e90ef2c 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf
@@ -117,6 +117,11 @@
Применить настройки для оператора using
+
+
+ Apply UTF-8 string literal preferences
+
+
Применить настройки для \"var\"
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf
index 6c2859c3e3f4f..62774cb90841a 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf
@@ -117,6 +117,11 @@
Using deyimi tercihlerini uygula
+
+
+ Apply UTF-8 string literal preferences
+
+
'var' tercihlerini uygula
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf
index 3898475613e83..5bdb196ca488e 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf
@@ -117,6 +117,11 @@
使用语句首选项应用
+
+
+ Apply UTF-8 string literal preferences
+
+
应用 \"var\" 首选项
diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf
index c93a20760e9a0..f7305f3c56e8d 100644
--- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf
+++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf
@@ -117,6 +117,11 @@
套用 Using 陳述式喜好設定
+
+
+ Apply UTF-8 string literal preferences
+
+
套用 'var' 喜好設定
diff --git a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs
index e9314d4df9fd5..ced9c909852e4 100644
--- a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs
+++ b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs
@@ -156,6 +156,7 @@ private IEnumerable GetExpressionCodeStyleOptions(AnalyzerConf
yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferRangeOperator, description: ServicesVSResources.Prefer_range_operator, editorConfigOptions, visualStudioOptions, updaterService, FileName);
yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent, description: CSharpVSResources.Prefer_implicit_object_creation_when_type_is_apparent, editorConfigOptions, visualStudioOptions, updaterService, FileName);
yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferTupleSwap, description: ServicesVSResources.Prefer_tuple_swap, editorConfigOptions, visualStudioOptions, updaterService, FileName);
+ yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferUTF8StringLiterals, description: ServicesVSResources.Prefer_UTF8_string_literals, editorConfigOptions, visualStudioOptions, updaterService, FileName);
}
private IEnumerable GetPatternMatchingCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService)
diff --git a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs
index e5ec830ed808e..cd267e9b60cf8 100644
--- a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs
+++ b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs
@@ -381,6 +381,12 @@ public string Style_PreferTupleSwap
set { SetXmlOption(CSharpCodeStyleOptions.PreferTupleSwap, value); }
}
+ public string Style_PreferUTF8StringLiterals
+ {
+ get { return GetXmlOption(CSharpCodeStyleOptions.PreferUTF8StringLiterals); }
+ set { SetXmlOption(CSharpCodeStyleOptions.PreferUTF8StringLiterals, value); }
+ }
+
public string Style_PreferredUsingDirectivePlacement
{
get { return GetXmlOption(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement); }
diff --git a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs
index 5cffab73b13d5..86806ef13f103 100644
--- a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs
+++ b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs
@@ -1157,6 +1157,21 @@ void M2(string[] args)
//]
}}
}}
+";
+
+ private static readonly string s_preferUTF8StringLiterals = $@"
+using System;
+
+//[
+class WebResponse
+{{
+ // {ServicesVSResources.Prefer_colon}
+ private readonly byte[] header = ""Hello""u8;
+
+ // {ServicesVSResources.Over_colon}
+ private readonly byte[] header = new byte[] {{ 72, 101, 108, 108, 111 }};
+}}
+//]
";
private static readonly string s_preferIsNullOverReferenceEquals = $@"
@@ -2177,6 +2192,7 @@ internal StyleViewModel(OptionStore optionStore, IServiceProvider serviceProvide
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferRangeOperator, ServicesVSResources.Prefer_range_operator, s_preferRangeOperator, s_preferRangeOperator, this, optionStore, expressionPreferencesGroupTitle));
CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferTupleSwap, ServicesVSResources.Prefer_tuple_swap, s_preferTupleSwap, s_preferTupleSwap, this, optionStore, expressionPreferencesGroupTitle));
+ CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferUTF8StringLiterals, ServicesVSResources.Prefer_UTF8_string_literals, s_preferUTF8StringLiterals, s_preferUTF8StringLiterals, this, optionStore, expressionPreferencesGroupTitle));
AddExpressionBodyOptions(optionStore, expressionPreferencesGroupTitle);
AddUnusedValueOptions(optionStore, expressionPreferencesGroupTitle);
diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx
index 3b74164b90f77..9a49aff3d3369 100644
--- a/src/VisualStudio/Core/Def/ServicesVSResources.resx
+++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx
@@ -1947,4 +1947,7 @@ Additional information: {1}
{0} ({1})
Used to show a symbol name, followed by the project name it came from
+
+ Prefer UTF-8 string literals
+
\ No newline at end of file
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf
index 00a0a2b005e88..c8788b078f1ed 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf
@@ -1032,6 +1032,11 @@
Upřednostňovat System.HashCode v GetHashCode
+
+
+ Prefer UTF-8 string literals
+
+
Preferovat složená přiřazení
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf
index f78c7169dcad0..73bbe1b7f9582 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf
@@ -1032,6 +1032,11 @@
"System.HashCode" in "GetHashCode" bevorzugen
+
+
+ Prefer UTF-8 string literals
+
+
Zusammengesetzte Zuweisungen bevorzugen
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf
index 780f0609baf30..316144611ad99 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf
@@ -1032,6 +1032,11 @@
Preferir "System.HashCode" en "GetHashCode"
+
+
+ Prefer UTF-8 string literals
+
+
Preferir asignaciones compuestas
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf
index f3f8bd62d1c1d..f2dcdf4a6e158 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf
@@ -1032,6 +1032,11 @@
Préférer 'System.HashCode' dans 'GetHashCode'
+
+
+ Prefer UTF-8 string literals
+
+
Préférer les affectations composées
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf
index 6863c7b63bd36..7e25881bf775f 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf
@@ -1032,6 +1032,11 @@
Preferisci 'System.HashCode' in 'GetHashCode'
+
+
+ Prefer UTF-8 string literals
+
+
Preferisci assegnazioni composte
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf
index 19de8c5b3fd0f..f0d357a6ed80c 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf
@@ -1032,6 +1032,11 @@
'GetHashCode' の 'System.HashCode' を優先する
+
+
+ Prefer UTF-8 string literals
+
+
複合代入を優先
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf
index c15a75ecfcf91..31cdb1fb05f01 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf
@@ -1032,6 +1032,11 @@
'GetHashCode'에서 'System.HashCode' 선호
+
+
+ Prefer UTF-8 string literals
+
+
복합 대입 선호
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf
index 4d38d31cdc6cc..e2bb5474821ec 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf
@@ -1032,6 +1032,11 @@
Preferuj element „System.HashCode” w metodzie „GetHashCode”
+
+
+ Prefer UTF-8 string literals
+
+
Preferuj przypisania złożone
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf
index 07b53420b6bf6..5e11129212c9b 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf
@@ -1032,6 +1032,11 @@
Prefira 'System.HashCode' em 'GetHashCode'
+
+
+ Prefer UTF-8 string literals
+
+
Preferir atribuições de compostos
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf
index 2ce41a26e0c12..ade1dc58c3b2e 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf
@@ -1032,6 +1032,11 @@
Предпочитать "System.HashCode" в "GetHashCode"
+
+
+ Prefer UTF-8 string literals
+
+
Предпочитать составные назначения
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf
index 52af33f20cfdb..36688ee55265e 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf
@@ -1032,6 +1032,11 @@
'GetHashCode' içinde 'System.HashCode' tercih et
+
+
+ Prefer UTF-8 string literals
+
+
Bileşik atamaları tercih et
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf
index 95f4fccbf6c72..42d4c6e4fa50a 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf
@@ -1032,6 +1032,11 @@
在 "GetHashCode" 中首选 "System.HashCode"
+
+
+ Prefer UTF-8 string literals
+
+
首选复合赋值
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf
index 54fa98924be50..8864b32291ed9 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf
@@ -1032,6 +1032,11 @@
建議在 'GetHashCode' 中使用 'System.HashCode'
+
+
+ Prefer UTF-8 string literals
+
+
優先使用複合指派
diff --git a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb
index 9ffe78f5e40dc..6a66f104f2a98 100644
--- a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb
+++ b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb
@@ -142,6 +142,7 @@ csharp_style_prefer_local_over_anonymous_function = true
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_range_operator = true
csharp_style_prefer_tuple_swap = true
+csharp_style_prefer_utf8_string_literals = true
csharp_style_throw_expression = true
csharp_style_unused_value_assignment_preference = discard_variable
csharp_style_unused_value_expression_statement_preference = discard_variable
@@ -379,6 +380,7 @@ csharp_style_prefer_local_over_anonymous_function = true
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_range_operator = true
csharp_style_prefer_tuple_swap = true
+csharp_style_prefer_utf8_string_literals = true
csharp_style_throw_expression = true
csharp_style_unused_value_assignment_preference = discard_variable
csharp_style_unused_value_expression_statement_preference = discard_variable
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CSharpCompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CSharpCompilerExtensions.projitems
index fdcc69e845fff..198a7aff13bcc 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CSharpCompilerExtensions.projitems
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CSharpCompilerExtensions.projitems
@@ -15,6 +15,7 @@
+
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs
index 7250c5148a72c..2f1857297bc10 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs
@@ -137,6 +137,12 @@ private static Option2> CreateOption(
"csharp_style_prefer_range_operator",
"TextEditor.CSharp.Specific.PreferRangeOperator");
+ public static readonly Option2> PreferUTF8StringLiterals = CreateOption(
+ CSharpCodeStyleOptionGroups.ExpressionLevelPreferences, nameof(PreferUTF8StringLiterals),
+ defaultValue: s_trueWithSuggestionEnforcement,
+ "csharp_style_prefer_utf8_string_literals",
+ $"TextEditor.CSharp.Specific.{nameof(PreferUTF8StringLiterals)}");
+
public static readonly CodeStyleOption2 NeverWithSilentEnforcement =
new(ExpressionBodyPreference.Never, NotificationOption2.Silent);
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs
index b2a06a07bb75d..8e9d1e58ad99b 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs
@@ -238,33 +238,7 @@ private static bool TryAddEscape(
}
public override bool TryGetEscapeCharacter(VirtualChar ch, out char escapedChar)
- {
- // Keep in sync with TryAddSingleCharacterEscape
- switch (ch.Value)
- {
- // Note: we don't care about single quote as that doesn't need to be escaped when
- // producing a normal C# string literal.
-
- // case '\'':
-
- // escaped characters that translate to themselves.
- case '"': escapedChar = '"'; return true;
- case '\\': escapedChar = '\\'; return true;
-
- // translate escapes as per C# spec 2.4.4.4
- case '\0': escapedChar = '0'; return true;
- case '\a': escapedChar = 'a'; return true;
- case '\b': escapedChar = 'b'; return true;
- case '\f': escapedChar = 'f'; return true;
- case '\n': escapedChar = 'n'; return true;
- case '\r': escapedChar = 'r'; return true;
- case '\t': escapedChar = 't'; return true;
- case '\v': escapedChar = 'v'; return true;
- }
-
- escapedChar = default;
- return false;
- }
+ => ch.TryGetEscapeCharacter(out escapedChar);
private static bool TryAddSingleCharacterEscape(
ArrayBuilder<(char ch, TextSpan span)> result, string tokenText, int offset, int index)
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/RuneExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/RuneExtensions.cs
new file mode 100644
index 0000000000000..1963fe9bd5084
--- /dev/null
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/RuneExtensions.cs
@@ -0,0 +1,49 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
+
+namespace Microsoft.CodeAnalysis.CSharp.EmbeddedLanguages.VirtualChars
+{
+ internal static class Extensions
+ {
+ public static bool TryGetEscapeCharacter(this VirtualChar ch, out char escapedChar)
+ => TryGetEscapeCharacter(ch.Value, out escapedChar);
+
+ public static bool TryGetEscapeCharacter(this Rune rune, out char escapedChar)
+ => TryGetEscapeCharacter(rune.Value, out escapedChar);
+
+ private static bool TryGetEscapeCharacter(int value, out char escapedChar)
+ {
+ // Keep in sync with CSharpVirtualCharService.TryAddSingleCharacterEscape
+ switch (value)
+ {
+ // Note: we don't care about single quote as that doesn't need to be escaped when
+ // producing a normal C# string literal.
+
+ // case '\'':
+
+ // escaped characters that translate to themselves.
+ case '"': escapedChar = '"'; return true;
+ case '\\': escapedChar = '\\'; return true;
+
+ // translate escapes as per C# spec 2.4.4.4
+ case '\0': escapedChar = '0'; return true;
+ case '\a': escapedChar = 'a'; return true;
+ case '\b': escapedChar = 'b'; return true;
+ case '\f': escapedChar = 'f'; return true;
+ case '\n': escapedChar = 'n'; return true;
+ case '\r': escapedChar = 'r'; return true;
+ case '\t': escapedChar = 't'; return true;
+ case '\v': escapedChar = 'v'; return true;
+ }
+
+ escapedChar = default;
+ return false;
+ }
+ }
+}