From f0842e430dbb15b8a70c3b06239fc31352a3196d Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 7 Oct 2025 15:15:34 +0200 Subject: [PATCH 1/3] C#: Respect the context when extracting locations for type parameters and tuple typles. --- .../Entities/Types/TupleType.cs | 4 ++-- .../Entities/Types/TypeParameter.cs | 6 ++---- .../Semmle.Extraction.CSharp/Extractor/Context.cs | 12 ++++++++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs index 7e7d3df107f4..18d71c997886 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs @@ -54,8 +54,8 @@ public override void Populate(TextWriter trapFile) // Note: symbol.Locations seems to be very inconsistent // 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) - WriteLocationToTrap(trapFile.type_location, this, Context.CreateLocation(l)); + var locations = Context.GetLocations(Symbol); + WriteLocationsToTrap(trapFile.type_location, this, locations); } private readonly Lazy tupleElementsLazy; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs index 303421d32e76..8c7c0edde761 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs @@ -28,10 +28,8 @@ public override void Populate(TextWriter trapFile) if (Context.ExtractLocation(Symbol)) { - foreach (var l in Symbol.Locations) - { - WriteLocationToTrap(trapFile.type_location, this, Context.CreateLocation(l)); - } + var locations = Context.GetLocations(Symbol); + WriteLocationsToTrap(trapFile.type_location, this, locations); } if (IsSourceDeclaration) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs index 44a2fcda5c22..74b3b186b517 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs @@ -554,6 +554,18 @@ public bool ExtractLocation(ISymbol symbol) => SymbolEqualityComparer.Default.Equals(symbol, symbol.OriginalDefinition) && scope.InScope(symbol); + /// + /// Gets the locations of the symbol that are either + /// (1) In assemblies. + /// (2) In the current context. + /// + /// The symbol + /// List of locations + public IEnumerable GetLocations(ISymbol symbol) => + symbol.Locations + .Where(l => !l.IsInSource || IsLocationInContext(l)) + .Select(CreateLocation); + public bool IsLocationInContext(Location location) => location.SourceTree == SourceTree; From eb84b1441a83e8dc26651e52c29d7ec62ebec229 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 7 Oct 2025 15:24:54 +0200 Subject: [PATCH 2/3] C#: Add some locations tests for type parameters and tuple types. --- .../ql/test/library-tests/locations/Base.cs | 2 ++ .../test/library-tests/locations/Multiple1.cs | 12 ++++++++++ .../test/library-tests/locations/Multiple2.cs | 10 ++++++++ .../locations/locations.expected | 23 +++++++++++++++++++ .../test/library-tests/locations/locations.ql | 7 +++++- 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/csharp/ql/test/library-tests/locations/Base.cs b/csharp/ql/test/library-tests/locations/Base.cs index 02082e0e6c4b..413534319c4e 100644 --- a/csharp/ql/test/library-tests/locations/Base.cs +++ b/csharp/ql/test/library-tests/locations/Base.cs @@ -4,3 +4,5 @@ public void M() { } public class InnerBase { } } + +public abstract class Base2 { } diff --git a/csharp/ql/test/library-tests/locations/Multiple1.cs b/csharp/ql/test/library-tests/locations/Multiple1.cs index 34bec96c0699..1d8a6491b145 100644 --- a/csharp/ql/test/library-tests/locations/Multiple1.cs +++ b/csharp/ql/test/library-tests/locations/Multiple1.cs @@ -1 +1,13 @@ public partial class Multiple { } + +public partial class MultipleGeneric { } + +public class Multiple1Specific +{ + public static (int, string) M() + { + (int, string) x = (0, ""); + (int, int) y = (0, 0); + return x; + } +} diff --git a/csharp/ql/test/library-tests/locations/Multiple2.cs b/csharp/ql/test/library-tests/locations/Multiple2.cs index 34bec96c0699..e63833066167 100644 --- a/csharp/ql/test/library-tests/locations/Multiple2.cs +++ b/csharp/ql/test/library-tests/locations/Multiple2.cs @@ -1 +1,11 @@ public partial class Multiple { } + +public partial class MultipleGeneric { } + +public class Multiple2Specific +{ + public void M() + { + (int, string) z = (0, ""); + } +} diff --git a/csharp/ql/test/library-tests/locations/locations.expected b/csharp/ql/test/library-tests/locations/locations.expected index 122921ac1e3a..1710f4e3cec3 100644 --- a/csharp/ql/test/library-tests/locations/locations.expected +++ b/csharp/ql/test/library-tests/locations/locations.expected @@ -28,6 +28,8 @@ member_locations | Base.cs:1:23:1:29 | Base`1 | Base.cs:3:17:3:17 | M | Base.cs:3:17:3:17 | Base.cs:3:17:3:17 | | Base.cs:1:23:1:29 | Base`1 | Base.cs:5:18:5:26 | InnerBase | Base.cs:5:18:5:26 | Base.cs:5:18:5:26 | | C.cs:3:7:3:7 | C | C.cs:5:17:5:17 | M | C.cs:5:17:5:17 | C.cs:5:17:5:17 | +| Multiple1.cs:5:14:5:30 | Multiple1Specific | Multiple1.cs:7:33:7:33 | M | Multiple1.cs:7:33:7:33 | Multiple1.cs:7:33:7:33 | +| Multiple2.cs:5:14:5:30 | Multiple2Specific | Multiple2.cs:7:17:7:17 | M | Multiple2.cs:7:17:7:17 | Multiple2.cs:7:17:7:17 | | Sub.cs:1:14:1:16 | Sub | Sub.cs:3:17:3:20 | SubM | Sub.cs:3:17:3:20 | Sub.cs:3:17:3:20 | accessor_location | A.cs:3:23:3:26 | A | A.cs:5:30:5:32 | get_Prop | A.cs:5:30:5:32 | A.cs:5:30:5:32 | @@ -67,11 +69,22 @@ type_location | Base.cs:1:28:1:28 | T | Base.cs:1:28:1:28 | Base.cs:1:28:1:28 | | Base.cs:5:18:5:26 | InnerBase | Base.cs:5:18:5:26 | Base.cs:5:18:5:26 | | Base.cs:5:18:5:26 | InnerBase | Base.cs:5:18:5:26 | Base.cs:5:18:5:26 | +| Base.cs:8:23:8:30 | Base2`1 | Base.cs:8:23:8:30 | Base.cs:8:23:8:30 | +| Base.cs:8:29:8:29 | T | Base.cs:8:29:8:29 | Base.cs:8:29:8:29 | | C.cs:3:7:3:7 | C | C.cs:3:7:3:7 | C.cs:3:7:3:7 | | Multiple1.cs:1:22:1:29 | Multiple | Multiple1.cs:1:22:1:29 | Multiple1.cs:1:22:1:29 | | Multiple1.cs:1:22:1:29 | Multiple | Multiple2.cs:1:22:1:29 | Multiple2.cs:1:22:1:29 | +| Multiple1.cs:3:22:3:39 | MultipleGeneric`1 | Multiple1.cs:3:22:3:39 | Multiple1.cs:3:22:3:39 | +| Multiple1.cs:3:22:3:39 | MultipleGeneric`1 | Multiple2.cs:3:22:3:39 | Multiple2.cs:3:22:3:39 | +| Multiple1.cs:3:38:3:38 | S | Multiple1.cs:3:38:3:38 | Multiple1.cs:3:38:3:38 | +| Multiple1.cs:5:14:5:30 | Multiple1Specific | Multiple1.cs:5:14:5:30 | Multiple1.cs:5:14:5:30 | +| Multiple1.cs:7:19:7:31 | (Int32,String) | Multiple1.cs:7:19:7:31 | Multiple1.cs:7:19:7:31 | +| Multiple1.cs:10:9:10:18 | (Int32,Int32) | Multiple1.cs:10:9:10:18 | Multiple1.cs:10:9:10:18 | | Multiple2.cs:1:22:1:29 | Multiple | Multiple1.cs:1:22:1:29 | Multiple1.cs:1:22:1:29 | | Multiple2.cs:1:22:1:29 | Multiple | Multiple2.cs:1:22:1:29 | Multiple2.cs:1:22:1:29 | +| Multiple2.cs:3:22:3:39 | MultipleGeneric`1 | Multiple1.cs:3:22:3:39 | Multiple1.cs:3:22:3:39 | +| Multiple2.cs:3:22:3:39 | MultipleGeneric`1 | Multiple2.cs:3:22:3:39 | Multiple2.cs:3:22:3:39 | +| Multiple2.cs:5:14:5:30 | Multiple2Specific | Multiple2.cs:5:14:5:30 | Multiple2.cs:5:14:5:30 | | Sub.cs:1:14:1:16 | Sub | Sub.cs:1:14:1:16 | Sub.cs:1:14:1:16 | calltype_location | A.cs:12:14:12:15 | call to constructor A | A.cs:3:23:3:26 | A | A.cs:3:23:3:26 | A.cs:3:23:3:26 | @@ -81,3 +94,13 @@ calltype_location | C.cs:9:17:9:24 | object creation of type A2 | A.cs:12:14:12:15 | A2 | A.cs:12:14:12:15 | A.cs:12:14:12:15 | | Sub.cs:1:14:1:16 | call to constructor Base | Base.cs:1:23:1:29 | Base | Base.cs:1:23:1:29 | Base.cs:1:23:1:29 | | Sub.cs:6:17:6:31 | object creation of type InnerBase | Base.cs:5:18:5:26 | InnerBase | Base.cs:5:18:5:26 | Base.cs:5:18:5:26 | +typeparameter_location +| A.cs:3:25:3:25 | T | A.cs:3:25:3:25 | A.cs:3:25:3:25 | +| Base.cs:1:28:1:28 | T | Base.cs:1:28:1:28 | Base.cs:1:28:1:28 | +| Base.cs:8:29:8:29 | T | Base.cs:8:29:8:29 | Base.cs:8:29:8:29 | +| Multiple1.cs:3:38:3:38 | S | Multiple1.cs:3:38:3:38 | Multiple1.cs:3:38:3:38 | +| Multiple1.cs:3:38:3:38 | S | Multiple2.cs:3:38:3:38 | Multiple2.cs:3:38:3:38 | +tupletype_location +| Multiple1.cs:7:19:7:31 | (Int32,String) | Multiple1.cs:7:19:7:31 | Multiple1.cs:7:19:7:31 | +| Multiple1.cs:7:19:7:31 | (Int32,String) | Multiple2.cs:9:9:9:21 | Multiple2.cs:9:9:9:21 | +| Multiple1.cs:10:9:10:18 | (Int32,Int32) | Multiple1.cs:10:9:10:18 | Multiple1.cs:10:9:10:18 | diff --git a/csharp/ql/test/library-tests/locations/locations.ql b/csharp/ql/test/library-tests/locations/locations.ql index 04ea140340bd..670a27408115 100644 --- a/csharp/ql/test/library-tests/locations/locations.ql +++ b/csharp/ql/test/library-tests/locations/locations.ql @@ -4,7 +4,8 @@ query predicate member_locations(Type t, Member m, SourceLocation l) { t = m.getDeclaringType() and l = m.getLocation() and not l instanceof EmptyLocation and - not m instanceof Constructor + not m instanceof Constructor and + t.fromSource() } query predicate accessor_location(Type t, Accessor a, SourceLocation l) { @@ -21,3 +22,7 @@ query predicate calltype_location(Call call, Type t, SourceLocation l) { t = call.getType() and l = t.getALocation() } + +query predicate typeparameter_location(TypeParameter tp, SourceLocation l) { tp.getALocation() = l } + +query predicate tupletype_location(TupleType tt, SourceLocation l) { tt.getALocation() = l } From cdfa58645a4695a842616367c2dc1b1c7adb29f1 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 8 Oct 2025 10:14:51 +0200 Subject: [PATCH 3/3] C#: Add change-note. --- csharp/ql/lib/change-notes/2025-10-08-entity-locations.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 csharp/ql/lib/change-notes/2025-10-08-entity-locations.md diff --git a/csharp/ql/lib/change-notes/2025-10-08-entity-locations.md b/csharp/ql/lib/change-notes/2025-10-08-entity-locations.md new file mode 100644 index 000000000000..a96afe072513 --- /dev/null +++ b/csharp/ql/lib/change-notes/2025-10-08-entity-locations.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The extraction of location information for type parameters and tuples types has been optimized. Previously, location information was extracted multiple times for each type when it was declared across multiple files. Now, the extraction context is respected during the extraction phase, ensuring locations are only extracted within the appropriate context. This change should be transparent to end-users but may improve extraction performance in some cases.