From 3165c076e2cdb3083c8965be94bfc5e790f2ab51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Thu, 2 Jan 2020 17:07:30 +0100 Subject: [PATCH] Fix identifiers generated from resx resource names (#2208) * Fix identifiers generated from resx resource names * Make AggressiveInlining NET20 conditional (cherry picked from commit 7af51d46ccb26df45bae798c97a804b0ba7f9490) --- .../GenerateResxSourceTests.cs | 13 +++ .../src/GenerateResxSource.cs | 90 +++++++++++++------ 2 files changed, 75 insertions(+), 28 deletions(-) diff --git a/src/Microsoft.DotNet.Arcade.Sdk.Tests/GenerateResxSourceTests.cs b/src/Microsoft.DotNet.Arcade.Sdk.Tests/GenerateResxSourceTests.cs index 7007b839990..afffb0d5bbe 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk.Tests/GenerateResxSourceTests.cs +++ b/src/Microsoft.DotNet.Arcade.Sdk.Tests/GenerateResxSourceTests.cs @@ -59,5 +59,18 @@ public void GeneratesCSharp(bool emitFormatMethods, bool asConstants, bool omitG _output.WriteLine(actualFileContents); Assert.Equal(File.ReadAllText(expectedFile), actualFileContents, ignoreLineEndingDifferences: true); } + + [Theory] + [InlineData("a", "a")] + [InlineData("A", "A")] + [InlineData("_A", "_A")] + [InlineData(".A", "_A")] + [InlineData("4A", "_4A")] + [InlineData("4(.-)A", "_4____A")] + [InlineData("A\u0660\u2040\u0601\u0300\u0903", "A\u0660\u2040\u0601\u0300\u0903")] + public void GetIdentifierFromResourceName(string name, string expectedIdentifier) + { + Assert.Equal(expectedIdentifier, GenerateResxSource.GetIdentifierFromResourceName(name)); + } } } diff --git a/src/Microsoft.DotNet.Arcade.Sdk/src/GenerateResxSource.cs b/src/Microsoft.DotNet.Arcade.Sdk/src/GenerateResxSource.cs index 3d55135ccb3..bd150cf1e45 100644 --- a/src/Microsoft.DotNet.Arcade.Sdk/src/GenerateResxSource.cs +++ b/src/Microsoft.DotNet.Arcade.Sdk/src/GenerateResxSource.cs @@ -70,27 +70,6 @@ private enum Lang CSharp, VisualBasic, } - - private bool IsLetterChar(UnicodeCategory cat) - { - // letter-character: - // A Unicode character of classes Lu, Ll, Lt, Lm, Lo, or Nl - // A Unicode-escape-sequence representing a character of classes Lu, Ll, Lt, Lm, Lo, or Nl - - switch (cat) - { - case UnicodeCategory.UppercaseLetter: - case UnicodeCategory.LowercaseLetter: - case UnicodeCategory.TitlecaseLetter: - case UnicodeCategory.ModifierLetter: - case UnicodeCategory.OtherLetter: - case UnicodeCategory.LetterNumber: - return true; - } - - return false; - } - public override bool Execute() { string namespaceName; @@ -151,7 +130,7 @@ public override bool Execute() RenderDocComment(language, memberIndent, strings, docCommentString); - string identifier = IsLetterChar(CharUnicodeInfo.GetUnicodeCategory(name[0])) ? name : "_" + name; + string identifier = GetIdentifierFromResourceName(name); string defaultValue = IncludeDefaultValues ? ", " + CreateStringLiteral(value, language) : string.Empty; @@ -160,11 +139,11 @@ public override bool Execute() case Lang.CSharp: if (AsConstants) { - strings.AppendLine($"{memberIndent}internal const string {name} = nameof({name});"); + strings.AppendLine($"{memberIndent}internal const string @{identifier} = \"{name}\");"); } else { - strings.AppendLine($"{memberIndent}internal static string {identifier} => GetResourceString(\"{name}\"{defaultValue});"); + strings.AppendLine($"{memberIndent}internal static string @{identifier} => GetResourceString(\"{name}\"{defaultValue});"); } if (EmitFormatMethods) @@ -182,11 +161,11 @@ public override bool Execute() case Lang.VisualBasic: if (AsConstants) { - strings.AppendLine($"{memberIndent}Friend Const {name} As String = \"{name}\""); + strings.AppendLine($"{memberIndent}Friend Const [{identifier}] As String = \"{name}\""); } else { - strings.AppendLine($"{memberIndent}Friend Shared ReadOnly Property {identifier} As String"); + strings.AppendLine($"{memberIndent}Friend Shared ReadOnly Property [{identifier}] As String"); strings.AppendLine($"{memberIndent} Get"); strings.AppendLine($"{memberIndent} Return GetResourceString(\"{name}\"{defaultValue})"); strings.AppendLine($"{memberIndent} End Get"); @@ -215,8 +194,9 @@ public override bool Execute() { case Lang.CSharp: getStringMethod = $@"{memberIndent}internal static global::System.Globalization.CultureInfo Culture {{ get; set; }} - +#if !NET20 {memberIndent}[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#endif {memberIndent}internal static string GetResourceString(string resourceKey, string defaultValue = null) => ResourceManager.GetString(resourceKey, Culture);"; if (EmitFormatMethods) { @@ -240,8 +220,9 @@ public override bool Execute() case Lang.VisualBasic: getStringMethod = $@"{memberIndent}Friend Shared Property Culture As Global.System.Globalization.CultureInfo - +#If Not NET20 Then {memberIndent} +#End If {memberIndent}Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String {memberIndent} Return ResourceManager.GetString(resourceKey, Culture) {memberIndent}End Function"; @@ -387,6 +368,59 @@ Imports System.Reflection return true; } + internal static string GetIdentifierFromResourceName(string name) + { + if (name.All(IsIdentifierPartCharacter)) + { + return IsIdentifierStartCharacter(name[0]) ? name : "_" + name; + } + + var builder = new StringBuilder(name.Length); + + char f = name[0]; + if (IsIdentifierPartCharacter(f) && !IsIdentifierStartCharacter(f)) + { + builder.Append('_'); + } + + foreach (char c in name) + { + builder.Append(IsIdentifierPartCharacter(c) ? c : '_'); + } + + return builder.ToString(); + + static bool IsIdentifierStartCharacter(char ch) + => ch == '_' || IsLetterChar(CharUnicodeInfo.GetUnicodeCategory(ch)); + + static bool IsIdentifierPartCharacter(char ch) + { + var cat = CharUnicodeInfo.GetUnicodeCategory(ch); + return IsLetterChar(cat) + || cat == UnicodeCategory.DecimalDigitNumber + || cat == UnicodeCategory.ConnectorPunctuation + || cat == UnicodeCategory.Format + || cat == UnicodeCategory.NonSpacingMark + || cat == UnicodeCategory.SpacingCombiningMark; + } + + static bool IsLetterChar(UnicodeCategory cat) + { + switch (cat) + { + case UnicodeCategory.UppercaseLetter: + case UnicodeCategory.LowercaseLetter: + case UnicodeCategory.TitlecaseLetter: + case UnicodeCategory.ModifierLetter: + case UnicodeCategory.OtherLetter: + case UnicodeCategory.LetterNumber: + return true; + } + + return false; + } + } + private static void RenderDocComment(Lang language, string memberIndent, StringBuilder strings, string value) { string docCommentStart = language == Lang.CSharp