Skip to content

Commit 95e08a8

Browse files
committed
C#: Implicit extension parameters should be fresh entities.
1 parent 0f022d5 commit 95e08a8

File tree

7 files changed

+116
-90
lines changed

7 files changed

+116
-90
lines changed

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

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,16 @@ private static void BuildFunctionPointerTypeId(this IFunctionPointerTypeSymbol f
276276
public static IEnumerable<IFieldSymbol?> GetTupleElementsMaybeNull(this INamedTypeSymbol type) =>
277277
type.TupleElements;
278278

279+
private static string GetExtensionTypeName(this INamedTypeSymbol named, Context cx)
280+
{
281+
var type = named.ExtensionParameter?.Type.Name;
282+
if (type is null)
283+
{
284+
cx.ModelError(named, "Failed to get extension method type.");
285+
}
286+
return $"extension({type ?? "unknown"})";
287+
}
288+
279289
private static void BuildQualifierAndName(INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined)
280290
{
281291
if (named.ContainingType is not null)
@@ -290,8 +300,20 @@ private static void BuildQualifierAndName(INamedTypeSymbol named, Context cx, Es
290300
named.ContainingNamespace.BuildNamespace(cx, trapFile);
291301
}
292302

293-
var name = named.IsFileLocal ? named.MetadataName : named.Name;
294-
trapFile.Write(name);
303+
if (named.IsFileLocal)
304+
{
305+
trapFile.Write(named.MetadataName);
306+
}
307+
else if (named.IsExtension)
308+
{
309+
var name = GetExtensionTypeName(named, cx);
310+
trapFile.Write(name);
311+
}
312+
else
313+
{
314+
trapFile.Write(named.Name);
315+
}
316+
295317
}
296318

297319
private static void BuildTupleId(INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined)
@@ -488,9 +510,8 @@ private static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, C
488510

489511
if (namedType.IsExtension)
490512
{
491-
var type = namedType.ExtensionParameter?.Type.Name;
492-
var name = $"extension({type})";
493-
trapFile.Write(TrapExtensions.EncodeString(name));
513+
var name = GetExtensionTypeName(namedType, cx);
514+
trapFile.Write(name);
494515
return;
495516
}
496517

csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedEntity.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,6 @@ public string DebugContents
5454
}
5555
}
5656

57-
protected static void WriteLocationToTrap<T1>(Action<T1, Location> writeAction, T1 entity, Location l)
58-
{
59-
if (l is not EmptyLocation)
60-
{
61-
writeAction(entity, l);
62-
}
63-
}
64-
65-
protected static void WriteLocationsToTrap<T1>(Action<T1, Location> writeAction, T1 entity, IEnumerable<Location> locations)
66-
{
67-
foreach (var loc in locations)
68-
{
69-
WriteLocationToTrap(writeAction, entity, loc);
70-
}
71-
}
72-
7357
public override bool NeedsPopulation { get; }
7458

7559
public override int GetHashCode() => Symbol is null ? 0 : Symbol.GetHashCode();

csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,6 @@ protected void PopulateAttributes()
3232
Attribute.ExtractAttributes(Context, Symbol, this);
3333
}
3434

35-
protected void PopulateNullability(TextWriter trapFile, AnnotatedTypeSymbol type)
36-
{
37-
var n = NullabilityEntity.Create(Context, Nullability.Create(type));
38-
if (!type.HasObliviousNullability())
39-
{
40-
trapFile.type_nullability(this, n);
41-
}
42-
}
43-
44-
protected void PopulateRefKind(TextWriter trapFile, RefKind kind)
45-
{
46-
switch (kind)
47-
{
48-
case RefKind.Out:
49-
trapFile.type_annotation(this, Kinds.TypeAnnotation.Out);
50-
break;
51-
case RefKind.Ref:
52-
trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref);
53-
break;
54-
case RefKind.RefReadOnly:
55-
case RefKind.RefReadOnlyParameter:
56-
trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef);
57-
break;
58-
}
59-
}
60-
6135
protected void PopulateScopedKind(TextWriter trapFile, ScopedKind kind)
6236
{
6337
switch (kind)
@@ -107,7 +81,7 @@ public virtual IEnumerable<Location> Locations
10781
/// Bind comments to this symbol.
10882
/// Comments are only bound to source declarations.
10983
/// </summary>
110-
protected virtual void BindComments()
84+
protected void BindComments()
11185
{
11286
if (!Symbol.IsImplicitlyDeclared && IsSourceDeclaration && Symbol.FromSource())
11387
Context.BindComments(this, FullLocation);

csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/Entity.cs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
34
using Microsoft.CodeAnalysis;
5+
using Semmle.Extraction.CSharp.Entities;
46

57
namespace Semmle.Extraction.CSharp
68
{
@@ -24,7 +26,7 @@ public virtual void WriteQuotedId(EscapingTextWriter trapFile)
2426
trapFile.WriteUnescaped('\"');
2527
}
2628

27-
public abstract Location? ReportingLocation { get; }
29+
public abstract Microsoft.CodeAnalysis.Location? ReportingLocation { get; }
2830

2931
public abstract TrapStackBehaviour TrapStackBehaviour { get; }
3032

@@ -65,6 +67,48 @@ public string GetDebugLabel()
6567
}
6668
#endif
6769

70+
protected void PopulateRefKind(TextWriter trapFile, RefKind kind)
71+
{
72+
switch (kind)
73+
{
74+
case RefKind.Out:
75+
trapFile.type_annotation(this, Kinds.TypeAnnotation.Out);
76+
break;
77+
case RefKind.Ref:
78+
trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref);
79+
break;
80+
case RefKind.RefReadOnly:
81+
case RefKind.RefReadOnlyParameter:
82+
trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef);
83+
break;
84+
}
85+
}
86+
87+
protected void PopulateNullability(TextWriter trapFile, AnnotatedTypeSymbol type)
88+
{
89+
var n = NullabilityEntity.Create(Context, Nullability.Create(type));
90+
if (!type.HasObliviousNullability())
91+
{
92+
trapFile.type_nullability(this, n);
93+
}
94+
}
95+
96+
protected static void WriteLocationToTrap<T1>(Action<T1, Entities.Location> writeAction, T1 entity, Entities.Location l)
97+
{
98+
if (l is not EmptyLocation)
99+
{
100+
writeAction(entity, l);
101+
}
102+
}
103+
104+
protected static void WriteLocationsToTrap<T1>(Action<T1, Entities.Location> writeAction, T1 entity, IEnumerable<Entities.Location> locations)
105+
{
106+
foreach (var loc in locations)
107+
{
108+
WriteLocationToTrap(writeAction, entity, loc);
109+
}
110+
}
111+
68112
public override string ToString() => Label.ToString();
69113
}
70114
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ protected void PopulateParameters()
2222
// Synthesize implicit parameter for extension methods declared using extension(...) syntax.
2323
if (Symbol.ContainingSymbol is INamedTypeSymbol type &&
2424
type.IsExtension && !string.IsNullOrEmpty(type.ExtensionParameter?.Name) &&
25+
!Symbol.IsStatic &&
2526
Symbol.AssociatedExtensionImplementation is IMethodSymbol associated)
2627
{
2728
// TODO: Check that this works for generics as well. We might need to also take
@@ -36,8 +37,8 @@ protected void PopulateParameters()
3637
{
3738
var original = SymbolEqualityComparer.Default.Equals(p.paramSymbol, p.originalParam)
3839
? null
39-
: Parameter.Create(Context, p.originalParam, originalMethod);
40-
Parameter.Create(Context, p.paramSymbol, this, original);
40+
: Parameter.Create(Context, p.originalParam, originalMethod, null, positionOffset);
41+
Parameter.Create(Context, p.paramSymbol, this, original, positionOffset);
4142
}
4243

4344
if (Symbol.IsVararg)

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

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
namespace Semmle.Extraction.CSharp.Entities
99
{
1010
// Marker interface for parameter entities.
11-
public interface IParameterEntity : IEntity { }
12-
internal class Parameter : CachedSymbol<IParameterSymbol>, IExpressionParentEntity, IParameterEntity
11+
public interface IParameter : IEntity { }
12+
internal class Parameter : CachedSymbol<IParameterSymbol>, IExpressionParentEntity, IParameter
1313
{
1414
protected IEntity? Parent { get; set; }
1515
protected Parameter Original { get; }
@@ -267,80 +267,86 @@ private class VarargsTypeFactory : CachedEntityFactory<string?, VarargsType>
267267
/// Note, that we use the characteristics of the parameter of the associated (compiler generated) extension method
268268
/// to populate the database.
269269
/// </summary>
270-
internal class ImplicitExtensionParameter : Parameter
270+
internal class ImplicitExtensionParameter : FreshEntity, IParameter
271271
{
272-
private Method ExtensionMethod { get; init; }
272+
private Method ExtensionMethod { get; }
273+
private IParameterSymbol ExtensionParameter { get; }
273274

274-
private ImplicitExtensionParameter(Context cx, Method method)
275-
#nullable disable warnings
276-
: base(cx, method.Symbol.AssociatedExtensionImplementation.Parameters[0], method, null)
275+
private ImplicitExtensionParameter(Context cx, Method method) : base(cx)
277276
{
278277
ExtensionMethod = method;
278+
ExtensionParameter = method.Symbol.AssociatedExtensionImplementation!.Parameters[0];
279279
}
280-
#nullable restore warnings
281280

282-
protected override int Ordinal => 0;
281+
private static int Ordinal => 0;
283282

284-
private Kind ParamKind
283+
private Parameter.Kind ParamKind
285284
{
286285
get
287286
{
288-
switch (Symbol.RefKind)
287+
switch (ExtensionParameter.RefKind)
289288
{
290289
case RefKind.Ref:
291-
return Kind.Ref;
290+
return Parameter.Kind.Ref;
292291
case RefKind.In:
293-
return Kind.In;
292+
return Parameter.Kind.In;
294293
case RefKind.RefReadOnlyParameter:
295-
return Kind.RefReadOnly;
294+
return Parameter.Kind.RefReadOnly;
296295
default:
297-
return Kind.None;
296+
return Parameter.Kind.None;
298297
}
299298
}
300299
}
301300

302-
private string Name => Symbol.Name;
301+
private string Name => ExtensionParameter.Name;
302+
303+
private bool IsSourceDeclaration => ExtensionMethod.Symbol.IsSourceDeclaration();
303304

304-
public override bool IsSourceDeclaration => ExtensionMethod.Symbol.IsSourceDeclaration();
305+
private void PopulateAttributes()
306+
{
307+
// Only extract attributes for source declarations
308+
if (ReferenceEquals(ExtensionParameter, ExtensionParameter.OriginalDefinition))
309+
Attribute.ExtractAttributes(Context, ExtensionParameter, this);
310+
}
305311

306312
/// <summary>
307313
/// Bind comments to this symbol.
308314
/// Comments are only bound to source declarations.
309315
/// </summary>
310-
protected override void BindComments()
316+
private void BindComments()
311317
{
312-
if (IsSourceDeclaration && Symbol.FromSource())
313-
Context.BindComments(this, FullLocation);
318+
if (IsSourceDeclaration && ExtensionParameter.FromSource())
319+
Context.BindComments(this, ExtensionParameter.Locations.BestOrDefault());
314320
}
315321

316-
public override void Populate(TextWriter trapFile)
322+
protected override void Populate(TextWriter trapFile)
317323
{
318324
PopulateAttributes();
319-
PopulateNullability(trapFile, Symbol.GetAnnotatedType());
320-
PopulateRefKind(trapFile, Symbol.RefKind);
325+
PopulateNullability(trapFile, ExtensionParameter.GetAnnotatedType());
326+
PopulateRefKind(trapFile, ExtensionParameter.RefKind);
321327

322-
var type = Type.Create(Context, Symbol.Type);
323-
trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, Parent!, this);
328+
var type = Type.Create(Context, ExtensionParameter.Type);
329+
trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, ExtensionMethod, this);
324330

325331
if (Context.OnlyScaffold)
326332
{
327333
return;
328334
}
329335

330-
if (Context.ExtractLocation(Symbol))
336+
if (Context.ExtractLocation(ExtensionParameter))
331337
{
332-
var locations = Context.GetLocations(Symbol);
338+
var locations = Context.GetLocations(ExtensionParameter);
333339
WriteLocationsToTrap(trapFile.param_location, this, locations);
334340
}
335341

336-
if (!IsSourceDeclaration || !Symbol.FromSource())
342+
if (!IsSourceDeclaration || !ExtensionParameter.FromSource())
337343
return;
338344

339345
BindComments();
340346

341347
if (IsSourceDeclaration)
342348
{
343-
foreach (var syntax in Symbol.DeclaringSyntaxReferences
349+
foreach (var syntax in ExtensionParameter.DeclaringSyntaxReferences
344350
.Select(d => d.GetSyntax())
345351
.OfType<ParameterSyntax>()
346352
.Where(s => s.Type is not null))
@@ -350,16 +356,12 @@ public override void Populate(TextWriter trapFile)
350356
}
351357
}
352358

353-
public static ImplicitExtensionParameter Create(Context cx, Method method) => ImplicitExtensionParameterFactory.Instance.CreateEntity(cx, typeof(ImplicitExtensionParameter), method);
354-
355-
private class ImplicitExtensionParameterFactory : CachedEntityFactory<Method, ImplicitExtensionParameter>
359+
public static ImplicitExtensionParameter Create(Context cx, Method method)
356360
{
357-
public static ImplicitExtensionParameterFactory Instance { get; } = new ImplicitExtensionParameterFactory();
358-
359-
public override ImplicitExtensionParameter Create(Context cx, Method init) => new ImplicitExtensionParameter(cx, init);
361+
var parameter = new ImplicitExtensionParameter(cx, method);
362+
parameter.TryPopulate();
363+
return parameter;
360364
}
361-
362-
363365
}
364366

365367
internal class VarargsParam : Parameter

csharp/extractor/Semmle.Extraction.CSharp/Trap/Tuples.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,10 @@ internal static void operators(this TextWriter trapFile, UserOperator method, st
292292
internal static void overrides(this TextWriter trapFile, Method overriding, Method overridden) =>
293293
trapFile.WriteTuple("overrides", overriding, overridden);
294294

295-
internal static void param_location(this TextWriter trapFile, Parameter param, Location location) =>
295+
internal static void param_location(this TextWriter trapFile, IParameter param, Location location) =>
296296
trapFile.WriteTuple("param_location", param, location);
297297

298-
internal static void @params(this TextWriter trapFile, Parameter param, string name, Type type, int child, Parameter.Kind mode, IEntity method, Parameter originalDefinition) =>
298+
internal static void @params(this TextWriter trapFile, IParameter param, string name, Type type, int child, Parameter.Kind mode, IEntity method, IParameter originalDefinition) =>
299299
trapFile.WriteTuple("params", param, name, type, child, (int)mode, method, originalDefinition);
300300

301301
internal static void parent_namespace(this TextWriter trapFile, IEntity type, Namespace parent) =>

0 commit comments

Comments
 (0)