Skip to content

Commit 0dc6ada

Browse files
authored
Merge pull request github#12234 from michaelnebel/csharp/filescopedtypes
C# 11: Support for `file` scoped types.
2 parents 11e0efe + 2db3694 commit 0dc6ada

File tree

13 files changed

+379
-36
lines changed

13 files changed

+379
-36
lines changed

csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifier.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ private static void ExtractNamedTypeModifiers(Context cx, TextWriter trapFile, I
8585
if (nt.IsRecord)
8686
HasModifier(cx, trapFile, key, Modifiers.Record);
8787

88+
if (nt.IsFileLocal)
89+
HasModifier(cx, trapFile, key, Modifiers.File);
90+
8891
if (nt.TypeKind == TypeKind.Struct)
8992
{
9093
if (nt.IsReadOnly)
@@ -97,7 +100,11 @@ private static void ExtractNamedTypeModifiers(Context cx, TextWriter trapFile, I
97100

98101
public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key, ISymbol symbol)
99102
{
100-
HasAccessibility(cx, trapFile, key, symbol.DeclaredAccessibility);
103+
// A file scoped type has declared accessibility `internal` which we shouldn't extract.
104+
// The file modifier is extracted as a source level modifier.
105+
if (symbol.Kind != SymbolKind.NamedType || !((INamedTypeSymbol)symbol).IsFileLocal)
106+
HasAccessibility(cx, trapFile, key, symbol.DeclaredAccessibility);
107+
101108
if (symbol.Kind == SymbolKind.ErrorType)
102109
trapFile.has_modifiers(key, Modifier.Create(cx, Accessibility.Public));
103110

csharp/extractor/Semmle.Extraction.CSharp/Entities/Modifiers.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ internal static class Modifiers
44
public const string Async = "async";
55
public const string Const = "const";
66
public const string Extern = "extern";
7+
public const string File = "file";
78
public const string Internal = "internal";
89
public const string New = "new";
910
public const string Override = "override";

csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -282,54 +282,60 @@ private static void BuildFunctionPointerTypeId(this IFunctionPointerTypeSymbol f
282282
public static IEnumerable<IFieldSymbol?> GetTupleElementsMaybeNull(this INamedTypeSymbol type) =>
283283
type.TupleElements;
284284

285-
private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType)
285+
private static void BuildQualifierAndName(INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined)
286286
{
287-
if (!constructUnderlyingTupleType && named.IsTupleType)
287+
if (named.ContainingType is not null)
288288
{
289-
trapFile.Write('(');
290-
trapFile.BuildList(",", named.GetTupleElementsMaybeNull(),
291-
(i, f) =>
292-
{
293-
if (f is null)
294-
{
295-
trapFile.Write($"null({i})");
296-
}
297-
else
298-
{
299-
trapFile.Write((f.CorrespondingTupleField ?? f).Name);
300-
trapFile.Write(":");
301-
f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false);
302-
}
303-
}
304-
);
305-
trapFile.Write(")");
306-
return;
289+
named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false);
290+
trapFile.Write('.');
307291
}
308-
309-
void AddContaining()
292+
else if (named.ContainingNamespace is not null)
310293
{
311-
if (named.ContainingType is not null)
312-
{
313-
named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false);
314-
trapFile.Write('.');
315-
}
316-
else if (named.ContainingNamespace is not null)
294+
if (cx.ShouldAddAssemblyTrapPrefix && named.ContainingAssembly is not null)
295+
BuildAssembly(named.ContainingAssembly, trapFile);
296+
named.ContainingNamespace.BuildNamespace(cx, trapFile);
297+
}
298+
299+
var name = named.IsFileLocal ? named.MetadataName : named.Name;
300+
trapFile.Write(name);
301+
}
302+
303+
private static void BuildTupleId(INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined)
304+
{
305+
trapFile.Write('(');
306+
trapFile.BuildList(",", named.GetTupleElementsMaybeNull(),
307+
(i, f) =>
317308
{
318-
if (cx.ShouldAddAssemblyTrapPrefix && named.ContainingAssembly is not null)
319-
BuildAssembly(named.ContainingAssembly, trapFile);
320-
named.ContainingNamespace.BuildNamespace(cx, trapFile);
309+
if (f is null)
310+
{
311+
trapFile.Write($"null({i})");
312+
}
313+
else
314+
{
315+
trapFile.Write((f.CorrespondingTupleField ?? f).Name);
316+
trapFile.Write(":");
317+
f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType: false);
318+
}
321319
}
320+
);
321+
trapFile.Write(")");
322+
}
323+
324+
private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType)
325+
{
326+
if (!constructUnderlyingTupleType && named.IsTupleType)
327+
{
328+
BuildTupleId(named, cx, trapFile, symbolBeingDefined);
329+
return;
322330
}
323331

324332
if (named.TypeParameters.IsEmpty)
325333
{
326-
AddContaining();
327-
trapFile.Write(named.Name);
334+
BuildQualifierAndName(named, cx, trapFile, symbolBeingDefined);
328335
}
329336
else if (named.IsReallyUnbound())
330337
{
331-
AddContaining();
332-
trapFile.Write(named.Name);
338+
BuildQualifierAndName(named, cx, trapFile, symbolBeingDefined);
333339
trapFile.Write("`");
334340
trapFile.Write(named.TypeParameters.Length);
335341
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* C# 11: Added extractor and library support for `file` scoped types.

csharp/ql/lib/semmle/code/csharp/Member.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ class Modifiable extends Declaration, @modifiable {
9393
/** Holds if this declaration has the modifier `required`. */
9494
predicate isRequired() { this.hasModifier("required") }
9595

96+
/** Holds if this declaration is `file` local. */
97+
predicate isFile() { this.hasModifier("file") }
98+
9699
/** Holds if this declaration is `unsafe`. */
97100
predicate isUnsafe() {
98101
this.hasModifier("unsafe") or
@@ -183,6 +186,8 @@ class Member extends DotNet::Member, Modifiable, @member {
183186
override predicate isStatic() { Modifiable.super.isStatic() }
184187

185188
override predicate isRequired() { Modifiable.super.isRequired() }
189+
190+
override predicate isFile() { Modifiable.super.isFile() }
186191
}
187192

188193
private class TOverridable = @virtualizable or @callable_accessor;

csharp/ql/lib/semmle/code/dotnet/Declaration.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ class Member extends Declaration, @dotnet_member {
8383
/** Holds if this member is declared `required`. */
8484
predicate isRequired() { none() }
8585

86+
/** Holds if this member is declared `file` local. */
87+
predicate isFile() { none() }
88+
8689
/**
8790
* Holds if this member has name `name` and is defined in type `type`
8891
* with namespace `namespace`.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
file interface I1 { }
2+
3+
file interface I2 { }
4+
5+
file class C1 : I1 { }
6+
7+
public class C2 { }
8+
9+
public class C3 : I2 { }
10+
11+
file interface IC { }
12+
13+
file class C4<T> { }
14+
15+
file class C5<S> : C4<S> { }
16+
17+
file struct S1 { }
18+
19+
file enum E1 { }
20+
21+
file delegate void D1();
22+
23+
file record R1 { }
24+
25+
file record struct RS1 { }
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
file interface I1 { }
2+
3+
public interface I2 { }
4+
5+
file class C1 { }
6+
7+
file class C2 : I2 { }
8+
9+
file class IC { }
10+
11+
file class C4<T> { }
12+
13+
file class C5<S> : C4<S> { }
14+
15+
file struct S1 { }
16+
17+
file enum E1 { }
18+
19+
file delegate void D1();
20+
21+
file record R1 { }
22+
23+
file record struct RS1 { }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace TestFileScoped;
2+
3+
file interface I10 { }
4+
5+
file class C10 { }
6+
7+
public class C11 : I10 { }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace TestFileScoped;
2+
3+
public interface I10 { }
4+
5+
file class C10 { }
6+
7+
file class C11 : I10 { }

0 commit comments

Comments
 (0)