Skip to content

Commit dd86da3

Browse files
committed
C#: Remove base class from type IDs in trap files
1 parent 31ac644 commit dd86da3

File tree

6 files changed

+4233
-58
lines changed

6 files changed

+4233
-58
lines changed

csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -81,22 +81,45 @@ protected void PopulateType(TextWriter trapFile, bool constructUnderlyingTupleTy
8181
Symbol.BuildDisplayName(Context, trapFile, constructUnderlyingTupleType);
8282
trapFile.WriteLine("\")");
8383

84+
var baseTypes = GetBaseTypeDeclarations();
85+
8486
// Visit base types
85-
var baseTypes = new List<Type>();
8687
if (Symbol.GetNonObjectBaseType(Context) is INamedTypeSymbol @base)
8788
{
88-
var baseKey = Create(Context, @base);
89-
trapFile.extend(this, baseKey.TypeRef);
90-
if (Symbol.TypeKind != TypeKind.Struct)
91-
baseTypes.Add(baseKey);
89+
var bts = GetBaseTypeDeclarations(baseTypes, @base);
90+
91+
Context.PopulateLater(() =>
92+
{
93+
var baseKey = Create(Context, @base);
94+
trapFile.extend(this, baseKey.TypeRef);
95+
96+
if (Symbol.TypeKind != TypeKind.Struct)
97+
{
98+
foreach (var bt in bts)
99+
{
100+
TypeMention.Create(Context, bt.Type, this, baseKey);
101+
}
102+
}
103+
});
92104
}
93105

106+
// Visit implemented interfaces
94107
if (!(base.Symbol is IArrayTypeSymbol))
95108
{
96-
foreach (var t in base.Symbol.Interfaces.Select(i => Create(Context, i)))
109+
foreach (var i in base.Symbol.Interfaces)
97110
{
98-
trapFile.implement(this, t.TypeRef);
99-
baseTypes.Add(t);
111+
var bts = GetBaseTypeDeclarations(baseTypes, i);
112+
113+
Context.PopulateLater(() =>
114+
{
115+
var interfaceKey = Create(Context, i);
116+
trapFile.implement(this, interfaceKey.TypeRef);
117+
118+
foreach (var bt in bts)
119+
{
120+
TypeMention.Create(Context, bt.Type, this, interfaceKey);
121+
}
122+
});
100123
}
101124
}
102125

@@ -145,23 +168,30 @@ protected void PopulateType(TextWriter trapFile, bool constructUnderlyingTupleTy
145168
}
146169

147170
Modifier.ExtractModifiers(Context, trapFile, this, Symbol);
171+
}
148172

149-
if (IsSourceDeclaration && Symbol.FromSource())
173+
private IEnumerable<BaseTypeSyntax> GetBaseTypeDeclarations()
174+
{
175+
if (!IsSourceDeclaration || !Symbol.FromSource())
150176
{
151-
var declSyntaxReferences = Symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax()).ToArray();
152-
153-
var baseLists = declSyntaxReferences.OfType<ClassDeclarationSyntax>().Select(c => c.BaseList);
154-
baseLists = baseLists.Concat(declSyntaxReferences.OfType<InterfaceDeclarationSyntax>().Select(c => c.BaseList));
155-
baseLists = baseLists.Concat(declSyntaxReferences.OfType<StructDeclarationSyntax>().Select(c => c.BaseList));
156-
157-
baseLists
158-
.Where(bl => bl is not null)
159-
.SelectMany(bl => bl!.Types)
160-
.Zip(
161-
baseTypes.Where(bt => bt.Symbol.SpecialType != SpecialType.System_Object),
162-
(s, t) => TypeMention.Create(Context, s.Type, this, t))
163-
.Enumerate();
177+
return Enumerable.Empty<BaseTypeSyntax>();
164178
}
179+
180+
var declSyntaxReferences = Symbol.DeclaringSyntaxReferences.Select(d => d.GetSyntax()).ToArray();
181+
182+
var baseLists = declSyntaxReferences.OfType<ClassDeclarationSyntax>().Select(c => c.BaseList);
183+
baseLists = baseLists.Concat(declSyntaxReferences.OfType<InterfaceDeclarationSyntax>().Select(c => c.BaseList));
184+
baseLists = baseLists.Concat(declSyntaxReferences.OfType<StructDeclarationSyntax>().Select(c => c.BaseList));
185+
186+
return baseLists
187+
.Where(bl => bl is not null)
188+
.SelectMany(bl => bl!.Types)
189+
.ToList();
190+
}
191+
192+
private IEnumerable<BaseTypeSyntax> GetBaseTypeDeclarations(IEnumerable<BaseTypeSyntax> baseTypes, INamedTypeSymbol type)
193+
{
194+
return baseTypes.Where(bt => SymbolEqualityComparer.Default.Equals(Context.GetModel(bt).GetTypeInfo(bt.Type).Type, type));
165195
}
166196

167197
private void ExtractParametersForDelegateLikeType(TextWriter trapFile, IMethodSymbol invokeMethod, Action<Type> storeReturnType)

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

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,6 @@ bool IdDependsOnImpl(ITypeSymbol? type)
121121
named = named.TupleUnderlyingType;
122122
if (IdDependsOnImpl(named.ContainingType))
123123
return true;
124-
if (IdDependsOnImpl(named.GetNonObjectBaseType(cx)))
125-
return true;
126124
if (IdDependsOnImpl(named.ConstructedFrom))
127125
return true;
128126
return named.TypeArguments.Any(IdDependsOnImpl);
@@ -160,18 +158,15 @@ bool IdDependsOnImpl(ITypeSymbol? type)
160158
/// <param name="trapFile">The trap builder used to store the result.</param>
161159
/// <param name="symbolBeingDefined">The outer symbol being defined (to avoid recursive ids).</param>
162160
/// <param name="constructUnderlyingTupleType">Whether to build a type ID for the underlying `System.ValueTuple` struct in the case of tuple types.</param>
163-
public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false) =>
164-
type.BuildTypeId(cx, trapFile, symbolBeingDefined, true, constructUnderlyingTupleType);
165-
166-
private static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType)
161+
public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false)
167162
{
168163
using (cx.StackGuard)
169164
{
170165
switch (type.TypeKind)
171166
{
172167
case TypeKind.Array:
173168
var array = (IArrayTypeSymbol)type;
174-
array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass);
169+
array.ElementType.BuildOrWriteId(cx, trapFile, symbolBeingDefined);
175170
array.BuildArraySuffix(trapFile);
176171
return;
177172
case TypeKind.Class:
@@ -181,16 +176,16 @@ private static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextW
181176
case TypeKind.Delegate:
182177
case TypeKind.Error:
183178
var named = (INamedTypeSymbol)type;
184-
named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType);
179+
named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType);
185180
return;
186181
case TypeKind.Pointer:
187182
var ptr = (IPointerTypeSymbol)type;
188-
ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass);
183+
ptr.PointedAtType.BuildOrWriteId(cx, trapFile, symbolBeingDefined);
189184
trapFile.Write('*');
190185
return;
191186
case TypeKind.TypeParameter:
192187
var tp = (ITypeParameterSymbol)type;
193-
tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass);
188+
tp.ContainingSymbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined);
194189
trapFile.Write('_');
195190
trapFile.Write(tp.Name);
196191
return;
@@ -207,7 +202,7 @@ private static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextW
207202
}
208203
}
209204

210-
private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType = false)
205+
private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType = false)
211206
{
212207
if (symbol is null)
213208
{
@@ -232,7 +227,7 @@ private static void BuildOrWriteId(this ISymbol? symbol, Context cx, EscapingTex
232227
if (SymbolEqualityComparer.Default.Equals(symbol, symbolBeingDefined))
233228
trapFile.Write("__self__");
234229
else if (symbol is ITypeSymbol type && type.IdDependsOn(cx, symbolBeingDefined))
235-
type.BuildTypeId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType);
230+
type.BuildTypeId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType);
236231
else if (symbol is INamedTypeSymbol namedType && namedType.IsTupleType && constructUnderlyingTupleType)
237232
trapFile.WriteSubId(NamedType.CreateNamedTypeFromTupleType(cx, namedType));
238233
else
@@ -287,7 +282,7 @@ private static void BuildFunctionPointerTypeId(this IFunctionPointerTypeSymbol f
287282
BuildFunctionPointerSignature(funptr, trapFile, s => s.BuildOrWriteId(cx, trapFile, symbolBeingDefined));
288283
}
289284

290-
private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool addBaseClass, bool constructUnderlyingTupleType)
285+
private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined, bool constructUnderlyingTupleType)
291286
{
292287
if (!constructUnderlyingTupleType && named.IsTupleType)
293288
{
@@ -297,7 +292,7 @@ private static void BuildNamedTypeId(this INamedTypeSymbol named, Context cx, Es
297292
{
298293
trapFile.Write((f.CorrespondingTupleField ?? f).Name);
299294
trapFile.Write(":");
300-
f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass);
295+
f.Type.BuildOrWriteId(cx, trapFile, symbolBeingDefined);
301296
}
302297
);
303298
trapFile.Write(")");
@@ -308,7 +303,7 @@ void AddContaining()
308303
{
309304
if (named.ContainingType is not null)
310305
{
311-
named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass);
306+
named.ContainingType.BuildOrWriteId(cx, trapFile, symbolBeingDefined);
312307
trapFile.Write('.');
313308
}
314309
else if (named.ContainingNamespace is not null)
@@ -333,35 +328,17 @@ void AddContaining()
333328
}
334329
else
335330
{
336-
named.ConstructedFrom.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass, constructUnderlyingTupleType);
331+
named.ConstructedFrom.BuildOrWriteId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType);
337332
trapFile.Write('<');
338333
// Encode the nullability of the type arguments in the label.
339334
// Type arguments with different nullability can result in
340335
// a constructed type with different nullability of its members and methods,
341336
// so we need to create a distinct database entity for it.
342337
trapFile.BuildList(",", named.GetAnnotatedTypeArguments(),
343-
ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass)
338+
ta => ta.Symbol.BuildOrWriteId(cx, trapFile, symbolBeingDefined)
344339
);
345340
trapFile.Write('>');
346341
}
347-
348-
if (addBaseClass && named.GetNonObjectBaseType(cx) is INamedTypeSymbol @base)
349-
{
350-
// We need to limit unfolding of base classes. For example, in
351-
//
352-
// ```csharp
353-
// class C1<T> { }
354-
// class C2<T> : C1<C3<T>> { }
355-
// class C3<T> : C1<C2<T>> { }
356-
// class C4 : C3<C4> { }
357-
// ```
358-
//
359-
// when we generate the label for `C4`, the base class `C3<C4>` has itself `C1<C2<C4>>` as
360-
// a base class, which in turn has `C1<C3<C4>>` as a base class. The latter has the original
361-
// base class `C3<C4>` as a type argument, which would lead to infinite unfolding.
362-
trapFile.Write(" : ");
363-
@base.BuildOrWriteId(cx, trapFile, symbolBeingDefined, addBaseClass: false);
364-
}
365342
}
366343

367344
private static void BuildNamespace(this INamespaceSymbol ns, Context cx, EscapingTextWriter trapFile)

csharp/ql/src/semmlecode.csharp.dbscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ function_pointer_return_type(
529529
int return_type_id: @type_or_ref ref);
530530

531531
extend(
532-
unique int sub: @type ref,
532+
int sub: @type ref,
533533
int super: @type_or_ref ref);
534534

535535
anonymous_types(

0 commit comments

Comments
 (0)