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 + Convert to UTF-8 string literal + + Convert to block scoped namespace 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 + Use UTF-8 string literal + + Use block body for accessors 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace In namespace eines Blockbereichs konvertieren @@ -182,6 +187,11 @@ Unerreichbarer Code wurde entdeckt. + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace Convertir en namespace con ámbito de bloque @@ -182,6 +187,11 @@ Se detectó código inaccesible + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace Convertir en namespace bloc inclus dans l'étendue @@ -182,6 +187,11 @@ Code inaccessible détecté + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace Converti in namespace con ambito blocco @@ -182,6 +187,11 @@ È stato rilevato codice non raggiungibile + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace 範囲指定されたブロックが設定された namespace に変換 @@ -182,6 +187,11 @@ 到達できないコードが検出されました + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors アクセサーにブロック本体を使用する 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace 블록 범위 namespace 스로 변환 @@ -182,6 +187,11 @@ 접근할 수 없는 코드가 있습니다. + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors 접근자에 블록 본문 사용 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace Konwertuj do zakresu bloku elementu namespace @@ -182,6 +187,11 @@ Wykryto nieosiągalny kod + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace Converter para bloquear o namespace com escopo @@ -182,6 +187,11 @@ Código inacessível detectado + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace Преобразовать в namespace с заданной областью видимости блока @@ -182,6 +187,11 @@ Обнаружен недостижимый код + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors Использовать тело блока для методов доступа 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace Kapsamlı namespace öğesini engellemek için dönüştür @@ -182,6 +187,11 @@ Ulaşılamayan kod algılandı + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace 转换为块范围限定的 namespace @@ -182,6 +187,11 @@ 检测到无法访问的代码 + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors 使用访问器的程序块主体 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 + Convert to UTF-8 string literal + + Convert to block scoped namespace 轉換為已設定區塊範圍的 namespace @@ -182,6 +187,11 @@ 偵測到執行不到的程式碼 + + Use UTF-8 string literal + Use UTF-8 string literal + + Use block body for accessors 使用存取子的區塊主體 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 UTF8 byte representation. {0} + The input string cannot be converted into the equivalent UTF-8 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 UTF8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. + An expression tree may not contain UTF-8 string conversion or literal. @@ -1563,8 +1563,8 @@ - Utf8 String Literals - Utf8 String Literals + UTF-8 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Apply UTF-8 string literal preferences + + Apply 'var' 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 + Prefer UTF-8 string literals + + Prefer compound assignments 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 + Prefer UTF-8 string literals + + Prefer compound assignments 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 + Prefer UTF-8 string literals + + Prefer compound assignments 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 + Prefer UTF-8 string literals + + Prefer compound assignments 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 + Prefer UTF-8 string literals + + Prefer compound assignments 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 + Prefer UTF-8 string literals + + Prefer compound assignments 複合代入を優先 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 + Prefer UTF-8 string literals + + Prefer compound assignments 복합 대입 선호 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 + Prefer UTF-8 string literals + + Prefer compound assignments 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 + Prefer UTF-8 string literals + + Prefer compound assignments 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 + Prefer UTF-8 string literals + + Prefer compound assignments Предпочитать составные назначения 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 + Prefer UTF-8 string literals + + Prefer compound assignments 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 + Prefer UTF-8 string literals + + Prefer compound assignments 首选复合赋值 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 + Prefer UTF-8 string literals + + Prefer compound assignments 優先使用複合指派 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; + } + } +}