Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,10 @@ public override void Populate(TextWriter trapFile)

trapFile.accessors(this, kind, Symbol.Name, parent, unboundAccessor);

foreach (var l in Locations)
trapFile.accessor_location(this, l);
if (Context.ExtractLocation(Symbol))
{
WriteLocationsToTrap(trapFile.accessor_location, this, Locations);
}

Overrides(trapFile);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ public override void Populate(TextWriter trapFile)
{
var type = Type.Create(Context, Symbol.AttributeClass);
trapFile.attributes(this, kind, type.TypeRef, entity);
trapFile.attribute_location(this, Location);
WriteLocationToTrap(trapFile.attribute_location, this, Location);

if (attributeSyntax is not null)
{
trapFile.attribute_location(this, Assembly.CreateOutputAssembly(Context));
WriteLocationToTrap(trapFile.attribute_location, this, Assembly.CreateOutputAssembly(Context));
Copy link
Preview

Copilot AI Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This call to WriteLocationToTrap appears incorrect. The second parameter should be the location, but Assembly.CreateOutputAssembly(Context) returns an Assembly entity, not a Location. This will likely cause a compilation error or runtime issue.

Suggested change
WriteLocationToTrap(trapFile.attribute_location, this, Assembly.CreateOutputAssembly(Context));
WriteLocationToTrap(trapFile.attribute_location, this, attributeSyntax.GetLocation());

Copilot uses AI. Check for mistakes.


TypeMention.Create(Context, attributeSyntax.Name, this, type);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -52,6 +54,22 @@ public string DebugContents
}
}

protected static void WriteLocationToTrap<T1>(Action<T1, Location> writeAction, T1 entity, Location l)
{
if (l is not EmptyLocation)
{
writeAction(entity, l);
}
}

protected static void WriteLocationsToTrap<T1>(Action<T1, Location> writeAction, T1 entity, IEnumerable<Location> 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();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ private CommentBlock(Context cx, Comments.CommentBlock init)
public override void Populate(TextWriter trapFile)
{
trapFile.commentblock(this);
trapFile.commentblock_location(this, Context.CreateLocation(Symbol.Location));
WriteLocationToTrap(trapFile.commentblock_location, this, Context.CreateLocation(Symbol.Location));
Symbol.CommentLines.ForEach((l, child) => trapFile.commentblock_child(this, l, child));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public override void Populate(TextWriter trapFile)
{
location = Context.CreateLocation(Location);
trapFile.commentline(this, Type == CommentLineType.MultilineContinuation ? CommentLineType.Multiline : Type, Text, RawText);
trapFile.commentline_location(this, location);
WriteLocationToTrap(trapFile.commentline_location, this, location);
}

public override Microsoft.CodeAnalysis.Location? ReportingLocation => location?.Symbol;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public override void Populate(TextWriter trapFile)
ContainingType!.PopulateGenerics();

trapFile.constructors(this, Symbol.ContainingType.Name, ContainingType, (Constructor)OriginalDefinition);
trapFile.constructor_location(this, Location);
WriteLocationToTrap(trapFile.constructor_location, this, Location);

if (MakeSynthetic)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public override void Populate(TextWriter trapFile)
ContainingType!.PopulateGenerics();

trapFile.destructors(this, $"~{Symbol.ContainingType.Name}", ContainingType, OriginalDefinition(Context, this, Symbol));
trapFile.destructor_location(this, Location);
WriteLocationToTrap(trapFile.destructor_location, this, Location);
}

private static new Destructor OriginalDefinition(Context cx, Destructor original, IMethodSymbol symbol)
Expand Down
6 changes: 4 additions & 2 deletions csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ public override void Populate(TextWriter trapFile)
TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier!.Name, this, explicitInterface);
}

foreach (var l in Locations)
trapFile.event_location(this, l);
if (Context.ExtractLocation(Symbol))
{
WriteLocationsToTrap(trapFile.event_location, this, Locations);
}

foreach (var syntaxType in declSyntaxReferences
.OfType<VariableDeclaratorSyntax>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ public override void Populate(TextWriter trapFile)

trapFile.event_accessors(this, kind, Symbol.Name, parent, unboundAccessor);

foreach (var l in Locations)
trapFile.event_accessor_location(this, l);
if (Context.ExtractLocation(Symbol))
{
WriteLocationsToTrap(trapFile.event_accessor_location, this, Locations);
}

Overrides(trapFile);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ protected sealed override void Populate(TextWriter trapFile)
trapFile.expr_parent_top_level(this, info.Child, info.Parent);
else
trapFile.expr_parent(this, info.Child, info.Parent);

trapFile.expr_location(this, Location);

if (Type.HasValue && !Type.Value.HasObliviousNullability())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ public override void Populate(TextWriter trapFile)
}
}

foreach (var l in Locations)
trapFile.field_location(this, l);
WriteLocationsToTrap(trapFile.field_location, this, Locations);

if (!IsSourceDeclaration || !Symbol.FromSource())
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ public override void Populate(TextWriter trapFile)

var type = Type.Create(Context, Symbol.Type);
trapFile.indexers(this, Symbol.GetName(useMetadataName: true), ContainingType!, type.TypeRef, OriginalDefinition);
foreach (var l in Locations)
trapFile.indexer_location(this, l);
if (Context.ExtractLocation(Symbol))
{
WriteLocationsToTrap(trapFile.indexer_location, this, Locations);
}

var getter = BodyDeclaringSymbol.GetMethod;
var setter = BodyDeclaringSymbol.SetMethod;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void PopulateManual(Expression parent, bool isVar)
trapFile.localvars(this, Kinds.VariableKind.None, Symbol.Name, @var, Type.Create(Context, parent.Type).TypeRef, parent);
}

trapFile.localvar_location(this, Location);
WriteLocationToTrap(trapFile.localvar_location, this, Location);

DefineConstantValue(trapFile);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

namespace Semmle.Extraction.CSharp.Entities
{
public class GeneratedLocation : SourceLocation
public class EmptyLocation : SourceLocation
{
private readonly File generatedFile;

private GeneratedLocation(Context cx)
private EmptyLocation(Context cx)
: base(cx, null)
{
generatedFile = GeneratedFile.Create(cx);
Expand All @@ -26,15 +26,16 @@ public override void WriteId(EscapingTextWriter trapFile)

public override int GetHashCode() => 98732567;

public override bool Equals(object? obj) => obj is not null && obj.GetType() == typeof(GeneratedLocation);
public override bool Equals(object? obj) => obj is not null && obj.GetType() == typeof(EmptyLocation);

public static GeneratedLocation Create(Context cx) => GeneratedLocationFactory.Instance.CreateEntity(cx, typeof(GeneratedLocation), null);
public static EmptyLocation Create(Context cx)
=> EmptyLocationFactory.Instance.CreateEntity(cx, typeof(EmptyLocation), null);

private class GeneratedLocationFactory : CachedEntityFactory<string?, GeneratedLocation>
private class EmptyLocationFactory : CachedEntityFactory<string?, EmptyLocation>
{
public static GeneratedLocationFactory Instance { get; } = new GeneratedLocationFactory();
public static EmptyLocationFactory Instance { get; } = new EmptyLocationFactory();

public override GeneratedLocation Create(Context cx, string? init) => new GeneratedLocation(cx);
public override EmptyLocation Create(Context cx, string? init) => new EmptyLocation(cx);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public override void Populate(TextWriter trapFile)

var ns = Namespace.Create(Context, @namespace);
trapFile.namespace_declarations(this, ns);
trapFile.namespace_declaration_location(this, Context.CreateLocation(node.Name.GetLocation()));
WriteLocationToTrap(trapFile.namespace_declaration_location, this, Context.CreateLocation(node.Name.GetLocation()));

var visitor = new Populators.TypeOrNamespaceVisitor(Context, trapFile, this);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ public override void Populate(TextWriter trapFile)
}
}

foreach (var l in Locations)
trapFile.method_location(this, l);
if (Context.ExtractLocation(Symbol))
{
WriteLocationsToTrap(trapFile.method_location, this, Locations);
}

PopulateGenerics(trapFile);
Overrides(trapFile);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,16 @@ public override void Populate(TextWriter trapFile)
trapFile.@params(this, Name, type.TypeRef, Ordinal, ParamKind, Parent!, Original);

foreach (var l in Symbol.Locations)
trapFile.param_location(this, Context.CreateLocation(l));
{
WriteLocationToTrap(trapFile.param_location, this, Context.CreateLocation(l));
}

if (!Symbol.Locations.Any() &&
Symbol.ContainingSymbol is IMethodSymbol ms &&
ms.Name == WellKnownMemberNames.TopLevelStatementsEntryPointMethodName &&
ms.ContainingType.Name == WellKnownMemberNames.TopLevelStatementsEntryPointTypeName)
{
trapFile.param_location(this, Context.CreateLocation());
WriteLocationToTrap(trapFile.param_location, this, Context.CreateLocation());
}

if (Symbol.HasExplicitDefaultValue && Context.Defines(Symbol))
Expand Down Expand Up @@ -247,7 +249,6 @@ public override void Populate(TextWriter trapFile)
var typeKey = VarargsType.Create(Context);
// !! Maybe originaldefinition is wrong
Copy link
Preview

Copilot AI Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment has a spelling error: 'originaldefinition' should be 'original definition' (two words).

Suggested change
// !! Maybe originaldefinition is wrong
// !! Maybe original definition is wrong

Copilot uses AI. Check for mistakes.

trapFile.@params(this, "", typeKey, Ordinal, Kind.None, Parent!, this);
trapFile.param_location(this, GeneratedLocation.Create(Context));
}

protected override int Ordinal => ((Method)Parent!).OriginalDefinition.Symbol.Parameters.Length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public sealed override void Populate(TextWriter trapFile)
PopulatePreprocessor(trapFile);

trapFile.preprocessor_directive_active(this, Symbol.IsActive);
trapFile.preprocessor_directive_location(this, Context.CreateLocation(ReportingLocation));
WriteLocationToTrap(trapFile.preprocessor_directive_location, this, Context.CreateLocation(ReportingLocation));

var compilation = Compilation.Create(Context);
trapFile.preprocessor_directive_compilation(this, compilation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ public override void Populate(TextWriter trapFile)
TypeMention.Create(Context, syntax.ExplicitInterfaceSpecifier!.Name, this, explicitInterface);
}

foreach (var l in Locations)
trapFile.property_location(this, l);
if (Context.ExtractLocation(Symbol))
{
WriteLocationsToTrap(trapFile.property_location, this, Locations);
}

if (IsSourceDeclaration && Symbol.FromSource())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ private DynamicType(Context cx, IDynamicTypeSymbol init)
public override void Populate(TextWriter trapFile)
{
trapFile.types(this, Kinds.TypeKind.DYNAMIC, "dynamic");
trapFile.type_location(this, Location);
WriteLocationToTrap(trapFile.type_location, this, Location);

trapFile.has_modifiers(this, Modifier.Create(Context, "public"));
trapFile.parent_namespace(this, Namespace.Create(Context, Context.Compilation.GlobalNamespace));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ public override void Populate(TextWriter trapFile)
// Class location
if (!Symbol.IsGenericType || Symbol.IsReallyUnbound())
{
foreach (var l in Locations)
trapFile.type_location(this, l);
WriteLocationsToTrap(trapFile.type_location, this, Locations);
}

if (Symbol.IsAnonymousType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public override void Populate(TextWriter trapFile)
// about what locations are available for a tuple type.
// Sometimes it's the source code, and sometimes it's empty.
foreach (var l in Symbol.Locations)
trapFile.type_location(this, Context.CreateLocation(l));
WriteLocationToTrap(trapFile.type_location, this, Context.CreateLocation(l));
}

private readonly Lazy<Field?[]> tupleElementsLazy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public override void Populate(TextWriter trapFile)

foreach (var l in Symbol.Locations)
{
trapFile.type_location(this, Context.CreateLocation(l));
WriteLocationToTrap(trapFile.type_location, this, Context.CreateLocation(l));
}

if (IsSourceDeclaration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ public override void Populate(TextWriter trapFile)
returnType.TypeRef,
(UserOperator)OriginalDefinition);

foreach (var l in Locations)
trapFile.operator_location(this, l);
WriteLocationsToTrap(trapFile.operator_location, this, Locations);

if (IsSourceDeclaration)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ private void DoAnalyseCompilation()

compilationEntity = Entities.Compilation.Create(cx);

// Ensure that the empty location is always created.
Entities.EmptyLocation.Create(cx);

ExtractionContext.CompilationInfos.ForEach(ci => trapWriter.Writer.compilation_info(compilationEntity, ci.key, ci.value));

ReportProgressTaskDone(currentTaskId, assemblyPath, trapWriter.TrapFile, stopwatch.Elapsed, AnalysisAction.Extracted);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,10 @@ public bool Defines(ISymbol symbol) =>
!SymbolEqualityComparer.Default.Equals(symbol, symbol.OriginalDefinition) ||
scope.InScope(symbol);

public bool ExtractLocation(ISymbol symbol) =>
SymbolEqualityComparer.Default.Equals(symbol, symbol.OriginalDefinition) &&
scope.InScope(symbol);

/// <summary>
/// Runs the given action <paramref name="a"/>, guarding for trap duplication
/// based on key <paramref name="key"/>.
Expand Down Expand Up @@ -582,14 +586,14 @@ public void WithDuplicationGuard(Key key, Action a)
public Entities.Location CreateLocation()
{
return SourceTree is null
? Entities.GeneratedLocation.Create(this)
? Entities.EmptyLocation.Create(this)
: CreateLocation(Microsoft.CodeAnalysis.Location.Create(SourceTree, Microsoft.CodeAnalysis.Text.TextSpan.FromBounds(0, 0)));
}

public Entities.Location CreateLocation(Microsoft.CodeAnalysis.Location? location)
{
return (location is null || location.Kind == LocationKind.None)
? Entities.GeneratedLocation.Create(this)
? Entities.EmptyLocation.Create(this)
: location.IsInSource
? Entities.NonGeneratedSourceLocation.Create(this, location)
: Entities.Assembly.Create(this, location);
Expand Down
4 changes: 4 additions & 0 deletions csharp/ql/lib/change-notes/2025-10-02-entity-locations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The extraction of the location for bound generic entities (methods, accessors, indexers, properties, and events) has been optimized. Previously, location information was extracted multiple times for each bound generic. Now, only the location of the unbound generic declaration is extracted during the extraction phase, and the QL library explicitly reuses this location for all bound instances of the same generic.
2 changes: 1 addition & 1 deletion csharp/ql/lib/semmle/code/csharp/Callable.qll
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ class Method extends Callable, Virtualizable, Attributable, @method {
result = Virtualizable.super.getAnUltimateImplementor()
}

override Location getALocation() { method_location(this, result) }
override Location getALocation() { method_location(this.getUnboundDeclaration(), result) }

/** Holds if this method is an extension method. */
predicate isExtensionMethod() { this.getParameter(0).hasExtensionMethodModifier() }
Expand Down
4 changes: 2 additions & 2 deletions csharp/ql/lib/semmle/code/csharp/Event.qll
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class Event extends DeclarationWithAccessors, @event {
result = DeclarationWithAccessors.super.getAnUltimateImplementor()
}

override Location getALocation() { event_location(this, result) }
override Location getALocation() { event_location(this.getUnboundDeclaration(), result) }

override string getAPrimaryQlClass() { result = "Event" }
}
Expand Down Expand Up @@ -99,7 +99,7 @@ class EventAccessor extends Accessor, @event_accessor {

override Event getDeclaration() { event_accessors(this, _, _, result, _) }

override Location getALocation() { event_accessor_location(this, result) }
override Location getALocation() { event_accessor_location(this.getUnboundDeclaration(), result) }
}

/**
Expand Down
Loading