Skip to content

Commit ca58fda

Browse files
authored
escape C# identifiers when needed (#105)
1 parent f1d1bf5 commit ca58fda

File tree

4 files changed

+54
-12
lines changed

4 files changed

+54
-12
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
namespace LinqToDB.LINQPad
2+
{
3+
// TODO: move to linq2db.Tools (see CSharpCodeGenerator.KeyWords)
4+
internal static class CSharpUtils
5+
{
6+
// C# keywords and contextual words
7+
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/
8+
private static readonly HashSet<string> KeyWords = new ()
9+
{
10+
"abstract", "as" , "base" , "bool" , "break" , "byte" , "case" , "catch" , "char" , "checked",
11+
"class" , "const" , "continue" , "decimal" , "default" , "delegate", "do" , "double" , "else" , "enum",
12+
"event" , "explicit", "extern" , "false" , "finally" , "fixed" , "float" , "for" , "foreach" , "goto",
13+
"if" , "implicit", "in" , "int" , "interface", "internal", "is" , "lock" , "long" , "namespace",
14+
"new" , "null" , "object" , "operator" , "out" , "override", "params" , "private" , "protected", "public",
15+
"readonly", "ref" , "return" , "sbyte" , "sealed" , "short" , "sizeof" , "stackalloc", "static" , "string",
16+
"struct" , "switch" , "this" , "throw" , "true" , "try" , "typeof" , "uint" , "ulong" , "unchecked",
17+
"unsafe" , "ushort" , "using" , "virtual" , "void" , "volatile", "while",
18+
// contextual words
19+
// we don't analyze context for them and tread as keywords to avoid unnecessary complexity in codegeneration
20+
"add" , "and" , "alias" , "ascending", "async" , "await" , "by" , "descending", "dynamic" , "equals",
21+
"from" , "get" , "global" , "group" , "init" , "into" , "join" , "let" , "managed" , "nameof",
22+
"nint" , "not" , "notnull" , "nuint" , "on" , "or" , "orderby", "partial" , "record" , "remove",
23+
"select" , "set" , "unmanaged", "value" , "var" , "when" , "where" , "with" , "yield"
24+
};
25+
26+
public static string EscapeIdentifier(string identifier)
27+
{
28+
if (KeyWords.Contains(identifier))
29+
{
30+
return $"@{identifier}";
31+
}
32+
33+
return identifier;
34+
}
35+
}
36+
}

Source/Drivers/Scaffold/ModelProviderInterceptor.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ private ExplorerItem PopulateStoredProcedures(List<ProcedureData> procedures)
355355

356356
items.Add(new ExplorerItem(func.MethodName, ExplorerItemKind.QueryableObject, ExplorerIcon.StoredProc)
357357
{
358-
DragText = $"this.{func.MethodName}({string.Join(", ", func.Parameters.Select(GetParameterName))})",
358+
DragText = $"this.{CSharpUtils.EscapeIdentifier(func.MethodName)}({string.Join(", ", func.Parameters.Select(GetParameterNameEscaped))})",
359359
Children = children,
360360
IsEnumerable = func.Result != null,
361361
SqlName = func.DbName
@@ -380,7 +380,7 @@ private void AddResultTable(IReadOnlyList<ResultColumnData> resultColumns, List<
380380
columns.Add(new ExplorerItem($"{column.MemberName} : {SimpleBuildTypeName(column.Type)}", ExplorerItemKind.Property, ExplorerIcon.Column)
381381
{
382382
ToolTipText = $"{dbName} {dbType}",
383-
DragText = column.MemberName,
383+
DragText = CSharpUtils.EscapeIdentifier(column.MemberName),
384384
SqlName = dbName,
385385
SqlTypeDeclaration = dbType,
386386
});
@@ -403,6 +403,11 @@ private static string GetParameterName(ParameterData param)
403403
return $"{(param.Direction == ParameterDirection.Out ? "out " : param.Direction == ParameterDirection.Ref ? "ref " : null)}{param.Name}";
404404
}
405405

406+
private static string GetParameterNameEscaped(ParameterData param)
407+
{
408+
return $"{(param.Direction == ParameterDirection.Out ? "out " : param.Direction == ParameterDirection.Ref ? "ref " : null)}{CSharpUtils.EscapeIdentifier(param.Name)}";
409+
}
410+
406411
private ExplorerItem PopulateTableFunctions(List<TableFunctionData> functions)
407412
{
408413
var items = new List<ExplorerItem>(functions.Count);
@@ -417,7 +422,7 @@ private ExplorerItem PopulateTableFunctions(List<TableFunctionData> functions)
417422

418423
items.Add(new ExplorerItem(func.MethodName, ExplorerItemKind.QueryableObject, ExplorerIcon.TableFunction)
419424
{
420-
DragText = $"{func.MethodName}({string.Join(", ", func.Parameters.Select(GetParameterName))})",
425+
DragText = $"{CSharpUtils.EscapeIdentifier(func.MethodName)}({string.Join(", ", func.Parameters.Select(GetParameterNameEscaped))})",
421426
Children = children,
422427
IsEnumerable = true,
423428
SqlName = func.DbName
@@ -445,7 +450,7 @@ private ExplorerItem PopulateScalarFunctions(List<ScalarOrAggregateFunctionData>
445450

446451
items.Add(new ExplorerItem(func.MethodName, ExplorerItemKind.QueryableObject, ExplorerIcon.TableFunction)
447452
{
448-
DragText = $"ExtensionMethods.{func.MethodName}({string.Join(", ", func.Parameters.Select(GetParameterName))})",
453+
DragText = $"ExtensionMethods.{CSharpUtils.EscapeIdentifier(func.MethodName)}({string.Join(", ", func.Parameters.Select(GetParameterNameEscaped))})",
449454
Children = children,
450455
IsEnumerable = false,
451456
SqlName = func.DbName
@@ -478,15 +483,15 @@ private ExplorerItem PopulateTables(List<TableData> tables, string category, Exp
478483
column.IsPrimaryKey ? ExplorerIcon.Key : ExplorerIcon.Column)
479484
{
480485
ToolTipText = $"{dbName} {dbType}",
481-
DragText = column.MemberName,
486+
DragText = CSharpUtils.EscapeIdentifier(column.MemberName),
482487
SqlName = dbName,
483488
SqlTypeDeclaration = dbType,
484489
});
485490
}
486491

487492
var tableNode = new ExplorerItem(table.ContextName, ExplorerItemKind.QueryableObject, icon)
488493
{
489-
DragText = table.ContextName,
494+
DragText = CSharpUtils.EscapeIdentifier(table.ContextName),
490495
ToolTipText = SimpleBuildTypeName(table.ContextType),
491496
SqlName = table.DbName,
492497
IsEnumerable = true,
@@ -524,7 +529,7 @@ private void PopulateAssociations(List<AssociationData> associations, Dictionary
524529
? ExplorerIcon.ManyToOne
525530
: ExplorerIcon.OneToOne)
526531
{
527-
DragText = association.MemberName,
532+
DragText = CSharpUtils.EscapeIdentifier(association.MemberName),
528533
ToolTipText = $"{SimpleBuildTypeName(association.Type)}{(!association.FromSide ? " // Back Reference" : null)}",
529534
SqlName = association.KeyName,
530535
IsEnumerable = association.OneToMany && association.FromSide,

Source/Drivers/StaticSchemaGenerator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public static List<ExplorerItem> GetSchema(Type customContextType)
103103
? ExplorerIcon.ManyToOne
104104
: ExplorerIcon.OneToOne)
105105
{
106-
DragText = ma.Name,
106+
DragText = CSharpUtils.EscapeIdentifier(ma.Name),
107107
ToolTipText = GetTypeName(ma.Type),
108108
IsEnumerable = isToMany,
109109
HyperlinkTarget = otherItem
@@ -152,13 +152,13 @@ from ma in table.TypeAccessor.Members
152152
pk != null || ca != null && ca.IsPrimaryKey ? ExplorerIcon.Key : ExplorerIcon.Column)
153153
{
154154
Text = $"{ma.Name} : {GetTypeName(ma.Type)}",
155-
DragText = ma.Name,
155+
DragText = CSharpUtils.EscapeIdentifier(ma.Name),
156156
}
157157
).ToList();
158158

159159
var ret = new ExplorerItem(table.Name, ExplorerItemKind.QueryableObject, icon)
160160
{
161-
DragText = table.Name,
161+
DragText = CSharpUtils.EscapeIdentifier(table.Name),
162162
IsEnumerable = true,
163163
Children = columns
164164
};

release-notes.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
Issues fixed:
44

5-
- [#97](https://github.com/linq2db/linq2db.LINQPad/pull/97), [#103](https://github.com/linq2db/linq2db.LINQPad/pull/103): update dependencies to get rid of vulnerable transient dependencies
6-
- [#102](https://github.com/linq2db/linq2db.LINQPad/pull/102): support custom `IDataContext`-based contexts; don't try to load missing connection configuration files
5+
- [#97](https://github.com/linq2db/linq2db.LINQPad/issues/97), [#103](https://github.com/linq2db/linq2db.LINQPad/issues/103): update dependencies to get rid of vulnerable transient dependencies
6+
- [#101](https://github.com/linq2db/linq2db.LINQPad/issues/101): fix snippets generation from object names matching C# keywords
7+
- [#102](https://github.com/linq2db/linq2db.LINQPad/pull/102): support custom `IDataContext`-based contexts; don't try to load missing connection configuration files. Thanks to [@cal-tlabwest](https://github.com/cal-tlabwest) for fix
78

89
# Release 5.1.0
910

0 commit comments

Comments
 (0)