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; 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. 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 }