From d1410718469fab872239782fb0708f5dfde64dfd Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 26 Jan 2026 11:53:59 +0100 Subject: [PATCH 01/39] C#: Extractor support for extension blocks. --- .../SymbolExtensions.cs | 16 +++ .../Entities/Base/CachedSymbol.cs | 2 +- .../Entities/Expressions/Invocation.cs | 24 +++- .../Entities/Method.cs | 12 ++ .../Entities/OrdinaryMethod.cs | 6 +- .../Entities/Parameter.cs | 135 ++++++++++++++++-- .../Entities/Property.cs | 1 + .../Entities/Types/Type.cs | 3 +- .../Entities/UserOperator.cs | 1 + .../Kinds/TypeKind.cs | 1 + 10 files changed, 188 insertions(+), 13 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs index 72f78f160598..20679325a6bc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs @@ -164,6 +164,7 @@ public static void BuildTypeId(this ITypeSymbol type, Context cx, EscapingTextWr case TypeKind.Enum: case TypeKind.Delegate: case TypeKind.Error: + case TypeKind.Extension: var named = (INamedTypeSymbol)type; named.BuildNamedTypeId(cx, trapFile, symbolBeingDefined, constructUnderlyingTupleType); return; @@ -391,6 +392,7 @@ public static void BuildDisplayName(this ITypeSymbol type, Context cx, TextWrite case TypeKind.Enum: case TypeKind.Delegate: case TypeKind.Error: + case TypeKind.Extension: var named = (INamedTypeSymbol)type; named.BuildNamedTypeDisplayName(cx, trapFile, constructUnderlyingTupleType); return; @@ -484,6 +486,14 @@ private static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, C return; } + if (namedType.IsExtension) + { + var type = namedType.ExtensionParameter?.Type.Name; + var name = $"extension({type})"; + trapFile.Write(TrapExtensions.EncodeString(name)); + return; + } + if (namedType.IsAnonymousType) { namedType.BuildAnonymousName(cx, trapFile); @@ -596,6 +606,12 @@ public static bool IsSourceDeclaration(this IParameterSymbol parameter) return true; } + /// + /// Return true if this method is a compiler-generated extension method. + /// + public static bool IsCompilerGeneratedExtensionMethod(this IMethodSymbol method) => + method.IsImplicitlyDeclared && method.IsExtensionMethod; + /// /// Gets the base type of `symbol`. Unlike `symbol.BaseType`, this excludes effective base /// types of type parameters as well as `object` base types. diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs index 92861e97fdd8..37273425adb0 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs @@ -107,7 +107,7 @@ public virtual IEnumerable Locations /// Bind comments to this symbol. /// Comments are only bound to source declarations. /// - protected void BindComments() + protected virtual void BindComments() { if (!Symbol.IsImplicitlyDeclared && IsSourceDeclaration && Symbol.FromSource()) Context.BindComments(this, FullLocation); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs index a6272974c22b..6241f46bb722 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs @@ -119,8 +119,28 @@ public IMethodSymbol? TargetSymbol { var si = SymbolInfo; - if (si.Symbol is not null) - return si.Symbol as IMethodSymbol; + if (si.Symbol is ISymbol symbol) + { + var method = symbol as IMethodSymbol; + // Case for compiler-generated extension methods. + if (method is not null && + method.IsCompilerGeneratedExtensionMethod() && + method.ContainingSymbol is INamedTypeSymbol containingType) + { + // Extension types are declared within the same type as the generated + // extension method implementation. + var extensions = containingType.GetMembers() + .OfType() + .Where(t => t.IsExtension); + // Find the original extension method that maps to this implementation (if any). + var original = extensions.SelectMany(e => e.GetMembers()) + .OfType() + .FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method)); + return original ?? method; + } + + return method; + } if (si.CandidateReason == CandidateReason.OverloadResolutionFailure) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index c92c561f31b6..196093ff6e68 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -16,7 +16,19 @@ protected Method(Context cx, IMethodSymbol init) protected void PopulateParameters() { + var positionOffset = 0; var originalMethod = OriginalDefinition; + + // Synthesize implicit parameter for extension methods declared using extension(...) syntax. + if (Symbol.ContainingSymbol is INamedTypeSymbol type && + type.IsExtension && !string.IsNullOrEmpty(type.ExtensionParameter?.Name) && + Symbol.AssociatedExtensionImplementation is IMethodSymbol associated) + { + // TODO: Check that this works for generics as well. We might need to also take + ImplicitExtensionParameter.Create(Context, this); + positionOffset++; + } + IEnumerable parameters = Symbol.Parameters; IEnumerable originalParameters = originalMethod.Symbol.Parameters; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs index 22bcd1dce2c8..2fb148358e8c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs @@ -23,7 +23,11 @@ protected OrdinaryMethod(Context cx, IMethodSymbol init) ? Symbol.ContainingType.GetSymbolLocation() : BodyDeclaringSymbol.GetSymbolLocation(); - public override bool NeedsPopulation => base.NeedsPopulation || IsCompilerGeneratedDelegate(); + public override bool NeedsPopulation => + (base.NeedsPopulation || IsCompilerGeneratedDelegate()) && + // Exclude compiler-generated extension methods. A call to such a method + // is replaced by a call to the defining extension method. + !Symbol.IsCompilerGeneratedExtensionMethod(); public override void Populate(TextWriter trapFile) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs index 49ef9a4a6e9a..dfed4051282a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -7,16 +7,25 @@ namespace Semmle.Extraction.CSharp.Entities { - internal class Parameter : CachedSymbol, IExpressionParentEntity + // Marker interface for parameter entities. + public interface IParameterEntity : IEntity { } + internal class Parameter : CachedSymbol, IExpressionParentEntity, IParameterEntity { protected IEntity? Parent { get; set; } protected Parameter Original { get; } + private int PositionOffset { get; set; } - protected Parameter(Context cx, IParameterSymbol init, IEntity? parent, Parameter? original) + private Parameter(Context cx, IParameterSymbol init, IEntity? parent, Parameter? original, int positionOffset) : base(cx, init) { Parent = parent; Original = original ?? this; + PositionOffset = positionOffset; + } + + protected Parameter(Context cx, IParameterSymbol init, IEntity? parent, Parameter? original) + : this(cx, init, parent, original, 0) + { } public override Microsoft.CodeAnalysis.Location ReportingLocation => Symbol.GetSymbolLocation(); @@ -32,7 +41,7 @@ public enum Kind RefReadOnly = 6 } - protected virtual int Ordinal => Symbol.Ordinal; + protected virtual int Ordinal => Symbol.Ordinal + PositionOffset; private Kind ParamKind { @@ -55,23 +64,25 @@ private Kind ParamKind if (Ordinal == 0) { if (Symbol.ContainingSymbol is IMethodSymbol method && method.IsExtensionMethod) + { return Kind.This; + } } return Kind.None; } } } - public static Parameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter? original = null) + public static Parameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter? original = null, int positionOffset = 0) { var cachedSymbol = cx.GetPossiblyCachedParameterSymbol(param); - return ParameterFactory.Instance.CreateEntity(cx, cachedSymbol, (cachedSymbol, parent, original)); + return ParameterFactory.Instance.CreateEntity(cx, cachedSymbol, (cachedSymbol, parent, original, positionOffset)); } public static Parameter Create(Context cx, IParameterSymbol param) { var cachedSymbol = cx.GetPossiblyCachedParameterSymbol(param); - return ParameterFactory.Instance.CreateEntity(cx, cachedSymbol, (cachedSymbol, null, null)); + return ParameterFactory.Instance.CreateEntity(cx, cachedSymbol, (cachedSymbol, null, null, 0)); } public override void WriteId(EscapingTextWriter trapFile) @@ -79,6 +90,9 @@ public override void WriteId(EscapingTextWriter trapFile) if (Parent is null) Parent = Method.Create(Context, Symbol.ContainingSymbol as IMethodSymbol); + if (Parent is null && Symbol.ContainingSymbol is INamedTypeSymbol type && type.IsExtension) + Parent = Type.Create(Context, type); + if (Parent is null) throw new InternalError(Symbol, "Couldn't get parent of symbol."); @@ -194,11 +208,11 @@ Symbol.ContainingSymbol is IMethodSymbol ms && return syntax?.Default; } - private class ParameterFactory : CachedEntityFactory<(IParameterSymbol, IEntity?, Parameter?), Parameter> + private class ParameterFactory : CachedEntityFactory<(IParameterSymbol, IEntity?, Parameter?, int), Parameter> { public static ParameterFactory Instance { get; } = new ParameterFactory(); - public override Parameter Create(Context cx, (IParameterSymbol, IEntity?, Parameter?) init) => new Parameter(cx, init.Item1, init.Item2, init.Item3); + public override Parameter Create(Context cx, (IParameterSymbol, IEntity?, Parameter?, int) init) => new Parameter(cx, init.Item1, init.Item2, init.Item3, init.Item4); } public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel; @@ -243,6 +257,111 @@ private class VarargsTypeFactory : CachedEntityFactory } } + /// + /// Synthetic parameter for extension methods declared using the extensions syntax. + /// That is, we add a synthetic parameter s to IsValid in the following example: + /// extension(string s) { + /// public bool IsValid() { ... } + /// } + /// + /// Note, that we use the characteristics of the parameter of the associated (compiler generated) extension method + /// to populate the database. + /// + internal class ImplicitExtensionParameter : Parameter + { + private Method ExtensionMethod { get; init; } + + private ImplicitExtensionParameter(Context cx, Method method) +#nullable disable warnings + : base(cx, method.Symbol.AssociatedExtensionImplementation.Parameters[0], method, null) + { + ExtensionMethod = method; + } +#nullable restore warnings + + protected override int Ordinal => 0; + + private Kind ParamKind + { + get + { + switch (Symbol.RefKind) + { + case RefKind.Ref: + return Kind.Ref; + case RefKind.In: + return Kind.In; + case RefKind.RefReadOnlyParameter: + return Kind.RefReadOnly; + default: + return Kind.None; + } + } + } + + private string Name => Symbol.Name; + + public override bool IsSourceDeclaration => ExtensionMethod.Symbol.IsSourceDeclaration(); + + /// + /// Bind comments to this symbol. + /// Comments are only bound to source declarations. + /// + protected override void BindComments() + { + if (IsSourceDeclaration && Symbol.FromSource()) + Context.BindComments(this, FullLocation); + } + + public override void Populate(TextWriter trapFile) + { + PopulateAttributes(); + PopulateNullability(trapFile, Symbol.GetAnnotatedType()); + PopulateRefKind(trapFile, Symbol.RefKind); + + var type = Type.Create(Context, Symbol.Type); + trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, Parent!, this); + + if (Context.OnlyScaffold) + { + return; + } + + if (Context.ExtractLocation(Symbol)) + { + var locations = Context.GetLocations(Symbol); + WriteLocationsToTrap(trapFile.param_location, this, locations); + } + + if (!IsSourceDeclaration || !Symbol.FromSource()) + return; + + BindComments(); + + if (IsSourceDeclaration) + { + foreach (var syntax in Symbol.DeclaringSyntaxReferences + .Select(d => d.GetSyntax()) + .OfType() + .Where(s => s.Type is not null)) + { + TypeMention.Create(Context, syntax.Type!, this, type); + } + } + } + + public static ImplicitExtensionParameter Create(Context cx, Method method) => ImplicitExtensionParameterFactory.Instance.CreateEntity(cx, typeof(ImplicitExtensionParameter), method); + + private class ImplicitExtensionParameterFactory : CachedEntityFactory + { + public static ImplicitExtensionParameterFactory Instance { get; } = new ImplicitExtensionParameterFactory(); + + public override ImplicitExtensionParameter Create(Context cx, Method init) => new ImplicitExtensionParameter(cx, init); + } + + + } + internal class VarargsParam : Parameter { #nullable disable warnings diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs index d48d778cb75a..4010142655cd 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs @@ -103,6 +103,7 @@ public override void Populate(TextWriter trapFile) trapFile.expr_access(access, this); if (!Symbol.IsStatic) { + // TODO: What about the containing type for extensions? This.CreateImplicit(Context, Symbol.ContainingType, Location, access, -1); } }); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs index 3e79a8f81018..0f28a1153e22 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs @@ -105,6 +105,7 @@ public Kinds.TypeKind GetTypeKind(Context cx, bool constructUnderlyingTupleType) case TypeKind.Pointer: return Kinds.TypeKind.POINTER; case TypeKind.FunctionPointer: return Kinds.TypeKind.FUNCTION_POINTER; case TypeKind.Error: return Kinds.TypeKind.UNKNOWN; + case TypeKind.Extension: return Kinds.TypeKind.EXTENSION; default: cx.ModelError(Symbol, $"Unhandled type kind '{Symbol.TypeKind}'"); return Kinds.TypeKind.UNKNOWN; @@ -366,7 +367,7 @@ private class DelegateTypeParameter : Parameter private DelegateTypeParameter(Context cx, IParameterSymbol init, IEntity parent, Parameter? original) : base(cx, init, parent, original) { } - public static new DelegateTypeParameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter? original = null) => + public static DelegateTypeParameter Create(Context cx, IParameterSymbol param, IEntity parent, Parameter? original = null) => // We need to use a different cache key than `param` to avoid mixing up // `DelegateTypeParameter`s and `Parameter`s DelegateTypeParameterFactory.Instance.CreateEntity(cx, (typeof(DelegateTypeParameter), new SymbolEqualityWrapper(param)), (param, parent, original)); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs index fc9358ffc2dc..b87bf9e10d08 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs @@ -68,6 +68,7 @@ public override Type ContainingType /// private bool IsImplicitOperator(out ITypeSymbol containingType) { + // TODO: Do we need to handle extension operators here? containingType = Symbol.ContainingType; if (containingType is not null) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Kinds/TypeKind.cs b/csharp/extractor/Semmle.Extraction.CSharp/Kinds/TypeKind.cs index a35f25c7596c..9088a11da61f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Kinds/TypeKind.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Kinds/TypeKind.cs @@ -38,5 +38,6 @@ public enum TypeKind TUPLE = 32, FUNCTION_POINTER = 33, INLINE_ARRAY = 34, + EXTENSION = 35 } } From a48ccc0b9921620f68f8c2ae7a3ab218557bf0b0 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 26 Jan 2026 16:20:19 +0100 Subject: [PATCH 02/39] C#: Implicit extension parameters should be fresh entities. --- .../SymbolExtensions.cs | 31 ++++++-- .../Entities/Base/CachedEntity.cs | 16 ---- .../Entities/Base/CachedSymbol.cs | 28 +------ .../Entities/Base/Entity.cs | 46 ++++++++++- .../Entities/Method.cs | 5 +- .../Entities/Parameter.cs | 76 ++++++++++--------- .../Semmle.Extraction.CSharp/Trap/Tuples.cs | 4 +- 7 files changed, 116 insertions(+), 90 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs index 20679325a6bc..9e94daf4e62e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs @@ -276,6 +276,16 @@ private static void BuildFunctionPointerTypeId(this IFunctionPointerTypeSymbol f public static IEnumerable GetTupleElementsMaybeNull(this INamedTypeSymbol type) => type.TupleElements; + private static string GetExtensionTypeName(this INamedTypeSymbol named, Context cx) + { + var type = named.ExtensionParameter?.Type.Name; + if (type is null) + { + cx.ModelError(named, "Failed to get extension method type."); + } + return $"extension({type ?? "unknown"})"; + } + private static void BuildQualifierAndName(INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined) { if (named.ContainingType is not null) @@ -290,8 +300,20 @@ private static void BuildQualifierAndName(INamedTypeSymbol named, Context cx, Es named.ContainingNamespace.BuildNamespace(cx, trapFile); } - var name = named.IsFileLocal ? named.MetadataName : named.Name; - trapFile.Write(name); + if (named.IsFileLocal) + { + trapFile.Write(named.MetadataName); + } + else if (named.IsExtension) + { + var name = GetExtensionTypeName(named, cx); + trapFile.Write(name); + } + else + { + trapFile.Write(named.Name); + } + } private static void BuildTupleId(INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined) @@ -488,9 +510,8 @@ private static void BuildNamedTypeDisplayName(this INamedTypeSymbol namedType, C if (namedType.IsExtension) { - var type = namedType.ExtensionParameter?.Type.Name; - var name = $"extension({type})"; - trapFile.Write(TrapExtensions.EncodeString(name)); + var name = GetExtensionTypeName(namedType, cx); + trapFile.Write(name); return; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedEntity.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedEntity.cs index 2002fe0f1d7a..39d0f886b813 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedEntity.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedEntity.cs @@ -54,22 +54,6 @@ public string DebugContents } } - protected static void WriteLocationToTrap(Action writeAction, T1 entity, Location l) - { - if (l is not EmptyLocation) - { - writeAction(entity, l); - } - } - - protected static void WriteLocationsToTrap(Action writeAction, T1 entity, IEnumerable locations) - { - foreach (var loc in locations) - { - WriteLocationToTrap(writeAction, entity, loc); - } - } - public override bool NeedsPopulation { get; } public override int GetHashCode() => Symbol is null ? 0 : Symbol.GetHashCode(); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs index 37273425adb0..1a69b0e08b2b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs @@ -32,32 +32,6 @@ protected void PopulateAttributes() Attribute.ExtractAttributes(Context, Symbol, this); } - protected void PopulateNullability(TextWriter trapFile, AnnotatedTypeSymbol type) - { - var n = NullabilityEntity.Create(Context, Nullability.Create(type)); - if (!type.HasObliviousNullability()) - { - trapFile.type_nullability(this, n); - } - } - - protected void PopulateRefKind(TextWriter trapFile, RefKind kind) - { - switch (kind) - { - case RefKind.Out: - trapFile.type_annotation(this, Kinds.TypeAnnotation.Out); - break; - case RefKind.Ref: - trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref); - break; - case RefKind.RefReadOnly: - case RefKind.RefReadOnlyParameter: - trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef); - break; - } - } - protected void PopulateScopedKind(TextWriter trapFile, ScopedKind kind) { switch (kind) @@ -107,7 +81,7 @@ public virtual IEnumerable Locations /// Bind comments to this symbol. /// Comments are only bound to source declarations. /// - protected virtual void BindComments() + protected void BindComments() { if (!Symbol.IsImplicitlyDeclared && IsSourceDeclaration && Symbol.FromSource()) Context.BindComments(this, FullLocation); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/Entity.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/Entity.cs index ca1887b3be9c..94b2e16e9e6b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/Entity.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/Entity.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.IO; using Microsoft.CodeAnalysis; +using Semmle.Extraction.CSharp.Entities; namespace Semmle.Extraction.CSharp { @@ -24,7 +26,7 @@ public virtual void WriteQuotedId(EscapingTextWriter trapFile) trapFile.WriteUnescaped('\"'); } - public abstract Location? ReportingLocation { get; } + public abstract Microsoft.CodeAnalysis.Location? ReportingLocation { get; } public abstract TrapStackBehaviour TrapStackBehaviour { get; } @@ -65,6 +67,48 @@ public string GetDebugLabel() } #endif + protected void PopulateRefKind(TextWriter trapFile, RefKind kind) + { + switch (kind) + { + case RefKind.Out: + trapFile.type_annotation(this, Kinds.TypeAnnotation.Out); + break; + case RefKind.Ref: + trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref); + break; + case RefKind.RefReadOnly: + case RefKind.RefReadOnlyParameter: + trapFile.type_annotation(this, Kinds.TypeAnnotation.ReadonlyRef); + break; + } + } + + protected void PopulateNullability(TextWriter trapFile, AnnotatedTypeSymbol type) + { + var n = NullabilityEntity.Create(Context, Nullability.Create(type)); + if (!type.HasObliviousNullability()) + { + trapFile.type_nullability(this, n); + } + } + + protected static void WriteLocationToTrap(Action writeAction, T1 entity, Entities.Location l) + { + if (l is not EmptyLocation) + { + writeAction(entity, l); + } + } + + protected static void WriteLocationsToTrap(Action writeAction, T1 entity, IEnumerable locations) + { + foreach (var loc in locations) + { + WriteLocationToTrap(writeAction, entity, loc); + } + } + public override string ToString() => Label.ToString(); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index 196093ff6e68..bbb29c269af8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -22,6 +22,7 @@ protected void PopulateParameters() // Synthesize implicit parameter for extension methods declared using extension(...) syntax. if (Symbol.ContainingSymbol is INamedTypeSymbol type && type.IsExtension && !string.IsNullOrEmpty(type.ExtensionParameter?.Name) && + !Symbol.IsStatic && Symbol.AssociatedExtensionImplementation is IMethodSymbol associated) { // TODO: Check that this works for generics as well. We might need to also take @@ -36,8 +37,8 @@ protected void PopulateParameters() { var original = SymbolEqualityComparer.Default.Equals(p.paramSymbol, p.originalParam) ? null - : Parameter.Create(Context, p.originalParam, originalMethod); - Parameter.Create(Context, p.paramSymbol, this, original); + : Parameter.Create(Context, p.originalParam, originalMethod, null, positionOffset); + Parameter.Create(Context, p.paramSymbol, this, original, positionOffset); } if (Symbol.IsVararg) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs index dfed4051282a..5e77ba0f8103 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -8,8 +8,8 @@ namespace Semmle.Extraction.CSharp.Entities { // Marker interface for parameter entities. - public interface IParameterEntity : IEntity { } - internal class Parameter : CachedSymbol, IExpressionParentEntity, IParameterEntity + public interface IParameter : IEntity { } + internal class Parameter : CachedSymbol, IExpressionParentEntity, IParameter { protected IEntity? Parent { get; set; } protected Parameter Original { get; } @@ -267,80 +267,86 @@ private class VarargsTypeFactory : CachedEntityFactory /// Note, that we use the characteristics of the parameter of the associated (compiler generated) extension method /// to populate the database. /// - internal class ImplicitExtensionParameter : Parameter + internal class ImplicitExtensionParameter : FreshEntity, IParameter { - private Method ExtensionMethod { get; init; } + private Method ExtensionMethod { get; } + private IParameterSymbol ExtensionParameter { get; } - private ImplicitExtensionParameter(Context cx, Method method) -#nullable disable warnings - : base(cx, method.Symbol.AssociatedExtensionImplementation.Parameters[0], method, null) + private ImplicitExtensionParameter(Context cx, Method method) : base(cx) { ExtensionMethod = method; + ExtensionParameter = method.Symbol.AssociatedExtensionImplementation!.Parameters[0]; } -#nullable restore warnings - protected override int Ordinal => 0; + private static int Ordinal => 0; - private Kind ParamKind + private Parameter.Kind ParamKind { get { - switch (Symbol.RefKind) + switch (ExtensionParameter.RefKind) { case RefKind.Ref: - return Kind.Ref; + return Parameter.Kind.Ref; case RefKind.In: - return Kind.In; + return Parameter.Kind.In; case RefKind.RefReadOnlyParameter: - return Kind.RefReadOnly; + return Parameter.Kind.RefReadOnly; default: - return Kind.None; + return Parameter.Kind.None; } } } - private string Name => Symbol.Name; + private string Name => ExtensionParameter.Name; + + private bool IsSourceDeclaration => ExtensionMethod.Symbol.IsSourceDeclaration(); - public override bool IsSourceDeclaration => ExtensionMethod.Symbol.IsSourceDeclaration(); + private void PopulateAttributes() + { + // Only extract attributes for source declarations + if (ReferenceEquals(ExtensionParameter, ExtensionParameter.OriginalDefinition)) + Attribute.ExtractAttributes(Context, ExtensionParameter, this); + } /// /// Bind comments to this symbol. /// Comments are only bound to source declarations. /// - protected override void BindComments() + private void BindComments() { - if (IsSourceDeclaration && Symbol.FromSource()) - Context.BindComments(this, FullLocation); + if (IsSourceDeclaration && ExtensionParameter.FromSource()) + Context.BindComments(this, ExtensionParameter.Locations.BestOrDefault()); } - public override void Populate(TextWriter trapFile) + protected override void Populate(TextWriter trapFile) { PopulateAttributes(); - PopulateNullability(trapFile, Symbol.GetAnnotatedType()); - PopulateRefKind(trapFile, Symbol.RefKind); + PopulateNullability(trapFile, ExtensionParameter.GetAnnotatedType()); + PopulateRefKind(trapFile, ExtensionParameter.RefKind); - var type = Type.Create(Context, Symbol.Type); - trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, Parent!, this); + var type = Type.Create(Context, ExtensionParameter.Type); + trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, ExtensionMethod, this); if (Context.OnlyScaffold) { return; } - if (Context.ExtractLocation(Symbol)) + if (Context.ExtractLocation(ExtensionParameter)) { - var locations = Context.GetLocations(Symbol); + var locations = Context.GetLocations(ExtensionParameter); WriteLocationsToTrap(trapFile.param_location, this, locations); } - if (!IsSourceDeclaration || !Symbol.FromSource()) + if (!IsSourceDeclaration || !ExtensionParameter.FromSource()) return; BindComments(); if (IsSourceDeclaration) { - foreach (var syntax in Symbol.DeclaringSyntaxReferences + foreach (var syntax in ExtensionParameter.DeclaringSyntaxReferences .Select(d => d.GetSyntax()) .OfType() .Where(s => s.Type is not null)) @@ -350,16 +356,12 @@ public override void Populate(TextWriter trapFile) } } - public static ImplicitExtensionParameter Create(Context cx, Method method) => ImplicitExtensionParameterFactory.Instance.CreateEntity(cx, typeof(ImplicitExtensionParameter), method); - - private class ImplicitExtensionParameterFactory : CachedEntityFactory + public static ImplicitExtensionParameter Create(Context cx, Method method) { - public static ImplicitExtensionParameterFactory Instance { get; } = new ImplicitExtensionParameterFactory(); - - public override ImplicitExtensionParameter Create(Context cx, Method init) => new ImplicitExtensionParameter(cx, init); + var parameter = new ImplicitExtensionParameter(cx, method); + parameter.TryPopulate(); + return parameter; } - - } internal class VarargsParam : Parameter diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Trap/Tuples.cs b/csharp/extractor/Semmle.Extraction.CSharp/Trap/Tuples.cs index b789eaa2e9c7..c1d082bbee2b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Trap/Tuples.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Trap/Tuples.cs @@ -292,10 +292,10 @@ internal static void operators(this TextWriter trapFile, UserOperator method, st internal static void overrides(this TextWriter trapFile, Method overriding, Method overridden) => trapFile.WriteTuple("overrides", overriding, overridden); - internal static void param_location(this TextWriter trapFile, Parameter param, Location location) => + internal static void param_location(this TextWriter trapFile, IParameter param, Location location) => trapFile.WriteTuple("param_location", param, location); - internal static void @params(this TextWriter trapFile, Parameter param, string name, Type type, int child, Parameter.Kind mode, IEntity method, Parameter originalDefinition) => + internal static void @params(this TextWriter trapFile, IParameter param, string name, Type type, int child, Parameter.Kind mode, IEntity method, IParameter originalDefinition) => trapFile.WriteTuple("params", param, name, type, child, (int)mode, method, originalDefinition); internal static void parent_namespace(this TextWriter trapFile, IEntity type, Namespace parent) => From 9a952dbb4c9ba0c2ae8bba810904c464fb426dc4 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 26 Jan 2026 16:21:26 +0100 Subject: [PATCH 03/39] C#: Add extension types to the db scheme. --- csharp/ql/lib/semmlecode.csharp.dbscheme | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/csharp/ql/lib/semmlecode.csharp.dbscheme b/csharp/ql/lib/semmlecode.csharp.dbscheme index 68b5aec54e50..8cdbcbb6c7dc 100644 --- a/csharp/ql/lib/semmlecode.csharp.dbscheme +++ b/csharp/ql/lib/semmlecode.csharp.dbscheme @@ -222,7 +222,7 @@ overlayChangedFiles( | @using_directive | @type_parameter_constraints | @externalDataElement | @xmllocatable | @asp_element | @namespace | @preprocessor_directive; -@declaration = @callable | @generic | @assignable | @namespace; +@declaration = @callable | @generic | @assignable | @namespace | @extension_type; @named_element = @namespace | @declaration; @@ -492,6 +492,7 @@ case @type.kind of | 32 = @tuple_type | 33 = @function_pointer_type | 34 = @inline_array_type +| 35 = @extension_type ; @simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type; @@ -502,7 +503,7 @@ case @type.kind of @value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type | @uint_ptr_type | @tuple_type | @void_type | @inline_array_type; @ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type - | @dynamic_type; + | @dynamic_type | @extension_type; @value_or_ref_type = @value_type | @ref_type; typerefs( @@ -903,7 +904,7 @@ localvar_location( unique int id: @local_variable ref, int loc: @location ref); -@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type; +@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type | @extension_type; #keyset[name, parent_id] #keyset[index, parent_id] From 8c738315d2cfda45b23bec17cafb5841cc1499e3 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 26 Jan 2026 17:29:13 +0100 Subject: [PATCH 04/39] C#: Static extension methods in extension types should also have their call target replaced. --- .../SymbolExtensions.cs | 25 ++++++++++++++++++- .../Entities/Expressions/Invocation.cs | 14 ++--------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs index 9e94daf4e62e..d98a2a6cc1f0 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs @@ -5,6 +5,7 @@ using System.Linq; using Microsoft.CodeAnalysis; using Semmle.Extraction.CSharp.Entities; +using Semmle.Extraction.CSharp.Entities.Statements; namespace Semmle.Extraction.CSharp { @@ -631,8 +632,30 @@ public static bool IsSourceDeclaration(this IParameterSymbol parameter) /// Return true if this method is a compiler-generated extension method. /// public static bool IsCompilerGeneratedExtensionMethod(this IMethodSymbol method) => - method.IsImplicitlyDeclared && method.IsExtensionMethod; + method.TryGetExtensionMethod(out _); + /// + /// Returns true if this method is a compiler-generated extension method, + /// and outputs the original extension method declaration. + /// + public static bool TryGetExtensionMethod(this IMethodSymbol method, out IMethodSymbol? declaration) + { + declaration = null; + if (method.IsImplicitlyDeclared && method.ContainingSymbol is INamedTypeSymbol containingType) + { + // Extension types are declared within the same type as the generated + // extension method implementation. + var extensions = containingType.GetMembers() + .OfType() + .Where(t => t.IsExtension); + // Find the original extension method that maps to this implementation (if any). + declaration = extensions.SelectMany(e => e.GetMembers()) + .OfType() + .FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method)); + return declaration is not null; + } + return false; + } /// /// Gets the base type of `symbol`. Unlike `symbol.BaseType`, this excludes effective base /// types of type parameters as well as `object` base types. diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs index 6241f46bb722..961ff88b8618 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs @@ -124,19 +124,9 @@ public IMethodSymbol? TargetSymbol var method = symbol as IMethodSymbol; // Case for compiler-generated extension methods. if (method is not null && - method.IsCompilerGeneratedExtensionMethod() && - method.ContainingSymbol is INamedTypeSymbol containingType) + method.TryGetExtensionMethod(out var original)) { - // Extension types are declared within the same type as the generated - // extension method implementation. - var extensions = containingType.GetMembers() - .OfType() - .Where(t => t.IsExtension); - // Find the original extension method that maps to this implementation (if any). - var original = extensions.SelectMany(e => e.GetMembers()) - .OfType() - .FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method)); - return original ?? method; + return original; } return method; From d3c84cee7dc7a233b06a6c48551c8b9ca16b40e2 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 27 Jan 2026 13:23:18 +0100 Subject: [PATCH 05/39] C#: The call to an extension operator should be considered an operator invocation. --- .../Entities/Expressions/Invocation.cs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs index 961ff88b8618..8c0e664375c4 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs @@ -24,6 +24,8 @@ private Invocation(ExpressionNodeInfo info) private bool IsExplicitDelegateInvokeCall() => Kind == ExprKind.DELEGATE_INVOCATION && Context.GetModel(Syntax.Expression).GetSymbolInfo(Syntax.Expression).Symbol is IMethodSymbol m && m.MethodKind == MethodKind.DelegateInvoke; + private bool IsOperatorCall() => Kind == ExprKind.OPERATOR_INVOCATION; + protected override void PopulateExpression(TextWriter trapFile) { if (IsNameof(Syntax)) @@ -37,7 +39,7 @@ protected override void PopulateExpression(TextWriter trapFile) var target = TargetSymbol; switch (Syntax.Expression) { - case MemberAccessExpressionSyntax memberAccess when Kind == ExprKind.METHOD_INVOCATION || IsEventDelegateCall() || IsExplicitDelegateInvokeCall(): + case MemberAccessExpressionSyntax memberAccess when Kind == ExprKind.METHOD_INVOCATION || IsEventDelegateCall() || IsExplicitDelegateInvokeCall() || IsOperatorCall(): memberName = memberAccess.Name.Identifier.Text; if (Syntax.Expression.Kind() == SyntaxKind.SimpleMemberAccessExpression) // Qualified method call; `x.M()` @@ -113,6 +115,13 @@ private static bool IsDynamicCall(ExpressionNodeInfo info) public SymbolInfo SymbolInfo => info.SymbolInfo; + private static bool IsOperatorLikeCall(ExpressionNodeInfo info) + { + return info.SymbolInfo.Symbol is IMethodSymbol method && + method.TryGetExtensionMethod(out var original) && + original!.MethodKind == MethodKind.UserDefinedOperator; + } + public IMethodSymbol? TargetSymbol { get @@ -206,15 +215,25 @@ private static bool IsLocalFunctionInvocation(ExpressionNodeInfo info) private static ExprKind GetKind(ExpressionNodeInfo info) { - return IsNameof((InvocationExpressionSyntax)info.Node) - ? ExprKind.NAMEOF - : IsDelegateLikeCall(info) - ? IsDelegateInvokeCall(info) - ? ExprKind.DELEGATE_INVOCATION - : ExprKind.FUNCTION_POINTER_INVOCATION - : IsLocalFunctionInvocation(info) - ? ExprKind.LOCAL_FUNCTION_INVOCATION - : ExprKind.METHOD_INVOCATION; + if (IsNameof((InvocationExpressionSyntax)info.Node)) + { + return ExprKind.NAMEOF; + } + if (IsDelegateLikeCall(info)) + { + return IsDelegateInvokeCall(info) + ? ExprKind.DELEGATE_INVOCATION + : ExprKind.FUNCTION_POINTER_INVOCATION; + } + if (IsLocalFunctionInvocation(info)) + { + return ExprKind.LOCAL_FUNCTION_INVOCATION; + } + if (IsOperatorLikeCall(info)) + { + return ExprKind.OPERATOR_INVOCATION; + } + return ExprKind.METHOD_INVOCATION; } private static bool IsNameof(InvocationExpressionSyntax syntax) From 20f235c2cbdc1717e17a969142438b4df0422b8a Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 27 Jan 2026 14:17:54 +0100 Subject: [PATCH 06/39] C#: Minor improvements. --- .../CodeAnalysisExtensions/SymbolExtensions.cs | 1 - csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs index d98a2a6cc1f0..70d5865a88a3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs @@ -314,7 +314,6 @@ private static void BuildQualifierAndName(INamedTypeSymbol named, Context cx, Es { trapFile.Write(named.Name); } - } private static void BuildTupleId(INamedTypeSymbol named, Context cx, EscapingTextWriter trapFile, ISymbol symbolBeingDefined) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index bbb29c269af8..b3e2ae68a0a7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -23,7 +23,7 @@ protected void PopulateParameters() if (Symbol.ContainingSymbol is INamedTypeSymbol type && type.IsExtension && !string.IsNullOrEmpty(type.ExtensionParameter?.Name) && !Symbol.IsStatic && - Symbol.AssociatedExtensionImplementation is IMethodSymbol associated) + Symbol.AssociatedExtensionImplementation is not null) { // TODO: Check that this works for generics as well. We might need to also take ImplicitExtensionParameter.Create(Context, this); From 76f1c314e0f3201035f91ee1e8540994582a6fe4 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 28 Jan 2026 17:26:32 +0100 Subject: [PATCH 07/39] C#: Introduce a new expr kind for when accessors are invoked directly. --- .../Entities/Expressions/Invocation.cs | 25 ++++++++++++++++++- .../Kinds/ExprKind.cs | 1 + csharp/ql/lib/semmlecode.csharp.dbscheme | 9 ++++--- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs index 8c0e664375c4..a2fb9387a7af 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs @@ -26,6 +26,17 @@ private Invocation(ExpressionNodeInfo info) private bool IsOperatorCall() => Kind == ExprKind.OPERATOR_INVOCATION; + private bool IsAccessorInvocation() => Kind == ExprKind.ACCESSOR_INVOCATION; + + private bool IsValidMemberAccessKind() + { + return Kind == ExprKind.METHOD_INVOCATION || + IsEventDelegateCall() || + IsExplicitDelegateInvokeCall() || + IsOperatorCall() || + IsAccessorInvocation(); + } + protected override void PopulateExpression(TextWriter trapFile) { if (IsNameof(Syntax)) @@ -39,7 +50,7 @@ protected override void PopulateExpression(TextWriter trapFile) var target = TargetSymbol; switch (Syntax.Expression) { - case MemberAccessExpressionSyntax memberAccess when Kind == ExprKind.METHOD_INVOCATION || IsEventDelegateCall() || IsExplicitDelegateInvokeCall() || IsOperatorCall(): + case MemberAccessExpressionSyntax memberAccess when IsValidMemberAccessKind(): memberName = memberAccess.Name.Identifier.Text; if (Syntax.Expression.Kind() == SyntaxKind.SimpleMemberAccessExpression) // Qualified method call; `x.M()` @@ -122,6 +133,14 @@ private static bool IsOperatorLikeCall(ExpressionNodeInfo info) original!.MethodKind == MethodKind.UserDefinedOperator; } + private static bool IsAccessorLikeInvocation(ExpressionNodeInfo info) + { + return info.SymbolInfo.Symbol is IMethodSymbol method && + method.TryGetExtensionMethod(out var original) && + (original!.MethodKind == MethodKind.PropertyGet || + original!.MethodKind == MethodKind.PropertySet); + } + public IMethodSymbol? TargetSymbol { get @@ -233,6 +252,10 @@ private static ExprKind GetKind(ExpressionNodeInfo info) { return ExprKind.OPERATOR_INVOCATION; } + if (IsAccessorLikeInvocation(info)) + { + return ExprKind.ACCESSOR_INVOCATION; + } return ExprKind.METHOD_INVOCATION; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs b/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs index 46a694192842..8b7edc1f208e 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs @@ -133,6 +133,7 @@ public enum ExprKind COLLECTION = 136, SPREAD_ELEMENT = 137, INTERPOLATED_STRING_INSERT = 138, + ACCESSOR_INVOCATION = 139, DEFINE_SYMBOL = 999, } } diff --git a/csharp/ql/lib/semmlecode.csharp.dbscheme b/csharp/ql/lib/semmlecode.csharp.dbscheme index 8cdbcbb6c7dc..3784b667055b 100644 --- a/csharp/ql/lib/semmlecode.csharp.dbscheme +++ b/csharp/ql/lib/semmlecode.csharp.dbscheme @@ -1194,6 +1194,8 @@ case @expr.kind of | 136 = @collection_expr | 137 = @spread_element_expr | 138 = @interpolated_string_insert_expr +/* C# 14.0 */ +| 139 = @accessor_invocation_expr /* Preprocessor */ | 999 = @define_symbol_expr ; @@ -1268,9 +1270,9 @@ case @expr.kind of @call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr | @delegate_invocation_expr | @object_creation_expr | @call_access_expr - | @local_function_invocation_expr | @function_pointer_invocation_expr; + | @local_function_invocation_expr | @function_pointer_invocation_expr | @accessor_invocation_expr; -@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr | @accessor_invocation_expr; @late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr; @@ -1319,7 +1321,8 @@ dynamic_member_name( @qualifiable_expr = @member_access_expr | @method_invocation_expr - | @element_access_expr; + | @element_access_expr + | @accessor_invocation_expr; conditional_access( unique int id: @qualifiable_expr ref); From 4cd0c0f9335fb4a2bdd3933ac2d7ddb1aade4db3 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 28 Jan 2026 20:51:37 +0100 Subject: [PATCH 08/39] C#: Add various extension related classes to the QL library. --- csharp/ql/lib/semmle/code/csharp/Callable.qll | 74 ++++++++++++++--- csharp/ql/lib/semmle/code/csharp/Member.qll | 2 +- csharp/ql/lib/semmle/code/csharp/Property.qll | 29 +++++++ csharp/ql/lib/semmle/code/csharp/Type.qll | 28 ++++++- .../ql/lib/semmle/code/csharp/exprs/Call.qll | 79 ++++++++++++++++++- 5 files changed, 199 insertions(+), 13 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/Callable.qll b/csharp/ql/lib/semmle/code/csharp/Callable.qll index 49a2271b27c8..985cdb8f1b26 100644 --- a/csharp/ql/lib/semmle/code/csharp/Callable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Callable.qll @@ -221,6 +221,23 @@ class Callable extends Parameterizable, ExprOrStmtParent, @callable { /** Gets a `Call` that has this callable as a target. */ Call getACall() { this = result.getTarget() } + + /** Holds if this callable is declared in an extension type. */ + predicate isInExtension() { this.getDeclaringType() instanceof ExtensionType } +} + +/** + * A callable that is declared as an extension. + * + * Either an extension method (`ExtensionMethod`), an extension operator + * (`ExtensionOperator`) or an extension accessor (`ExtensionAccessor`). + */ +abstract class ExtensionCallable extends Callable { + /** Gets the type being extended by this method. */ + pragma[noinline] + Type getExtendedType() { result = this.getDeclaringType().(ExtensionType).getExtendedType() } + + override string getAPrimaryQlClass() { result = "ExtensionCallable" } } /** @@ -267,8 +284,11 @@ class Method extends Callable, Virtualizable, Attributable, @method { override Location getALocation() { method_location(this.getUnboundDeclaration(), result) } + /** Holds if this method is a classic extension method. */ + predicate isClassicExtensionMethod() { this.getParameter(0).hasExtensionMethodModifier() } + /** Holds if this method is an extension method. */ - predicate isExtensionMethod() { this.getParameter(0).hasExtensionMethodModifier() } + predicate isExtensionMethod() { this.isClassicExtensionMethod() or this.isInExtension() } /** Gets the type of the `params` parameter of this method, if any. */ Type getParamsType() { @@ -296,7 +316,17 @@ class Method extends Callable, Virtualizable, Attributable, @method { } /** - * An extension method, for example + * An extension method. + * + * Either a classic extension method (`ClassicExtensionMethod`) or an extension + * type extension method (`ExtensionTypeExtensionMethod`). + */ +abstract class ExtensionMethod extends ExtensionCallable, Method { + override string getAPrimaryQlClass() { result = "ExtensionMethod" } +} + +/** + * An extension method, for example * * ```csharp * static bool IsDefined(this Widget w) { @@ -304,16 +334,28 @@ class Method extends Callable, Virtualizable, Attributable, @method { * } * ``` */ -class ExtensionMethod extends Method { - ExtensionMethod() { this.isExtensionMethod() } - - override predicate isStatic() { any() } +class ClassicExtensionMethod extends ExtensionMethod { + ClassicExtensionMethod() { this.isClassicExtensionMethod() } - /** Gets the type being extended by this method. */ pragma[noinline] - Type getExtendedType() { result = this.getParameter(0).getType() } + override Type getExtendedType() { result = this.getParameter(0).getType() } - override string getAPrimaryQlClass() { result = "ExtensionMethod" } + override predicate isStatic() { any() } +} + +/** + * An extension method declared in an extension type, for example `IsNullOrEmpty` in + * + * ```csharp + * static class MyExtensions { + * extension(string s) { + * public bool IsNullOrEmpty() { ... } + * } + * } + * ``` + */ +class ExtensionTypeExtensionMethod extends ExtensionMethod { + ExtensionTypeExtensionMethod() { this.isInExtension() } } /** @@ -536,6 +578,20 @@ class RecordCloneMethod extends Method { } } +/** + * An extension operator, for example `*` in + * ```csharp + * static class MyExtensions { + * extension(string s) { + * public static string operator *(int s1, string s2) { ... } + * } + * } + * ``` + */ +class ExtensionOperator extends ExtensionCallable, Operator { + ExtensionOperator() { this.isInExtension() } +} + /** * A user-defined unary operator - an operator taking one operand. * diff --git a/csharp/ql/lib/semmle/code/csharp/Member.qll b/csharp/ql/lib/semmle/code/csharp/Member.qll index a196d3b3fc70..d4be2eaae972 100644 --- a/csharp/ql/lib/semmle/code/csharp/Member.qll +++ b/csharp/ql/lib/semmle/code/csharp/Member.qll @@ -469,7 +469,7 @@ class Virtualizable extends Overridable, Member, @virtualizable { /** * A parameterizable declaration. Either a callable (`Callable`), a delegate - * type (`DelegateType`), or an indexer (`Indexer`). + * type (`DelegateType`), an indexer (`Indexer`) or an extension block (`ExtensionType`). */ class Parameterizable extends Declaration, @parameterizable { /** Gets raw parameter `i`, including the `this` parameter at index 0. */ diff --git a/csharp/ql/lib/semmle/code/csharp/Property.qll b/csharp/ql/lib/semmle/code/csharp/Property.qll index e651639b6313..37156929b9cc 100644 --- a/csharp/ql/lib/semmle/code/csharp/Property.qll +++ b/csharp/ql/lib/semmle/code/csharp/Property.qll @@ -260,6 +260,20 @@ class Property extends DeclarationWithGetSetAccessors, @property { override string getAPrimaryQlClass() { result = "Property" } } +/** + * An extension property, for example `FirstChar` in + * ```csharp + * static class MyExtensions { + * extension(string s) { + * public char FirstChar { get { ... } } + * } + * } + * ``` + */ +class ExtensionProperty extends Property { + ExtensionProperty() { this.getDeclaringType() instanceof ExtensionType } +} + /** * An indexer, for example `string this[int i]` on line 2 in * @@ -413,6 +427,21 @@ class Accessor extends Callable, Modifiable, Attributable, Overridable, @callabl override string toString() { result = this.getName() } } +/** + * An extension accessor. Either a getter (`Getter`), a setter (`Setter`) of an + * extension property, for example `get` in + * ```csharp + * static class MyExtensions { + * extension(string s) { + * public char FirstChar { get { ... } } + * } + * } + * ``` + */ +class ExtensionAccessor extends ExtensionCallable, Accessor { + ExtensionAccessor() { this.getDeclaringType() instanceof ExtensionType } +} + /** * A `get` accessor, for example `get { return p; }` in * diff --git a/csharp/ql/lib/semmle/code/csharp/Type.qll b/csharp/ql/lib/semmle/code/csharp/Type.qll index 1efb1aa93bff..e478fa094a23 100644 --- a/csharp/ql/lib/semmle/code/csharp/Type.qll +++ b/csharp/ql/lib/semmle/code/csharp/Type.qll @@ -17,7 +17,8 @@ private import semmle.code.csharp.frameworks.system.runtime.CompilerServices * * Either a value or reference type (`ValueOrRefType`), the `void` type (`VoidType`), * a pointer type (`PointerType`), the arglist type (`ArglistType`), an unknown - * type (`UnknownType`), or a type parameter (`TypeParameter`). + * type (`UnknownType`), a type parameter (`TypeParameter`) or + * an extension type (`ExtensionType`). */ class Type extends Member, TypeContainer, @type { /** Gets the name of this type without additional syntax such as `[]` or `*`. */ @@ -1326,3 +1327,28 @@ class TypeMention extends @type_mention { /** Gets the location of this type mention. */ Location getLocation() { type_mention_location(this, result) } } + +/** + * A type extension declaration, for example `extensions(string s) { ... }` in + * ```csharp + * static class MyExtensions { + * extensions(string s) { ... } + * ``` + */ +class ExtensionType extends Parameterizable, @extension_type { + /** + * Gets the receiver parameter of this extension type, if any. + */ + Parameter getReceiverParameter() { params(result, _, _, 0, _, this, _) } + + /** + * Holds if this extension type has a receiver parameter. + */ + predicate hasReceiverParameter() { exists(this.getReceiverParameter()) } + + /** Gets the type being extended by this extension type. */ + // TODO: This doesn't handle the case where there is no parameter. + Type getExtendedType() { result = this.getReceiverParameter().getType() } + + override string getAPrimaryQlClass() { result = "ExtensionType" } +} diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll index eecbc35900aa..ce8391c0f29b 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll @@ -479,6 +479,29 @@ class OperatorCall extends Call, LateBindableExpr, @operator_invocation_expr { override string getAPrimaryQlClass() { result = "OperatorCall" } } +/** + * A call to an extension operator, for example `3 * s` on + * line 9 in + * ```csharp + * static class MyExtensions { + * extension(string s) { + * public static string operator *(int i, string s) { ... } + * } + * } + * + * class A { + * string M(string s) { + * return 3 * s; + * } + * } + * ``` + */ +class ExtensionOperatorCall extends OperatorCall { + ExtensionOperatorCall() { this.getTarget() instanceof ExtensionOperator } + + override string getAPrimaryQlClass() { result = "ExtensionOperatorCall" } +} + /** * A call to a user-defined mutator operator, for example `a++` on * line 7 in @@ -577,8 +600,8 @@ class FunctionPointerCall extends DelegateLikeCall, @function_pointer_invocation /** * A call to an accessor. Either a property accessor call (`PropertyCall`), - * an indexer accessor call (`IndexerCall`), or an event accessor call - * (`EventCall`). + * an indexer accessor call (`IndexerCall`), an event accessor call + * (`EventCall`) or an extension accessor call (`ExtensionAccessorCall`). */ class AccessorCall extends Call, QualifiableExpr, @call_access_expr { override Accessor getTarget() { none() } @@ -588,6 +611,35 @@ class AccessorCall extends Call, QualifiableExpr, @call_access_expr { override Accessor getARuntimeTarget() { result = Call.super.getARuntimeTarget() } } +/** + * A direct call to an extension accessor, for example `MyExtensions.get_FirstChar(s)` + * in + * ```csharp + * static class MyExtensions { + * extension(string s) { + * public char FirstChar { get { ... } } + * } + * } + * + * class A { + * char M(string s) { + * return MyExtensions.get_FirstChar(s); + * } + * } + * ``` + */ +class ExtensionAccessorCall extends AccessorCall, @accessor_invocation_expr { + override Accessor getTarget() { expr_call(this, result) } + + override Expr getArgument(int i) { result = this.getChild(i) and i >= 0 } + + override string toString() { + result = "call to extension accessor " + concat(this.getTarget().getName()) + } + + override string getAPrimaryQlClass() { result = "ExtensionAccessorCall" } +} + /** * A call to a property accessor, for example the call to `get_P` on * line 5 in @@ -658,6 +710,29 @@ class IndexerCall extends AccessorCall, IndexerAccessExpr { override string getAPrimaryQlClass() { result = "IndexerCall" } } +/** + * A call to an extension property accessor (via the property), for example + * `s.FirstChar` on line 9 in + * ```csharp + * static class MyExtensions { + * extension(string s) { + * public char FirstChar { get { ... } } + * } + * } + * + * class A { + * char M(string s) { + * return s.FirstChar; + * } + * } + * ``` + */ +class ExtensionPropertyCall extends PropertyCall { + ExtensionPropertyCall() { this.getProperty() instanceof ExtensionProperty } + + override string getAPrimaryQlClass() { result = "ExtensionPropertyCall" } +} + /** * A call to an event accessor, for example the call to `add_Click` * (defined on line 5) on line 12 in From 4b5e1e97f52afccbca5d02130d2c45fa7e9968ee Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 28 Jan 2026 21:07:56 +0100 Subject: [PATCH 09/39] C#: Add extensions tests. --- .../library-tests/extension/extensions.cs | 75 +++++++++++++++++++ .../extension/extensions.expected | 50 +++++++++++++ .../library-tests/extension/extensions.ql | 71 ++++++++++++++++++ .../ql/test/library-tests/extension/options | 2 + 4 files changed, 198 insertions(+) create mode 100644 csharp/ql/test/library-tests/extension/extensions.cs create mode 100644 csharp/ql/test/library-tests/extension/extensions.expected create mode 100644 csharp/ql/test/library-tests/extension/extensions.ql create mode 100644 csharp/ql/test/library-tests/extension/options diff --git a/csharp/ql/test/library-tests/extension/extensions.cs b/csharp/ql/test/library-tests/extension/extensions.cs new file mode 100644 index 000000000000..ae5595dd9700 --- /dev/null +++ b/csharp/ql/test/library-tests/extension/extensions.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; + +public static class MyExtensions +{ + extension(string s) + { + public bool Prop1 => s.Length > 0; + public bool Prop2 { get { return true; } set { } } + public static bool StaticProp1 { get { return false; } } + public bool M1() => s is not null; + public string M2(string other) { return s + other; } + public static int StaticM1() { return 0; } + public static int StaticM2(string x) { return x.Length; } + public static string operator *(int a, string b) { return ""; } + } + + extension(object) + { + public static int StaticObjectM1() { return 0; } + public static int StaticObjectM2(string s) { return s.Length; } + public static bool StaticProp => true; + } +} + +public static class ClassicExtensions +{ + public static bool M3(this string s) => s is not null; +} + +public class C +{ + public static void CallingExtensions() + { + var s = "Hello World."; + + // // Calling the extensions properties + var x11 = s.Prop1; + var x12 = s.Prop2; + s.Prop2 = true; + var x13 = string.StaticProp1; + var x14 = object.StaticProp; + + // Calling the extensions methods. + var x21 = s.M1(); + var x22 = s.M2("!!!"); + var x23 = string.StaticM1(); + var x24 = string.StaticM2(s); + var x25 = object.StaticObjectM1(); + var x26 = object.StaticObjectM2(s); + + // Calling the extension operator. + var x30 = 3 * s; + + // Calling the classic extension method. + var y = s.M3(); + + // Calling the compiler generated static extension methods. + MyExtensions.M1(s); + MyExtensions.M2(s, "!!!"); + MyExtensions.StaticM1(); + MyExtensions.StaticM2(s); + MyExtensions.StaticObjectM1(); + MyExtensions.StaticObjectM2(s); + + // Calling the compiler generated operator method. + MyExtensions.op_Multiply(3, s); + + // Calling the compiler generated methods used by the extension property accessors. + MyExtensions.get_Prop1(s); + MyExtensions.get_Prop2(s); + MyExtensions.set_Prop2(s, false); + MyExtensions.get_StaticProp(); + } +} diff --git a/csharp/ql/test/library-tests/extension/extensions.expected b/csharp/ql/test/library-tests/extension/extensions.expected new file mode 100644 index 000000000000..384e539e06f9 --- /dev/null +++ b/csharp/ql/test/library-tests/extension/extensions.expected @@ -0,0 +1,50 @@ +extensionMethodCallArgument +| extensions.cs:45:19:45:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:45:19:45:19 | access to local variable s | +| extensions.cs:46:19:46:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:46:19:46:19 | access to local variable s | +| extensions.cs:46:19:46:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:46:24:46:28 | "!!!" | +| extensions.cs:48:19:48:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:48:35:48:35 | access to local variable s | +| extensions.cs:50:19:50:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:50:41:50:41 | access to local variable s | +| extensions.cs:56:17:56:22 | call to method M3 | extensions.cs:28:24:28:25 | M3 | extensions.cs:28:39:28:39 | s | 0 | extensions.cs:56:17:56:17 | access to local variable s | +| extensions.cs:59:9:59:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:59:25:59:25 | access to local variable s | +| extensions.cs:60:9:60:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:60:25:60:25 | access to local variable s | +| extensions.cs:60:9:60:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:60:28:60:32 | "!!!" | +| extensions.cs:62:9:62:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:62:31:62:31 | access to local variable s | +| extensions.cs:64:9:64:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:64:37:64:37 | access to local variable s | +extensionMethodCalls +| extensions.cs:45:19:45:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | +| extensions.cs:46:19:46:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | +| extensions.cs:47:19:47:35 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | +| extensions.cs:48:19:48:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | +| extensions.cs:49:19:49:41 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | +| extensions.cs:50:19:50:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | +| extensions.cs:56:17:56:22 | call to method M3 | extensions.cs:28:24:28:25 | M3 | extensions.cs:26:21:26:37 | ClassicExtensions | ClassicExtensions.M3 | +| extensions.cs:59:9:59:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | +| extensions.cs:60:9:60:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | +| extensions.cs:61:9:61:31 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | +| extensions.cs:62:9:62:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | +| extensions.cs:63:9:63:37 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | +| extensions.cs:64:9:64:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | +extensionOperatorCallArgument +| extensions.cs:15:39:15:39 | * | extensions.cs:53:19:53:23 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:53:19:53:19 | 3 | +| extensions.cs:15:39:15:39 | * | extensions.cs:53:19:53:23 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:53:23:53:23 | access to local variable s | +| extensions.cs:15:39:15:39 | * | extensions.cs:67:9:67:38 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:67:34:67:34 | 3 | +| extensions.cs:15:39:15:39 | * | extensions.cs:67:9:67:38 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:67:37:67:37 | access to local variable s | +extensionOperatorCalls +| extensions.cs:53:19:53:23 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | +| extensions.cs:67:9:67:38 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | +extensionProperty +| extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:16:5 | extension(String) | +| extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | +| extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:16:5 | extension(String) | +| extensions.cs:22:28:22:37 | StaticProp | extensions.cs:18:5:23:5 | extension(Object) | +extensionPropertyCall +| extensions.cs:38:19:38:25 | access to property Prop1 | extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop1 | +| extensions.cs:39:19:39:25 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | +| extensions.cs:40:9:40:15 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | +| extensions.cs:41:19:41:36 | access to property StaticProp1 | extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticProp1 | +| extensions.cs:42:19:42:35 | access to property StaticProp | extensions.cs:22:28:22:37 | StaticProp | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticProp | +extensionAccessorCall +| extensions.cs:70:9:70:33 | call to extension accessor get_Prop1 | extensions.cs:8:30:8:41 | get_Prop1 | extensions.cs:8:21:8:25 | Prop1 | MyExtensions+extension(String).get_Prop1 | +| extensions.cs:71:9:71:33 | call to extension accessor get_Prop2 | extensions.cs:9:29:9:31 | get_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).get_Prop2 | +| extensions.cs:72:9:72:40 | call to extension accessor set_Prop2 | extensions.cs:9:50:9:52 | set_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).set_Prop2 | +| extensions.cs:73:9:73:37 | call to extension accessor get_StaticProp | extensions.cs:22:42:22:45 | get_StaticProp | extensions.cs:22:28:22:37 | StaticProp | MyExtensions+extension(Object).get_StaticProp | diff --git a/csharp/ql/test/library-tests/extension/extensions.ql b/csharp/ql/test/library-tests/extension/extensions.ql new file mode 100644 index 000000000000..ed67b980e6b3 --- /dev/null +++ b/csharp/ql/test/library-tests/extension/extensions.ql @@ -0,0 +1,71 @@ +import csharp + +// - Method should be from source (it looks like it is considered) compiler generated. +// - It appears that two methods are extracted +// - One "generated" (which is what the compiler will generate). +// - One "source" method, which reflects the method as it looks in source code. +//. - Design decision: Only extract the associated extension implementation. +// - Make tests for ExtendedType. +// - Like there are extension methods, we need to declare isExtension for properties. +// - Make QL classes similar to ExtensionMethod for Properties and Operators. +// - Make sure we can declare MaD +// - Check that control flow works as expected. +// - Check that dataflow works as expected. +query predicate extensionMethodCallArgument( + ExtensionMethodCall emc, ExtensionMethod em, Parameter p, int i, Expr e +) { + em.getFile().getBaseName() = "extensions.cs" and + emc.getTarget() = em and + em.getParameter(i) = p and + emc.getArgument(i) = e +} + +query predicate extensionMethodCalls( + ExtensionMethodCall emc, ExtensionMethod em, Type t, string type +) { + em.getFile().getBaseName() = "extensions.cs" and + emc.getTarget() = em and + em.getDeclaringType() = t and + em.getFullyQualifiedNameDebug() = type +} + +query predicate extensionOperatorCallArgument( + ExtensionOperator op, ExtensionOperatorCall opc, Parameter p, int pos, Expr e +) { + opc.getTarget() = op and + op.getFile().getBaseName() = "extensions.cs" and + p = op.getParameter(pos) and + e = opc.getArgument(pos) +} + +query predicate extensionOperatorCalls( + ExtensionOperatorCall opc, ExtensionOperator op, Type t, string type +) { + op.getFile().getBaseName() = "extensions.cs" and + opc.getTarget() = op and + op.getDeclaringType() = t and + op.getFullyQualifiedNameDebug() = type +} + +query predicate extensionProperty(ExtensionProperty p, Type t) { + p.getFile().getBaseName() = "extensions.cs" and + p.getDeclaringType() = t +} + +query predicate extensionPropertyCall( + ExtensionPropertyCall pc, ExtensionProperty p, Type t, string type +) { + p.getFile().getBaseName() = "extensions.cs" and + pc.getProperty() = p and + p.getDeclaringType() = t and + p.getFullyQualifiedNameDebug() = type +} + +query predicate extensionAccessorCall( + ExtensionAccessorCall ac, ExtensionAccessor a, ExtensionProperty p, string type +) { + p.getFile().getBaseName() = "extensions.cs" and + (a.(Getter).getDeclaration() = p or a.(Setter).getDeclaration() = p) and + ac.getTarget() = a and + a.getFullyQualifiedNameDebug() = type +} diff --git a/csharp/ql/test/library-tests/extension/options b/csharp/ql/test/library-tests/extension/options new file mode 100644 index 000000000000..77b22963f5c8 --- /dev/null +++ b/csharp/ql/test/library-tests/extension/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj From 272f17d34ac4ee4da2ab4c09a874b25a11f02c77 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 29 Jan 2026 10:00:34 +0100 Subject: [PATCH 10/39] C#: Extract the receiver type for extension types. --- .../Entities/Types/NamedType.cs | 14 ++++++++++++++ .../Semmle.Extraction.CSharp/Trap/Tuples.cs | 3 +++ 2 files changed, 17 insertions(+) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs index dcf2bffe095f..11c524c91bf6 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs @@ -90,6 +90,20 @@ public override void Populate(TextWriter trapFile) { trapFile.anonymous_types(this); } + + if (Symbol.IsExtension && Symbol.ExtensionParameter is IParameterSymbol parameter) + { + // For some reason an extension type has a receiver parameter with an empty name + // even when there is no parameter. + if (!string.IsNullOrEmpty(parameter.Name)) + { + Parameter.Create(Context, parameter, this); + } + + // Use the parameter type as the receiver type. + var receiverType = Type.Create(Context, parameter.Type).TypeRef; + trapFile.extension_receiver_type(this, receiverType); + } } private readonly Lazy typeArgumentsLazy; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Trap/Tuples.cs b/csharp/extractor/Semmle.Extraction.CSharp/Trap/Tuples.cs index c1d082bbee2b..1a25da058bd8 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Trap/Tuples.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Trap/Tuples.cs @@ -202,6 +202,9 @@ internal static void exprorstmt_name(this TextWriter trapFile, IEntity expr, str internal static void extend(this TextWriter trapFile, Type type, Type super) => trapFile.WriteTuple("extend", type, super); + internal static void extension_receiver_type(this TextWriter trapFile, Type @extension, Type receiverType) => + trapFile.WriteTuple("extension_receiver_type", extension, receiverType); + internal static void anonymous_types(this TextWriter trapFile, Type type) => trapFile.WriteTuple("anonymous_types", type); From 529091348aec96b824793d97a5f729caaced74f6 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 29 Jan 2026 10:01:17 +0100 Subject: [PATCH 11/39] C#: Add a extension receiver type table to the DB scheme. --- csharp/ql/lib/semmlecode.csharp.dbscheme | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/csharp/ql/lib/semmlecode.csharp.dbscheme b/csharp/ql/lib/semmlecode.csharp.dbscheme index 3784b667055b..ec6a1dfb9f69 100644 --- a/csharp/ql/lib/semmlecode.csharp.dbscheme +++ b/csharp/ql/lib/semmlecode.csharp.dbscheme @@ -542,6 +542,10 @@ function_pointer_return_type( unique int function_pointer_id: @function_pointer_type ref, int return_type_id: @type_or_ref ref); +extension_receiver_type( + unique int extension: @extension_type ref, + int receiver_type_id: @type_or_ref ref); + extend( int sub: @type ref, int super: @type_or_ref ref); From 47194d51116921a128b6f5d128169527b40ce48b Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 29 Jan 2026 10:03:58 +0100 Subject: [PATCH 12/39] C#: Use the relation in the ExtensionType relation in the QL library. --- csharp/ql/lib/semmle/code/csharp/Type.qll | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/Type.qll b/csharp/ql/lib/semmle/code/csharp/Type.qll index e478fa094a23..12fce3cb7f93 100644 --- a/csharp/ql/lib/semmle/code/csharp/Type.qll +++ b/csharp/ql/lib/semmle/code/csharp/Type.qll @@ -1339,16 +1339,22 @@ class ExtensionType extends Parameterizable, @extension_type { /** * Gets the receiver parameter of this extension type, if any. */ - Parameter getReceiverParameter() { params(result, _, _, 0, _, this, _) } + Parameter getReceiverParameter() { result = this.getParameter(0) } /** * Holds if this extension type has a receiver parameter. */ predicate hasReceiverParameter() { exists(this.getReceiverParameter()) } - /** Gets the type being extended by this extension type. */ - // TODO: This doesn't handle the case where there is no parameter. - Type getExtendedType() { result = this.getReceiverParameter().getType() } + /** + * Gets the type being extended by this extension type. + */ + Type getExtendedType() { + extension_receiver_type(this, result) + or + not extension_receiver_type(this, any(Type t)) and + extension_receiver_type(this, getTypeRef(result)) + } override string getAPrimaryQlClass() { result = "ExtensionType" } } From daa088eba9aae65f51c19d61949b775957d4769a Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 29 Jan 2026 10:05:46 +0100 Subject: [PATCH 13/39] C#: Add some QL tests for extension types. --- .../library-tests/extension/extensionType.expected | 5 +++++ .../ql/test/library-tests/extension/extensionType.ql | 11 +++++++++++ 2 files changed, 16 insertions(+) create mode 100644 csharp/ql/test/library-tests/extension/extensionType.expected create mode 100644 csharp/ql/test/library-tests/extension/extensionType.ql diff --git a/csharp/ql/test/library-tests/extension/extensionType.expected b/csharp/ql/test/library-tests/extension/extensionType.expected new file mode 100644 index 000000000000..022748ee18ef --- /dev/null +++ b/csharp/ql/test/library-tests/extension/extensionType.expected @@ -0,0 +1,5 @@ +extensionTypeReceiverParameter +| extensions.cs:6:5:16:5 | extension(String) | extensions.cs:6:22:6:22 | s | +extensionTypeExtendedType +| extensions.cs:6:5:16:5 | extension(String) | string | +| extensions.cs:18:5:23:5 | extension(Object) | object | diff --git a/csharp/ql/test/library-tests/extension/extensionType.ql b/csharp/ql/test/library-tests/extension/extensionType.ql new file mode 100644 index 000000000000..c584252c4bd3 --- /dev/null +++ b/csharp/ql/test/library-tests/extension/extensionType.ql @@ -0,0 +1,11 @@ +import csharp + +query predicate extensionTypeReceiverParameter(ExtensionType et, Parameter p) { + et.getFile().getBaseName() = "extensions.cs" and + p = et.getReceiverParameter() +} + +query predicate extensionTypeExtendedType(ExtensionType et, string t) { + et.getFile().getBaseName() = "extensions.cs" and + t = et.getExtendedType().toStringWithTypes() +} From e1ec0087fbaec16b955fd711eba1e05b1d3a0ed1 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 29 Jan 2026 10:29:54 +0100 Subject: [PATCH 14/39] C#: Remove some TODO reminder comments. --- csharp/ql/test/library-tests/extension/extensions.ql | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/csharp/ql/test/library-tests/extension/extensions.ql b/csharp/ql/test/library-tests/extension/extensions.ql index ed67b980e6b3..b3b0e05ac92a 100644 --- a/csharp/ql/test/library-tests/extension/extensions.ql +++ b/csharp/ql/test/library-tests/extension/extensions.ql @@ -1,16 +1,5 @@ import csharp -// - Method should be from source (it looks like it is considered) compiler generated. -// - It appears that two methods are extracted -// - One "generated" (which is what the compiler will generate). -// - One "source" method, which reflects the method as it looks in source code. -//. - Design decision: Only extract the associated extension implementation. -// - Make tests for ExtendedType. -// - Like there are extension methods, we need to declare isExtension for properties. -// - Make QL classes similar to ExtensionMethod for Properties and Operators. -// - Make sure we can declare MaD -// - Check that control flow works as expected. -// - Check that dataflow works as expected. query predicate extensionMethodCallArgument( ExtensionMethodCall emc, ExtensionMethod em, Parameter p, int i, Expr e ) { From 834e09c9bcff8c826574737991aeaa6f86a3994b Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 29 Jan 2026 16:24:27 +0100 Subject: [PATCH 15/39] C#: Synthesize the implicit extension parameters for generics. --- .../Entities/Method.cs | 26 ++++++++++++------- .../Entities/Parameter.cs | 16 +++++++----- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index b3e2ae68a0a7..4617e1a50e38 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -14,22 +14,28 @@ internal abstract class Method : CachedSymbol, IExpressionParentE protected Method(Context cx, IMethodSymbol init) : base(cx, init) { } - protected void PopulateParameters() - { - var positionOffset = 0; - var originalMethod = OriginalDefinition; + private IParameter? SyntheticParameter { get; set; } + private int SynthesizeExtensionParameter() + { // Synthesize implicit parameter for extension methods declared using extension(...) syntax. if (Symbol.ContainingSymbol is INamedTypeSymbol type && - type.IsExtension && !string.IsNullOrEmpty(type.ExtensionParameter?.Name) && - !Symbol.IsStatic && - Symbol.AssociatedExtensionImplementation is not null) + type.IsExtension && type.ExtensionParameter is IParameterSymbol parameter && + !string.IsNullOrEmpty(parameter.Name) && !Symbol.IsStatic) { - // TODO: Check that this works for generics as well. We might need to also take - ImplicitExtensionParameter.Create(Context, this); - positionOffset++; + var originalSyntheticParam = OriginalDefinition.SyntheticParameter; + SyntheticParameter = ImplicitExtensionParameter.Create(Context, this, parameter, originalSyntheticParam); + return 1; } + return 0; + } + + protected void PopulateParameters() + { + var originalMethod = OriginalDefinition; + var positionOffset = SynthesizeExtensionParameter(); + IEnumerable parameters = Symbol.Parameters; IEnumerable originalParameters = originalMethod.Symbol.Parameters; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs index 5e77ba0f8103..540de3ad61bd 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -271,11 +271,13 @@ internal class ImplicitExtensionParameter : FreshEntity, IParameter { private Method ExtensionMethod { get; } private IParameterSymbol ExtensionParameter { get; } + private IParameter Original { get; } - private ImplicitExtensionParameter(Context cx, Method method) : base(cx) + private ImplicitExtensionParameter(Context cx, Method method, IParameterSymbol parameter, IParameter? original) : base(cx) { ExtensionMethod = method; - ExtensionParameter = method.Symbol.AssociatedExtensionImplementation!.Parameters[0]; + ExtensionParameter = parameter; + Original = original ?? this; } private static int Ordinal => 0; @@ -326,7 +328,7 @@ protected override void Populate(TextWriter trapFile) PopulateRefKind(trapFile, ExtensionParameter.RefKind); var type = Type.Create(Context, ExtensionParameter.Type); - trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, ExtensionMethod, this); + trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, ExtensionMethod, Original); if (Context.OnlyScaffold) { @@ -356,11 +358,11 @@ protected override void Populate(TextWriter trapFile) } } - public static ImplicitExtensionParameter Create(Context cx, Method method) + public static ImplicitExtensionParameter Create(Context cx, Method method, IParameterSymbol parameter, IParameter? original) { - var parameter = new ImplicitExtensionParameter(cx, method); - parameter.TryPopulate(); - return parameter; + var p = new ImplicitExtensionParameter(cx, method, parameter, original); + p.TryPopulate(); + return p; } } From f16a040a229dc05a6deac0e8bb05af4e4a56caf5 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 29 Jan 2026 16:31:52 +0100 Subject: [PATCH 16/39] C#: Add generic test. --- .../extension/extensionType.expected | 6 ++ .../library-tests/extension/extensions.cs | 14 ++- .../extension/extensions.expected | 92 +++++++++++-------- .../library-tests/extension/extensions.ql | 9 ++ 4 files changed, 81 insertions(+), 40 deletions(-) diff --git a/csharp/ql/test/library-tests/extension/extensionType.expected b/csharp/ql/test/library-tests/extension/extensionType.expected index 022748ee18ef..8e25777b170a 100644 --- a/csharp/ql/test/library-tests/extension/extensionType.expected +++ b/csharp/ql/test/library-tests/extension/extensionType.expected @@ -1,5 +1,11 @@ extensionTypeReceiverParameter | extensions.cs:6:5:16:5 | extension(String) | extensions.cs:6:22:6:22 | s | +| extensions.cs:25:5:28:5 | extension(Object) | file://:0:0:0:0 | t | +| extensions.cs:25:5:28:5 | extension(String) | file://:0:0:0:0 | t | +| extensions.cs:25:5:28:5 | extension(T)`1 | extensions.cs:25:20:25:20 | t | extensionTypeExtendedType | extensions.cs:6:5:16:5 | extension(String) | string | | extensions.cs:18:5:23:5 | extension(Object) | object | +| extensions.cs:25:5:28:5 | extension(Object) | object | +| extensions.cs:25:5:28:5 | extension(String) | string | +| extensions.cs:25:5:28:5 | extension(T)`1 | T | diff --git a/csharp/ql/test/library-tests/extension/extensions.cs b/csharp/ql/test/library-tests/extension/extensions.cs index ae5595dd9700..27a3fabc5c50 100644 --- a/csharp/ql/test/library-tests/extension/extensions.cs +++ b/csharp/ql/test/library-tests/extension/extensions.cs @@ -21,6 +21,11 @@ public static class MyExtensions public static int StaticObjectM2(string s) { return s.Length; } public static bool StaticProp => true; } + + extension(T t) where T : class + { + public bool GenericM() => t is not null; + } } public static class ClassicExtensions @@ -34,7 +39,7 @@ public static void CallingExtensions() { var s = "Hello World."; - // // Calling the extensions properties + // Calling the extensions properties var x11 = s.Prop1; var x12 = s.Prop2; s.Prop2 = true; @@ -71,5 +76,12 @@ public static void CallingExtensions() MyExtensions.get_Prop2(s); MyExtensions.set_Prop2(s, false); MyExtensions.get_StaticProp(); + + // Calling generic extension method + var o = new object(); + o.GenericM(); + s.GenericM(); + MyExtensions.GenericM(o); // NOTE, Not handled in the extractor + MyExtensions.GenericM(s); // NOTE, Not handled in the extractor } } diff --git a/csharp/ql/test/library-tests/extension/extensions.expected b/csharp/ql/test/library-tests/extension/extensions.expected index 384e539e06f9..be418d14d6fb 100644 --- a/csharp/ql/test/library-tests/extension/extensions.expected +++ b/csharp/ql/test/library-tests/extension/extensions.expected @@ -1,50 +1,64 @@ extensionMethodCallArgument -| extensions.cs:45:19:45:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:45:19:45:19 | access to local variable s | -| extensions.cs:46:19:46:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:46:19:46:19 | access to local variable s | -| extensions.cs:46:19:46:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:46:24:46:28 | "!!!" | -| extensions.cs:48:19:48:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:48:35:48:35 | access to local variable s | -| extensions.cs:50:19:50:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:50:41:50:41 | access to local variable s | -| extensions.cs:56:17:56:22 | call to method M3 | extensions.cs:28:24:28:25 | M3 | extensions.cs:28:39:28:39 | s | 0 | extensions.cs:56:17:56:17 | access to local variable s | -| extensions.cs:59:9:59:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:59:25:59:25 | access to local variable s | -| extensions.cs:60:9:60:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:60:25:60:25 | access to local variable s | -| extensions.cs:60:9:60:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:60:28:60:32 | "!!!" | -| extensions.cs:62:9:62:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:62:31:62:31 | access to local variable s | -| extensions.cs:64:9:64:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:64:37:64:37 | access to local variable s | +| extensions.cs:50:19:50:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:50:19:50:19 | access to local variable s | +| extensions.cs:51:19:51:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:51:19:51:19 | access to local variable s | +| extensions.cs:51:19:51:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:51:24:51:28 | "!!!" | +| extensions.cs:53:19:53:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:53:35:53:35 | access to local variable s | +| extensions.cs:55:19:55:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:55:41:55:41 | access to local variable s | +| extensions.cs:61:17:61:22 | call to method M3 | extensions.cs:33:24:33:25 | M3 | extensions.cs:33:39:33:39 | s | 0 | extensions.cs:61:17:61:17 | access to local variable s | +| extensions.cs:64:9:64:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:64:25:64:25 | access to local variable s | +| extensions.cs:65:9:65:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:65:25:65:25 | access to local variable s | +| extensions.cs:65:9:65:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:65:28:65:32 | "!!!" | +| extensions.cs:67:9:67:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:67:31:67:31 | access to local variable s | +| extensions.cs:69:9:69:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:69:37:69:37 | access to local variable s | +| extensions.cs:82:9:82:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:82:9:82:9 | access to local variable o | +| extensions.cs:83:9:83:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:83:9:83:9 | access to local variable s | extensionMethodCalls -| extensions.cs:45:19:45:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | -| extensions.cs:46:19:46:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | -| extensions.cs:47:19:47:35 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | -| extensions.cs:48:19:48:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | -| extensions.cs:49:19:49:41 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | -| extensions.cs:50:19:50:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | -| extensions.cs:56:17:56:22 | call to method M3 | extensions.cs:28:24:28:25 | M3 | extensions.cs:26:21:26:37 | ClassicExtensions | ClassicExtensions.M3 | -| extensions.cs:59:9:59:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | -| extensions.cs:60:9:60:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | -| extensions.cs:61:9:61:31 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | -| extensions.cs:62:9:62:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | -| extensions.cs:63:9:63:37 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | -| extensions.cs:64:9:64:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | +| extensions.cs:50:19:50:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | +| extensions.cs:51:19:51:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | +| extensions.cs:52:19:52:35 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | +| extensions.cs:53:19:53:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | +| extensions.cs:54:19:54:41 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | +| extensions.cs:55:19:55:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | +| extensions.cs:61:17:61:22 | call to method M3 | extensions.cs:33:24:33:25 | M3 | extensions.cs:31:21:31:37 | ClassicExtensions | ClassicExtensions.M3 | +| extensions.cs:64:9:64:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | +| extensions.cs:65:9:65:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | +| extensions.cs:66:9:66:31 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | +| extensions.cs:67:9:67:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | +| extensions.cs:68:9:68:37 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | +| extensions.cs:69:9:69:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | +| extensions.cs:82:9:82:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:5:28:5 | extension(Object) | MyExtensions+extension(Object).GenericM | +| extensions.cs:83:9:83:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:5:28:5 | extension(String) | MyExtensions+extension(String).GenericM | +extensionParameter +| extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | +| extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | +| extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | string | extensions.cs:12:33:12:37 | other | +| extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | string | extensions.cs:14:43:14:43 | x | +| extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | string | extensions.cs:21:49:21:49 | s | +| extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | T | extensions.cs:25:20:25:20 | t | +| extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | +| extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | string | extensions.cs:25:20:25:20 | t | +| extensions.cs:33:24:33:25 | M3 | extensions.cs:33:39:33:39 | s | 0 | string | extensions.cs:33:39:33:39 | s | extensionOperatorCallArgument -| extensions.cs:15:39:15:39 | * | extensions.cs:53:19:53:23 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:53:19:53:19 | 3 | -| extensions.cs:15:39:15:39 | * | extensions.cs:53:19:53:23 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:53:23:53:23 | access to local variable s | -| extensions.cs:15:39:15:39 | * | extensions.cs:67:9:67:38 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:67:34:67:34 | 3 | -| extensions.cs:15:39:15:39 | * | extensions.cs:67:9:67:38 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:67:37:67:37 | access to local variable s | +| extensions.cs:15:39:15:39 | * | extensions.cs:58:19:58:23 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:58:19:58:19 | 3 | +| extensions.cs:15:39:15:39 | * | extensions.cs:58:19:58:23 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:58:23:58:23 | access to local variable s | +| extensions.cs:15:39:15:39 | * | extensions.cs:72:9:72:38 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:72:34:72:34 | 3 | +| extensions.cs:15:39:15:39 | * | extensions.cs:72:9:72:38 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:72:37:72:37 | access to local variable s | extensionOperatorCalls -| extensions.cs:53:19:53:23 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | -| extensions.cs:67:9:67:38 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | +| extensions.cs:58:19:58:23 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | +| extensions.cs:72:9:72:38 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | extensionProperty | extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:16:5 | extension(String) | | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | | extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:16:5 | extension(String) | | extensions.cs:22:28:22:37 | StaticProp | extensions.cs:18:5:23:5 | extension(Object) | extensionPropertyCall -| extensions.cs:38:19:38:25 | access to property Prop1 | extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop1 | -| extensions.cs:39:19:39:25 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | -| extensions.cs:40:9:40:15 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | -| extensions.cs:41:19:41:36 | access to property StaticProp1 | extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticProp1 | -| extensions.cs:42:19:42:35 | access to property StaticProp | extensions.cs:22:28:22:37 | StaticProp | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticProp | +| extensions.cs:43:19:43:25 | access to property Prop1 | extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop1 | +| extensions.cs:44:19:44:25 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | +| extensions.cs:45:9:45:15 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | +| extensions.cs:46:19:46:36 | access to property StaticProp1 | extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticProp1 | +| extensions.cs:47:19:47:35 | access to property StaticProp | extensions.cs:22:28:22:37 | StaticProp | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticProp | extensionAccessorCall -| extensions.cs:70:9:70:33 | call to extension accessor get_Prop1 | extensions.cs:8:30:8:41 | get_Prop1 | extensions.cs:8:21:8:25 | Prop1 | MyExtensions+extension(String).get_Prop1 | -| extensions.cs:71:9:71:33 | call to extension accessor get_Prop2 | extensions.cs:9:29:9:31 | get_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).get_Prop2 | -| extensions.cs:72:9:72:40 | call to extension accessor set_Prop2 | extensions.cs:9:50:9:52 | set_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).set_Prop2 | -| extensions.cs:73:9:73:37 | call to extension accessor get_StaticProp | extensions.cs:22:42:22:45 | get_StaticProp | extensions.cs:22:28:22:37 | StaticProp | MyExtensions+extension(Object).get_StaticProp | +| extensions.cs:75:9:75:33 | call to extension accessor get_Prop1 | extensions.cs:8:30:8:41 | get_Prop1 | extensions.cs:8:21:8:25 | Prop1 | MyExtensions+extension(String).get_Prop1 | +| extensions.cs:76:9:76:33 | call to extension accessor get_Prop2 | extensions.cs:9:29:9:31 | get_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).get_Prop2 | +| extensions.cs:77:9:77:40 | call to extension accessor set_Prop2 | extensions.cs:9:50:9:52 | set_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).set_Prop2 | +| extensions.cs:78:9:78:37 | call to extension accessor get_StaticProp | extensions.cs:22:42:22:45 | get_StaticProp | extensions.cs:22:28:22:37 | StaticProp | MyExtensions+extension(Object).get_StaticProp | diff --git a/csharp/ql/test/library-tests/extension/extensions.ql b/csharp/ql/test/library-tests/extension/extensions.ql index b3b0e05ac92a..23d01d4848c3 100644 --- a/csharp/ql/test/library-tests/extension/extensions.ql +++ b/csharp/ql/test/library-tests/extension/extensions.ql @@ -18,6 +18,15 @@ query predicate extensionMethodCalls( em.getFullyQualifiedNameDebug() = type } +query predicate extensionParameter( + ExtensionMethod em, Parameter p, int i, string type, Parameter unbound +) { + em.getFile().getBaseName() = "extensions.cs" and + p = em.getParameter(i) and + type = p.getType().toStringWithTypes() and + unbound = p.getUnboundDeclaration() +} + query predicate extensionOperatorCallArgument( ExtensionOperator op, ExtensionOperatorCall opc, Parameter p, int pos, Expr e ) { From f8a2168a7acc4bc59cabf54bde2171bb509d341e Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 30 Jan 2026 10:31:43 +0100 Subject: [PATCH 17/39] C#: Replace calls to constructed generic extensions methods with calls to constructed extensions from extension types (if possible). --- .../SymbolExtensions.cs | 33 +++++++++++++++---- .../Entities/Method.cs | 4 +-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs index 70d5865a88a3..e90c3d13891b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs @@ -5,7 +5,6 @@ using System.Linq; using Microsoft.CodeAnalysis; using Semmle.Extraction.CSharp.Entities; -using Semmle.Extraction.CSharp.Entities.Statements; namespace Semmle.Extraction.CSharp { @@ -647,14 +646,36 @@ public static bool TryGetExtensionMethod(this IMethodSymbol method, out IMethodS var extensions = containingType.GetMembers() .OfType() .Where(t => t.IsExtension); - // Find the original extension method that maps to this implementation (if any). - declaration = extensions.SelectMany(e => e.GetMembers()) + // Find the (possibly unbound) original extension method that maps to this implementation (if any). + var unboundDeclaration = extensions.SelectMany(e => e.GetMembers()) .OfType() - .FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method)); - return declaration is not null; + .FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method.ConstructedFrom)); + + var isFullyConstructed = method.IsBoundGenericMethod(); + if (isFullyConstructed && unboundDeclaration?.ContainingType is INamedTypeSymbol extensionType && extensionType.IsGenericType) + { + // Use the type arguments from the constructed extension method to construct the extension type. + var arguments = method.TypeArguments.ToArray(); + var boundExtensionType = extensionType.Construct(arguments); + var boundDeclaration = boundExtensionType.GetMembers() + .OfType() + .FirstOrDefault(c => SymbolEqualityComparer.Default.Equals(c.OriginalDefinition, unboundDeclaration)); + declaration = boundDeclaration; + } + else + { + declaration = unboundDeclaration; + } + } - return false; + return declaration is not null; } + + public static bool IsUnboundGenericMethod(this IMethodSymbol method) => + method.IsGenericMethod && SymbolEqualityComparer.Default.Equals(method.ConstructedFrom, method); + + public static bool IsBoundGenericMethod(this IMethodSymbol method) => method.IsGenericMethod && !IsUnboundGenericMethod(method); + /// /// Gets the base type of `symbol`. Unlike `symbol.BaseType`, this excludes effective base /// types of type parameters as well as `object` base types. diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs index 4617e1a50e38..ecf1e1f0ee93 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs @@ -321,9 +321,9 @@ public static void AddExplicitInterfaceQualifierToId(Context cx, EscapingTextWri /// /// Whether this method has unbound type parameters. /// - public bool IsUnboundGeneric => IsGeneric && SymbolEqualityComparer.Default.Equals(Symbol.ConstructedFrom, Symbol); + public bool IsUnboundGeneric => Symbol.IsUnboundGenericMethod(); - public bool IsBoundGeneric => IsGeneric && !IsUnboundGeneric; + public bool IsBoundGeneric => Symbol.IsBoundGenericMethod(); protected IMethodSymbol ConstructedFromSymbol => Symbol.ConstructedFrom; From 8ddd08e1793916f202ed907ade9b6625f607652c Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 30 Jan 2026 11:33:06 +0100 Subject: [PATCH 18/39] C#: Update test expected output. --- csharp/ql/test/library-tests/extension/extensions.cs | 4 ++-- csharp/ql/test/library-tests/extension/extensions.expected | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/csharp/ql/test/library-tests/extension/extensions.cs b/csharp/ql/test/library-tests/extension/extensions.cs index 27a3fabc5c50..30af32860ca8 100644 --- a/csharp/ql/test/library-tests/extension/extensions.cs +++ b/csharp/ql/test/library-tests/extension/extensions.cs @@ -81,7 +81,7 @@ public static void CallingExtensions() var o = new object(); o.GenericM(); s.GenericM(); - MyExtensions.GenericM(o); // NOTE, Not handled in the extractor - MyExtensions.GenericM(s); // NOTE, Not handled in the extractor + MyExtensions.GenericM(o); + MyExtensions.GenericM(s); } } diff --git a/csharp/ql/test/library-tests/extension/extensions.expected b/csharp/ql/test/library-tests/extension/extensions.expected index be418d14d6fb..bca96ec4dc84 100644 --- a/csharp/ql/test/library-tests/extension/extensions.expected +++ b/csharp/ql/test/library-tests/extension/extensions.expected @@ -12,6 +12,8 @@ extensionMethodCallArgument | extensions.cs:69:9:69:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:69:37:69:37 | access to local variable s | | extensions.cs:82:9:82:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:82:9:82:9 | access to local variable o | | extensions.cs:83:9:83:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:83:9:83:9 | access to local variable s | +| extensions.cs:84:9:84:32 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:84:31:84:31 | access to local variable o | +| extensions.cs:85:9:85:32 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:85:31:85:31 | access to local variable s | extensionMethodCalls | extensions.cs:50:19:50:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | | extensions.cs:51:19:51:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | @@ -28,6 +30,8 @@ extensionMethodCalls | extensions.cs:69:9:69:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | | extensions.cs:82:9:82:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:5:28:5 | extension(Object) | MyExtensions+extension(Object).GenericM | | extensions.cs:83:9:83:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:5:28:5 | extension(String) | MyExtensions+extension(String).GenericM | +| extensions.cs:84:9:84:32 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:5:28:5 | extension(Object) | MyExtensions+extension(Object).GenericM | +| extensions.cs:85:9:85:32 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:5:28:5 | extension(String) | MyExtensions+extension(String).GenericM | extensionParameter | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | From b16abc93f5b2a80af57275e0560f864b909b2987 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 30 Jan 2026 13:03:30 +0100 Subject: [PATCH 19/39] C#: Add some more generic types and methods. --- .../extension/extensionType.expected | 12 +- .../library-tests/extension/extensions.cs | 25 +++- .../extension/extensions.expected | 120 ++++++++++-------- 3 files changed, 94 insertions(+), 63 deletions(-) diff --git a/csharp/ql/test/library-tests/extension/extensionType.expected b/csharp/ql/test/library-tests/extension/extensionType.expected index 8e25777b170a..b84e62b0e383 100644 --- a/csharp/ql/test/library-tests/extension/extensionType.expected +++ b/csharp/ql/test/library-tests/extension/extensionType.expected @@ -1,11 +1,11 @@ extensionTypeReceiverParameter | extensions.cs:6:5:16:5 | extension(String) | extensions.cs:6:22:6:22 | s | -| extensions.cs:25:5:28:5 | extension(Object) | file://:0:0:0:0 | t | -| extensions.cs:25:5:28:5 | extension(String) | file://:0:0:0:0 | t | -| extensions.cs:25:5:28:5 | extension(T)`1 | extensions.cs:25:20:25:20 | t | +| extensions.cs:25:5:34:5 | extension(Object) | file://:0:0:0:0 | t | +| extensions.cs:25:5:34:5 | extension(String) | file://:0:0:0:0 | t | +| extensions.cs:25:5:34:5 | extension(T)`1 | extensions.cs:25:20:25:20 | t | extensionTypeExtendedType | extensions.cs:6:5:16:5 | extension(String) | string | | extensions.cs:18:5:23:5 | extension(Object) | object | -| extensions.cs:25:5:28:5 | extension(Object) | object | -| extensions.cs:25:5:28:5 | extension(String) | string | -| extensions.cs:25:5:28:5 | extension(T)`1 | T | +| extensions.cs:25:5:34:5 | extension(Object) | object | +| extensions.cs:25:5:34:5 | extension(String) | string | +| extensions.cs:25:5:34:5 | extension(T)`1 | T | diff --git a/csharp/ql/test/library-tests/extension/extensions.cs b/csharp/ql/test/library-tests/extension/extensions.cs index 30af32860ca8..37c648fd2c0f 100644 --- a/csharp/ql/test/library-tests/extension/extensions.cs +++ b/csharp/ql/test/library-tests/extension/extensions.cs @@ -24,7 +24,13 @@ public static class MyExtensions extension(T t) where T : class { - public bool GenericM() => t is not null; + public bool GenericProp1 => t is not null; + public bool GenericProp2 { get { return true; } set { } } + public bool GenericM1() => t is not null; + public void GenericM2(S other) { } + public void GenericStaticM1() { } + public static void GenericStaticM2(S other) { } + public static T operator +(T a, T b) { return null; } } } @@ -76,12 +82,19 @@ public static void CallingExtensions() MyExtensions.get_Prop2(s); MyExtensions.set_Prop2(s, false); MyExtensions.get_StaticProp(); + } - // Calling generic extension method + public static void CallingGenericExtensions() + { + var s = "Hello Generic World."; var o = new object(); - o.GenericM(); - s.GenericM(); - MyExtensions.GenericM(o); - MyExtensions.GenericM(s); + + // Calling generic extension method + o.GenericM1(); + s.GenericM1(); + + // Calling the compiler generated static extension methods. + MyExtensions.GenericM1(o); + MyExtensions.GenericM1(s); } } diff --git a/csharp/ql/test/library-tests/extension/extensions.expected b/csharp/ql/test/library-tests/extension/extensions.expected index bca96ec4dc84..6582e1953fe9 100644 --- a/csharp/ql/test/library-tests/extension/extensions.expected +++ b/csharp/ql/test/library-tests/extension/extensions.expected @@ -1,68 +1,86 @@ extensionMethodCallArgument -| extensions.cs:50:19:50:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:50:19:50:19 | access to local variable s | -| extensions.cs:51:19:51:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:51:19:51:19 | access to local variable s | -| extensions.cs:51:19:51:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:51:24:51:28 | "!!!" | -| extensions.cs:53:19:53:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:53:35:53:35 | access to local variable s | -| extensions.cs:55:19:55:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:55:41:55:41 | access to local variable s | -| extensions.cs:61:17:61:22 | call to method M3 | extensions.cs:33:24:33:25 | M3 | extensions.cs:33:39:33:39 | s | 0 | extensions.cs:61:17:61:17 | access to local variable s | -| extensions.cs:64:9:64:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:64:25:64:25 | access to local variable s | -| extensions.cs:65:9:65:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:65:25:65:25 | access to local variable s | -| extensions.cs:65:9:65:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:65:28:65:32 | "!!!" | -| extensions.cs:67:9:67:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:67:31:67:31 | access to local variable s | -| extensions.cs:69:9:69:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:69:37:69:37 | access to local variable s | -| extensions.cs:82:9:82:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:82:9:82:9 | access to local variable o | -| extensions.cs:83:9:83:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:83:9:83:9 | access to local variable s | -| extensions.cs:84:9:84:32 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:84:31:84:31 | access to local variable o | -| extensions.cs:85:9:85:32 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:85:31:85:31 | access to local variable s | +| extensions.cs:56:19:56:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:56:19:56:19 | access to local variable s | +| extensions.cs:57:19:57:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:57:19:57:19 | access to local variable s | +| extensions.cs:57:19:57:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:57:24:57:28 | "!!!" | +| extensions.cs:59:19:59:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:59:35:59:35 | access to local variable s | +| extensions.cs:61:19:61:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:61:41:61:41 | access to local variable s | +| extensions.cs:67:17:67:22 | call to method M3 | extensions.cs:39:24:39:25 | M3 | extensions.cs:39:39:39:39 | s | 0 | extensions.cs:67:17:67:17 | access to local variable s | +| extensions.cs:70:9:70:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:70:25:70:25 | access to local variable s | +| extensions.cs:71:9:71:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:71:25:71:25 | access to local variable s | +| extensions.cs:71:9:71:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:71:28:71:32 | "!!!" | +| extensions.cs:73:9:73:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:73:31:73:31 | access to local variable s | +| extensions.cs:75:9:75:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:75:37:75:37 | access to local variable s | +| extensions.cs:93:9:93:21 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:93:9:93:9 | access to local variable o | +| extensions.cs:94:9:94:21 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:94:9:94:9 | access to local variable s | +| extensions.cs:97:9:97:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:97:32:97:32 | access to local variable o | +| extensions.cs:98:9:98:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:98:32:98:32 | access to local variable s | extensionMethodCalls -| extensions.cs:50:19:50:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | -| extensions.cs:51:19:51:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | -| extensions.cs:52:19:52:35 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | -| extensions.cs:53:19:53:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | -| extensions.cs:54:19:54:41 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | -| extensions.cs:55:19:55:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | -| extensions.cs:61:17:61:22 | call to method M3 | extensions.cs:33:24:33:25 | M3 | extensions.cs:31:21:31:37 | ClassicExtensions | ClassicExtensions.M3 | -| extensions.cs:64:9:64:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | -| extensions.cs:65:9:65:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | -| extensions.cs:66:9:66:31 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | -| extensions.cs:67:9:67:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | -| extensions.cs:68:9:68:37 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | -| extensions.cs:69:9:69:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | -| extensions.cs:82:9:82:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:5:28:5 | extension(Object) | MyExtensions+extension(Object).GenericM | -| extensions.cs:83:9:83:20 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:5:28:5 | extension(String) | MyExtensions+extension(String).GenericM | -| extensions.cs:84:9:84:32 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:5:28:5 | extension(Object) | MyExtensions+extension(Object).GenericM | -| extensions.cs:85:9:85:32 | call to method GenericM | extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:5:28:5 | extension(String) | MyExtensions+extension(String).GenericM | +| extensions.cs:56:19:56:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | +| extensions.cs:57:19:57:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | +| extensions.cs:58:19:58:35 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | +| extensions.cs:59:19:59:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | +| extensions.cs:60:19:60:41 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | +| extensions.cs:61:19:61:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | +| extensions.cs:67:17:67:22 | call to method M3 | extensions.cs:39:24:39:25 | M3 | extensions.cs:37:21:37:37 | ClassicExtensions | ClassicExtensions.M3 | +| extensions.cs:70:9:70:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | +| extensions.cs:71:9:71:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | +| extensions.cs:72:9:72:31 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | +| extensions.cs:73:9:73:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | +| extensions.cs:74:9:74:37 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | +| extensions.cs:75:9:75:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | +| extensions.cs:93:9:93:21 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:5:34:5 | extension(Object) | MyExtensions+extension(Object).GenericM1 | +| extensions.cs:94:9:94:21 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:5:34:5 | extension(String) | MyExtensions+extension(String).GenericM1 | +| extensions.cs:97:9:97:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:5:34:5 | extension(Object) | MyExtensions+extension(Object).GenericM1 | +| extensions.cs:98:9:98:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:5:34:5 | extension(String) | MyExtensions+extension(String).GenericM1 | extensionParameter | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | string | extensions.cs:12:33:12:37 | other | | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | string | extensions.cs:14:43:14:43 | x | | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | string | extensions.cs:21:49:21:49 | s | -| extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | T | extensions.cs:25:20:25:20 | t | -| extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | -| extensions.cs:27:21:27:28 | GenericM | extensions.cs:25:20:25:20 | t | 0 | string | extensions.cs:25:20:25:20 | t | -| extensions.cs:33:24:33:25 | M3 | extensions.cs:33:39:33:39 | s | 0 | string | extensions.cs:33:39:33:39 | s | +| extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | T | extensions.cs:25:20:25:20 | t | +| extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | +| extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | string | extensions.cs:25:20:25:20 | t | +| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:25:20:25:20 | t | 0 | T | extensions.cs:25:20:25:20 | t | +| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | +| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:25:20:25:20 | t | 0 | string | extensions.cs:25:20:25:20 | t | +| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:30:36:30:40 | other | 1 | S | extensions.cs:30:36:30:40 | other | +| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:30:36:30:40 | other | 1 | S | extensions.cs:30:36:30:40 | other | +| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:30:36:30:40 | other | 1 | S | extensions.cs:30:36:30:40 | other | +| extensions.cs:31:21:31:35 | GenericStaticM1 | extensions.cs:25:20:25:20 | t | 0 | T | extensions.cs:25:20:25:20 | t | +| extensions.cs:31:21:31:35 | GenericStaticM1 | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | +| extensions.cs:31:21:31:35 | GenericStaticM1 | extensions.cs:25:20:25:20 | t | 0 | string | extensions.cs:25:20:25:20 | t | +| extensions.cs:32:28:32:45 | GenericStaticM2`1 | extensions.cs:32:49:32:53 | other | 0 | S | extensions.cs:32:49:32:53 | other | +| extensions.cs:32:28:32:45 | GenericStaticM2`1 | extensions.cs:32:49:32:53 | other | 0 | S | extensions.cs:32:49:32:53 | other | +| extensions.cs:32:28:32:45 | GenericStaticM2`1 | extensions.cs:32:49:32:53 | other | 0 | S | extensions.cs:32:49:32:53 | other | +| extensions.cs:39:24:39:25 | M3 | extensions.cs:39:39:39:39 | s | 0 | string | extensions.cs:39:39:39:39 | s | extensionOperatorCallArgument -| extensions.cs:15:39:15:39 | * | extensions.cs:58:19:58:23 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:58:19:58:19 | 3 | -| extensions.cs:15:39:15:39 | * | extensions.cs:58:19:58:23 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:58:23:58:23 | access to local variable s | -| extensions.cs:15:39:15:39 | * | extensions.cs:72:9:72:38 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:72:34:72:34 | 3 | -| extensions.cs:15:39:15:39 | * | extensions.cs:72:9:72:38 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:72:37:72:37 | access to local variable s | +| extensions.cs:15:39:15:39 | * | extensions.cs:64:19:64:23 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:64:19:64:19 | 3 | +| extensions.cs:15:39:15:39 | * | extensions.cs:64:19:64:23 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:64:23:64:23 | access to local variable s | +| extensions.cs:15:39:15:39 | * | extensions.cs:78:9:78:38 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:78:34:78:34 | 3 | +| extensions.cs:15:39:15:39 | * | extensions.cs:78:9:78:38 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:78:37:78:37 | access to local variable s | extensionOperatorCalls -| extensions.cs:58:19:58:23 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | -| extensions.cs:72:9:72:38 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | +| extensions.cs:64:19:64:23 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | +| extensions.cs:78:9:78:38 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | extensionProperty | extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:16:5 | extension(String) | | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | | extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:16:5 | extension(String) | | extensions.cs:22:28:22:37 | StaticProp | extensions.cs:18:5:23:5 | extension(Object) | +| extensions.cs:27:21:27:32 | GenericProp1 | extensions.cs:25:5:34:5 | extension(Object) | +| extensions.cs:27:21:27:32 | GenericProp1 | extensions.cs:25:5:34:5 | extension(String) | +| extensions.cs:27:21:27:32 | GenericProp1 | extensions.cs:25:5:34:5 | extension(T)`1 | +| extensions.cs:28:21:28:32 | GenericProp2 | extensions.cs:25:5:34:5 | extension(Object) | +| extensions.cs:28:21:28:32 | GenericProp2 | extensions.cs:25:5:34:5 | extension(String) | +| extensions.cs:28:21:28:32 | GenericProp2 | extensions.cs:25:5:34:5 | extension(T)`1 | extensionPropertyCall -| extensions.cs:43:19:43:25 | access to property Prop1 | extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop1 | -| extensions.cs:44:19:44:25 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | -| extensions.cs:45:9:45:15 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | -| extensions.cs:46:19:46:36 | access to property StaticProp1 | extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticProp1 | -| extensions.cs:47:19:47:35 | access to property StaticProp | extensions.cs:22:28:22:37 | StaticProp | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticProp | +| extensions.cs:49:19:49:25 | access to property Prop1 | extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop1 | +| extensions.cs:50:19:50:25 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | +| extensions.cs:51:9:51:15 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | +| extensions.cs:52:19:52:36 | access to property StaticProp1 | extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticProp1 | +| extensions.cs:53:19:53:35 | access to property StaticProp | extensions.cs:22:28:22:37 | StaticProp | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticProp | extensionAccessorCall -| extensions.cs:75:9:75:33 | call to extension accessor get_Prop1 | extensions.cs:8:30:8:41 | get_Prop1 | extensions.cs:8:21:8:25 | Prop1 | MyExtensions+extension(String).get_Prop1 | -| extensions.cs:76:9:76:33 | call to extension accessor get_Prop2 | extensions.cs:9:29:9:31 | get_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).get_Prop2 | -| extensions.cs:77:9:77:40 | call to extension accessor set_Prop2 | extensions.cs:9:50:9:52 | set_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).set_Prop2 | -| extensions.cs:78:9:78:37 | call to extension accessor get_StaticProp | extensions.cs:22:42:22:45 | get_StaticProp | extensions.cs:22:28:22:37 | StaticProp | MyExtensions+extension(Object).get_StaticProp | +| extensions.cs:81:9:81:33 | call to extension accessor get_Prop1 | extensions.cs:8:30:8:41 | get_Prop1 | extensions.cs:8:21:8:25 | Prop1 | MyExtensions+extension(String).get_Prop1 | +| extensions.cs:82:9:82:33 | call to extension accessor get_Prop2 | extensions.cs:9:29:9:31 | get_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).get_Prop2 | +| extensions.cs:83:9:83:40 | call to extension accessor set_Prop2 | extensions.cs:9:50:9:52 | set_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).set_Prop2 | +| extensions.cs:84:9:84:37 | call to extension accessor get_StaticProp | extensions.cs:22:42:22:45 | get_StaticProp | extensions.cs:22:28:22:37 | StaticProp | MyExtensions+extension(Object).get_StaticProp | From b7451e464a359e6620fff3dcdf63abb70507af1e Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 30 Jan 2026 13:47:20 +0100 Subject: [PATCH 20/39] C#: Add unbound parameter for constructed generic extensions. --- .../Semmle.Extraction.CSharp/Entities/Types/NamedType.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs index 11c524c91bf6..d89f3db11e64 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs @@ -20,6 +20,8 @@ private NamedType(Context cx, INamedTypeSymbol init, bool constructUnderlyingTup public static NamedType Create(Context cx, INamedTypeSymbol type) => NamedTypeFactory.Instance.CreateEntityFromSymbol(cx, type); + public NamedType OriginalDefinition => Create(Context, Symbol.OriginalDefinition); + /// /// Creates a named type entity from a tuple type. Unlike , this /// will create an entity for the underlying `System.ValueTuple` struct. @@ -97,7 +99,12 @@ public override void Populate(TextWriter trapFile) // even when there is no parameter. if (!string.IsNullOrEmpty(parameter.Name)) { - Parameter.Create(Context, parameter, this); + var originalType = OriginalDefinition; + // In case the this is constructed generic, we also need to create the unbound parameter. + var originalParameter = SymbolEqualityComparer.Default.Equals(Symbol, originalType.Symbol.ExtensionParameter) || originalType.Symbol.ExtensionParameter is null + ? null + : Parameter.Create(Context, originalType.Symbol.ExtensionParameter, originalType); + Parameter.Create(Context, parameter, this, originalParameter); } // Use the parameter type as the receiver type. From e1f78b765235cfcb51ef47c678601c4f2dacf290 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 30 Jan 2026 13:47:52 +0100 Subject: [PATCH 21/39] C#: Update extensionType expected output. --- csharp/ql/test/library-tests/extension/extensionType.expected | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/csharp/ql/test/library-tests/extension/extensionType.expected b/csharp/ql/test/library-tests/extension/extensionType.expected index b84e62b0e383..1714a5db2524 100644 --- a/csharp/ql/test/library-tests/extension/extensionType.expected +++ b/csharp/ql/test/library-tests/extension/extensionType.expected @@ -1,7 +1,7 @@ extensionTypeReceiverParameter | extensions.cs:6:5:16:5 | extension(String) | extensions.cs:6:22:6:22 | s | -| extensions.cs:25:5:34:5 | extension(Object) | file://:0:0:0:0 | t | -| extensions.cs:25:5:34:5 | extension(String) | file://:0:0:0:0 | t | +| extensions.cs:25:5:34:5 | extension(Object) | extensions.cs:25:20:25:20 | t | +| extensions.cs:25:5:34:5 | extension(String) | extensions.cs:25:20:25:20 | t | | extensions.cs:25:5:34:5 | extension(T)`1 | extensions.cs:25:20:25:20 | t | extensionTypeExtendedType | extensions.cs:6:5:16:5 | extension(String) | string | From bba2bd4235209f0943344e3c4bb03c9fca80429b Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 30 Jan 2026 15:02:16 +0100 Subject: [PATCH 22/39] C#: Support replacing invocations of generic methods in generic extensions. --- .../SymbolExtensions.cs | 33 +++++++++++++++---- .../Semmle.Util/IEnumerableExtensions.cs | 19 +++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs index e90c3d13891b..58aa07e9719f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using Microsoft.CodeAnalysis; +using Semmle.Util; using Semmle.Extraction.CSharp.Entities; namespace Semmle.Extraction.CSharp @@ -652,15 +653,33 @@ public static bool TryGetExtensionMethod(this IMethodSymbol method, out IMethodS .FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method.ConstructedFrom)); var isFullyConstructed = method.IsBoundGenericMethod(); + // TODO: We also need to handle generic methods in non-generic extension types. if (isFullyConstructed && unboundDeclaration?.ContainingType is INamedTypeSymbol extensionType && extensionType.IsGenericType) { - // Use the type arguments from the constructed extension method to construct the extension type. - var arguments = method.TypeArguments.ToArray(); - var boundExtensionType = extensionType.Construct(arguments); - var boundDeclaration = boundExtensionType.GetMembers() - .OfType() - .FirstOrDefault(c => SymbolEqualityComparer.Default.Equals(c.OriginalDefinition, unboundDeclaration)); - declaration = boundDeclaration; + try + { + // Use the type arguments from the constructed extension method to construct the extension type. + var arguments = method.TypeArguments.ToArray(); + var (extensionTypeArguments, extensionMethodArguments) = arguments.SplitAt(extensionType.TypeParameters.Length); + + // Construct the extension type. + var boundExtensionType = extensionType.Construct(extensionTypeArguments.ToArray()); + + // Find the extension method declaration within the constructed extension type. + var extensionDeclaration = boundExtensionType.GetMembers() + .OfType() + .First(c => SymbolEqualityComparer.Default.Equals(c.OriginalDefinition, unboundDeclaration)); + + // If the extension declaration is unbound apply the remaning type arguments and construct it. + declaration = extensionDeclaration.IsUnboundGenericMethod() + ? extensionDeclaration.Construct(extensionMethodArguments.ToArray()) + : extensionDeclaration; + } + catch (Exception) + { + // If anything goes wrong, fall back to the unbound declaration. + declaration = unboundDeclaration; + } } else { diff --git a/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs b/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs index 1ca676f0ce6d..b43d85d2ad42 100644 --- a/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs +++ b/csharp/extractor/Semmle.Util/IEnumerableExtensions.cs @@ -119,5 +119,24 @@ public static int SequenceHash(this IEnumerable items) where T : notnull /// public static IEnumerable WhereNotNull(this IEnumerable items) where T : class => items.Where(i => i is not null)!; + + /// + /// Splits the sequence at the given index. + /// + public static (IEnumerable, IEnumerable) SplitAt(this IEnumerable items, int index) + { + var left = new List(); + var right = new List(); + var i = 0; + foreach (var item in items) + { + if (i < index) + left.Add(item); + else + right.Add(item); + i++; + } + return (left, right); + } } } From 2bdfd6db884e53a80bc3f83d45ec11495d8994c7 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 30 Jan 2026 15:10:54 +0100 Subject: [PATCH 23/39] C#: Add some more tests and update the expected test output. --- csharp/ql/test/library-tests/extension/extensions.cs | 3 +++ .../ql/test/library-tests/extension/extensions.expected | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/csharp/ql/test/library-tests/extension/extensions.cs b/csharp/ql/test/library-tests/extension/extensions.cs index 37c648fd2c0f..a40e924b936a 100644 --- a/csharp/ql/test/library-tests/extension/extensions.cs +++ b/csharp/ql/test/library-tests/extension/extensions.cs @@ -96,5 +96,8 @@ public static void CallingGenericExtensions() // Calling the compiler generated static extension methods. MyExtensions.GenericM1(o); MyExtensions.GenericM1(s); + + o.GenericM2(42); + MyExtensions.GenericM2(o, 42); } } diff --git a/csharp/ql/test/library-tests/extension/extensions.expected b/csharp/ql/test/library-tests/extension/extensions.expected index 6582e1953fe9..e5f8bbec17fc 100644 --- a/csharp/ql/test/library-tests/extension/extensions.expected +++ b/csharp/ql/test/library-tests/extension/extensions.expected @@ -14,6 +14,10 @@ extensionMethodCallArgument | extensions.cs:94:9:94:21 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:94:9:94:9 | access to local variable s | | extensions.cs:97:9:97:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:97:32:97:32 | access to local variable o | | extensions.cs:98:9:98:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:98:32:98:32 | access to local variable s | +| extensions.cs:100:9:100:23 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:100:9:100:9 | access to local variable o | +| extensions.cs:100:9:100:23 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:30:36:30:40 | other | 1 | extensions.cs:100:21:100:22 | 42 | +| extensions.cs:101:9:101:37 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:101:32:101:32 | access to local variable o | +| extensions.cs:101:9:101:37 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:30:36:30:40 | other | 1 | extensions.cs:101:35:101:36 | 42 | extensionMethodCalls | extensions.cs:56:19:56:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | | extensions.cs:57:19:57:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | @@ -32,6 +36,8 @@ extensionMethodCalls | extensions.cs:94:9:94:21 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:5:34:5 | extension(String) | MyExtensions+extension(String).GenericM1 | | extensions.cs:97:9:97:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:5:34:5 | extension(Object) | MyExtensions+extension(Object).GenericM1 | | extensions.cs:98:9:98:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:5:34:5 | extension(String) | MyExtensions+extension(String).GenericM1 | +| extensions.cs:100:9:100:23 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:25:5:34:5 | extension(Object) | MyExtensions+extension(Object).GenericM2 | +| extensions.cs:101:9:101:37 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:25:5:34:5 | extension(Object) | MyExtensions+extension(Object).GenericM2 | extensionParameter | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | @@ -41,6 +47,8 @@ extensionParameter | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | T | extensions.cs:25:20:25:20 | t | | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | string | extensions.cs:25:20:25:20 | t | +| extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | +| extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:30:36:30:40 | other | 1 | int | extensions.cs:30:36:30:40 | other | | extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:25:20:25:20 | t | 0 | T | extensions.cs:25:20:25:20 | t | | extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | | extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:25:20:25:20 | t | 0 | string | extensions.cs:25:20:25:20 | t | From d5fc092fbd553cd33a29d6fc41084bd5e6db7063 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 2 Feb 2026 10:02:19 +0100 Subject: [PATCH 24/39] C#: Add some more tests. --- .../extension/extensionType.expected | 18 +- .../library-tests/extension/extensions.cs | 4 + .../extension/extensions.expected | 181 ++++++++++-------- 3 files changed, 112 insertions(+), 91 deletions(-) diff --git a/csharp/ql/test/library-tests/extension/extensionType.expected b/csharp/ql/test/library-tests/extension/extensionType.expected index 1714a5db2524..b622c2f4c263 100644 --- a/csharp/ql/test/library-tests/extension/extensionType.expected +++ b/csharp/ql/test/library-tests/extension/extensionType.expected @@ -1,11 +1,11 @@ extensionTypeReceiverParameter -| extensions.cs:6:5:16:5 | extension(String) | extensions.cs:6:22:6:22 | s | -| extensions.cs:25:5:34:5 | extension(Object) | extensions.cs:25:20:25:20 | t | -| extensions.cs:25:5:34:5 | extension(String) | extensions.cs:25:20:25:20 | t | -| extensions.cs:25:5:34:5 | extension(T)`1 | extensions.cs:25:20:25:20 | t | +| extensions.cs:6:5:17:5 | extension(String) | extensions.cs:6:22:6:22 | s | +| extensions.cs:26:5:35:5 | extension(Object) | extensions.cs:26:20:26:20 | t | +| extensions.cs:26:5:35:5 | extension(String) | extensions.cs:26:20:26:20 | t | +| extensions.cs:26:5:35:5 | extension(T)`1 | extensions.cs:26:20:26:20 | t | extensionTypeExtendedType -| extensions.cs:6:5:16:5 | extension(String) | string | -| extensions.cs:18:5:23:5 | extension(Object) | object | -| extensions.cs:25:5:34:5 | extension(Object) | object | -| extensions.cs:25:5:34:5 | extension(String) | string | -| extensions.cs:25:5:34:5 | extension(T)`1 | T | +| extensions.cs:6:5:17:5 | extension(String) | string | +| extensions.cs:19:5:24:5 | extension(Object) | object | +| extensions.cs:26:5:35:5 | extension(Object) | object | +| extensions.cs:26:5:35:5 | extension(String) | string | +| extensions.cs:26:5:35:5 | extension(T)`1 | T | diff --git a/csharp/ql/test/library-tests/extension/extensions.cs b/csharp/ql/test/library-tests/extension/extensions.cs index a40e924b936a..1117a98f8a07 100644 --- a/csharp/ql/test/library-tests/extension/extensions.cs +++ b/csharp/ql/test/library-tests/extension/extensions.cs @@ -13,6 +13,7 @@ public static class MyExtensions public static int StaticM1() { return 0; } public static int StaticM2(string x) { return x.Length; } public static string operator *(int a, string b) { return ""; } + public T StringGenericM1(T t, object o) { return t; } } extension(object) @@ -99,5 +100,8 @@ public static void CallingGenericExtensions() o.GenericM2(42); MyExtensions.GenericM2(o, 42); + + s.StringGenericM1(7, new object()); + MyExtensions.StringGenericM1(s, "test", new object()); } } diff --git a/csharp/ql/test/library-tests/extension/extensions.expected b/csharp/ql/test/library-tests/extension/extensions.expected index e5f8bbec17fc..c892ff08947b 100644 --- a/csharp/ql/test/library-tests/extension/extensions.expected +++ b/csharp/ql/test/library-tests/extension/extensions.expected @@ -1,94 +1,111 @@ extensionMethodCallArgument -| extensions.cs:56:19:56:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:56:19:56:19 | access to local variable s | -| extensions.cs:57:19:57:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:57:19:57:19 | access to local variable s | -| extensions.cs:57:19:57:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:57:24:57:28 | "!!!" | -| extensions.cs:59:19:59:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:59:35:59:35 | access to local variable s | -| extensions.cs:61:19:61:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:61:41:61:41 | access to local variable s | -| extensions.cs:67:17:67:22 | call to method M3 | extensions.cs:39:24:39:25 | M3 | extensions.cs:39:39:39:39 | s | 0 | extensions.cs:67:17:67:17 | access to local variable s | -| extensions.cs:70:9:70:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:70:25:70:25 | access to local variable s | -| extensions.cs:71:9:71:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:71:25:71:25 | access to local variable s | -| extensions.cs:71:9:71:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:71:28:71:32 | "!!!" | -| extensions.cs:73:9:73:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:73:31:73:31 | access to local variable s | -| extensions.cs:75:9:75:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | extensions.cs:75:37:75:37 | access to local variable s | -| extensions.cs:93:9:93:21 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:93:9:93:9 | access to local variable o | -| extensions.cs:94:9:94:21 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:94:9:94:9 | access to local variable s | -| extensions.cs:97:9:97:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:97:32:97:32 | access to local variable o | -| extensions.cs:98:9:98:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:98:32:98:32 | access to local variable s | -| extensions.cs:100:9:100:23 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:100:9:100:9 | access to local variable o | -| extensions.cs:100:9:100:23 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:30:36:30:40 | other | 1 | extensions.cs:100:21:100:22 | 42 | -| extensions.cs:101:9:101:37 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:25:20:25:20 | t | 0 | extensions.cs:101:32:101:32 | access to local variable o | -| extensions.cs:101:9:101:37 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:30:36:30:40 | other | 1 | extensions.cs:101:35:101:36 | 42 | +| extensions.cs:57:19:57:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:57:19:57:19 | access to local variable s | +| extensions.cs:58:19:58:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:58:19:58:19 | access to local variable s | +| extensions.cs:58:19:58:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:58:24:58:28 | "!!!" | +| extensions.cs:60:19:60:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:60:35:60:35 | access to local variable s | +| extensions.cs:62:19:62:42 | call to method StaticObjectM2 | extensions.cs:22:27:22:40 | StaticObjectM2 | extensions.cs:22:49:22:49 | s | 0 | extensions.cs:62:41:62:41 | access to local variable s | +| extensions.cs:68:17:68:22 | call to method M3 | extensions.cs:40:24:40:25 | M3 | extensions.cs:40:39:40:39 | s | 0 | extensions.cs:68:17:68:17 | access to local variable s | +| extensions.cs:71:9:71:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:71:25:71:25 | access to local variable s | +| extensions.cs:72:9:72:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:72:25:72:25 | access to local variable s | +| extensions.cs:72:9:72:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | extensions.cs:72:28:72:32 | "!!!" | +| extensions.cs:74:9:74:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | extensions.cs:74:31:74:31 | access to local variable s | +| extensions.cs:76:9:76:38 | call to method StaticObjectM2 | extensions.cs:22:27:22:40 | StaticObjectM2 | extensions.cs:22:49:22:49 | s | 0 | extensions.cs:76:37:76:37 | access to local variable s | +| extensions.cs:94:9:94:21 | call to method GenericM1 | extensions.cs:30:21:30:29 | GenericM1 | extensions.cs:26:20:26:20 | t | 0 | extensions.cs:94:9:94:9 | access to local variable o | +| extensions.cs:95:9:95:21 | call to method GenericM1 | extensions.cs:30:21:30:29 | GenericM1 | extensions.cs:26:20:26:20 | t | 0 | extensions.cs:95:9:95:9 | access to local variable s | +| extensions.cs:98:9:98:33 | call to method GenericM1 | extensions.cs:30:21:30:29 | GenericM1 | extensions.cs:26:20:26:20 | t | 0 | extensions.cs:98:32:98:32 | access to local variable o | +| extensions.cs:99:9:99:33 | call to method GenericM1 | extensions.cs:30:21:30:29 | GenericM1 | extensions.cs:26:20:26:20 | t | 0 | extensions.cs:99:32:99:32 | access to local variable s | +| extensions.cs:101:9:101:23 | call to method GenericM2 | extensions.cs:31:21:31:32 | GenericM2 | extensions.cs:26:20:26:20 | t | 0 | extensions.cs:101:9:101:9 | access to local variable o | +| extensions.cs:101:9:101:23 | call to method GenericM2 | extensions.cs:31:21:31:32 | GenericM2 | extensions.cs:31:36:31:40 | other | 1 | extensions.cs:101:21:101:22 | 42 | +| extensions.cs:102:9:102:37 | call to method GenericM2 | extensions.cs:31:21:31:32 | GenericM2 | extensions.cs:26:20:26:20 | t | 0 | extensions.cs:102:32:102:32 | access to local variable o | +| extensions.cs:102:9:102:37 | call to method GenericM2 | extensions.cs:31:21:31:32 | GenericM2 | extensions.cs:31:36:31:40 | other | 1 | extensions.cs:102:35:102:36 | 42 | +| extensions.cs:104:9:104:47 | call to method StringGenericM1 | extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:104:9:104:9 | access to local variable s | +| extensions.cs:104:9:104:47 | call to method StringGenericM1 | extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:16:39:16:39 | t | 1 | extensions.cs:104:32:104:32 | 7 | +| extensions.cs:104:9:104:47 | call to method StringGenericM1 | extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:16:49:16:49 | o | 2 | extensions.cs:104:35:104:46 | object creation of type Object | +| extensions.cs:105:9:105:69 | call to method StringGenericM1 | extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:6:22:6:22 | s | 0 | extensions.cs:105:46:105:46 | access to local variable s | +| extensions.cs:105:9:105:69 | call to method StringGenericM1 | extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:16:39:16:39 | t | 1 | extensions.cs:105:49:105:54 | "test" | +| extensions.cs:105:9:105:69 | call to method StringGenericM1 | extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:16:49:16:49 | o | 2 | extensions.cs:105:57:105:68 | object creation of type Object | extensionMethodCalls -| extensions.cs:56:19:56:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | -| extensions.cs:57:19:57:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | -| extensions.cs:58:19:58:35 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | -| extensions.cs:59:19:59:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | -| extensions.cs:60:19:60:41 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | -| extensions.cs:61:19:61:42 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | -| extensions.cs:67:17:67:22 | call to method M3 | extensions.cs:39:24:39:25 | M3 | extensions.cs:37:21:37:37 | ClassicExtensions | ClassicExtensions.M3 | -| extensions.cs:70:9:70:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M1 | -| extensions.cs:71:9:71:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).M2 | -| extensions.cs:72:9:72:31 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM1 | -| extensions.cs:73:9:73:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticM2 | -| extensions.cs:74:9:74:37 | call to method StaticObjectM1 | extensions.cs:20:27:20:40 | StaticObjectM1 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | -| extensions.cs:75:9:75:38 | call to method StaticObjectM2 | extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | -| extensions.cs:93:9:93:21 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:5:34:5 | extension(Object) | MyExtensions+extension(Object).GenericM1 | -| extensions.cs:94:9:94:21 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:5:34:5 | extension(String) | MyExtensions+extension(String).GenericM1 | -| extensions.cs:97:9:97:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:5:34:5 | extension(Object) | MyExtensions+extension(Object).GenericM1 | -| extensions.cs:98:9:98:33 | call to method GenericM1 | extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:5:34:5 | extension(String) | MyExtensions+extension(String).GenericM1 | -| extensions.cs:100:9:100:23 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:25:5:34:5 | extension(Object) | MyExtensions+extension(Object).GenericM2 | -| extensions.cs:101:9:101:37 | call to method GenericM2 | extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:25:5:34:5 | extension(Object) | MyExtensions+extension(Object).GenericM2 | +| extensions.cs:57:19:57:24 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).M1 | +| extensions.cs:58:19:58:29 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).M2 | +| extensions.cs:59:19:59:35 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).StaticM1 | +| extensions.cs:60:19:60:36 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).StaticM2 | +| extensions.cs:61:19:61:41 | call to method StaticObjectM1 | extensions.cs:21:27:21:40 | StaticObjectM1 | extensions.cs:19:5:24:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | +| extensions.cs:62:19:62:42 | call to method StaticObjectM2 | extensions.cs:22:27:22:40 | StaticObjectM2 | extensions.cs:19:5:24:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | +| extensions.cs:68:17:68:22 | call to method M3 | extensions.cs:40:24:40:25 | M3 | extensions.cs:38:21:38:37 | ClassicExtensions | ClassicExtensions.M3 | +| extensions.cs:71:9:71:26 | call to method M1 | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).M1 | +| extensions.cs:72:9:72:33 | call to method M2 | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).M2 | +| extensions.cs:73:9:73:31 | call to method StaticM1 | extensions.cs:13:27:13:34 | StaticM1 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).StaticM1 | +| extensions.cs:74:9:74:32 | call to method StaticM2 | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).StaticM2 | +| extensions.cs:75:9:75:37 | call to method StaticObjectM1 | extensions.cs:21:27:21:40 | StaticObjectM1 | extensions.cs:19:5:24:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM1 | +| extensions.cs:76:9:76:38 | call to method StaticObjectM2 | extensions.cs:22:27:22:40 | StaticObjectM2 | extensions.cs:19:5:24:5 | extension(Object) | MyExtensions+extension(Object).StaticObjectM2 | +| extensions.cs:94:9:94:21 | call to method GenericM1 | extensions.cs:30:21:30:29 | GenericM1 | extensions.cs:26:5:35:5 | extension(Object) | MyExtensions+extension(Object).GenericM1 | +| extensions.cs:95:9:95:21 | call to method GenericM1 | extensions.cs:30:21:30:29 | GenericM1 | extensions.cs:26:5:35:5 | extension(String) | MyExtensions+extension(String).GenericM1 | +| extensions.cs:98:9:98:33 | call to method GenericM1 | extensions.cs:30:21:30:29 | GenericM1 | extensions.cs:26:5:35:5 | extension(Object) | MyExtensions+extension(Object).GenericM1 | +| extensions.cs:99:9:99:33 | call to method GenericM1 | extensions.cs:30:21:30:29 | GenericM1 | extensions.cs:26:5:35:5 | extension(String) | MyExtensions+extension(String).GenericM1 | +| extensions.cs:101:9:101:23 | call to method GenericM2 | extensions.cs:31:21:31:32 | GenericM2 | extensions.cs:26:5:35:5 | extension(Object) | MyExtensions+extension(Object).GenericM2 | +| extensions.cs:102:9:102:37 | call to method GenericM2 | extensions.cs:31:21:31:32 | GenericM2 | extensions.cs:26:5:35:5 | extension(Object) | MyExtensions+extension(Object).GenericM2 | +| extensions.cs:104:9:104:47 | call to method StringGenericM1 | extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).StringGenericM1 | +| extensions.cs:105:9:105:69 | call to method StringGenericM1 | extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).StringGenericM1 | extensionParameter | extensions.cs:11:21:11:22 | M1 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | | extensions.cs:12:23:12:24 | M2 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | | extensions.cs:12:23:12:24 | M2 | extensions.cs:12:33:12:37 | other | 1 | string | extensions.cs:12:33:12:37 | other | | extensions.cs:14:27:14:34 | StaticM2 | extensions.cs:14:43:14:43 | x | 0 | string | extensions.cs:14:43:14:43 | x | -| extensions.cs:21:27:21:40 | StaticObjectM2 | extensions.cs:21:49:21:49 | s | 0 | string | extensions.cs:21:49:21:49 | s | -| extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | T | extensions.cs:25:20:25:20 | t | -| extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | -| extensions.cs:29:21:29:29 | GenericM1 | extensions.cs:25:20:25:20 | t | 0 | string | extensions.cs:25:20:25:20 | t | -| extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | -| extensions.cs:30:21:30:32 | GenericM2 | extensions.cs:30:36:30:40 | other | 1 | int | extensions.cs:30:36:30:40 | other | -| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:25:20:25:20 | t | 0 | T | extensions.cs:25:20:25:20 | t | -| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | -| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:25:20:25:20 | t | 0 | string | extensions.cs:25:20:25:20 | t | -| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:30:36:30:40 | other | 1 | S | extensions.cs:30:36:30:40 | other | -| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:30:36:30:40 | other | 1 | S | extensions.cs:30:36:30:40 | other | -| extensions.cs:30:21:30:32 | GenericM2`1 | extensions.cs:30:36:30:40 | other | 1 | S | extensions.cs:30:36:30:40 | other | -| extensions.cs:31:21:31:35 | GenericStaticM1 | extensions.cs:25:20:25:20 | t | 0 | T | extensions.cs:25:20:25:20 | t | -| extensions.cs:31:21:31:35 | GenericStaticM1 | extensions.cs:25:20:25:20 | t | 0 | object | extensions.cs:25:20:25:20 | t | -| extensions.cs:31:21:31:35 | GenericStaticM1 | extensions.cs:25:20:25:20 | t | 0 | string | extensions.cs:25:20:25:20 | t | -| extensions.cs:32:28:32:45 | GenericStaticM2`1 | extensions.cs:32:49:32:53 | other | 0 | S | extensions.cs:32:49:32:53 | other | -| extensions.cs:32:28:32:45 | GenericStaticM2`1 | extensions.cs:32:49:32:53 | other | 0 | S | extensions.cs:32:49:32:53 | other | -| extensions.cs:32:28:32:45 | GenericStaticM2`1 | extensions.cs:32:49:32:53 | other | 0 | S | extensions.cs:32:49:32:53 | other | -| extensions.cs:39:24:39:25 | M3 | extensions.cs:39:39:39:39 | s | 0 | string | extensions.cs:39:39:39:39 | s | +| extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | +| extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:16:39:16:39 | t | 1 | int | extensions.cs:16:39:16:39 | t | +| extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:16:49:16:49 | o | 2 | object | extensions.cs:16:49:16:49 | o | +| extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | +| extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:16:39:16:39 | t | 1 | string | extensions.cs:16:39:16:39 | t | +| extensions.cs:16:18:16:35 | StringGenericM1 | extensions.cs:16:49:16:49 | o | 2 | object | extensions.cs:16:49:16:49 | o | +| extensions.cs:16:18:16:35 | StringGenericM1`1 | extensions.cs:6:22:6:22 | s | 0 | string | extensions.cs:6:22:6:22 | s | +| extensions.cs:16:18:16:35 | StringGenericM1`1 | extensions.cs:16:39:16:39 | t | 1 | T | extensions.cs:16:39:16:39 | t | +| extensions.cs:16:18:16:35 | StringGenericM1`1 | extensions.cs:16:49:16:49 | o | 2 | object | extensions.cs:16:49:16:49 | o | +| extensions.cs:22:27:22:40 | StaticObjectM2 | extensions.cs:22:49:22:49 | s | 0 | string | extensions.cs:22:49:22:49 | s | +| extensions.cs:30:21:30:29 | GenericM1 | extensions.cs:26:20:26:20 | t | 0 | T | extensions.cs:26:20:26:20 | t | +| extensions.cs:30:21:30:29 | GenericM1 | extensions.cs:26:20:26:20 | t | 0 | object | extensions.cs:26:20:26:20 | t | +| extensions.cs:30:21:30:29 | GenericM1 | extensions.cs:26:20:26:20 | t | 0 | string | extensions.cs:26:20:26:20 | t | +| extensions.cs:31:21:31:32 | GenericM2 | extensions.cs:26:20:26:20 | t | 0 | object | extensions.cs:26:20:26:20 | t | +| extensions.cs:31:21:31:32 | GenericM2 | extensions.cs:31:36:31:40 | other | 1 | int | extensions.cs:31:36:31:40 | other | +| extensions.cs:31:21:31:32 | GenericM2`1 | extensions.cs:26:20:26:20 | t | 0 | T | extensions.cs:26:20:26:20 | t | +| extensions.cs:31:21:31:32 | GenericM2`1 | extensions.cs:26:20:26:20 | t | 0 | object | extensions.cs:26:20:26:20 | t | +| extensions.cs:31:21:31:32 | GenericM2`1 | extensions.cs:26:20:26:20 | t | 0 | string | extensions.cs:26:20:26:20 | t | +| extensions.cs:31:21:31:32 | GenericM2`1 | extensions.cs:31:36:31:40 | other | 1 | S | extensions.cs:31:36:31:40 | other | +| extensions.cs:31:21:31:32 | GenericM2`1 | extensions.cs:31:36:31:40 | other | 1 | S | extensions.cs:31:36:31:40 | other | +| extensions.cs:31:21:31:32 | GenericM2`1 | extensions.cs:31:36:31:40 | other | 1 | S | extensions.cs:31:36:31:40 | other | +| extensions.cs:32:21:32:35 | GenericStaticM1 | extensions.cs:26:20:26:20 | t | 0 | T | extensions.cs:26:20:26:20 | t | +| extensions.cs:32:21:32:35 | GenericStaticM1 | extensions.cs:26:20:26:20 | t | 0 | object | extensions.cs:26:20:26:20 | t | +| extensions.cs:32:21:32:35 | GenericStaticM1 | extensions.cs:26:20:26:20 | t | 0 | string | extensions.cs:26:20:26:20 | t | +| extensions.cs:33:28:33:45 | GenericStaticM2`1 | extensions.cs:33:49:33:53 | other | 0 | S | extensions.cs:33:49:33:53 | other | +| extensions.cs:33:28:33:45 | GenericStaticM2`1 | extensions.cs:33:49:33:53 | other | 0 | S | extensions.cs:33:49:33:53 | other | +| extensions.cs:33:28:33:45 | GenericStaticM2`1 | extensions.cs:33:49:33:53 | other | 0 | S | extensions.cs:33:49:33:53 | other | +| extensions.cs:40:24:40:25 | M3 | extensions.cs:40:39:40:39 | s | 0 | string | extensions.cs:40:39:40:39 | s | extensionOperatorCallArgument -| extensions.cs:15:39:15:39 | * | extensions.cs:64:19:64:23 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:64:19:64:19 | 3 | -| extensions.cs:15:39:15:39 | * | extensions.cs:64:19:64:23 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:64:23:64:23 | access to local variable s | -| extensions.cs:15:39:15:39 | * | extensions.cs:78:9:78:38 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:78:34:78:34 | 3 | -| extensions.cs:15:39:15:39 | * | extensions.cs:78:9:78:38 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:78:37:78:37 | access to local variable s | +| extensions.cs:15:39:15:39 | * | extensions.cs:65:19:65:23 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:65:19:65:19 | 3 | +| extensions.cs:15:39:15:39 | * | extensions.cs:65:19:65:23 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:65:23:65:23 | access to local variable s | +| extensions.cs:15:39:15:39 | * | extensions.cs:79:9:79:38 | call to operator * | extensions.cs:15:45:15:45 | a | 0 | extensions.cs:79:34:79:34 | 3 | +| extensions.cs:15:39:15:39 | * | extensions.cs:79:9:79:38 | call to operator * | extensions.cs:15:55:15:55 | b | 1 | extensions.cs:79:37:79:37 | access to local variable s | extensionOperatorCalls -| extensions.cs:64:19:64:23 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | -| extensions.cs:78:9:78:38 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).op_Multiply | +| extensions.cs:65:19:65:23 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).op_Multiply | +| extensions.cs:79:9:79:38 | call to operator * | extensions.cs:15:39:15:39 | * | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).op_Multiply | extensionProperty -| extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:16:5 | extension(String) | -| extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | -| extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:16:5 | extension(String) | -| extensions.cs:22:28:22:37 | StaticProp | extensions.cs:18:5:23:5 | extension(Object) | -| extensions.cs:27:21:27:32 | GenericProp1 | extensions.cs:25:5:34:5 | extension(Object) | -| extensions.cs:27:21:27:32 | GenericProp1 | extensions.cs:25:5:34:5 | extension(String) | -| extensions.cs:27:21:27:32 | GenericProp1 | extensions.cs:25:5:34:5 | extension(T)`1 | -| extensions.cs:28:21:28:32 | GenericProp2 | extensions.cs:25:5:34:5 | extension(Object) | -| extensions.cs:28:21:28:32 | GenericProp2 | extensions.cs:25:5:34:5 | extension(String) | -| extensions.cs:28:21:28:32 | GenericProp2 | extensions.cs:25:5:34:5 | extension(T)`1 | +| extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:17:5 | extension(String) | +| extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:17:5 | extension(String) | +| extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:17:5 | extension(String) | +| extensions.cs:23:28:23:37 | StaticProp | extensions.cs:19:5:24:5 | extension(Object) | +| extensions.cs:28:21:28:32 | GenericProp1 | extensions.cs:26:5:35:5 | extension(Object) | +| extensions.cs:28:21:28:32 | GenericProp1 | extensions.cs:26:5:35:5 | extension(String) | +| extensions.cs:28:21:28:32 | GenericProp1 | extensions.cs:26:5:35:5 | extension(T)`1 | +| extensions.cs:29:21:29:32 | GenericProp2 | extensions.cs:26:5:35:5 | extension(Object) | +| extensions.cs:29:21:29:32 | GenericProp2 | extensions.cs:26:5:35:5 | extension(String) | +| extensions.cs:29:21:29:32 | GenericProp2 | extensions.cs:26:5:35:5 | extension(T)`1 | extensionPropertyCall -| extensions.cs:49:19:49:25 | access to property Prop1 | extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop1 | -| extensions.cs:50:19:50:25 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | -| extensions.cs:51:9:51:15 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).Prop2 | -| extensions.cs:52:19:52:36 | access to property StaticProp1 | extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:16:5 | extension(String) | MyExtensions+extension(String).StaticProp1 | -| extensions.cs:53:19:53:35 | access to property StaticProp | extensions.cs:22:28:22:37 | StaticProp | extensions.cs:18:5:23:5 | extension(Object) | MyExtensions+extension(Object).StaticProp | +| extensions.cs:50:19:50:25 | access to property Prop1 | extensions.cs:8:21:8:25 | Prop1 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).Prop1 | +| extensions.cs:51:19:51:25 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).Prop2 | +| extensions.cs:52:9:52:15 | access to property Prop2 | extensions.cs:9:21:9:25 | Prop2 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).Prop2 | +| extensions.cs:53:19:53:36 | access to property StaticProp1 | extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).StaticProp1 | +| extensions.cs:54:19:54:35 | access to property StaticProp | extensions.cs:23:28:23:37 | StaticProp | extensions.cs:19:5:24:5 | extension(Object) | MyExtensions+extension(Object).StaticProp | extensionAccessorCall -| extensions.cs:81:9:81:33 | call to extension accessor get_Prop1 | extensions.cs:8:30:8:41 | get_Prop1 | extensions.cs:8:21:8:25 | Prop1 | MyExtensions+extension(String).get_Prop1 | -| extensions.cs:82:9:82:33 | call to extension accessor get_Prop2 | extensions.cs:9:29:9:31 | get_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).get_Prop2 | -| extensions.cs:83:9:83:40 | call to extension accessor set_Prop2 | extensions.cs:9:50:9:52 | set_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).set_Prop2 | -| extensions.cs:84:9:84:37 | call to extension accessor get_StaticProp | extensions.cs:22:42:22:45 | get_StaticProp | extensions.cs:22:28:22:37 | StaticProp | MyExtensions+extension(Object).get_StaticProp | +| extensions.cs:82:9:82:33 | call to extension accessor get_Prop1 | extensions.cs:8:30:8:41 | get_Prop1 | extensions.cs:8:21:8:25 | Prop1 | MyExtensions+extension(String).get_Prop1 | +| extensions.cs:83:9:83:33 | call to extension accessor get_Prop2 | extensions.cs:9:29:9:31 | get_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).get_Prop2 | +| extensions.cs:84:9:84:40 | call to extension accessor set_Prop2 | extensions.cs:9:50:9:52 | set_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).set_Prop2 | +| extensions.cs:85:9:85:37 | call to extension accessor get_StaticProp | extensions.cs:23:42:23:45 | get_StaticProp | extensions.cs:23:28:23:37 | StaticProp | MyExtensions+extension(Object).get_StaticProp | From 42c9ac76ebbe3cf11b0519acb1ab572511eb8202 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 2 Feb 2026 10:20:37 +0100 Subject: [PATCH 25/39] C#: Support generic extensions in non-generic extension types. --- .../SymbolExtensions.cs | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs index 58aa07e9719f..c69ca53452cb 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs @@ -653,8 +653,7 @@ public static bool TryGetExtensionMethod(this IMethodSymbol method, out IMethodS .FirstOrDefault(m => SymbolEqualityComparer.Default.Equals(m.AssociatedExtensionImplementation, method.ConstructedFrom)); var isFullyConstructed = method.IsBoundGenericMethod(); - // TODO: We also need to handle generic methods in non-generic extension types. - if (isFullyConstructed && unboundDeclaration?.ContainingType is INamedTypeSymbol extensionType && extensionType.IsGenericType) + if (isFullyConstructed && unboundDeclaration?.ContainingType is INamedTypeSymbol extensionType) { try { @@ -663,7 +662,9 @@ public static bool TryGetExtensionMethod(this IMethodSymbol method, out IMethodS var (extensionTypeArguments, extensionMethodArguments) = arguments.SplitAt(extensionType.TypeParameters.Length); // Construct the extension type. - var boundExtensionType = extensionType.Construct(extensionTypeArguments.ToArray()); + var boundExtensionType = extensionType.IsUnboundGenericType() + ? extensionType.Construct(extensionTypeArguments.ToArray()) + : extensionType; // Find the extension method declaration within the constructed extension type. var extensionDeclaration = boundExtensionType.GetMembers() @@ -690,10 +691,22 @@ public static bool TryGetExtensionMethod(this IMethodSymbol method, out IMethodS return declaration is not null; } + /// + /// Returns true if this method is an unbound generic method. + /// public static bool IsUnboundGenericMethod(this IMethodSymbol method) => method.IsGenericMethod && SymbolEqualityComparer.Default.Equals(method.ConstructedFrom, method); - public static bool IsBoundGenericMethod(this IMethodSymbol method) => method.IsGenericMethod && !IsUnboundGenericMethod(method); + /// + /// Returns true if this method is a bound generic method. + /// + public static bool IsBoundGenericMethod(this IMethodSymbol method) => method.IsGenericMethod && !method.IsUnboundGenericMethod(); + + /// + /// Returns true if this type is an unbound generic type. + /// + public static bool IsUnboundGenericType(this INamedTypeSymbol type) => + type.IsGenericType && SymbolEqualityComparer.Default.Equals(type.ConstructedFrom, type); /// /// Gets the base type of `symbol`. Unlike `symbol.BaseType`, this excludes effective base From f5287eba7175387240fe7237c2831da0303302e6 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 2 Feb 2026 10:38:09 +0100 Subject: [PATCH 26/39] C#: Remove the accessor invocation expr from the DB scheme. --- csharp/ql/lib/semmlecode.csharp.dbscheme | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/csharp/ql/lib/semmlecode.csharp.dbscheme b/csharp/ql/lib/semmlecode.csharp.dbscheme index ec6a1dfb9f69..a39a96d1f33b 100644 --- a/csharp/ql/lib/semmlecode.csharp.dbscheme +++ b/csharp/ql/lib/semmlecode.csharp.dbscheme @@ -1198,8 +1198,6 @@ case @expr.kind of | 136 = @collection_expr | 137 = @spread_element_expr | 138 = @interpolated_string_insert_expr -/* C# 14.0 */ -| 139 = @accessor_invocation_expr /* Preprocessor */ | 999 = @define_symbol_expr ; @@ -1274,9 +1272,9 @@ case @expr.kind of @call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr | @delegate_invocation_expr | @object_creation_expr | @call_access_expr - | @local_function_invocation_expr | @function_pointer_invocation_expr | @accessor_invocation_expr; + | @local_function_invocation_expr | @function_pointer_invocation_expr; -@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr | @accessor_invocation_expr; +@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr; @late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr; @@ -1325,8 +1323,7 @@ dynamic_member_name( @qualifiable_expr = @member_access_expr | @method_invocation_expr - | @element_access_expr - | @accessor_invocation_expr; + | @element_access_expr; conditional_access( unique int id: @qualifiable_expr ref); From 05268737ee1d44244aaa9d94e5420478f536ad97 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 2 Feb 2026 10:39:08 +0100 Subject: [PATCH 27/39] C#: Cleanup the extractor code. --- .../Entities/Expressions/Invocation.cs | 17 +---------------- .../Semmle.Extraction.CSharp/Kinds/ExprKind.cs | 1 - 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs index a2fb9387a7af..26d64339ef06 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs @@ -26,15 +26,12 @@ private Invocation(ExpressionNodeInfo info) private bool IsOperatorCall() => Kind == ExprKind.OPERATOR_INVOCATION; - private bool IsAccessorInvocation() => Kind == ExprKind.ACCESSOR_INVOCATION; - private bool IsValidMemberAccessKind() { return Kind == ExprKind.METHOD_INVOCATION || IsEventDelegateCall() || IsExplicitDelegateInvokeCall() || - IsOperatorCall() || - IsAccessorInvocation(); + IsOperatorCall(); } protected override void PopulateExpression(TextWriter trapFile) @@ -133,14 +130,6 @@ private static bool IsOperatorLikeCall(ExpressionNodeInfo info) original!.MethodKind == MethodKind.UserDefinedOperator; } - private static bool IsAccessorLikeInvocation(ExpressionNodeInfo info) - { - return info.SymbolInfo.Symbol is IMethodSymbol method && - method.TryGetExtensionMethod(out var original) && - (original!.MethodKind == MethodKind.PropertyGet || - original!.MethodKind == MethodKind.PropertySet); - } - public IMethodSymbol? TargetSymbol { get @@ -252,10 +241,6 @@ private static ExprKind GetKind(ExpressionNodeInfo info) { return ExprKind.OPERATOR_INVOCATION; } - if (IsAccessorLikeInvocation(info)) - { - return ExprKind.ACCESSOR_INVOCATION; - } return ExprKind.METHOD_INVOCATION; } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs b/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs index 8b7edc1f208e..46a694192842 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs @@ -133,7 +133,6 @@ public enum ExprKind COLLECTION = 136, SPREAD_ELEMENT = 137, INTERPOLATED_STRING_INSERT = 138, - ACCESSOR_INVOCATION = 139, DEFINE_SYMBOL = 999, } } From 2ea588a4df1d4d87992da51eac3659471ed3f070 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 2 Feb 2026 10:39:53 +0100 Subject: [PATCH 28/39] C#: Cleaup the QL implementation. --- .../ql/lib/semmle/code/csharp/exprs/Call.qll | 52 ++++++++----------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll index ce8391c0f29b..239687a84f68 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll @@ -267,6 +267,25 @@ class Call extends Expr, @call { class MethodCall extends Call, QualifiableExpr, LateBindableExpr, @method_invocation_expr { override Method getTarget() { expr_call(this, result) } + /** + * Gets the accessor that was used to generate this method, if any. For example, the + * method call `MyExtensions.get_FirstChar(s)` on line 9 is generated from the property + * accessor `get_FirstChar` on line 3 in + * ```csharp + * static class MyExtensions { + * extension(string s) { + * public char FirstChar { get { ... } } + * } + * } + * + * class A { + * char M(string s) { + * return MyExtensions.get_FirstChar(s); + * } + * } + */ + Accessor getTargetAccessor() { expr_call(this, result) } + override Method getQualifiedDeclaration() { result = this.getTarget() } override string toString() { result = "call to method " + concat(this.getTarget().getName()) } @@ -600,8 +619,8 @@ class FunctionPointerCall extends DelegateLikeCall, @function_pointer_invocation /** * A call to an accessor. Either a property accessor call (`PropertyCall`), - * an indexer accessor call (`IndexerCall`), an event accessor call - * (`EventCall`) or an extension accessor call (`ExtensionAccessorCall`). + * an indexer accessor call (`IndexerCall`) or an event accessor call + * (`EventCall`). */ class AccessorCall extends Call, QualifiableExpr, @call_access_expr { override Accessor getTarget() { none() } @@ -611,35 +630,6 @@ class AccessorCall extends Call, QualifiableExpr, @call_access_expr { override Accessor getARuntimeTarget() { result = Call.super.getARuntimeTarget() } } -/** - * A direct call to an extension accessor, for example `MyExtensions.get_FirstChar(s)` - * in - * ```csharp - * static class MyExtensions { - * extension(string s) { - * public char FirstChar { get { ... } } - * } - * } - * - * class A { - * char M(string s) { - * return MyExtensions.get_FirstChar(s); - * } - * } - * ``` - */ -class ExtensionAccessorCall extends AccessorCall, @accessor_invocation_expr { - override Accessor getTarget() { expr_call(this, result) } - - override Expr getArgument(int i) { result = this.getChild(i) and i >= 0 } - - override string toString() { - result = "call to extension accessor " + concat(this.getTarget().getName()) - } - - override string getAPrimaryQlClass() { result = "ExtensionAccessorCall" } -} - /** * A call to a property accessor, for example the call to `get_P` on * line 5 in From e2c0623709427a1b0f2559f7d3ff2eeaa140af4e Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 2 Feb 2026 10:41:28 +0100 Subject: [PATCH 29/39] C#: Update tests and test expected output. --- .../ql/test/library-tests/extension/extensions.expected | 8 ++++---- csharp/ql/test/library-tests/extension/extensions.ql | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/csharp/ql/test/library-tests/extension/extensions.expected b/csharp/ql/test/library-tests/extension/extensions.expected index c892ff08947b..c3b268b1aa2f 100644 --- a/csharp/ql/test/library-tests/extension/extensions.expected +++ b/csharp/ql/test/library-tests/extension/extensions.expected @@ -105,7 +105,7 @@ extensionPropertyCall | extensions.cs:53:19:53:36 | access to property StaticProp1 | extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).StaticProp1 | | extensions.cs:54:19:54:35 | access to property StaticProp | extensions.cs:23:28:23:37 | StaticProp | extensions.cs:19:5:24:5 | extension(Object) | MyExtensions+extension(Object).StaticProp | extensionAccessorCall -| extensions.cs:82:9:82:33 | call to extension accessor get_Prop1 | extensions.cs:8:30:8:41 | get_Prop1 | extensions.cs:8:21:8:25 | Prop1 | MyExtensions+extension(String).get_Prop1 | -| extensions.cs:83:9:83:33 | call to extension accessor get_Prop2 | extensions.cs:9:29:9:31 | get_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).get_Prop2 | -| extensions.cs:84:9:84:40 | call to extension accessor set_Prop2 | extensions.cs:9:50:9:52 | set_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).set_Prop2 | -| extensions.cs:85:9:85:37 | call to extension accessor get_StaticProp | extensions.cs:23:42:23:45 | get_StaticProp | extensions.cs:23:28:23:37 | StaticProp | MyExtensions+extension(Object).get_StaticProp | +| extensions.cs:82:9:82:33 | call to method | extensions.cs:8:30:8:41 | get_Prop1 | extensions.cs:8:21:8:25 | Prop1 | MyExtensions+extension(String).get_Prop1 | +| extensions.cs:83:9:83:33 | call to method | extensions.cs:9:29:9:31 | get_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).get_Prop2 | +| extensions.cs:84:9:84:40 | call to method | extensions.cs:9:50:9:52 | set_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).set_Prop2 | +| extensions.cs:85:9:85:37 | call to method | extensions.cs:23:42:23:45 | get_StaticProp | extensions.cs:23:28:23:37 | StaticProp | MyExtensions+extension(Object).get_StaticProp | diff --git a/csharp/ql/test/library-tests/extension/extensions.ql b/csharp/ql/test/library-tests/extension/extensions.ql index 23d01d4848c3..03830c5851d0 100644 --- a/csharp/ql/test/library-tests/extension/extensions.ql +++ b/csharp/ql/test/library-tests/extension/extensions.ql @@ -60,10 +60,10 @@ query predicate extensionPropertyCall( } query predicate extensionAccessorCall( - ExtensionAccessorCall ac, ExtensionAccessor a, ExtensionProperty p, string type + MethodCall m, ExtensionAccessor a, ExtensionProperty p, string type ) { p.getFile().getBaseName() = "extensions.cs" and (a.(Getter).getDeclaration() = p or a.(Setter).getDeclaration() = p) and - ac.getTarget() = a and + m.getTargetAccessor() = a and a.getFullyQualifiedNameDebug() = type } From 10918c7b15d0ab0796903d371fcb5a24f95b02a9 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 2 Feb 2026 11:02:10 +0100 Subject: [PATCH 30/39] C#: Update the printing of extension accessor calls. --- csharp/ql/lib/semmle/code/csharp/exprs/Call.qll | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll index 239687a84f68..1d30bf7a9e71 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll @@ -288,7 +288,11 @@ class MethodCall extends Call, QualifiableExpr, LateBindableExpr, @method_invoca override Method getQualifiedDeclaration() { result = this.getTarget() } - override string toString() { result = "call to method " + concat(this.getTarget().getName()) } + override string toString() { + if exists(this.getTargetAccessor()) + then result = "call to extension accessor " + concat(this.getTargetAccessor().getName()) + else result = "call to method " + concat(this.getTarget().getName()) + } override string getAPrimaryQlClass() { result = "MethodCall" } From 47814fe961616113079e9966acf5e6da92fc0bde Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 2 Feb 2026 11:04:20 +0100 Subject: [PATCH 31/39] C#: Update test expected output. --- .../ql/test/library-tests/extension/extensions.expected | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/csharp/ql/test/library-tests/extension/extensions.expected b/csharp/ql/test/library-tests/extension/extensions.expected index c3b268b1aa2f..c892ff08947b 100644 --- a/csharp/ql/test/library-tests/extension/extensions.expected +++ b/csharp/ql/test/library-tests/extension/extensions.expected @@ -105,7 +105,7 @@ extensionPropertyCall | extensions.cs:53:19:53:36 | access to property StaticProp1 | extensions.cs:10:28:10:38 | StaticProp1 | extensions.cs:6:5:17:5 | extension(String) | MyExtensions+extension(String).StaticProp1 | | extensions.cs:54:19:54:35 | access to property StaticProp | extensions.cs:23:28:23:37 | StaticProp | extensions.cs:19:5:24:5 | extension(Object) | MyExtensions+extension(Object).StaticProp | extensionAccessorCall -| extensions.cs:82:9:82:33 | call to method | extensions.cs:8:30:8:41 | get_Prop1 | extensions.cs:8:21:8:25 | Prop1 | MyExtensions+extension(String).get_Prop1 | -| extensions.cs:83:9:83:33 | call to method | extensions.cs:9:29:9:31 | get_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).get_Prop2 | -| extensions.cs:84:9:84:40 | call to method | extensions.cs:9:50:9:52 | set_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).set_Prop2 | -| extensions.cs:85:9:85:37 | call to method | extensions.cs:23:42:23:45 | get_StaticProp | extensions.cs:23:28:23:37 | StaticProp | MyExtensions+extension(Object).get_StaticProp | +| extensions.cs:82:9:82:33 | call to extension accessor get_Prop1 | extensions.cs:8:30:8:41 | get_Prop1 | extensions.cs:8:21:8:25 | Prop1 | MyExtensions+extension(String).get_Prop1 | +| extensions.cs:83:9:83:33 | call to extension accessor get_Prop2 | extensions.cs:9:29:9:31 | get_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).get_Prop2 | +| extensions.cs:84:9:84:40 | call to extension accessor set_Prop2 | extensions.cs:9:50:9:52 | set_Prop2 | extensions.cs:9:21:9:25 | Prop2 | MyExtensions+extension(String).set_Prop2 | +| extensions.cs:85:9:85:37 | call to extension accessor get_StaticProp | extensions.cs:23:42:23:45 | get_StaticProp | extensions.cs:23:28:23:37 | StaticProp | MyExtensions+extension(Object).get_StaticProp | From a92ade67a9900b8b64b1f8f00a8e1eed5fa615e3 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 3 Feb 2026 09:25:58 +0100 Subject: [PATCH 32/39] C#: Replace extension type parameter access with synthetic parameter access. --- .../lib/semmle/code/csharp/exprs/Access.qll | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll index eafc4fac491f..614b82f30d98 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll @@ -223,6 +223,42 @@ class ParameterAccess extends LocalScopeVariableAccess, @parameter_access_expr { override string getAPrimaryQlClass() { result = "ParameterAccess" } } +/** + * An access to a synthetic parameter for an extension method, for example the + * access to `s` on line 3 in + * + * ```csharp + * static class MyExtensions { + * extension(string s) { + * public bool IsEmpty() { return s == string.Empty; } + * } + * } + * ``` + */ +class SyntheticParameterAccess extends ParameterAccess { + private Parameter p; + + SyntheticParameterAccess() { + exists(ExtensionType et | + p = et.getReceiverParameter() and + expr_access(this, p) + ) + } + + override Parameter getTarget() { + exists(ExtensionCallable c | + this.getEnclosingCallable+() = c and + result = c.getParameter(0) + ) + } + + override string toString() { + result = "access to synthetic parameter " + this.getTarget().getName() + } + + override string getAPrimaryQlClass() { result = "SyntheticParameterAccess" } +} + /** * An access to a parameter that reads the underlying value, for example * the access to `p` on line 2 in From 7c3126f96a76c2330e400926c98b5f327d1f67bc Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 3 Feb 2026 13:33:54 +0100 Subject: [PATCH 33/39] C#: Shift arguments to extension property calls as the qualifier is an explicit argument to the getter/setter. --- csharp/ql/lib/semmle/code/csharp/exprs/Call.qll | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll index 1d30bf7a9e71..8a6576cf5d6b 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll @@ -722,7 +722,21 @@ class IndexerCall extends AccessorCall, IndexerAccessExpr { * ``` */ class ExtensionPropertyCall extends PropertyCall { - ExtensionPropertyCall() { this.getProperty() instanceof ExtensionProperty } + private ExtensionProperty prop; + + ExtensionPropertyCall() { this.getProperty() = prop } + + override Expr getArgument(int i) { + if prop.isStatic() + then result = super.getArgument(i) + else ( + // Shift arguments as the qualifier is an explicit argument in the getter/setter. + i = 0 and + result = this.getQualifier() + or + result = super.getArgument(i - 1) + ) + } override string getAPrimaryQlClass() { result = "ExtensionPropertyCall" } } From 8136deb47310abeb7bfe03b9db785afe738cf3fd Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 3 Feb 2026 13:34:44 +0100 Subject: [PATCH 34/39] C#: Add dispatching logic for direct (method) accessor calls. --- .../semmle/code/csharp/dispatch/Dispatch.qll | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll index c61ad0f2a2a9..999c6277a490 100644 --- a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll @@ -87,7 +87,8 @@ private module Internal { newtype TDispatchCall = TDispatchMethodCall(MethodCall mc) { not isReflectionCall(mc, _, _, _, _) and - not mc.isLateBound() + not mc.isLateBound() and + not isExtensionAccossorCall(mc) } or TDispatchAccessorCall(AccessorCall ac) or TDispatchOperatorCall(OperatorCall oc) { not oc.isLateBound() } or @@ -110,7 +111,8 @@ private module Internal { c instanceof ConstructorInitializer or c instanceof LocalFunctionCall - } + } or + TDispatchExtensionAccessorCall(MethodCall mc) { isExtensionAccossorCall(mc) } cached Expr getCall(DispatchCall dc) { result = dc.(DispatchCallImpl).getCall() } @@ -142,6 +144,10 @@ private module Internal { import Cached + private predicate isExtensionAccossorCall(MethodCall mc) { + exists(ExtensionAccessor a | mc.getTargetAccessor() = a) + } + /** * Holds if `mc` is a reflection call to a method named `name`, where * `object` is the object on which to invoke the method (`null` if a @@ -819,6 +825,33 @@ private module Internal { override Method getAStaticTarget() { result = this.getCall().getTarget() } } + /** + * A call to an extension accessor method. + */ + private class DispatchExtensionAccessorCall extends DispatchCallImpl, + TDispatchExtensionAccessorCall + { + override MethodCall getCall() { this = TDispatchExtensionAccessorCall(result) } + + private Expr getArgumentForParameter(Parameter p) { + this.getCall().getTargetAccessor().getAParameter() = p and + result = this.getCall().getArgument(p.getPosition()) + } + + override Expr getArgument(int i) { + exists(MethodCall call, Parameter p | call = this.getCall() | + p = call.getTargetAccessor().getParameter(i) and + result = this.getArgumentForParameter(p) + ) + } + + override Expr getQualifier() { result = this.getCall().getQualifier() } + + override Accessor getAStaticTarget() { result = this.getCall().getTargetAccessor() } + + override RuntimeCallable getADynamicTarget() { result = this.getAStaticTarget() } + } + /** * An ordinary operator call. * From c0b1d7b8e16371e3e1c2869ca0f89d900964a0ca Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 3 Feb 2026 13:51:14 +0100 Subject: [PATCH 35/39] C#: Add extensions data flow tests. --- .../extensions/ExtensionFlow.expected | 502 ++++++++++++++++++ .../dataflow/extensions/ExtensionFlow.ql | 12 + .../dataflow/extensions/extensions.cs | 243 +++++++++ 3 files changed, 757 insertions(+) create mode 100644 csharp/ql/test/library-tests/dataflow/extensions/ExtensionFlow.expected create mode 100644 csharp/ql/test/library-tests/dataflow/extensions/ExtensionFlow.ql create mode 100644 csharp/ql/test/library-tests/dataflow/extensions/extensions.cs diff --git a/csharp/ql/test/library-tests/dataflow/extensions/ExtensionFlow.expected b/csharp/ql/test/library-tests/dataflow/extensions/ExtensionFlow.expected new file mode 100644 index 000000000000..cce3fd86550d --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/extensions/ExtensionFlow.expected @@ -0,0 +1,502 @@ +models +edges +| extensions.cs:5:22:5:24 | obj : B | extensions.cs:23:24:23:26 | access to synthetic parameter obj | provenance | | +| extensions.cs:5:22:5:24 | obj : B | extensions.cs:23:24:23:26 | access to synthetic parameter obj | provenance | | +| extensions.cs:5:22:5:24 | obj : B | extensions.cs:28:24:28:26 | access to synthetic parameter obj | provenance | | +| extensions.cs:5:22:5:24 | obj : B | extensions.cs:28:24:28:26 | access to synthetic parameter obj | provenance | | +| extensions.cs:5:22:5:24 | obj : B | extensions.cs:51:20:51:22 | access to synthetic parameter obj | provenance | | +| extensions.cs:5:22:5:24 | obj : B | extensions.cs:51:20:51:22 | access to synthetic parameter obj | provenance | | +| extensions.cs:11:24:11:37 | call to method Source : B | extensions.cs:108:18:108:26 | access to property Prop1 : B | provenance | | +| extensions.cs:11:24:11:37 | call to method Source : B | extensions.cs:108:18:108:26 | access to property Prop1 : B | provenance | | +| extensions.cs:11:24:11:37 | call to method Source : B | extensions.cs:111:18:111:44 | call to extension accessor get_Prop1 : B | provenance | | +| extensions.cs:11:24:11:37 | call to method Source : B | extensions.cs:111:18:111:44 | call to extension accessor get_Prop1 : B | provenance | | +| extensions.cs:13:13:13:15 | value : B | extensions.cs:15:24:15:28 | access to parameter value | provenance | | +| extensions.cs:13:13:13:15 | value : B | extensions.cs:15:24:15:28 | access to parameter value | provenance | | +| extensions.cs:36:24:36:38 | call to method Source : B | extensions.cs:194:18:194:35 | access to property StaticProp1 : B | provenance | | +| extensions.cs:36:24:36:38 | call to method Source : B | extensions.cs:194:18:194:35 | access to property StaticProp1 : B | provenance | | +| extensions.cs:36:24:36:38 | call to method Source : B | extensions.cs:197:18:197:47 | call to extension accessor get_StaticProp1 : B | provenance | | +| extensions.cs:36:24:36:38 | call to method Source : B | extensions.cs:197:18:197:47 | call to extension accessor get_StaticProp1 : B | provenance | | +| extensions.cs:38:13:38:15 | value : B | extensions.cs:40:24:40:28 | access to parameter value | provenance | | +| extensions.cs:38:13:38:15 | value : B | extensions.cs:40:24:40:28 | access to parameter value | provenance | | +| extensions.cs:46:20:46:33 | call to method Source : B | extensions.cs:127:18:127:25 | call to method M1 : B | provenance | | +| extensions.cs:46:20:46:33 | call to method Source : B | extensions.cs:127:18:127:25 | call to method M1 : B | provenance | | +| extensions.cs:46:20:46:33 | call to method Source : B | extensions.cs:130:18:130:37 | call to method M1 : B | provenance | | +| extensions.cs:46:20:46:33 | call to method Source : B | extensions.cs:130:18:130:37 | call to method M1 : B | provenance | | +| extensions.cs:59:48:59:48 | a : B | extensions.cs:61:20:61:20 | access to parameter a | provenance | | +| extensions.cs:59:48:59:48 | a : B | extensions.cs:61:20:61:20 | access to parameter a | provenance | | +| extensions.cs:67:20:67:33 | call to method Source : B | extensions.cs:185:18:185:24 | call to operator - : B | provenance | | +| extensions.cs:67:20:67:33 | call to method Source : B | extensions.cs:185:18:185:24 | call to operator - : B | provenance | | +| extensions.cs:67:20:67:33 | call to method Source : B | extensions.cs:188:18:188:52 | call to operator - : B | provenance | | +| extensions.cs:67:20:67:33 | call to method Source : B | extensions.cs:188:18:188:52 | call to operator - : B | provenance | | +| extensions.cs:76:17:76:17 | b : B | extensions.cs:80:20:80:20 | access to synthetic parameter b : B | provenance | | +| extensions.cs:76:17:76:17 | b : B | extensions.cs:80:20:80:20 | access to synthetic parameter b : B | provenance | | +| extensions.cs:76:17:76:17 | b : B | extensions.cs:85:20:85:20 | access to synthetic parameter b | provenance | | +| extensions.cs:76:17:76:17 | b : B | extensions.cs:85:20:85:20 | access to synthetic parameter b | provenance | | +| extensions.cs:89:20:89:20 | t : B | extensions.cs:93:20:93:20 | access to synthetic parameter t | provenance | | +| extensions.cs:89:20:89:20 | t : B | extensions.cs:93:20:93:20 | access to synthetic parameter t | provenance | | +| extensions.cs:96:33:96:37 | other : B | extensions.cs:98:20:98:24 | access to parameter other : B | provenance | | +| extensions.cs:96:33:96:37 | other : B | extensions.cs:98:20:98:24 | access to parameter other : B | provenance | | +| extensions.cs:108:13:108:14 | access to local variable b1 : B | extensions.cs:109:14:109:15 | access to local variable b1 | provenance | | +| extensions.cs:108:13:108:14 | access to local variable b1 : B | extensions.cs:109:14:109:15 | access to local variable b1 | provenance | | +| extensions.cs:108:18:108:26 | access to property Prop1 : B | extensions.cs:108:13:108:14 | access to local variable b1 : B | provenance | | +| extensions.cs:108:18:108:26 | access to property Prop1 : B | extensions.cs:108:13:108:14 | access to local variable b1 : B | provenance | | +| extensions.cs:111:13:111:14 | access to local variable b2 : B | extensions.cs:112:14:112:15 | access to local variable b2 | provenance | | +| extensions.cs:111:13:111:14 | access to local variable b2 : B | extensions.cs:112:14:112:15 | access to local variable b2 | provenance | | +| extensions.cs:111:18:111:44 | call to extension accessor get_Prop1 : B | extensions.cs:111:13:111:14 | access to local variable b2 : B | provenance | | +| extensions.cs:111:18:111:44 | call to extension accessor get_Prop1 : B | extensions.cs:111:13:111:14 | access to local variable b2 : B | provenance | | +| extensions.cs:118:21:118:32 | call to method Source : B | extensions.cs:13:13:13:15 | value : B | provenance | | +| extensions.cs:118:21:118:32 | call to method Source : B | extensions.cs:13:13:13:15 | value : B | provenance | | +| extensions.cs:120:13:120:13 | access to local variable b : B | extensions.cs:121:37:121:37 | access to local variable b : B | provenance | | +| extensions.cs:120:13:120:13 | access to local variable b : B | extensions.cs:121:37:121:37 | access to local variable b : B | provenance | | +| extensions.cs:120:17:120:30 | call to method Source : B | extensions.cs:120:13:120:13 | access to local variable b : B | provenance | | +| extensions.cs:120:17:120:30 | call to method Source : B | extensions.cs:120:13:120:13 | access to local variable b : B | provenance | | +| extensions.cs:121:37:121:37 | access to local variable b : B | extensions.cs:13:13:13:15 | value : B | provenance | | +| extensions.cs:121:37:121:37 | access to local variable b : B | extensions.cs:13:13:13:15 | value : B | provenance | | +| extensions.cs:127:13:127:14 | access to local variable b1 : B | extensions.cs:128:14:128:15 | access to local variable b1 | provenance | | +| extensions.cs:127:13:127:14 | access to local variable b1 : B | extensions.cs:128:14:128:15 | access to local variable b1 | provenance | | +| extensions.cs:127:18:127:25 | call to method M1 : B | extensions.cs:127:13:127:14 | access to local variable b1 : B | provenance | | +| extensions.cs:127:18:127:25 | call to method M1 : B | extensions.cs:127:13:127:14 | access to local variable b1 : B | provenance | | +| extensions.cs:130:13:130:14 | access to local variable b2 : B | extensions.cs:131:14:131:15 | access to local variable b2 | provenance | | +| extensions.cs:130:13:130:14 | access to local variable b2 : B | extensions.cs:131:14:131:15 | access to local variable b2 | provenance | | +| extensions.cs:130:18:130:37 | call to method M1 : B | extensions.cs:130:13:130:14 | access to local variable b2 : B | provenance | | +| extensions.cs:130:18:130:37 | call to method M1 : B | extensions.cs:130:13:130:14 | access to local variable b2 : B | provenance | | +| extensions.cs:136:13:136:14 | access to local variable b1 : B | extensions.cs:137:9:137:10 | access to local variable b1 : B | provenance | | +| extensions.cs:136:13:136:14 | access to local variable b1 : B | extensions.cs:137:9:137:10 | access to local variable b1 : B | provenance | | +| extensions.cs:136:18:136:29 | call to method Source : B | extensions.cs:136:13:136:14 | access to local variable b1 : B | provenance | | +| extensions.cs:136:18:136:29 | call to method Source : B | extensions.cs:136:13:136:14 | access to local variable b1 : B | provenance | | +| extensions.cs:137:9:137:10 | access to local variable b1 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:137:9:137:10 | access to local variable b1 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:139:13:139:14 | access to local variable b2 : B | extensions.cs:140:25:140:26 | access to local variable b2 : B | provenance | | +| extensions.cs:139:13:139:14 | access to local variable b2 : B | extensions.cs:140:25:140:26 | access to local variable b2 : B | provenance | | +| extensions.cs:139:18:139:31 | call to method Source : B | extensions.cs:139:13:139:14 | access to local variable b2 : B | provenance | | +| extensions.cs:139:18:139:31 | call to method Source : B | extensions.cs:139:13:139:14 | access to local variable b2 : B | provenance | | +| extensions.cs:140:25:140:26 | access to local variable b2 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:140:25:140:26 | access to local variable b2 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:145:13:145:14 | access to local variable b1 : B | extensions.cs:146:18:146:19 | access to local variable b1 : B | provenance | | +| extensions.cs:145:13:145:14 | access to local variable b1 : B | extensions.cs:146:18:146:19 | access to local variable b1 : B | provenance | | +| extensions.cs:145:13:145:14 | access to local variable b1 : B | extensions.cs:149:34:149:35 | access to local variable b1 : B | provenance | | +| extensions.cs:145:13:145:14 | access to local variable b1 : B | extensions.cs:149:34:149:35 | access to local variable b1 : B | provenance | | +| extensions.cs:145:18:145:29 | call to method Source : B | extensions.cs:145:13:145:14 | access to local variable b1 : B | provenance | | +| extensions.cs:145:18:145:29 | call to method Source : B | extensions.cs:145:13:145:14 | access to local variable b1 : B | provenance | | +| extensions.cs:146:13:146:14 | access to local variable b2 : B | extensions.cs:147:14:147:15 | access to local variable b2 | provenance | | +| extensions.cs:146:13:146:14 | access to local variable b2 : B | extensions.cs:147:14:147:15 | access to local variable b2 | provenance | | +| extensions.cs:146:18:146:19 | access to local variable b1 : B | extensions.cs:76:17:76:17 | b : B | provenance | | +| extensions.cs:146:18:146:19 | access to local variable b1 : B | extensions.cs:76:17:76:17 | b : B | provenance | | +| extensions.cs:146:18:146:19 | access to local variable b1 : B | extensions.cs:146:18:146:24 | call to method B1 : B | provenance | | +| extensions.cs:146:18:146:19 | access to local variable b1 : B | extensions.cs:146:18:146:24 | call to method B1 : B | provenance | | +| extensions.cs:146:18:146:24 | call to method B1 : B | extensions.cs:146:13:146:14 | access to local variable b2 : B | provenance | | +| extensions.cs:146:18:146:24 | call to method B1 : B | extensions.cs:146:13:146:14 | access to local variable b2 : B | provenance | | +| extensions.cs:149:13:149:14 | access to local variable b3 : B | extensions.cs:150:14:150:15 | access to local variable b3 | provenance | | +| extensions.cs:149:13:149:14 | access to local variable b3 : B | extensions.cs:150:14:150:15 | access to local variable b3 | provenance | | +| extensions.cs:149:18:149:36 | call to method B1 : B | extensions.cs:149:13:149:14 | access to local variable b3 : B | provenance | | +| extensions.cs:149:18:149:36 | call to method B1 : B | extensions.cs:149:13:149:14 | access to local variable b3 : B | provenance | | +| extensions.cs:149:34:149:35 | access to local variable b1 : B | extensions.cs:76:17:76:17 | b : B | provenance | | +| extensions.cs:149:34:149:35 | access to local variable b1 : B | extensions.cs:76:17:76:17 | b : B | provenance | | +| extensions.cs:149:34:149:35 | access to local variable b1 : B | extensions.cs:149:18:149:36 | call to method B1 : B | provenance | | +| extensions.cs:149:34:149:35 | access to local variable b1 : B | extensions.cs:149:18:149:36 | call to method B1 : B | provenance | | +| extensions.cs:155:13:155:14 | access to local variable b1 : B | extensions.cs:156:18:156:19 | access to local variable b1 : B | provenance | | +| extensions.cs:155:13:155:14 | access to local variable b1 : B | extensions.cs:156:18:156:19 | access to local variable b1 : B | provenance | | +| extensions.cs:155:18:155:29 | call to method Source : B | extensions.cs:155:13:155:14 | access to local variable b1 : B | provenance | | +| extensions.cs:155:18:155:29 | call to method Source : B | extensions.cs:155:13:155:14 | access to local variable b1 : B | provenance | | +| extensions.cs:156:18:156:19 | access to local variable b1 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:156:18:156:19 | access to local variable b1 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:158:13:158:14 | access to local variable b3 : B | extensions.cs:159:41:159:42 | access to local variable b3 : B | provenance | | +| extensions.cs:158:13:158:14 | access to local variable b3 : B | extensions.cs:159:41:159:42 | access to local variable b3 : B | provenance | | +| extensions.cs:158:18:158:31 | call to method Source : B | extensions.cs:158:13:158:14 | access to local variable b3 : B | provenance | | +| extensions.cs:158:18:158:31 | call to method Source : B | extensions.cs:158:13:158:14 | access to local variable b3 : B | provenance | | +| extensions.cs:159:41:159:42 | access to local variable b3 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:159:41:159:42 | access to local variable b3 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:164:13:164:14 | access to local variable b1 : B | extensions.cs:165:9:165:10 | access to local variable b1 : B | provenance | | +| extensions.cs:164:13:164:14 | access to local variable b1 : B | extensions.cs:165:9:165:10 | access to local variable b1 : B | provenance | | +| extensions.cs:164:18:164:29 | call to method Source : B | extensions.cs:164:13:164:14 | access to local variable b1 : B | provenance | | +| extensions.cs:164:18:164:29 | call to method Source : B | extensions.cs:164:13:164:14 | access to local variable b1 : B | provenance | | +| extensions.cs:165:9:165:10 | access to local variable b1 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:165:9:165:10 | access to local variable b1 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:167:13:167:14 | access to local variable b2 : B | extensions.cs:168:32:168:33 | access to local variable b2 : B | provenance | | +| extensions.cs:167:13:167:14 | access to local variable b2 : B | extensions.cs:168:32:168:33 | access to local variable b2 : B | provenance | | +| extensions.cs:167:18:167:31 | call to method Source : B | extensions.cs:167:13:167:14 | access to local variable b2 : B | provenance | | +| extensions.cs:167:18:167:31 | call to method Source : B | extensions.cs:167:13:167:14 | access to local variable b2 : B | provenance | | +| extensions.cs:168:32:168:33 | access to local variable b2 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:168:32:168:33 | access to local variable b2 : B | extensions.cs:5:22:5:24 | obj : B | provenance | | +| extensions.cs:173:13:173:14 | access to local variable b1 : B | extensions.cs:175:18:175:19 | access to local variable b1 : B | provenance | | +| extensions.cs:173:13:173:14 | access to local variable b1 : B | extensions.cs:175:18:175:19 | access to local variable b1 : B | provenance | | +| extensions.cs:173:18:173:29 | call to method Source : B | extensions.cs:173:13:173:14 | access to local variable b1 : B | provenance | | +| extensions.cs:173:18:173:29 | call to method Source : B | extensions.cs:173:13:173:14 | access to local variable b1 : B | provenance | | +| extensions.cs:175:18:175:19 | access to local variable b1 : B | extensions.cs:59:48:59:48 | a : B | provenance | | +| extensions.cs:175:18:175:19 | access to local variable b1 : B | extensions.cs:59:48:59:48 | a : B | provenance | | +| extensions.cs:177:13:177:14 | access to local variable b4 : B | extensions.cs:178:43:178:44 | access to local variable b4 : B | provenance | | +| extensions.cs:177:13:177:14 | access to local variable b4 : B | extensions.cs:178:43:178:44 | access to local variable b4 : B | provenance | | +| extensions.cs:177:18:177:31 | call to method Source : B | extensions.cs:177:13:177:14 | access to local variable b4 : B | provenance | | +| extensions.cs:177:18:177:31 | call to method Source : B | extensions.cs:177:13:177:14 | access to local variable b4 : B | provenance | | +| extensions.cs:178:43:178:44 | access to local variable b4 : B | extensions.cs:59:48:59:48 | a : B | provenance | | +| extensions.cs:178:43:178:44 | access to local variable b4 : B | extensions.cs:59:48:59:48 | a : B | provenance | | +| extensions.cs:185:13:185:14 | access to local variable b3 : B | extensions.cs:186:14:186:15 | access to local variable b3 | provenance | | +| extensions.cs:185:13:185:14 | access to local variable b3 : B | extensions.cs:186:14:186:15 | access to local variable b3 | provenance | | +| extensions.cs:185:18:185:24 | call to operator - : B | extensions.cs:185:13:185:14 | access to local variable b3 : B | provenance | | +| extensions.cs:185:18:185:24 | call to operator - : B | extensions.cs:185:13:185:14 | access to local variable b3 : B | provenance | | +| extensions.cs:188:13:188:14 | access to local variable b4 : B | extensions.cs:189:14:189:15 | access to local variable b4 | provenance | | +| extensions.cs:188:13:188:14 | access to local variable b4 : B | extensions.cs:189:14:189:15 | access to local variable b4 | provenance | | +| extensions.cs:188:18:188:52 | call to operator - : B | extensions.cs:188:13:188:14 | access to local variable b4 : B | provenance | | +| extensions.cs:188:18:188:52 | call to operator - : B | extensions.cs:188:13:188:14 | access to local variable b4 : B | provenance | | +| extensions.cs:194:13:194:14 | access to local variable b1 : B | extensions.cs:195:14:195:15 | access to local variable b1 | provenance | | +| extensions.cs:194:13:194:14 | access to local variable b1 : B | extensions.cs:195:14:195:15 | access to local variable b1 | provenance | | +| extensions.cs:194:18:194:35 | access to property StaticProp1 : B | extensions.cs:194:13:194:14 | access to local variable b1 : B | provenance | | +| extensions.cs:194:18:194:35 | access to property StaticProp1 : B | extensions.cs:194:13:194:14 | access to local variable b1 : B | provenance | | +| extensions.cs:197:13:197:14 | access to local variable b2 : B | extensions.cs:198:14:198:15 | access to local variable b2 | provenance | | +| extensions.cs:197:13:197:14 | access to local variable b2 : B | extensions.cs:198:14:198:15 | access to local variable b2 | provenance | | +| extensions.cs:197:18:197:47 | call to extension accessor get_StaticProp1 : B | extensions.cs:197:13:197:14 | access to local variable b2 : B | provenance | | +| extensions.cs:197:18:197:47 | call to extension accessor get_StaticProp1 : B | extensions.cs:197:13:197:14 | access to local variable b2 : B | provenance | | +| extensions.cs:203:13:203:14 | access to local variable b1 : B | extensions.cs:204:30:204:31 | access to local variable b1 : B | provenance | | +| extensions.cs:203:13:203:14 | access to local variable b1 : B | extensions.cs:204:30:204:31 | access to local variable b1 : B | provenance | | +| extensions.cs:203:18:203:30 | call to method Source : B | extensions.cs:203:13:203:14 | access to local variable b1 : B | provenance | | +| extensions.cs:203:18:203:30 | call to method Source : B | extensions.cs:203:13:203:14 | access to local variable b1 : B | provenance | | +| extensions.cs:204:30:204:31 | access to local variable b1 : B | extensions.cs:38:13:38:15 | value : B | provenance | | +| extensions.cs:204:30:204:31 | access to local variable b1 : B | extensions.cs:38:13:38:15 | value : B | provenance | | +| extensions.cs:206:13:206:14 | access to local variable b2 : B | extensions.cs:207:38:207:39 | access to local variable b2 : B | provenance | | +| extensions.cs:206:13:206:14 | access to local variable b2 : B | extensions.cs:207:38:207:39 | access to local variable b2 : B | provenance | | +| extensions.cs:206:18:206:31 | call to method Source : B | extensions.cs:206:13:206:14 | access to local variable b2 : B | provenance | | +| extensions.cs:206:18:206:31 | call to method Source : B | extensions.cs:206:13:206:14 | access to local variable b2 : B | provenance | | +| extensions.cs:207:38:207:39 | access to local variable b2 : B | extensions.cs:38:13:38:15 | value : B | provenance | | +| extensions.cs:207:38:207:39 | access to local variable b2 : B | extensions.cs:38:13:38:15 | value : B | provenance | | +| extensions.cs:212:13:212:14 | access to local variable b1 : B | extensions.cs:213:9:213:10 | access to local variable b1 : B | provenance | | +| extensions.cs:212:13:212:14 | access to local variable b1 : B | extensions.cs:213:9:213:10 | access to local variable b1 : B | provenance | | +| extensions.cs:212:18:212:30 | call to method Source : B | extensions.cs:212:13:212:14 | access to local variable b1 : B | provenance | | +| extensions.cs:212:18:212:30 | call to method Source : B | extensions.cs:212:13:212:14 | access to local variable b1 : B | provenance | | +| extensions.cs:213:9:213:10 | access to local variable b1 : B | extensions.cs:76:17:76:17 | b : B | provenance | | +| extensions.cs:213:9:213:10 | access to local variable b1 : B | extensions.cs:76:17:76:17 | b : B | provenance | | +| extensions.cs:215:13:215:14 | access to local variable b2 : B | extensions.cs:216:25:216:26 | access to local variable b2 : B | provenance | | +| extensions.cs:215:13:215:14 | access to local variable b2 : B | extensions.cs:216:25:216:26 | access to local variable b2 : B | provenance | | +| extensions.cs:215:18:215:31 | call to method Source : B | extensions.cs:215:13:215:14 | access to local variable b2 : B | provenance | | +| extensions.cs:215:18:215:31 | call to method Source : B | extensions.cs:215:13:215:14 | access to local variable b2 : B | provenance | | +| extensions.cs:216:25:216:26 | access to local variable b2 : B | extensions.cs:76:17:76:17 | b : B | provenance | | +| extensions.cs:216:25:216:26 | access to local variable b2 : B | extensions.cs:76:17:76:17 | b : B | provenance | | +| extensions.cs:221:13:221:14 | access to local variable b1 : B | extensions.cs:222:9:222:10 | access to local variable b1 : B | provenance | | +| extensions.cs:221:13:221:14 | access to local variable b1 : B | extensions.cs:222:9:222:10 | access to local variable b1 : B | provenance | | +| extensions.cs:221:18:221:30 | call to method Source : B | extensions.cs:221:13:221:14 | access to local variable b1 : B | provenance | | +| extensions.cs:221:18:221:30 | call to method Source : B | extensions.cs:221:13:221:14 | access to local variable b1 : B | provenance | | +| extensions.cs:222:9:222:10 | access to local variable b1 : B | extensions.cs:89:20:89:20 | t : B | provenance | | +| extensions.cs:222:9:222:10 | access to local variable b1 : B | extensions.cs:89:20:89:20 | t : B | provenance | | +| extensions.cs:224:13:224:14 | access to local variable b2 : B | extensions.cs:225:32:225:33 | access to local variable b2 : B | provenance | | +| extensions.cs:224:13:224:14 | access to local variable b2 : B | extensions.cs:225:32:225:33 | access to local variable b2 : B | provenance | | +| extensions.cs:224:18:224:31 | call to method Source : B | extensions.cs:224:13:224:14 | access to local variable b2 : B | provenance | | +| extensions.cs:224:18:224:31 | call to method Source : B | extensions.cs:224:13:224:14 | access to local variable b2 : B | provenance | | +| extensions.cs:225:32:225:33 | access to local variable b2 : B | extensions.cs:89:20:89:20 | t : B | provenance | | +| extensions.cs:225:32:225:33 | access to local variable b2 : B | extensions.cs:89:20:89:20 | t : B | provenance | | +| extensions.cs:231:13:231:14 | access to local variable b1 : B | extensions.cs:232:32:232:33 | access to local variable b1 : B | provenance | | +| extensions.cs:231:13:231:14 | access to local variable b1 : B | extensions.cs:232:32:232:33 | access to local variable b1 : B | provenance | | +| extensions.cs:231:13:231:14 | access to local variable b1 : B | extensions.cs:235:46:235:47 | access to local variable b1 : B | provenance | | +| extensions.cs:231:13:231:14 | access to local variable b1 : B | extensions.cs:235:46:235:47 | access to local variable b1 : B | provenance | | +| extensions.cs:231:18:231:30 | call to method Source : B | extensions.cs:231:13:231:14 | access to local variable b1 : B | provenance | | +| extensions.cs:231:18:231:30 | call to method Source : B | extensions.cs:231:13:231:14 | access to local variable b1 : B | provenance | | +| extensions.cs:232:13:232:14 | access to local variable b2 : B | extensions.cs:233:14:233:15 | access to local variable b2 | provenance | | +| extensions.cs:232:13:232:14 | access to local variable b2 : B | extensions.cs:233:14:233:15 | access to local variable b2 | provenance | | +| extensions.cs:232:18:232:34 | call to method GenericM2 : B | extensions.cs:232:13:232:14 | access to local variable b2 : B | provenance | | +| extensions.cs:232:18:232:34 | call to method GenericM2 : B | extensions.cs:232:13:232:14 | access to local variable b2 : B | provenance | | +| extensions.cs:232:32:232:33 | access to local variable b1 : B | extensions.cs:96:33:96:37 | other : B | provenance | | +| extensions.cs:232:32:232:33 | access to local variable b1 : B | extensions.cs:96:33:96:37 | other : B | provenance | | +| extensions.cs:232:32:232:33 | access to local variable b1 : B | extensions.cs:232:18:232:34 | call to method GenericM2 : B | provenance | | +| extensions.cs:232:32:232:33 | access to local variable b1 : B | extensions.cs:232:18:232:34 | call to method GenericM2 : B | provenance | | +| extensions.cs:235:13:235:14 | access to local variable b3 : B | extensions.cs:236:14:236:15 | access to local variable b3 | provenance | | +| extensions.cs:235:13:235:14 | access to local variable b3 : B | extensions.cs:236:14:236:15 | access to local variable b3 | provenance | | +| extensions.cs:235:18:235:48 | call to method GenericM2 : B | extensions.cs:235:13:235:14 | access to local variable b3 : B | provenance | | +| extensions.cs:235:18:235:48 | call to method GenericM2 : B | extensions.cs:235:13:235:14 | access to local variable b3 : B | provenance | | +| extensions.cs:235:46:235:47 | access to local variable b1 : B | extensions.cs:96:33:96:37 | other : B | provenance | | +| extensions.cs:235:46:235:47 | access to local variable b1 : B | extensions.cs:96:33:96:37 | other : B | provenance | | +| extensions.cs:235:46:235:47 | access to local variable b1 : B | extensions.cs:235:18:235:48 | call to method GenericM2 : B | provenance | | +| extensions.cs:235:46:235:47 | access to local variable b1 : B | extensions.cs:235:18:235:48 | call to method GenericM2 : B | provenance | | +nodes +| extensions.cs:5:22:5:24 | obj : B | semmle.label | obj : B | +| extensions.cs:5:22:5:24 | obj : B | semmle.label | obj : B | +| extensions.cs:5:22:5:24 | obj : B | semmle.label | obj : B | +| extensions.cs:5:22:5:24 | obj : B | semmle.label | obj : B | +| extensions.cs:5:22:5:24 | obj : B | semmle.label | obj : B | +| extensions.cs:5:22:5:24 | obj : B | semmle.label | obj : B | +| extensions.cs:11:24:11:37 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:11:24:11:37 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:13:13:13:15 | value : B | semmle.label | value : B | +| extensions.cs:13:13:13:15 | value : B | semmle.label | value : B | +| extensions.cs:15:24:15:28 | access to parameter value | semmle.label | access to parameter value | +| extensions.cs:15:24:15:28 | access to parameter value | semmle.label | access to parameter value | +| extensions.cs:23:24:23:26 | access to synthetic parameter obj | semmle.label | access to synthetic parameter obj | +| extensions.cs:23:24:23:26 | access to synthetic parameter obj | semmle.label | access to synthetic parameter obj | +| extensions.cs:28:24:28:26 | access to synthetic parameter obj | semmle.label | access to synthetic parameter obj | +| extensions.cs:28:24:28:26 | access to synthetic parameter obj | semmle.label | access to synthetic parameter obj | +| extensions.cs:36:24:36:38 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:36:24:36:38 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:38:13:38:15 | value : B | semmle.label | value : B | +| extensions.cs:38:13:38:15 | value : B | semmle.label | value : B | +| extensions.cs:40:24:40:28 | access to parameter value | semmle.label | access to parameter value | +| extensions.cs:40:24:40:28 | access to parameter value | semmle.label | access to parameter value | +| extensions.cs:46:20:46:33 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:46:20:46:33 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:51:20:51:22 | access to synthetic parameter obj | semmle.label | access to synthetic parameter obj | +| extensions.cs:51:20:51:22 | access to synthetic parameter obj | semmle.label | access to synthetic parameter obj | +| extensions.cs:59:48:59:48 | a : B | semmle.label | a : B | +| extensions.cs:59:48:59:48 | a : B | semmle.label | a : B | +| extensions.cs:61:20:61:20 | access to parameter a | semmle.label | access to parameter a | +| extensions.cs:61:20:61:20 | access to parameter a | semmle.label | access to parameter a | +| extensions.cs:67:20:67:33 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:67:20:67:33 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:76:17:76:17 | b : B | semmle.label | b : B | +| extensions.cs:76:17:76:17 | b : B | semmle.label | b : B | +| extensions.cs:76:17:76:17 | b : B | semmle.label | b : B | +| extensions.cs:76:17:76:17 | b : B | semmle.label | b : B | +| extensions.cs:80:20:80:20 | access to synthetic parameter b : B | semmle.label | access to synthetic parameter b : B | +| extensions.cs:80:20:80:20 | access to synthetic parameter b : B | semmle.label | access to synthetic parameter b : B | +| extensions.cs:85:20:85:20 | access to synthetic parameter b | semmle.label | access to synthetic parameter b | +| extensions.cs:85:20:85:20 | access to synthetic parameter b | semmle.label | access to synthetic parameter b | +| extensions.cs:89:20:89:20 | t : B | semmle.label | t : B | +| extensions.cs:89:20:89:20 | t : B | semmle.label | t : B | +| extensions.cs:93:20:93:20 | access to synthetic parameter t | semmle.label | access to synthetic parameter t | +| extensions.cs:93:20:93:20 | access to synthetic parameter t | semmle.label | access to synthetic parameter t | +| extensions.cs:96:33:96:37 | other : B | semmle.label | other : B | +| extensions.cs:96:33:96:37 | other : B | semmle.label | other : B | +| extensions.cs:98:20:98:24 | access to parameter other : B | semmle.label | access to parameter other : B | +| extensions.cs:98:20:98:24 | access to parameter other : B | semmle.label | access to parameter other : B | +| extensions.cs:108:13:108:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:108:13:108:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:108:18:108:26 | access to property Prop1 : B | semmle.label | access to property Prop1 : B | +| extensions.cs:108:18:108:26 | access to property Prop1 : B | semmle.label | access to property Prop1 : B | +| extensions.cs:109:14:109:15 | access to local variable b1 | semmle.label | access to local variable b1 | +| extensions.cs:109:14:109:15 | access to local variable b1 | semmle.label | access to local variable b1 | +| extensions.cs:111:13:111:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:111:13:111:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:111:18:111:44 | call to extension accessor get_Prop1 : B | semmle.label | call to extension accessor get_Prop1 : B | +| extensions.cs:111:18:111:44 | call to extension accessor get_Prop1 : B | semmle.label | call to extension accessor get_Prop1 : B | +| extensions.cs:112:14:112:15 | access to local variable b2 | semmle.label | access to local variable b2 | +| extensions.cs:112:14:112:15 | access to local variable b2 | semmle.label | access to local variable b2 | +| extensions.cs:118:21:118:32 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:118:21:118:32 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:120:13:120:13 | access to local variable b : B | semmle.label | access to local variable b : B | +| extensions.cs:120:13:120:13 | access to local variable b : B | semmle.label | access to local variable b : B | +| extensions.cs:120:17:120:30 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:120:17:120:30 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:121:37:121:37 | access to local variable b : B | semmle.label | access to local variable b : B | +| extensions.cs:121:37:121:37 | access to local variable b : B | semmle.label | access to local variable b : B | +| extensions.cs:127:13:127:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:127:13:127:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:127:18:127:25 | call to method M1 : B | semmle.label | call to method M1 : B | +| extensions.cs:127:18:127:25 | call to method M1 : B | semmle.label | call to method M1 : B | +| extensions.cs:128:14:128:15 | access to local variable b1 | semmle.label | access to local variable b1 | +| extensions.cs:128:14:128:15 | access to local variable b1 | semmle.label | access to local variable b1 | +| extensions.cs:130:13:130:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:130:13:130:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:130:18:130:37 | call to method M1 : B | semmle.label | call to method M1 : B | +| extensions.cs:130:18:130:37 | call to method M1 : B | semmle.label | call to method M1 : B | +| extensions.cs:131:14:131:15 | access to local variable b2 | semmle.label | access to local variable b2 | +| extensions.cs:131:14:131:15 | access to local variable b2 | semmle.label | access to local variable b2 | +| extensions.cs:136:13:136:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:136:13:136:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:136:18:136:29 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:136:18:136:29 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:137:9:137:10 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:137:9:137:10 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:139:13:139:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:139:13:139:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:139:18:139:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:139:18:139:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:140:25:140:26 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:140:25:140:26 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:145:13:145:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:145:13:145:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:145:18:145:29 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:145:18:145:29 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:146:13:146:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:146:13:146:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:146:18:146:19 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:146:18:146:19 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:146:18:146:24 | call to method B1 : B | semmle.label | call to method B1 : B | +| extensions.cs:146:18:146:24 | call to method B1 : B | semmle.label | call to method B1 : B | +| extensions.cs:147:14:147:15 | access to local variable b2 | semmle.label | access to local variable b2 | +| extensions.cs:147:14:147:15 | access to local variable b2 | semmle.label | access to local variable b2 | +| extensions.cs:149:13:149:14 | access to local variable b3 : B | semmle.label | access to local variable b3 : B | +| extensions.cs:149:13:149:14 | access to local variable b3 : B | semmle.label | access to local variable b3 : B | +| extensions.cs:149:18:149:36 | call to method B1 : B | semmle.label | call to method B1 : B | +| extensions.cs:149:18:149:36 | call to method B1 : B | semmle.label | call to method B1 : B | +| extensions.cs:149:34:149:35 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:149:34:149:35 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:150:14:150:15 | access to local variable b3 | semmle.label | access to local variable b3 | +| extensions.cs:150:14:150:15 | access to local variable b3 | semmle.label | access to local variable b3 | +| extensions.cs:155:13:155:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:155:13:155:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:155:18:155:29 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:155:18:155:29 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:156:18:156:19 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:156:18:156:19 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:158:13:158:14 | access to local variable b3 : B | semmle.label | access to local variable b3 : B | +| extensions.cs:158:13:158:14 | access to local variable b3 : B | semmle.label | access to local variable b3 : B | +| extensions.cs:158:18:158:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:158:18:158:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:159:41:159:42 | access to local variable b3 : B | semmle.label | access to local variable b3 : B | +| extensions.cs:159:41:159:42 | access to local variable b3 : B | semmle.label | access to local variable b3 : B | +| extensions.cs:164:13:164:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:164:13:164:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:164:18:164:29 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:164:18:164:29 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:165:9:165:10 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:165:9:165:10 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:167:13:167:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:167:13:167:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:167:18:167:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:167:18:167:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:168:32:168:33 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:168:32:168:33 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:173:13:173:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:173:13:173:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:173:18:173:29 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:173:18:173:29 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:175:18:175:19 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:175:18:175:19 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:177:13:177:14 | access to local variable b4 : B | semmle.label | access to local variable b4 : B | +| extensions.cs:177:13:177:14 | access to local variable b4 : B | semmle.label | access to local variable b4 : B | +| extensions.cs:177:18:177:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:177:18:177:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:178:43:178:44 | access to local variable b4 : B | semmle.label | access to local variable b4 : B | +| extensions.cs:178:43:178:44 | access to local variable b4 : B | semmle.label | access to local variable b4 : B | +| extensions.cs:185:13:185:14 | access to local variable b3 : B | semmle.label | access to local variable b3 : B | +| extensions.cs:185:13:185:14 | access to local variable b3 : B | semmle.label | access to local variable b3 : B | +| extensions.cs:185:18:185:24 | call to operator - : B | semmle.label | call to operator - : B | +| extensions.cs:185:18:185:24 | call to operator - : B | semmle.label | call to operator - : B | +| extensions.cs:186:14:186:15 | access to local variable b3 | semmle.label | access to local variable b3 | +| extensions.cs:186:14:186:15 | access to local variable b3 | semmle.label | access to local variable b3 | +| extensions.cs:188:13:188:14 | access to local variable b4 : B | semmle.label | access to local variable b4 : B | +| extensions.cs:188:13:188:14 | access to local variable b4 : B | semmle.label | access to local variable b4 : B | +| extensions.cs:188:18:188:52 | call to operator - : B | semmle.label | call to operator - : B | +| extensions.cs:188:18:188:52 | call to operator - : B | semmle.label | call to operator - : B | +| extensions.cs:189:14:189:15 | access to local variable b4 | semmle.label | access to local variable b4 | +| extensions.cs:189:14:189:15 | access to local variable b4 | semmle.label | access to local variable b4 | +| extensions.cs:194:13:194:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:194:13:194:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:194:18:194:35 | access to property StaticProp1 : B | semmle.label | access to property StaticProp1 : B | +| extensions.cs:194:18:194:35 | access to property StaticProp1 : B | semmle.label | access to property StaticProp1 : B | +| extensions.cs:195:14:195:15 | access to local variable b1 | semmle.label | access to local variable b1 | +| extensions.cs:195:14:195:15 | access to local variable b1 | semmle.label | access to local variable b1 | +| extensions.cs:197:13:197:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:197:13:197:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:197:18:197:47 | call to extension accessor get_StaticProp1 : B | semmle.label | call to extension accessor get_StaticProp1 : B | +| extensions.cs:197:18:197:47 | call to extension accessor get_StaticProp1 : B | semmle.label | call to extension accessor get_StaticProp1 : B | +| extensions.cs:198:14:198:15 | access to local variable b2 | semmle.label | access to local variable b2 | +| extensions.cs:198:14:198:15 | access to local variable b2 | semmle.label | access to local variable b2 | +| extensions.cs:203:13:203:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:203:13:203:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:203:18:203:30 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:203:18:203:30 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:204:30:204:31 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:204:30:204:31 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:206:13:206:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:206:13:206:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:206:18:206:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:206:18:206:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:207:38:207:39 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:207:38:207:39 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:212:13:212:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:212:13:212:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:212:18:212:30 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:212:18:212:30 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:213:9:213:10 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:213:9:213:10 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:215:13:215:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:215:13:215:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:215:18:215:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:215:18:215:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:216:25:216:26 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:216:25:216:26 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:221:13:221:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:221:13:221:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:221:18:221:30 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:221:18:221:30 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:222:9:222:10 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:222:9:222:10 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:224:13:224:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:224:13:224:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:224:18:224:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:224:18:224:31 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:225:32:225:33 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:225:32:225:33 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:231:13:231:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:231:13:231:14 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:231:18:231:30 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:231:18:231:30 | call to method Source : B | semmle.label | call to method Source : B | +| extensions.cs:232:13:232:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:232:13:232:14 | access to local variable b2 : B | semmle.label | access to local variable b2 : B | +| extensions.cs:232:18:232:34 | call to method GenericM2 : B | semmle.label | call to method GenericM2 : B | +| extensions.cs:232:18:232:34 | call to method GenericM2 : B | semmle.label | call to method GenericM2 : B | +| extensions.cs:232:32:232:33 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:232:32:232:33 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:233:14:233:15 | access to local variable b2 | semmle.label | access to local variable b2 | +| extensions.cs:233:14:233:15 | access to local variable b2 | semmle.label | access to local variable b2 | +| extensions.cs:235:13:235:14 | access to local variable b3 : B | semmle.label | access to local variable b3 : B | +| extensions.cs:235:13:235:14 | access to local variable b3 : B | semmle.label | access to local variable b3 : B | +| extensions.cs:235:18:235:48 | call to method GenericM2 : B | semmle.label | call to method GenericM2 : B | +| extensions.cs:235:18:235:48 | call to method GenericM2 : B | semmle.label | call to method GenericM2 : B | +| extensions.cs:235:46:235:47 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:235:46:235:47 | access to local variable b1 : B | semmle.label | access to local variable b1 : B | +| extensions.cs:236:14:236:15 | access to local variable b3 | semmle.label | access to local variable b3 | +| extensions.cs:236:14:236:15 | access to local variable b3 | semmle.label | access to local variable b3 | +subpaths +| extensions.cs:146:18:146:19 | access to local variable b1 : B | extensions.cs:76:17:76:17 | b : B | extensions.cs:80:20:80:20 | access to synthetic parameter b : B | extensions.cs:146:18:146:24 | call to method B1 : B | +| extensions.cs:146:18:146:19 | access to local variable b1 : B | extensions.cs:76:17:76:17 | b : B | extensions.cs:80:20:80:20 | access to synthetic parameter b : B | extensions.cs:146:18:146:24 | call to method B1 : B | +| extensions.cs:149:34:149:35 | access to local variable b1 : B | extensions.cs:76:17:76:17 | b : B | extensions.cs:80:20:80:20 | access to synthetic parameter b : B | extensions.cs:149:18:149:36 | call to method B1 : B | +| extensions.cs:149:34:149:35 | access to local variable b1 : B | extensions.cs:76:17:76:17 | b : B | extensions.cs:80:20:80:20 | access to synthetic parameter b : B | extensions.cs:149:18:149:36 | call to method B1 : B | +| extensions.cs:232:32:232:33 | access to local variable b1 : B | extensions.cs:96:33:96:37 | other : B | extensions.cs:98:20:98:24 | access to parameter other : B | extensions.cs:232:18:232:34 | call to method GenericM2 : B | +| extensions.cs:232:32:232:33 | access to local variable b1 : B | extensions.cs:96:33:96:37 | other : B | extensions.cs:98:20:98:24 | access to parameter other : B | extensions.cs:232:18:232:34 | call to method GenericM2 : B | +| extensions.cs:235:46:235:47 | access to local variable b1 : B | extensions.cs:96:33:96:37 | other : B | extensions.cs:98:20:98:24 | access to parameter other : B | extensions.cs:235:18:235:48 | call to method GenericM2 : B | +| extensions.cs:235:46:235:47 | access to local variable b1 : B | extensions.cs:96:33:96:37 | other : B | extensions.cs:98:20:98:24 | access to parameter other : B | extensions.cs:235:18:235:48 | call to method GenericM2 : B | +testFailures +#select +| extensions.cs:15:24:15:28 | access to parameter value | extensions.cs:118:21:118:32 | call to method Source : B | extensions.cs:15:24:15:28 | access to parameter value | $@ | extensions.cs:118:21:118:32 | call to method Source : B | call to method Source : B | +| extensions.cs:15:24:15:28 | access to parameter value | extensions.cs:118:21:118:32 | call to method Source : B | extensions.cs:15:24:15:28 | access to parameter value | $@ | extensions.cs:118:21:118:32 | call to method Source : B | call to method Source : B | +| extensions.cs:15:24:15:28 | access to parameter value | extensions.cs:120:17:120:30 | call to method Source : B | extensions.cs:15:24:15:28 | access to parameter value | $@ | extensions.cs:120:17:120:30 | call to method Source : B | call to method Source : B | +| extensions.cs:15:24:15:28 | access to parameter value | extensions.cs:120:17:120:30 | call to method Source : B | extensions.cs:15:24:15:28 | access to parameter value | $@ | extensions.cs:120:17:120:30 | call to method Source : B | call to method Source : B | +| extensions.cs:23:24:23:26 | access to synthetic parameter obj | extensions.cs:155:18:155:29 | call to method Source : B | extensions.cs:23:24:23:26 | access to synthetic parameter obj | $@ | extensions.cs:155:18:155:29 | call to method Source : B | call to method Source : B | +| extensions.cs:23:24:23:26 | access to synthetic parameter obj | extensions.cs:155:18:155:29 | call to method Source : B | extensions.cs:23:24:23:26 | access to synthetic parameter obj | $@ | extensions.cs:155:18:155:29 | call to method Source : B | call to method Source : B | +| extensions.cs:23:24:23:26 | access to synthetic parameter obj | extensions.cs:158:18:158:31 | call to method Source : B | extensions.cs:23:24:23:26 | access to synthetic parameter obj | $@ | extensions.cs:158:18:158:31 | call to method Source : B | call to method Source : B | +| extensions.cs:23:24:23:26 | access to synthetic parameter obj | extensions.cs:158:18:158:31 | call to method Source : B | extensions.cs:23:24:23:26 | access to synthetic parameter obj | $@ | extensions.cs:158:18:158:31 | call to method Source : B | call to method Source : B | +| extensions.cs:28:24:28:26 | access to synthetic parameter obj | extensions.cs:164:18:164:29 | call to method Source : B | extensions.cs:28:24:28:26 | access to synthetic parameter obj | $@ | extensions.cs:164:18:164:29 | call to method Source : B | call to method Source : B | +| extensions.cs:28:24:28:26 | access to synthetic parameter obj | extensions.cs:164:18:164:29 | call to method Source : B | extensions.cs:28:24:28:26 | access to synthetic parameter obj | $@ | extensions.cs:164:18:164:29 | call to method Source : B | call to method Source : B | +| extensions.cs:28:24:28:26 | access to synthetic parameter obj | extensions.cs:167:18:167:31 | call to method Source : B | extensions.cs:28:24:28:26 | access to synthetic parameter obj | $@ | extensions.cs:167:18:167:31 | call to method Source : B | call to method Source : B | +| extensions.cs:28:24:28:26 | access to synthetic parameter obj | extensions.cs:167:18:167:31 | call to method Source : B | extensions.cs:28:24:28:26 | access to synthetic parameter obj | $@ | extensions.cs:167:18:167:31 | call to method Source : B | call to method Source : B | +| extensions.cs:40:24:40:28 | access to parameter value | extensions.cs:203:18:203:30 | call to method Source : B | extensions.cs:40:24:40:28 | access to parameter value | $@ | extensions.cs:203:18:203:30 | call to method Source : B | call to method Source : B | +| extensions.cs:40:24:40:28 | access to parameter value | extensions.cs:203:18:203:30 | call to method Source : B | extensions.cs:40:24:40:28 | access to parameter value | $@ | extensions.cs:203:18:203:30 | call to method Source : B | call to method Source : B | +| extensions.cs:40:24:40:28 | access to parameter value | extensions.cs:206:18:206:31 | call to method Source : B | extensions.cs:40:24:40:28 | access to parameter value | $@ | extensions.cs:206:18:206:31 | call to method Source : B | call to method Source : B | +| extensions.cs:40:24:40:28 | access to parameter value | extensions.cs:206:18:206:31 | call to method Source : B | extensions.cs:40:24:40:28 | access to parameter value | $@ | extensions.cs:206:18:206:31 | call to method Source : B | call to method Source : B | +| extensions.cs:51:20:51:22 | access to synthetic parameter obj | extensions.cs:136:18:136:29 | call to method Source : B | extensions.cs:51:20:51:22 | access to synthetic parameter obj | $@ | extensions.cs:136:18:136:29 | call to method Source : B | call to method Source : B | +| extensions.cs:51:20:51:22 | access to synthetic parameter obj | extensions.cs:136:18:136:29 | call to method Source : B | extensions.cs:51:20:51:22 | access to synthetic parameter obj | $@ | extensions.cs:136:18:136:29 | call to method Source : B | call to method Source : B | +| extensions.cs:51:20:51:22 | access to synthetic parameter obj | extensions.cs:139:18:139:31 | call to method Source : B | extensions.cs:51:20:51:22 | access to synthetic parameter obj | $@ | extensions.cs:139:18:139:31 | call to method Source : B | call to method Source : B | +| extensions.cs:51:20:51:22 | access to synthetic parameter obj | extensions.cs:139:18:139:31 | call to method Source : B | extensions.cs:51:20:51:22 | access to synthetic parameter obj | $@ | extensions.cs:139:18:139:31 | call to method Source : B | call to method Source : B | +| extensions.cs:61:20:61:20 | access to parameter a | extensions.cs:173:18:173:29 | call to method Source : B | extensions.cs:61:20:61:20 | access to parameter a | $@ | extensions.cs:173:18:173:29 | call to method Source : B | call to method Source : B | +| extensions.cs:61:20:61:20 | access to parameter a | extensions.cs:173:18:173:29 | call to method Source : B | extensions.cs:61:20:61:20 | access to parameter a | $@ | extensions.cs:173:18:173:29 | call to method Source : B | call to method Source : B | +| extensions.cs:61:20:61:20 | access to parameter a | extensions.cs:177:18:177:31 | call to method Source : B | extensions.cs:61:20:61:20 | access to parameter a | $@ | extensions.cs:177:18:177:31 | call to method Source : B | call to method Source : B | +| extensions.cs:61:20:61:20 | access to parameter a | extensions.cs:177:18:177:31 | call to method Source : B | extensions.cs:61:20:61:20 | access to parameter a | $@ | extensions.cs:177:18:177:31 | call to method Source : B | call to method Source : B | +| extensions.cs:85:20:85:20 | access to synthetic parameter b | extensions.cs:212:18:212:30 | call to method Source : B | extensions.cs:85:20:85:20 | access to synthetic parameter b | $@ | extensions.cs:212:18:212:30 | call to method Source : B | call to method Source : B | +| extensions.cs:85:20:85:20 | access to synthetic parameter b | extensions.cs:212:18:212:30 | call to method Source : B | extensions.cs:85:20:85:20 | access to synthetic parameter b | $@ | extensions.cs:212:18:212:30 | call to method Source : B | call to method Source : B | +| extensions.cs:85:20:85:20 | access to synthetic parameter b | extensions.cs:215:18:215:31 | call to method Source : B | extensions.cs:85:20:85:20 | access to synthetic parameter b | $@ | extensions.cs:215:18:215:31 | call to method Source : B | call to method Source : B | +| extensions.cs:85:20:85:20 | access to synthetic parameter b | extensions.cs:215:18:215:31 | call to method Source : B | extensions.cs:85:20:85:20 | access to synthetic parameter b | $@ | extensions.cs:215:18:215:31 | call to method Source : B | call to method Source : B | +| extensions.cs:93:20:93:20 | access to synthetic parameter t | extensions.cs:221:18:221:30 | call to method Source : B | extensions.cs:93:20:93:20 | access to synthetic parameter t | $@ | extensions.cs:221:18:221:30 | call to method Source : B | call to method Source : B | +| extensions.cs:93:20:93:20 | access to synthetic parameter t | extensions.cs:221:18:221:30 | call to method Source : B | extensions.cs:93:20:93:20 | access to synthetic parameter t | $@ | extensions.cs:221:18:221:30 | call to method Source : B | call to method Source : B | +| extensions.cs:93:20:93:20 | access to synthetic parameter t | extensions.cs:224:18:224:31 | call to method Source : B | extensions.cs:93:20:93:20 | access to synthetic parameter t | $@ | extensions.cs:224:18:224:31 | call to method Source : B | call to method Source : B | +| extensions.cs:93:20:93:20 | access to synthetic parameter t | extensions.cs:224:18:224:31 | call to method Source : B | extensions.cs:93:20:93:20 | access to synthetic parameter t | $@ | extensions.cs:224:18:224:31 | call to method Source : B | call to method Source : B | +| extensions.cs:109:14:109:15 | access to local variable b1 | extensions.cs:11:24:11:37 | call to method Source : B | extensions.cs:109:14:109:15 | access to local variable b1 | $@ | extensions.cs:11:24:11:37 | call to method Source : B | call to method Source : B | +| extensions.cs:109:14:109:15 | access to local variable b1 | extensions.cs:11:24:11:37 | call to method Source : B | extensions.cs:109:14:109:15 | access to local variable b1 | $@ | extensions.cs:11:24:11:37 | call to method Source : B | call to method Source : B | +| extensions.cs:112:14:112:15 | access to local variable b2 | extensions.cs:11:24:11:37 | call to method Source : B | extensions.cs:112:14:112:15 | access to local variable b2 | $@ | extensions.cs:11:24:11:37 | call to method Source : B | call to method Source : B | +| extensions.cs:112:14:112:15 | access to local variable b2 | extensions.cs:11:24:11:37 | call to method Source : B | extensions.cs:112:14:112:15 | access to local variable b2 | $@ | extensions.cs:11:24:11:37 | call to method Source : B | call to method Source : B | +| extensions.cs:128:14:128:15 | access to local variable b1 | extensions.cs:46:20:46:33 | call to method Source : B | extensions.cs:128:14:128:15 | access to local variable b1 | $@ | extensions.cs:46:20:46:33 | call to method Source : B | call to method Source : B | +| extensions.cs:128:14:128:15 | access to local variable b1 | extensions.cs:46:20:46:33 | call to method Source : B | extensions.cs:128:14:128:15 | access to local variable b1 | $@ | extensions.cs:46:20:46:33 | call to method Source : B | call to method Source : B | +| extensions.cs:131:14:131:15 | access to local variable b2 | extensions.cs:46:20:46:33 | call to method Source : B | extensions.cs:131:14:131:15 | access to local variable b2 | $@ | extensions.cs:46:20:46:33 | call to method Source : B | call to method Source : B | +| extensions.cs:131:14:131:15 | access to local variable b2 | extensions.cs:46:20:46:33 | call to method Source : B | extensions.cs:131:14:131:15 | access to local variable b2 | $@ | extensions.cs:46:20:46:33 | call to method Source : B | call to method Source : B | +| extensions.cs:147:14:147:15 | access to local variable b2 | extensions.cs:145:18:145:29 | call to method Source : B | extensions.cs:147:14:147:15 | access to local variable b2 | $@ | extensions.cs:145:18:145:29 | call to method Source : B | call to method Source : B | +| extensions.cs:147:14:147:15 | access to local variable b2 | extensions.cs:145:18:145:29 | call to method Source : B | extensions.cs:147:14:147:15 | access to local variable b2 | $@ | extensions.cs:145:18:145:29 | call to method Source : B | call to method Source : B | +| extensions.cs:150:14:150:15 | access to local variable b3 | extensions.cs:145:18:145:29 | call to method Source : B | extensions.cs:150:14:150:15 | access to local variable b3 | $@ | extensions.cs:145:18:145:29 | call to method Source : B | call to method Source : B | +| extensions.cs:150:14:150:15 | access to local variable b3 | extensions.cs:145:18:145:29 | call to method Source : B | extensions.cs:150:14:150:15 | access to local variable b3 | $@ | extensions.cs:145:18:145:29 | call to method Source : B | call to method Source : B | +| extensions.cs:186:14:186:15 | access to local variable b3 | extensions.cs:67:20:67:33 | call to method Source : B | extensions.cs:186:14:186:15 | access to local variable b3 | $@ | extensions.cs:67:20:67:33 | call to method Source : B | call to method Source : B | +| extensions.cs:186:14:186:15 | access to local variable b3 | extensions.cs:67:20:67:33 | call to method Source : B | extensions.cs:186:14:186:15 | access to local variable b3 | $@ | extensions.cs:67:20:67:33 | call to method Source : B | call to method Source : B | +| extensions.cs:189:14:189:15 | access to local variable b4 | extensions.cs:67:20:67:33 | call to method Source : B | extensions.cs:189:14:189:15 | access to local variable b4 | $@ | extensions.cs:67:20:67:33 | call to method Source : B | call to method Source : B | +| extensions.cs:189:14:189:15 | access to local variable b4 | extensions.cs:67:20:67:33 | call to method Source : B | extensions.cs:189:14:189:15 | access to local variable b4 | $@ | extensions.cs:67:20:67:33 | call to method Source : B | call to method Source : B | +| extensions.cs:195:14:195:15 | access to local variable b1 | extensions.cs:36:24:36:38 | call to method Source : B | extensions.cs:195:14:195:15 | access to local variable b1 | $@ | extensions.cs:36:24:36:38 | call to method Source : B | call to method Source : B | +| extensions.cs:195:14:195:15 | access to local variable b1 | extensions.cs:36:24:36:38 | call to method Source : B | extensions.cs:195:14:195:15 | access to local variable b1 | $@ | extensions.cs:36:24:36:38 | call to method Source : B | call to method Source : B | +| extensions.cs:198:14:198:15 | access to local variable b2 | extensions.cs:36:24:36:38 | call to method Source : B | extensions.cs:198:14:198:15 | access to local variable b2 | $@ | extensions.cs:36:24:36:38 | call to method Source : B | call to method Source : B | +| extensions.cs:198:14:198:15 | access to local variable b2 | extensions.cs:36:24:36:38 | call to method Source : B | extensions.cs:198:14:198:15 | access to local variable b2 | $@ | extensions.cs:36:24:36:38 | call to method Source : B | call to method Source : B | +| extensions.cs:233:14:233:15 | access to local variable b2 | extensions.cs:231:18:231:30 | call to method Source : B | extensions.cs:233:14:233:15 | access to local variable b2 | $@ | extensions.cs:231:18:231:30 | call to method Source : B | call to method Source : B | +| extensions.cs:233:14:233:15 | access to local variable b2 | extensions.cs:231:18:231:30 | call to method Source : B | extensions.cs:233:14:233:15 | access to local variable b2 | $@ | extensions.cs:231:18:231:30 | call to method Source : B | call to method Source : B | +| extensions.cs:236:14:236:15 | access to local variable b3 | extensions.cs:231:18:231:30 | call to method Source : B | extensions.cs:236:14:236:15 | access to local variable b3 | $@ | extensions.cs:231:18:231:30 | call to method Source : B | call to method Source : B | +| extensions.cs:236:14:236:15 | access to local variable b3 | extensions.cs:231:18:231:30 | call to method Source : B | extensions.cs:236:14:236:15 | access to local variable b3 | $@ | extensions.cs:231:18:231:30 | call to method Source : B | call to method Source : B | diff --git a/csharp/ql/test/library-tests/dataflow/extensions/ExtensionFlow.ql b/csharp/ql/test/library-tests/dataflow/extensions/ExtensionFlow.ql new file mode 100644 index 000000000000..9ab95f59caf3 --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/extensions/ExtensionFlow.ql @@ -0,0 +1,12 @@ +/** + * @kind path-problem + */ + +import csharp +import utils.test.InlineFlowTest +import DefaultFlowTest +import PathGraph + +from PathNode source, PathNode sink +where flowPath(source, sink) +select sink, source, sink, "$@", source, source.toString() diff --git a/csharp/ql/test/library-tests/dataflow/extensions/extensions.cs b/csharp/ql/test/library-tests/dataflow/extensions/extensions.cs new file mode 100644 index 000000000000..0e1c2226683d --- /dev/null +++ b/csharp/ql/test/library-tests/dataflow/extensions/extensions.cs @@ -0,0 +1,243 @@ +using System; + +public static class MyExtensions +{ + extension(object obj) + { + public B Prop1 + { + get + { + return A.Source(1); + } + set + { + A.Sink(value); // $ hasValueFlow=2 hasValueFlow=102 + } + } + + public B Prop2 + { + get + { + A.Sink(obj); // $ hasValueFlow=6 hasValueFlow=106 + return new B(); + } + set + { + A.Sink(obj); // $ hasValueFlow=7 hasValueFlow=107 + } + } + + public static B StaticProp1 + { + get + { + return A.Source(10); + } + set + { + A.Sink(value); // $ hasValueFlow=11 hasValueFlow=111 + } + } + + public B M1() + { + return A.Source(3); + } + + public void M2() + { + A.Sink(obj); // $ hasValueFlow=4 hasValueFlow=104 + } + + public static B M3(B b) + { + return b; + } + + public static object operator +(object a, object b) + { + A.Sink(a); // $ hasValueFlow=8 hasValueFlow=108 + return new object(); + } + + public static object operator -(object a, object b) + { + return A.Source(9); + } + + public T GenericMethod(T t) + { + return t; + } + } + + extension(B b) + { + public B B1() + { + return b; + } + + public void B2() + { + A.Sink(b); // $ hasValueFlow=12 hasValueFlow=112 + } + } + + extension(T t) where T : class + { + public void GenericM1() + { + A.Sink(t); // $ hasValueFlow=13 hasValueFlow=113 + } + + public S GenericM2(S other) + { + return other; + } + } +} + +public class A +{ + public void Test1() + { + var obj = new object(); + var b1 = obj.Prop1; + Sink(b1); // $ hasValueFlow=1 + + var b2 = MyExtensions.get_Prop1(obj); + Sink(b2); // $ hasValueFlow=1 + } + + public void Test2() + { + var obj = new object(); + obj.Prop1 = Source(2); + + var b = Source(102); + MyExtensions.set_Prop1(obj, b); + } + + public void Test3() + { + var obj = new object(); + var b1 = obj.M1(); + Sink(b1); // $ hasValueFlow=3 + + var b2 = MyExtensions.M1(obj); + Sink(b2); // $ hasValueFlow=3 + } + + public void Test4() + { + var b1 = Source(4); + b1.M2(); + + var b2 = Source(104); + MyExtensions.M2(b2); + } + + public void Test5() + { + var b1 = Source(5); + var b2 = b1.B1(); + Sink(b2); // $ hasValueFlow=5 + + var b3 = MyExtensions.B1(b1); + Sink(b3); // $ hasValueFlow=5 + } + + public void Test6() + { + var b1 = Source(6); + var b2 = b1.Prop2; + + var b3 = Source(106); + var b4 = MyExtensions.get_Prop2(b3); + } + + public void Test7() + { + var b1 = Source(7); + b1.Prop2 = new B(); + + var b2 = Source(107); + MyExtensions.set_Prop2(b2, new B()); + } + + public void Test8() + { + var b1 = Source(8); + var b2 = Source(1); + var b3 = b1 + b2; + + var b4 = Source(108); + var b5 = MyExtensions.op_Addition(b4, b2); + } + + public void Test9() + { + var b1 = Source(0); + var b2 = Source(1); + var b3 = b1 - b2; + Sink(b3); // $ hasValueFlow=9 + + var b4 = MyExtensions.op_Subtraction(b1, b2); + Sink(b4); // $ hasValueFlow=9 + } + + public void Test10() + { + var b1 = object.StaticProp1; + Sink(b1); // $ hasValueFlow=10 + + var b2 = MyExtensions.get_StaticProp1(); + Sink(b2); // $ hasValueFlow=10 + } + + public void Test11() + { + var b1 = Source(11); + object.StaticProp1 = b1; + + var b2 = Source(111); + MyExtensions.set_StaticProp1(b2); + } + + public void Test12() + { + var b1 = Source(12); + b1.B2(); + + var b2 = Source(112); + MyExtensions.B2(b2); + } + + public void Test13() + { + var b1 = Source(13); + b1.GenericM1(); + + var b2 = Source(113); + MyExtensions.GenericM1(b2); + } + + public void Test14() + { + var obj = new object(); + var b1 = Source(14); + var b2 = obj.GenericM2(b1); + Sink(b2); // $ hasValueFlow=14 + + var b3 = MyExtensions.GenericM2(obj, b1); + Sink(b3); // $ hasValueFlow=14 + } + + public static T Source(object source) => throw null; + public static void Sink(object o) { } +} + +public class B { } From b133a6b934aa5c8dc61872a9d7f6e52896e90fa4 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 3 Feb 2026 15:40:53 +0100 Subject: [PATCH 36/39] C#: Relax models as data type name validation to accept parenthesis (needed for extension types). --- .../lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll index 61954f63e10b..d358c4aa441a 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll @@ -214,7 +214,7 @@ module ModelValidation { not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and result = "Dubious namespace \"" + namespace + "\" in " + pred + " model." or - not type.regexpMatch("[a-zA-Z0-9_<>,\\+]+") and + not type.regexpMatch("[a-zA-Z0-9_<>,\\(\\)\\+]+") and result = "Dubious type \"" + type + "\" in " + pred + " model." or not name.regexpMatch("[a-zA-Z0-9_<>,\\.]*") and From d42cfd04f41ff949dc22fa702e11a7ae176c60e9 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 3 Feb 2026 15:43:43 +0100 Subject: [PATCH 37/39] C#: Add MaD tests for extensions. --- .../dataflow/external-models/ExternalFlow.cs | 94 ++++++++++ .../external-models/ExternalFlow.expected | 161 ++++++++++++++++++ .../external-models/ExternalFlow.ext.yml | 8 + 3 files changed, 263 insertions(+) diff --git a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs index e28b8fdfcc92..1fa43ba456e5 100644 --- a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs @@ -348,4 +348,98 @@ public void M1() static void Sink(object o) { } } + // Test extensions + public static class TestExtensions + { + extension(object o) + { + public object Method1() => throw null; + public static object StaticMethod1(object s) => throw null; + public object Property1 { get { throw null; } set { throw null; } } + } + + extension(T t) where T : class + { + public T GenericMethod1() => throw null; + public static T GenericStaticMethod1(T t0) => throw null; + public T GenericProperty1 { get { throw null; } set { throw null; } } + } + } + + public class M + { + public void M1() + { + var obj = new object(); + var o1 = obj.Method1(); + Sink(o1); + + var o2 = TestExtensions.Method1(obj); + Sink(o2); + } + + public void M2() + { + var obj = new object(); + var o1 = object.StaticMethod1(obj); + Sink(o1); + + var o2 = TestExtensions.StaticMethod1(obj); + Sink(o2); + } + + public void M3(object o) + { + var obj = new object(); + o.Property1 = obj; + var o1 = o.Property1; + Sink(o1); + } + + public void M4(object o) + { + var obj = new object(); + TestExtensions.set_Property1(o, obj); + var o1 = TestExtensions.get_Property1(o); + Sink(o1); + } + + public void M5() + { + var obj = new object(); + var o1 = obj.GenericMethod1(); + Sink(o1); + + var o2 = TestExtensions.GenericMethod1(obj); + Sink(o2); + } + + public void M6() + { + var obj = new object(); + var o1 = object.GenericStaticMethod1(obj); + Sink(o1); + + var o2 = TestExtensions.GenericStaticMethod1(obj); + Sink(o2); + } + + public void M7(object o) + { + var obj = new object(); + o.GenericProperty1 = obj; + var o1 = o.GenericProperty1; + Sink(o1); + } + + public void M8(object o) + { + var obj = new object(); + TestExtensions.set_GenericProperty1(o, obj); + var o1 = TestExtensions.get_GenericProperty1(o); + Sink(o1); + } + + static void Sink(object o) { } + } } diff --git a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected index 3099a3fec7e6..b5c0dd3b59c9 100644 --- a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected @@ -32,6 +32,14 @@ models | 31 | Summary: My.Qltest; Library; false; GetValue; (); ; Argument[this].SyntheticField[X]; ReturnValue; value; dfc-generated | | 32 | Summary: My.Qltest; Library; false; MixedFlowArgs; (System.Object,System.Object); ; Argument[1]; ReturnValue; value; manual | | 33 | Summary: My.Qltest; Library; false; SetValue; (System.Object); ; Argument[0]; Argument[this].SyntheticField[X]; value; dfc-generated | +| 34 | Summary: My.Qltest; TestExtensions+extension(Object); false; Method1; (System.Object); ; Argument[0]; ReturnValue; value; manual | +| 35 | Summary: My.Qltest; TestExtensions+extension(Object); false; StaticMethod1; (System.Object); ; Argument[0]; ReturnValue; value; manual | +| 36 | Summary: My.Qltest; TestExtensions+extension(Object); false; get_Property1; (System.Object); ; Argument[0].SyntheticField[TestExtensions.Property1]; ReturnValue; value; manual | +| 37 | Summary: My.Qltest; TestExtensions+extension(Object); false; set_Property1; (System.Object,System.Object); ; Argument[1]; Argument[0].SyntheticField[TestExtensions.Property1]; value; manual | +| 38 | Summary: My.Qltest; TestExtensions+extension(T); false; GenericMethod1; (T); ; Argument[0]; ReturnValue; value; manual | +| 39 | Summary: My.Qltest; TestExtensions+extension(T); false; GenericStaticMethod1; (T); ; Argument[0]; ReturnValue; value; manual | +| 40 | Summary: My.Qltest; TestExtensions+extension(T); false; get_GenericProperty1; (T); ; Argument[0].SyntheticField[TestExtensions.GenericProperty1]; ReturnValue; value; manual | +| 41 | Summary: My.Qltest; TestExtensions+extension(T); false; set_GenericProperty1; (T,T); ; Argument[1]; Argument[0].SyntheticField[TestExtensions.GenericProperty1]; value; manual | edges | ExternalFlow.cs:9:20:9:23 | access to local variable arg1 : Object | ExternalFlow.cs:10:29:10:32 | access to local variable arg1 : Object | provenance | | | ExternalFlow.cs:9:27:9:38 | object creation of type Object : Object | ExternalFlow.cs:9:20:9:23 | access to local variable arg1 : Object | provenance | | @@ -150,6 +158,73 @@ edges | ExternalFlow.cs:344:13:344:13 | [post] access to local variable l : Library [synthetic X] : Object | ExternalFlow.cs:345:18:345:18 | access to local variable l : Library [synthetic X] : Object | provenance | | | ExternalFlow.cs:344:24:344:24 | access to local variable o : Object | ExternalFlow.cs:344:13:344:13 | [post] access to local variable l : Library [synthetic X] : Object | provenance | MaD:33 | | ExternalFlow.cs:345:18:345:18 | access to local variable l : Library [synthetic X] : Object | ExternalFlow.cs:345:18:345:29 | call to method GetValue | provenance | MaD:31 | +| ExternalFlow.cs:373:17:373:19 | access to local variable obj : Object | ExternalFlow.cs:374:22:374:24 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:373:17:373:19 | access to local variable obj : Object | ExternalFlow.cs:377:45:377:47 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:373:23:373:34 | object creation of type Object : Object | ExternalFlow.cs:373:17:373:19 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:374:17:374:18 | access to local variable o1 : Object | ExternalFlow.cs:375:18:375:19 | access to local variable o1 | provenance | | +| ExternalFlow.cs:374:22:374:24 | access to local variable obj : Object | ExternalFlow.cs:374:22:374:34 | call to method Method1 : Object | provenance | MaD:34 | +| ExternalFlow.cs:374:22:374:34 | call to method Method1 : Object | ExternalFlow.cs:374:17:374:18 | access to local variable o1 : Object | provenance | | +| ExternalFlow.cs:377:17:377:18 | access to local variable o2 : Object | ExternalFlow.cs:378:18:378:19 | access to local variable o2 | provenance | | +| ExternalFlow.cs:377:22:377:48 | call to method Method1 : Object | ExternalFlow.cs:377:17:377:18 | access to local variable o2 : Object | provenance | | +| ExternalFlow.cs:377:45:377:47 | access to local variable obj : Object | ExternalFlow.cs:377:22:377:48 | call to method Method1 : Object | provenance | MaD:34 | +| ExternalFlow.cs:383:17:383:19 | access to local variable obj : Object | ExternalFlow.cs:384:43:384:45 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:383:17:383:19 | access to local variable obj : Object | ExternalFlow.cs:387:51:387:53 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:383:23:383:34 | object creation of type Object : Object | ExternalFlow.cs:383:17:383:19 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:384:17:384:18 | access to local variable o1 : Object | ExternalFlow.cs:385:18:385:19 | access to local variable o1 | provenance | | +| ExternalFlow.cs:384:22:384:46 | call to method StaticMethod1 : Object | ExternalFlow.cs:384:17:384:18 | access to local variable o1 : Object | provenance | | +| ExternalFlow.cs:384:43:384:45 | access to local variable obj : Object | ExternalFlow.cs:384:22:384:46 | call to method StaticMethod1 : Object | provenance | MaD:35 | +| ExternalFlow.cs:387:17:387:18 | access to local variable o2 : Object | ExternalFlow.cs:388:18:388:19 | access to local variable o2 | provenance | | +| ExternalFlow.cs:387:22:387:54 | call to method StaticMethod1 : Object | ExternalFlow.cs:387:17:387:18 | access to local variable o2 : Object | provenance | | +| ExternalFlow.cs:387:51:387:53 | access to local variable obj : Object | ExternalFlow.cs:387:22:387:54 | call to method StaticMethod1 : Object | provenance | MaD:35 | +| ExternalFlow.cs:393:17:393:19 | access to local variable obj : Object | ExternalFlow.cs:394:27:394:29 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:393:23:393:34 | object creation of type Object : Object | ExternalFlow.cs:393:17:393:19 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:394:13:394:13 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | ExternalFlow.cs:395:22:395:22 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | provenance | | +| ExternalFlow.cs:394:27:394:29 | access to local variable obj : Object | ExternalFlow.cs:394:13:394:13 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | provenance | MaD:37 | +| ExternalFlow.cs:395:17:395:18 | access to local variable o1 : Object | ExternalFlow.cs:396:18:396:19 | access to local variable o1 | provenance | | +| ExternalFlow.cs:395:22:395:22 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | ExternalFlow.cs:395:22:395:32 | access to property Property1 : Object | provenance | MaD:36 | +| ExternalFlow.cs:395:22:395:32 | access to property Property1 : Object | ExternalFlow.cs:395:17:395:18 | access to local variable o1 : Object | provenance | | +| ExternalFlow.cs:401:17:401:19 | access to local variable obj : Object | ExternalFlow.cs:402:45:402:47 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:401:23:401:34 | object creation of type Object : Object | ExternalFlow.cs:401:17:401:19 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:402:42:402:42 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | ExternalFlow.cs:403:51:403:51 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | provenance | | +| ExternalFlow.cs:402:45:402:47 | access to local variable obj : Object | ExternalFlow.cs:402:42:402:42 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | provenance | MaD:37 | +| ExternalFlow.cs:403:17:403:18 | access to local variable o1 : Object | ExternalFlow.cs:404:18:404:19 | access to local variable o1 | provenance | | +| ExternalFlow.cs:403:22:403:52 | call to extension accessor get_Property1 : Object | ExternalFlow.cs:403:17:403:18 | access to local variable o1 : Object | provenance | | +| ExternalFlow.cs:403:51:403:51 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | ExternalFlow.cs:403:22:403:52 | call to extension accessor get_Property1 : Object | provenance | MaD:36 | +| ExternalFlow.cs:409:17:409:19 | access to local variable obj : Object | ExternalFlow.cs:410:22:410:24 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:409:17:409:19 | access to local variable obj : Object | ExternalFlow.cs:413:52:413:54 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:409:23:409:34 | object creation of type Object : Object | ExternalFlow.cs:409:17:409:19 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:410:17:410:18 | access to local variable o1 : Object | ExternalFlow.cs:411:18:411:19 | access to local variable o1 | provenance | | +| ExternalFlow.cs:410:22:410:24 | access to local variable obj : Object | ExternalFlow.cs:410:22:410:41 | call to method GenericMethod1 : Object | provenance | MaD:38 | +| ExternalFlow.cs:410:22:410:41 | call to method GenericMethod1 : Object | ExternalFlow.cs:410:17:410:18 | access to local variable o1 : Object | provenance | | +| ExternalFlow.cs:413:17:413:18 | access to local variable o2 : Object | ExternalFlow.cs:414:18:414:19 | access to local variable o2 | provenance | | +| ExternalFlow.cs:413:22:413:55 | call to method GenericMethod1 : Object | ExternalFlow.cs:413:17:413:18 | access to local variable o2 : Object | provenance | | +| ExternalFlow.cs:413:52:413:54 | access to local variable obj : Object | ExternalFlow.cs:413:22:413:55 | call to method GenericMethod1 : Object | provenance | MaD:38 | +| ExternalFlow.cs:419:17:419:19 | access to local variable obj : Object | ExternalFlow.cs:420:50:420:52 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:419:17:419:19 | access to local variable obj : Object | ExternalFlow.cs:423:58:423:60 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:419:23:419:34 | object creation of type Object : Object | ExternalFlow.cs:419:17:419:19 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:420:17:420:18 | access to local variable o1 : Object | ExternalFlow.cs:421:18:421:19 | access to local variable o1 | provenance | | +| ExternalFlow.cs:420:22:420:53 | call to method GenericStaticMethod1 : Object | ExternalFlow.cs:420:17:420:18 | access to local variable o1 : Object | provenance | | +| ExternalFlow.cs:420:50:420:52 | access to local variable obj : Object | ExternalFlow.cs:420:22:420:53 | call to method GenericStaticMethod1 : Object | provenance | MaD:39 | +| ExternalFlow.cs:423:17:423:18 | access to local variable o2 : Object | ExternalFlow.cs:424:18:424:19 | access to local variable o2 | provenance | | +| ExternalFlow.cs:423:22:423:61 | call to method GenericStaticMethod1 : Object | ExternalFlow.cs:423:17:423:18 | access to local variable o2 : Object | provenance | | +| ExternalFlow.cs:423:58:423:60 | access to local variable obj : Object | ExternalFlow.cs:423:22:423:61 | call to method GenericStaticMethod1 : Object | provenance | MaD:39 | +| ExternalFlow.cs:429:17:429:19 | access to local variable obj : Object | ExternalFlow.cs:430:34:430:36 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:429:23:429:34 | object creation of type Object : Object | ExternalFlow.cs:429:17:429:19 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:430:13:430:13 | [post] access to parameter o : Object [property GenericProperty1] : Object | ExternalFlow.cs:431:22:431:22 | access to parameter o : Object [property GenericProperty1] : Object | provenance | | +| ExternalFlow.cs:430:13:430:13 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | ExternalFlow.cs:431:22:431:22 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | provenance | | +| ExternalFlow.cs:430:34:430:36 | access to local variable obj : Object | ExternalFlow.cs:430:13:430:13 | [post] access to parameter o : Object [property GenericProperty1] : Object | provenance | | +| ExternalFlow.cs:430:34:430:36 | access to local variable obj : Object | ExternalFlow.cs:430:13:430:13 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | provenance | MaD:41 | +| ExternalFlow.cs:431:17:431:18 | access to local variable o1 : Object | ExternalFlow.cs:432:18:432:19 | access to local variable o1 | provenance | | +| ExternalFlow.cs:431:22:431:22 | access to parameter o : Object [property GenericProperty1] : Object | ExternalFlow.cs:431:22:431:39 | access to property GenericProperty1 : Object | provenance | | +| ExternalFlow.cs:431:22:431:22 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | ExternalFlow.cs:431:22:431:39 | access to property GenericProperty1 : Object | provenance | MaD:40 | +| ExternalFlow.cs:431:22:431:39 | access to property GenericProperty1 : Object | ExternalFlow.cs:431:17:431:18 | access to local variable o1 : Object | provenance | | +| ExternalFlow.cs:437:17:437:19 | access to local variable obj : Object | ExternalFlow.cs:438:52:438:54 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:437:23:437:34 | object creation of type Object : Object | ExternalFlow.cs:437:17:437:19 | access to local variable obj : Object | provenance | | +| ExternalFlow.cs:438:49:438:49 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | ExternalFlow.cs:439:58:439:58 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | provenance | | +| ExternalFlow.cs:438:52:438:54 | access to local variable obj : Object | ExternalFlow.cs:438:49:438:49 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | provenance | MaD:41 | +| ExternalFlow.cs:439:17:439:18 | access to local variable o1 : Object | ExternalFlow.cs:440:18:440:19 | access to local variable o1 | provenance | | +| ExternalFlow.cs:439:22:439:59 | call to extension accessor get_GenericProperty1 : Object | ExternalFlow.cs:439:17:439:18 | access to local variable o1 : Object | provenance | | +| ExternalFlow.cs:439:58:439:58 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | ExternalFlow.cs:439:22:439:59 | call to extension accessor get_GenericProperty1 : Object | provenance | MaD:40 | nodes | ExternalFlow.cs:9:20:9:23 | access to local variable arg1 : Object | semmle.label | access to local variable arg1 : Object | | ExternalFlow.cs:9:27:9:38 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | @@ -294,6 +369,80 @@ nodes | ExternalFlow.cs:344:24:344:24 | access to local variable o : Object | semmle.label | access to local variable o : Object | | ExternalFlow.cs:345:18:345:18 | access to local variable l : Library [synthetic X] : Object | semmle.label | access to local variable l : Library [synthetic X] : Object | | ExternalFlow.cs:345:18:345:29 | call to method GetValue | semmle.label | call to method GetValue | +| ExternalFlow.cs:373:17:373:19 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:373:23:373:34 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| ExternalFlow.cs:374:17:374:18 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| ExternalFlow.cs:374:22:374:24 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:374:22:374:34 | call to method Method1 : Object | semmle.label | call to method Method1 : Object | +| ExternalFlow.cs:375:18:375:19 | access to local variable o1 | semmle.label | access to local variable o1 | +| ExternalFlow.cs:377:17:377:18 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| ExternalFlow.cs:377:22:377:48 | call to method Method1 : Object | semmle.label | call to method Method1 : Object | +| ExternalFlow.cs:377:45:377:47 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:378:18:378:19 | access to local variable o2 | semmle.label | access to local variable o2 | +| ExternalFlow.cs:383:17:383:19 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:383:23:383:34 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| ExternalFlow.cs:384:17:384:18 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| ExternalFlow.cs:384:22:384:46 | call to method StaticMethod1 : Object | semmle.label | call to method StaticMethod1 : Object | +| ExternalFlow.cs:384:43:384:45 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:385:18:385:19 | access to local variable o1 | semmle.label | access to local variable o1 | +| ExternalFlow.cs:387:17:387:18 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| ExternalFlow.cs:387:22:387:54 | call to method StaticMethod1 : Object | semmle.label | call to method StaticMethod1 : Object | +| ExternalFlow.cs:387:51:387:53 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:388:18:388:19 | access to local variable o2 | semmle.label | access to local variable o2 | +| ExternalFlow.cs:393:17:393:19 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:393:23:393:34 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| ExternalFlow.cs:394:13:394:13 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | semmle.label | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | +| ExternalFlow.cs:394:27:394:29 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:395:17:395:18 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| ExternalFlow.cs:395:22:395:22 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | semmle.label | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | +| ExternalFlow.cs:395:22:395:32 | access to property Property1 : Object | semmle.label | access to property Property1 : Object | +| ExternalFlow.cs:396:18:396:19 | access to local variable o1 | semmle.label | access to local variable o1 | +| ExternalFlow.cs:401:17:401:19 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:401:23:401:34 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| ExternalFlow.cs:402:42:402:42 | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | semmle.label | [post] access to parameter o : Object [synthetic TestExtensions.Property1] : Object | +| ExternalFlow.cs:402:45:402:47 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:403:17:403:18 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| ExternalFlow.cs:403:22:403:52 | call to extension accessor get_Property1 : Object | semmle.label | call to extension accessor get_Property1 : Object | +| ExternalFlow.cs:403:51:403:51 | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | semmle.label | access to parameter o : Object [synthetic TestExtensions.Property1] : Object | +| ExternalFlow.cs:404:18:404:19 | access to local variable o1 | semmle.label | access to local variable o1 | +| ExternalFlow.cs:409:17:409:19 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:409:23:409:34 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| ExternalFlow.cs:410:17:410:18 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| ExternalFlow.cs:410:22:410:24 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:410:22:410:41 | call to method GenericMethod1 : Object | semmle.label | call to method GenericMethod1 : Object | +| ExternalFlow.cs:411:18:411:19 | access to local variable o1 | semmle.label | access to local variable o1 | +| ExternalFlow.cs:413:17:413:18 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| ExternalFlow.cs:413:22:413:55 | call to method GenericMethod1 : Object | semmle.label | call to method GenericMethod1 : Object | +| ExternalFlow.cs:413:52:413:54 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:414:18:414:19 | access to local variable o2 | semmle.label | access to local variable o2 | +| ExternalFlow.cs:419:17:419:19 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:419:23:419:34 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| ExternalFlow.cs:420:17:420:18 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| ExternalFlow.cs:420:22:420:53 | call to method GenericStaticMethod1 : Object | semmle.label | call to method GenericStaticMethod1 : Object | +| ExternalFlow.cs:420:50:420:52 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:421:18:421:19 | access to local variable o1 | semmle.label | access to local variable o1 | +| ExternalFlow.cs:423:17:423:18 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| ExternalFlow.cs:423:22:423:61 | call to method GenericStaticMethod1 : Object | semmle.label | call to method GenericStaticMethod1 : Object | +| ExternalFlow.cs:423:58:423:60 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:424:18:424:19 | access to local variable o2 | semmle.label | access to local variable o2 | +| ExternalFlow.cs:429:17:429:19 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:429:23:429:34 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| ExternalFlow.cs:430:13:430:13 | [post] access to parameter o : Object [property GenericProperty1] : Object | semmle.label | [post] access to parameter o : Object [property GenericProperty1] : Object | +| ExternalFlow.cs:430:13:430:13 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | semmle.label | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | +| ExternalFlow.cs:430:34:430:36 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:431:17:431:18 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| ExternalFlow.cs:431:22:431:22 | access to parameter o : Object [property GenericProperty1] : Object | semmle.label | access to parameter o : Object [property GenericProperty1] : Object | +| ExternalFlow.cs:431:22:431:22 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | semmle.label | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | +| ExternalFlow.cs:431:22:431:39 | access to property GenericProperty1 : Object | semmle.label | access to property GenericProperty1 : Object | +| ExternalFlow.cs:432:18:432:19 | access to local variable o1 | semmle.label | access to local variable o1 | +| ExternalFlow.cs:437:17:437:19 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:437:23:437:34 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | +| ExternalFlow.cs:438:49:438:49 | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | semmle.label | [post] access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | +| ExternalFlow.cs:438:52:438:54 | access to local variable obj : Object | semmle.label | access to local variable obj : Object | +| ExternalFlow.cs:439:17:439:18 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| ExternalFlow.cs:439:22:439:59 | call to extension accessor get_GenericProperty1 : Object | semmle.label | call to extension accessor get_GenericProperty1 : Object | +| ExternalFlow.cs:439:58:439:58 | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | semmle.label | access to parameter o : Object [synthetic TestExtensions.GenericProperty1] : Object | +| ExternalFlow.cs:440:18:440:19 | access to local variable o1 | semmle.label | access to local variable o1 | subpaths | ExternalFlow.cs:84:29:84:32 | access to local variable objs : null [element] : Object | ExternalFlow.cs:84:35:84:35 | o : Object | ExternalFlow.cs:84:40:84:40 | access to parameter o : Object | ExternalFlow.cs:84:25:84:41 | call to method Map : T[] [element] : Object | invalidModelRow @@ -328,3 +477,15 @@ invalidModelRow | ExternalFlow.cs:324:18:324:44 | call to method GetMyNestedSyntheticField | ExternalFlow.cs:322:21:322:32 | object creation of type Object : Object | ExternalFlow.cs:324:18:324:44 | call to method GetMyNestedSyntheticField | $@ | ExternalFlow.cs:322:21:322:32 | object creation of type Object : Object | object creation of type Object : Object | | ExternalFlow.cs:331:18:331:45 | call to method GetMyFieldOnSyntheticField | ExternalFlow.cs:329:21:329:32 | object creation of type Object : Object | ExternalFlow.cs:331:18:331:45 | call to method GetMyFieldOnSyntheticField | $@ | ExternalFlow.cs:329:21:329:32 | object creation of type Object : Object | object creation of type Object : Object | | ExternalFlow.cs:345:18:345:29 | call to method GetValue | ExternalFlow.cs:343:21:343:32 | object creation of type Object : Object | ExternalFlow.cs:345:18:345:29 | call to method GetValue | $@ | ExternalFlow.cs:343:21:343:32 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:375:18:375:19 | access to local variable o1 | ExternalFlow.cs:373:23:373:34 | object creation of type Object : Object | ExternalFlow.cs:375:18:375:19 | access to local variable o1 | $@ | ExternalFlow.cs:373:23:373:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:378:18:378:19 | access to local variable o2 | ExternalFlow.cs:373:23:373:34 | object creation of type Object : Object | ExternalFlow.cs:378:18:378:19 | access to local variable o2 | $@ | ExternalFlow.cs:373:23:373:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:385:18:385:19 | access to local variable o1 | ExternalFlow.cs:383:23:383:34 | object creation of type Object : Object | ExternalFlow.cs:385:18:385:19 | access to local variable o1 | $@ | ExternalFlow.cs:383:23:383:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:388:18:388:19 | access to local variable o2 | ExternalFlow.cs:383:23:383:34 | object creation of type Object : Object | ExternalFlow.cs:388:18:388:19 | access to local variable o2 | $@ | ExternalFlow.cs:383:23:383:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:396:18:396:19 | access to local variable o1 | ExternalFlow.cs:393:23:393:34 | object creation of type Object : Object | ExternalFlow.cs:396:18:396:19 | access to local variable o1 | $@ | ExternalFlow.cs:393:23:393:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:404:18:404:19 | access to local variable o1 | ExternalFlow.cs:401:23:401:34 | object creation of type Object : Object | ExternalFlow.cs:404:18:404:19 | access to local variable o1 | $@ | ExternalFlow.cs:401:23:401:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:411:18:411:19 | access to local variable o1 | ExternalFlow.cs:409:23:409:34 | object creation of type Object : Object | ExternalFlow.cs:411:18:411:19 | access to local variable o1 | $@ | ExternalFlow.cs:409:23:409:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:414:18:414:19 | access to local variable o2 | ExternalFlow.cs:409:23:409:34 | object creation of type Object : Object | ExternalFlow.cs:414:18:414:19 | access to local variable o2 | $@ | ExternalFlow.cs:409:23:409:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:421:18:421:19 | access to local variable o1 | ExternalFlow.cs:419:23:419:34 | object creation of type Object : Object | ExternalFlow.cs:421:18:421:19 | access to local variable o1 | $@ | ExternalFlow.cs:419:23:419:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:424:18:424:19 | access to local variable o2 | ExternalFlow.cs:419:23:419:34 | object creation of type Object : Object | ExternalFlow.cs:424:18:424:19 | access to local variable o2 | $@ | ExternalFlow.cs:419:23:419:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:432:18:432:19 | access to local variable o1 | ExternalFlow.cs:429:23:429:34 | object creation of type Object : Object | ExternalFlow.cs:432:18:432:19 | access to local variable o1 | $@ | ExternalFlow.cs:429:23:429:34 | object creation of type Object : Object | object creation of type Object : Object | +| ExternalFlow.cs:440:18:440:19 | access to local variable o1 | ExternalFlow.cs:437:23:437:34 | object creation of type Object : Object | ExternalFlow.cs:440:18:440:19 | access to local variable o1 | $@ | ExternalFlow.cs:437:23:437:34 | object creation of type Object : Object | object creation of type Object : Object | diff --git a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ext.yml b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ext.yml index a582a581cf12..3a4d5bcd91b8 100644 --- a/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ext.yml +++ b/csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ext.yml @@ -45,6 +45,14 @@ extensions: - ["My.Qltest", "K", false, "GetMyFieldOnSyntheticField", "()", "", "Argument[this].SyntheticField[My.Qltest.K.MySyntheticField2].Field[My.Qltest.K.MyField]", "ReturnValue", "value", "manual"] - ["My.Qltest", "Library", false, "SetValue", "(System.Object)", "", "Argument[0]", "Argument[this].SyntheticField[X]", "value", "dfc-generated"] - ["My.Qltest", "Library", false, "GetValue", "()", "", "Argument[this].SyntheticField[X]", "ReturnValue", "value", "dfc-generated"] + - ["My.Qltest", "TestExtensions+extension(Object)", false, "Method1", "(System.Object)", "", "Argument[0]", "ReturnValue", "value", "manual"] + - ["My.Qltest", "TestExtensions+extension(Object)", false, "StaticMethod1", "(System.Object)", "", "Argument[0]", "ReturnValue", "value", "manual"] + - ["My.Qltest", "TestExtensions+extension(Object)", false, "get_Property1", "(System.Object)", "", "Argument[0].SyntheticField[TestExtensions.Property1]", "ReturnValue", "value", "manual"] + - ["My.Qltest", "TestExtensions+extension(Object)", false, "set_Property1", "(System.Object,System.Object)", "", "Argument[1]", "Argument[0].SyntheticField[TestExtensions.Property1]", "value", "manual"] + - ["My.Qltest", "TestExtensions+extension(T)", false, "GenericMethod1", "(T)", "", "Argument[0]", "ReturnValue", "value", "manual"] + - ["My.Qltest", "TestExtensions+extension(T)", false, "GenericStaticMethod1", "(T)", "", "Argument[0]", "ReturnValue", "value", "manual"] + - ["My.Qltest", "TestExtensions+extension(T)", false, "get_GenericProperty1", "(T)", "", "Argument[0].SyntheticField[TestExtensions.GenericProperty1]", "ReturnValue", "value", "manual"] + - ["My.Qltest", "TestExtensions+extension(T)", false, "set_GenericProperty1", "(T,T)", "", "Argument[1]", "Argument[0].SyntheticField[TestExtensions.GenericProperty1]", "value", "manual"] - addsTo: pack: codeql/csharp-all From 85791add25bf26dc338ae35049d15863522b2937 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 3 Feb 2026 15:50:30 +0100 Subject: [PATCH 38/39] Fixup of dispatch. --- csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll index 999c6277a490..a83967441d74 100644 --- a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll @@ -88,7 +88,7 @@ private module Internal { TDispatchMethodCall(MethodCall mc) { not isReflectionCall(mc, _, _, _, _) and not mc.isLateBound() and - not isExtensionAccossorCall(mc) + not isExtensionAccessorCall(mc) } or TDispatchAccessorCall(AccessorCall ac) or TDispatchOperatorCall(OperatorCall oc) { not oc.isLateBound() } or @@ -112,7 +112,7 @@ private module Internal { or c instanceof LocalFunctionCall } or - TDispatchExtensionAccessorCall(MethodCall mc) { isExtensionAccossorCall(mc) } + TDispatchExtensionAccessorCall(MethodCall mc) { isExtensionAccessorCall(mc) } cached Expr getCall(DispatchCall dc) { result = dc.(DispatchCallImpl).getCall() } @@ -144,9 +144,7 @@ private module Internal { import Cached - private predicate isExtensionAccossorCall(MethodCall mc) { - exists(ExtensionAccessor a | mc.getTargetAccessor() = a) - } + private predicate isExtensionAccessorCall(MethodCall mc) { exists(mc.getTargetAccessor()) } /** * Holds if `mc` is a reflection call to a method named `name`, where From 5c44c08a98fdc4b28f9b7b149441c2ce84386a86 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 3 Feb 2026 16:03:37 +0100 Subject: [PATCH 39/39] C#: Add PrintAst test for extensions. --- .../library-tests/extension/PrintAst.expected | 404 ++++++++++++++++++ .../library-tests/extension/PrintAst.qlref | 1 + 2 files changed, 405 insertions(+) create mode 100644 csharp/ql/test/library-tests/extension/PrintAst.expected create mode 100644 csharp/ql/test/library-tests/extension/PrintAst.qlref diff --git a/csharp/ql/test/library-tests/extension/PrintAst.expected b/csharp/ql/test/library-tests/extension/PrintAst.expected new file mode 100644 index 000000000000..962c7c9b1583 --- /dev/null +++ b/csharp/ql/test/library-tests/extension/PrintAst.expected @@ -0,0 +1,404 @@ +extensions.cs: +# 4| [Class] MyExtensions +# 6| 4: [ExtensionType] extension(String) +#-----| 2: (Parameters) +# 6| 0: [Parameter] s +# 6| -1: [TypeMention] string +# 8| 4: [Property] Prop1 +# 8| -1: [TypeMention] bool +# 8| 3: [ExtensionCallable,Getter] get_Prop1 +#-----| 2: (Parameters) +# 6| 0: [Parameter] s +# 6| -1: [TypeMention] string +# 8| 4: [GTExpr] ... > ... +# 8| 0: [PropertyCall] access to property Length +# 8| -1: [SyntheticParameterAccess] access to synthetic parameter s +# 8| 1: [IntLiteral] 0 +# 9| 5: [Property] Prop2 +# 9| -1: [TypeMention] bool +# 9| 3: [ExtensionCallable,Getter] get_Prop2 +#-----| 2: (Parameters) +# 6| 0: [Parameter] s +# 6| -1: [TypeMention] string +# 9| 4: [BlockStmt] {...} +# 9| 0: [ReturnStmt] return ...; +# 9| 0: [BoolLiteral] true +# 9| 4: [ExtensionCallable,Setter] set_Prop2 +#-----| 2: (Parameters) +# 6| 0: [Parameter] s +# 6| -1: [TypeMention] string +# 9| 1: [Parameter] value +# 9| 4: [BlockStmt] {...} +# 10| 6: [Property] StaticProp1 +# 10| -1: [TypeMention] bool +# 10| 3: [ExtensionCallable,Getter] get_StaticProp1 +# 10| 4: [BlockStmt] {...} +# 10| 0: [ReturnStmt] return ...; +# 10| 0: [BoolLiteral] false +# 11| 7: [ExtensionMethod] M1 +# 11| -1: [TypeMention] bool +#-----| 2: (Parameters) +# 6| 0: [Parameter] s +# 6| -1: [TypeMention] string +# 11| 4: [IsExpr] ... is ... +# 11| 0: [SyntheticParameterAccess] access to synthetic parameter s +# 11| 1: [NotPatternExpr] not ... +# 11| 0: [ConstantPatternExpr,NullLiteral] null +# 12| 8: [ExtensionMethod] M2 +# 12| -1: [TypeMention] string +#-----| 2: (Parameters) +# 6| 0: [Parameter] s +# 6| -1: [TypeMention] string +# 12| 1: [Parameter] other +# 12| -1: [TypeMention] string +# 12| 4: [BlockStmt] {...} +# 12| 0: [ReturnStmt] return ...; +# 12| 0: [AddExpr] ... + ... +# 12| 0: [SyntheticParameterAccess] access to synthetic parameter s +# 12| 1: [ParameterAccess] access to parameter other +# 13| 9: [ExtensionMethod] StaticM1 +# 13| -1: [TypeMention] int +# 13| 4: [BlockStmt] {...} +# 13| 0: [ReturnStmt] return ...; +# 13| 0: [IntLiteral] 0 +# 14| 10: [ExtensionMethod] StaticM2 +# 14| -1: [TypeMention] int +#-----| 2: (Parameters) +# 14| 0: [Parameter] x +# 14| -1: [TypeMention] string +# 14| 4: [BlockStmt] {...} +# 14| 0: [ReturnStmt] return ...; +# 14| 0: [PropertyCall] access to property Length +# 14| -1: [ParameterAccess] access to parameter x +# 15| 11: [ExtensionCallable,MulOperator] * +# 15| -1: [TypeMention] string +#-----| 2: (Parameters) +# 15| 0: [Parameter] a +# 15| -1: [TypeMention] int +# 15| 1: [Parameter] b +# 15| -1: [TypeMention] string +# 15| 4: [BlockStmt] {...} +# 15| 0: [ReturnStmt] return ...; +# 15| 0: [StringLiteralUtf16] "" +# 16| 14: [ExtensionMethod] StringGenericM1`1 +# 16| -1: [TypeMention] T +#-----| 1: (Type parameters) +# 16| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 6| 0: [Parameter] s +# 6| -1: [TypeMention] string +# 16| 1: [Parameter] t +# 16| -1: [TypeMention] T +# 16| 2: [Parameter] o +# 16| -1: [TypeMention] object +# 16| 4: [BlockStmt] {...} +# 16| 0: [ReturnStmt] return ...; +# 16| 0: [ParameterAccess] access to parameter t +# 19| 5: [ExtensionType] extension(Object) +# 21| 4: [ExtensionMethod] StaticObjectM1 +# 21| -1: [TypeMention] int +# 21| 4: [BlockStmt] {...} +# 21| 0: [ReturnStmt] return ...; +# 21| 0: [IntLiteral] 0 +# 22| 5: [ExtensionMethod] StaticObjectM2 +# 22| -1: [TypeMention] int +#-----| 2: (Parameters) +# 22| 0: [Parameter] s +# 22| -1: [TypeMention] string +# 22| 4: [BlockStmt] {...} +# 22| 0: [ReturnStmt] return ...; +# 22| 0: [PropertyCall] access to property Length +# 22| -1: [ParameterAccess] access to parameter s +# 23| 6: [Property] StaticProp +# 23| -1: [TypeMention] bool +# 23| 3: [ExtensionCallable,Getter] get_StaticProp +# 23| 4: [BoolLiteral] true +# 26| 8: [ExtensionType] extension(T)`1 +#-----| 1: (Type parameters) +# 26| 0: [TypeParameter] T +#-----| 2: (Parameters) +# 26| 0: [Parameter] t +# 26| -1: [TypeMention] T +# 28| 4: [Property] GenericProp1 +# 28| -1: [TypeMention] bool +# 28| 3: [ExtensionCallable,Getter] get_GenericProp1 +#-----| 2: (Parameters) +# 26| 0: [Parameter] t +# 26| -1: [TypeMention] T +# 28| 4: [IsExpr] ... is ... +# 28| 0: [SyntheticParameterAccess] access to synthetic parameter t +# 28| 1: [NotPatternExpr] not ... +# 28| 0: [ConstantPatternExpr,NullLiteral] null +# 29| 5: [Property] GenericProp2 +# 29| -1: [TypeMention] bool +# 29| 3: [ExtensionCallable,Getter] get_GenericProp2 +#-----| 2: (Parameters) +# 26| 0: [Parameter] t +# 26| -1: [TypeMention] T +# 29| 4: [BlockStmt] {...} +# 29| 0: [ReturnStmt] return ...; +# 29| 0: [BoolLiteral] true +# 29| 4: [ExtensionCallable,Setter] set_GenericProp2 +#-----| 2: (Parameters) +# 26| 0: [Parameter] t +# 26| -1: [TypeMention] T +# 29| 1: [Parameter] value +# 29| 4: [BlockStmt] {...} +# 30| 6: [ExtensionMethod] GenericM1 +# 30| -1: [TypeMention] bool +#-----| 2: (Parameters) +# 26| 0: [Parameter] t +# 26| -1: [TypeMention] T +# 30| 4: [IsExpr] ... is ... +# 30| 0: [SyntheticParameterAccess] access to synthetic parameter t +# 30| 1: [NotPatternExpr] not ... +# 30| 0: [ConstantPatternExpr,NullLiteral] null +# 31| 7: [ExtensionMethod] GenericM2`1 +# 31| -1: [TypeMention] Void +#-----| 1: (Type parameters) +# 31| 0: [TypeParameter] S +#-----| 2: (Parameters) +# 26| 0: [Parameter] t +# 26| -1: [TypeMention] T +# 31| 1: [Parameter] other +# 31| -1: [TypeMention] S +# 31| 4: [BlockStmt] {...} +# 32| 8: [ExtensionMethod] GenericStaticM1 +# 32| -1: [TypeMention] Void +#-----| 2: (Parameters) +# 26| 0: [Parameter] t +# 26| -1: [TypeMention] T +# 32| 4: [BlockStmt] {...} +# 33| 9: [ExtensionMethod] GenericStaticM2`1 +# 33| -1: [TypeMention] Void +#-----| 1: (Type parameters) +# 33| 0: [TypeParameter] S +#-----| 2: (Parameters) +# 33| 0: [Parameter] other +# 33| -1: [TypeMention] S +# 33| 4: [BlockStmt] {...} +# 34| 10: [AddOperator,ExtensionCallable] + +# 34| -1: [TypeMention] T +#-----| 2: (Parameters) +# 34| 0: [Parameter] a +# 34| -1: [TypeMention] T +# 34| 1: [Parameter] b +# 34| -1: [TypeMention] T +# 34| 4: [BlockStmt] {...} +# 34| 0: [ReturnStmt] return ...; +# 34| 0: [NullLiteral] null +# 38| [Class] ClassicExtensions +# 40| 4: [ExtensionMethod] M3 +# 40| -1: [TypeMention] bool +#-----| 2: (Parameters) +# 40| 0: [Parameter] s +# 40| -1: [TypeMention] string +# 40| 4: [IsExpr] ... is ... +# 40| 0: [ParameterAccess] access to parameter s +# 40| 1: [NotPatternExpr] not ... +# 40| 0: [ConstantPatternExpr,NullLiteral] null +# 43| [Class] C +# 45| 6: [Method] CallingExtensions +# 45| -1: [TypeMention] Void +# 46| 4: [BlockStmt] {...} +# 47| 0: [LocalVariableDeclStmt] ... ...; +# 47| 0: [LocalVariableDeclAndInitExpr] String s = ... +# 47| -1: [TypeMention] string +# 47| 0: [LocalVariableAccess] access to local variable s +# 47| 1: [StringLiteralUtf16] "Hello World." +# 50| 1: [LocalVariableDeclStmt] ... ...; +# 50| 0: [LocalVariableDeclAndInitExpr] Boolean x11 = ... +# 50| -1: [TypeMention] bool +# 50| 0: [LocalVariableAccess] access to local variable x11 +# 50| 1: [ExtensionPropertyCall] access to property Prop1 +# 50| -1: [LocalVariableAccess] access to local variable s +# 51| 2: [LocalVariableDeclStmt] ... ...; +# 51| 0: [LocalVariableDeclAndInitExpr] Boolean x12 = ... +# 51| -1: [TypeMention] bool +# 51| 0: [LocalVariableAccess] access to local variable x12 +# 51| 1: [ExtensionPropertyCall] access to property Prop2 +# 51| -1: [LocalVariableAccess] access to local variable s +# 52| 3: [ExprStmt] ...; +# 52| 0: [AssignExpr] ... = ... +# 52| 0: [ExtensionPropertyCall] access to property Prop2 +# 52| -1: [LocalVariableAccess] access to local variable s +# 52| 1: [BoolLiteral] true +# 53| 4: [LocalVariableDeclStmt] ... ...; +# 53| 0: [LocalVariableDeclAndInitExpr] Boolean x13 = ... +# 53| -1: [TypeMention] bool +# 53| 0: [LocalVariableAccess] access to local variable x13 +# 53| 1: [ExtensionPropertyCall] access to property StaticProp1 +# 53| -1: [TypeAccess] access to type String +# 53| 0: [TypeMention] string +# 54| 5: [LocalVariableDeclStmt] ... ...; +# 54| 0: [LocalVariableDeclAndInitExpr] Boolean x14 = ... +# 54| -1: [TypeMention] bool +# 54| 0: [LocalVariableAccess] access to local variable x14 +# 54| 1: [ExtensionPropertyCall] access to property StaticProp +# 54| -1: [TypeAccess] access to type Object +# 54| 0: [TypeMention] object +# 57| 6: [LocalVariableDeclStmt] ... ...; +# 57| 0: [LocalVariableDeclAndInitExpr] Boolean x21 = ... +# 57| -1: [TypeMention] bool +# 57| 0: [LocalVariableAccess] access to local variable x21 +# 57| 1: [MethodCall] call to method M1 +# 57| -1: [LocalVariableAccess] access to local variable s +# 58| 7: [LocalVariableDeclStmt] ... ...; +# 58| 0: [LocalVariableDeclAndInitExpr] String x22 = ... +# 58| -1: [TypeMention] string +# 58| 0: [LocalVariableAccess] access to local variable x22 +# 58| 1: [MethodCall] call to method M2 +# 58| -1: [LocalVariableAccess] access to local variable s +# 58| 0: [StringLiteralUtf16] "!!!" +# 59| 8: [LocalVariableDeclStmt] ... ...; +# 59| 0: [LocalVariableDeclAndInitExpr] Int32 x23 = ... +# 59| -1: [TypeMention] int +# 59| 0: [LocalVariableAccess] access to local variable x23 +# 59| 1: [MethodCall] call to method StaticM1 +# 59| -1: [TypeAccess] access to type String +# 59| 0: [TypeMention] string +# 60| 9: [LocalVariableDeclStmt] ... ...; +# 60| 0: [LocalVariableDeclAndInitExpr] Int32 x24 = ... +# 60| -1: [TypeMention] int +# 60| 0: [LocalVariableAccess] access to local variable x24 +# 60| 1: [MethodCall] call to method StaticM2 +# 60| -1: [TypeAccess] access to type String +# 60| 0: [TypeMention] string +# 60| 0: [LocalVariableAccess] access to local variable s +# 61| 10: [LocalVariableDeclStmt] ... ...; +# 61| 0: [LocalVariableDeclAndInitExpr] Int32 x25 = ... +# 61| -1: [TypeMention] int +# 61| 0: [LocalVariableAccess] access to local variable x25 +# 61| 1: [MethodCall] call to method StaticObjectM1 +# 61| -1: [TypeAccess] access to type Object +# 61| 0: [TypeMention] object +# 62| 11: [LocalVariableDeclStmt] ... ...; +# 62| 0: [LocalVariableDeclAndInitExpr] Int32 x26 = ... +# 62| -1: [TypeMention] int +# 62| 0: [LocalVariableAccess] access to local variable x26 +# 62| 1: [MethodCall] call to method StaticObjectM2 +# 62| -1: [TypeAccess] access to type Object +# 62| 0: [TypeMention] object +# 62| 0: [LocalVariableAccess] access to local variable s +# 65| 12: [LocalVariableDeclStmt] ... ...; +# 65| 0: [LocalVariableDeclAndInitExpr] String x30 = ... +# 65| -1: [TypeMention] string +# 65| 0: [LocalVariableAccess] access to local variable x30 +# 65| 1: [ExtensionOperatorCall] call to operator * +# 65| 0: [IntLiteral] 3 +# 65| 1: [LocalVariableAccess] access to local variable s +# 68| 13: [LocalVariableDeclStmt] ... ...; +# 68| 0: [LocalVariableDeclAndInitExpr] Boolean y = ... +# 68| -1: [TypeMention] bool +# 68| 0: [LocalVariableAccess] access to local variable y +# 68| 1: [MethodCall] call to method M3 +# 68| -1: [LocalVariableAccess] access to local variable s +# 71| 14: [ExprStmt] ...; +# 71| 0: [MethodCall] call to method M1 +# 71| -1: [TypeAccess] access to type MyExtensions +# 71| 0: [TypeMention] MyExtensions +# 71| 0: [LocalVariableAccess] access to local variable s +# 72| 15: [ExprStmt] ...; +# 72| 0: [MethodCall] call to method M2 +# 72| -1: [TypeAccess] access to type MyExtensions +# 72| 0: [TypeMention] MyExtensions +# 72| 0: [LocalVariableAccess] access to local variable s +# 72| 1: [StringLiteralUtf16] "!!!" +# 73| 16: [ExprStmt] ...; +# 73| 0: [MethodCall] call to method StaticM1 +# 73| -1: [TypeAccess] access to type MyExtensions +# 73| 0: [TypeMention] MyExtensions +# 74| 17: [ExprStmt] ...; +# 74| 0: [MethodCall] call to method StaticM2 +# 74| -1: [TypeAccess] access to type MyExtensions +# 74| 0: [TypeMention] MyExtensions +# 74| 0: [LocalVariableAccess] access to local variable s +# 75| 18: [ExprStmt] ...; +# 75| 0: [MethodCall] call to method StaticObjectM1 +# 75| -1: [TypeAccess] access to type MyExtensions +# 75| 0: [TypeMention] MyExtensions +# 76| 19: [ExprStmt] ...; +# 76| 0: [MethodCall] call to method StaticObjectM2 +# 76| -1: [TypeAccess] access to type MyExtensions +# 76| 0: [TypeMention] MyExtensions +# 76| 0: [LocalVariableAccess] access to local variable s +# 79| 20: [ExprStmt] ...; +# 79| 0: [ExtensionOperatorCall] call to operator * +# 79| -1: [TypeAccess] access to type MyExtensions +# 79| 0: [TypeMention] MyExtensions +# 79| 0: [IntLiteral] 3 +# 79| 1: [LocalVariableAccess] access to local variable s +# 82| 21: [ExprStmt] ...; +# 82| 0: [MethodCall] call to extension accessor get_Prop1 +# 82| -1: [TypeAccess] access to type MyExtensions +# 82| 0: [TypeMention] MyExtensions +# 82| 0: [LocalVariableAccess] access to local variable s +# 83| 22: [ExprStmt] ...; +# 83| 0: [MethodCall] call to extension accessor get_Prop2 +# 83| -1: [TypeAccess] access to type MyExtensions +# 83| 0: [TypeMention] MyExtensions +# 83| 0: [LocalVariableAccess] access to local variable s +# 84| 23: [ExprStmt] ...; +# 84| 0: [MethodCall] call to extension accessor set_Prop2 +# 84| -1: [TypeAccess] access to type MyExtensions +# 84| 0: [TypeMention] MyExtensions +# 84| 0: [LocalVariableAccess] access to local variable s +# 84| 1: [BoolLiteral] false +# 85| 24: [ExprStmt] ...; +# 85| 0: [MethodCall] call to extension accessor get_StaticProp +# 85| -1: [TypeAccess] access to type MyExtensions +# 85| 0: [TypeMention] MyExtensions +# 88| 7: [Method] CallingGenericExtensions +# 88| -1: [TypeMention] Void +# 89| 4: [BlockStmt] {...} +# 90| 0: [LocalVariableDeclStmt] ... ...; +# 90| 0: [LocalVariableDeclAndInitExpr] String s = ... +# 90| -1: [TypeMention] string +# 90| 0: [LocalVariableAccess] access to local variable s +# 90| 1: [StringLiteralUtf16] "Hello Generic World." +# 91| 1: [LocalVariableDeclStmt] ... ...; +# 91| 0: [LocalVariableDeclAndInitExpr] Object o = ... +# 91| -1: [TypeMention] object +# 91| 0: [LocalVariableAccess] access to local variable o +# 91| 1: [ObjectCreation] object creation of type Object +# 91| 0: [TypeMention] object +# 94| 2: [ExprStmt] ...; +# 94| 0: [MethodCall] call to method GenericM1 +# 94| -1: [LocalVariableAccess] access to local variable o +# 95| 3: [ExprStmt] ...; +# 95| 0: [MethodCall] call to method GenericM1 +# 95| -1: [LocalVariableAccess] access to local variable s +# 98| 4: [ExprStmt] ...; +# 98| 0: [MethodCall] call to method GenericM1 +# 98| -1: [TypeAccess] access to type MyExtensions +# 98| 0: [TypeMention] MyExtensions +# 98| 0: [LocalVariableAccess] access to local variable o +# 99| 5: [ExprStmt] ...; +# 99| 0: [MethodCall] call to method GenericM1 +# 99| -1: [TypeAccess] access to type MyExtensions +# 99| 0: [TypeMention] MyExtensions +# 99| 0: [LocalVariableAccess] access to local variable s +# 101| 6: [ExprStmt] ...; +# 101| 0: [MethodCall] call to method GenericM2 +# 101| -1: [LocalVariableAccess] access to local variable o +# 101| 0: [IntLiteral] 42 +# 102| 7: [ExprStmt] ...; +# 102| 0: [MethodCall] call to method GenericM2 +# 102| -1: [TypeAccess] access to type MyExtensions +# 102| 0: [TypeMention] MyExtensions +# 102| 0: [LocalVariableAccess] access to local variable o +# 102| 1: [IntLiteral] 42 +# 104| 8: [ExprStmt] ...; +# 104| 0: [MethodCall] call to method StringGenericM1 +# 104| -1: [LocalVariableAccess] access to local variable s +# 104| 0: [IntLiteral] 7 +# 104| 1: [ObjectCreation] object creation of type Object +# 104| 0: [TypeMention] object +# 105| 9: [ExprStmt] ...; +# 105| 0: [MethodCall] call to method StringGenericM1 +# 105| -1: [TypeAccess] access to type MyExtensions +# 105| 0: [TypeMention] MyExtensions +# 105| 0: [LocalVariableAccess] access to local variable s +# 105| 1: [StringLiteralUtf16] "test" +# 105| 2: [ObjectCreation] object creation of type Object +# 105| 0: [TypeMention] object diff --git a/csharp/ql/test/library-tests/extension/PrintAst.qlref b/csharp/ql/test/library-tests/extension/PrintAst.qlref new file mode 100644 index 000000000000..f867dd01f9f8 --- /dev/null +++ b/csharp/ql/test/library-tests/extension/PrintAst.qlref @@ -0,0 +1 @@ +shared/PrintAst.ql \ No newline at end of file