From 40f7d101c131d8bf95a97fa61f6a2ee3577753dc Mon Sep 17 00:00:00 2001 From: volkanceylan Date: Sat, 5 Oct 2024 16:45:52 +0300 Subject: [PATCH] Check if the property name is a valid C# identifier, skip if not, and add a '@' before if a keyword --- .../CodeGeneration/Base/CSharpSyntaxRules.cs | 121 ++++++++++++++++++ .../ClientTypesGenerator.BasicType.cs | 7 +- 2 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/Serenity.Net.CodeGenerator/CodeGeneration/Base/CSharpSyntaxRules.cs diff --git a/src/Serenity.Net.CodeGenerator/CodeGeneration/Base/CSharpSyntaxRules.cs b/src/Serenity.Net.CodeGenerator/CodeGeneration/Base/CSharpSyntaxRules.cs new file mode 100644 index 0000000000..37ce835b9b --- /dev/null +++ b/src/Serenity.Net.CodeGenerator/CodeGeneration/Base/CSharpSyntaxRules.cs @@ -0,0 +1,121 @@ +namespace Serenity.CodeGeneration; + +public static partial class CSharpSyntaxRules +{ + private static readonly HashSet Keywords = new(StringComparer.Ordinal) + { + "abstract", + "as", + "base", + "bool", + "breal", + "byte", + "case", + "catch", + "char", + "checked", + "class", + "const", + "continue", + "decimal", + "default", + "delegate", + "do", + "double", + "else", + "enum", + "event", + "explicit", + "extern", + "false", + "finally", + "fixed", + "float", + "for", + "foreach", + "goto", + "if", + "implicit", + "in", + "int", + "interface", + "internal", + "is", + "lock", + "long", + "namespace", + "new", + "null", + "object", + "operator", + "out", + "override", + "params", + "private", + "protected", + "public", + "readonly", + "ref", + "return", + "sbyte", + "sealed", + "short", + "sizeof", + "stackalloc", + "static", + "string", + "struct", + "switch", + "this", + "throw", + "true", + "try", + "typeof", + "uint", + "ulong", + "unchecked", + "unsafe", + "ushort", + "using", + "virtual", + "void", + "volatile", + "while" + }; + + +#if ISSOURCEGENERATOR + const string formattingCharacter = @"\p{Cf}"; + const string connectingCharacter = @"\p{Pc}"; + const string decimalDigitCharacter = @"\p{Nd}"; + const string combiningCharacter = @"\p{Mn}|\p{Mc}"; + const string letterCharacter = @"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}"; + const string identifierPartCharacter = letterCharacter + "|" + decimalDigitCharacter + "|" + + connectingCharacter + "|" + combiningCharacter + "|" + formattingCharacter; + const string identifierPartCharacters = "(" + identifierPartCharacter + ")+"; + const string identifierStartCharacter = "(" + letterCharacter + "|_)"; + const string identifierOrKeyword = identifierStartCharacter + "(" + identifierPartCharacters + ")*"; + static readonly Regex ValidIdentifierRegex = new("^" + identifierOrKeyword + "$", RegexOptions.Compiled); +#else + static readonly Regex ValidIdentifierRegex = ValidIdentifierRegexGen(); + + [GeneratedRegex(@"^(\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|_)((\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}|\p{Nd}|\p{Pc}|\p{Mn}|\p{Mc}|\p{Cf})+)*$", RegexOptions.Compiled)] + private static partial Regex ValidIdentifierRegexGen(); +#endif + + public static bool IsKeyword(string identifier) + { + return Keywords.Contains(identifier); + } + + public static bool IsValidIdentifier(string identifier, bool ignoreKeywords) + { + if (string.IsNullOrEmpty(identifier)) + return false; + + if (!ignoreKeywords && IsKeyword(identifier)) + return false; + + return ValidIdentifierRegex.IsMatch(identifier); + } +} \ No newline at end of file diff --git a/src/Serenity.Net.CodeGenerator/CodeGeneration/ClientTypes/ClientTypesGenerator.BasicType.cs b/src/Serenity.Net.CodeGenerator/CodeGeneration/ClientTypes/ClientTypesGenerator.BasicType.cs index c491d7706c..a926b670ca 100644 --- a/src/Serenity.Net.CodeGenerator/CodeGeneration/ClientTypes/ClientTypesGenerator.BasicType.cs +++ b/src/Serenity.Net.CodeGenerator/CodeGeneration/ClientTypes/ClientTypesGenerator.BasicType.cs @@ -4,7 +4,6 @@ public partial class ClientTypesGenerator : ImportGeneratorBase { private void GenerateBasicType(ExternalType type) { - cw.IndentedLine("[System.CodeDom.Compiler.GeneratedCode(\"sergen\", null)]"); cw.Indented("public partial class "); sb.AppendLine(type.Name); @@ -24,12 +23,17 @@ private void GenerateBasicTypeMembers(ExternalType type, HashSet skip) skip.Contains(option.Name)) continue; + if (!CSharpSyntaxRules.IsValidIdentifier(option.Name, ignoreKeywords: true)) + continue; + var typeName = GetMemberTypeName(option.Type); sb.AppendLine(); cw.Indented("public "); sb.Append(typeName); sb.Append(' '); + if (CSharpSyntaxRules.IsKeyword(option.Name)) + sb.Append('@'); sb.Append(option.Name); sb.Append(" { get; set; }"); sb.AppendLine(); @@ -88,5 +92,4 @@ private void AddBasicTypeMembers(SortedDictionary dict, dict[member.Name] = member; } } - }