From 81adf5251d9d935ccef66dc32e750031c4cb78e7 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 18 Sep 2025 12:58:22 +0200 Subject: [PATCH 1/6] C#: Use * ID for Locations in buildless. --- .../Entities/Assembly.cs | 11 ++++----- .../Entities/Locations/GeneratedLocation.cs | 6 +++++ .../Entities/Locations/Location.cs | 24 ++++++++++++++++++- .../Locations/NonGeneratedSourceLocation.cs | 6 +++++ 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs index 4ad05eea3833..affb8b224a3d 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Assembly.cs @@ -72,15 +72,14 @@ public static Assembly CreateOutputAssembly(Context cx) public override void WriteId(EscapingTextWriter trapFile) { - if (isOutputAssembly && Context.ExtractionContext.IsStandalone) + if (Context.ExtractionContext.IsStandalone) { - trapFile.Write("buildlessOutputAssembly"); - } - else - { - trapFile.Write(assembly.ToString()); + WriteStarId(trapFile); + return; } + trapFile.Write(assembly.ToString()); + if (assemblyPath is not null) { trapFile.Write("#file:///"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/GeneratedLocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/GeneratedLocation.cs index d12f1ca51e00..06d5b2fe4c2b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/GeneratedLocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/GeneratedLocation.cs @@ -19,6 +19,12 @@ public override void Populate(TextWriter trapFile) public override void WriteId(EscapingTextWriter trapFile) { + if (Context.ExtractionContext.IsStandalone) + { + WriteStarId(trapFile); + return; + } + trapFile.Write("loc,"); trapFile.WriteSubId(generatedFile); trapFile.Write(",0,0,0,0"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/Location.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/Location.cs index 9f9e15e33f33..445308481bb3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/Location.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/Location.cs @@ -5,7 +5,29 @@ public abstract class Location : CachedEntity { #nullable restore warnings protected Location(Context cx, Microsoft.CodeAnalysis.Location? init) - : base(cx, init) { } + : base(cx, init) + { + if (cx.ExtractionContext.IsStandalone) + { + cx.AddFreshLabel(this); + } + } + + protected static void WriteStarId(EscapingTextWriter writer) + { + writer.Write('*'); + } + + public sealed override void WriteQuotedId(EscapingTextWriter writer) + { + if (Context.ExtractionContext.IsStandalone) + { + WriteStarId(writer); + return; + } + + base.WriteQuotedId(writer); + } public override Microsoft.CodeAnalysis.Location? ReportingLocation => Symbol; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/NonGeneratedSourceLocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/NonGeneratedSourceLocation.cs index 69e9ea4e9dc7..22a8277b9495 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/NonGeneratedSourceLocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/NonGeneratedSourceLocation.cs @@ -42,6 +42,12 @@ public File FileEntity public override void WriteId(EscapingTextWriter trapFile) { + if (Context.ExtractionContext.IsStandalone) + { + WriteStarId(trapFile); + return; + } + trapFile.Write("loc,"); trapFile.WriteSubId(FileEntity); trapFile.Write(','); From 3fd04ff01ccb93edea01c849c0374ff3dafcfd36 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Thu, 18 Sep 2025 13:39:22 +0200 Subject: [PATCH 2/6] C#: The cached entity factory ensures the creation of label and writing it to trap. --- .../Entities/Locations/Location.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/Location.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/Location.cs index 445308481bb3..0ba8aeaa4187 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/Location.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/Location.cs @@ -6,12 +6,7 @@ public abstract class Location : CachedEntity #nullable restore warnings protected Location(Context cx, Microsoft.CodeAnalysis.Location? init) : base(cx, init) - { - if (cx.ExtractionContext.IsStandalone) - { - cx.AddFreshLabel(this); - } - } + { } protected static void WriteStarId(EscapingTextWriter writer) { From 53fb4b0261e1bb097c5b8643d1cc2eaba1c182c9 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Fri, 19 Sep 2025 15:15:57 +0200 Subject: [PATCH 3/6] C#: Only create a single empty location element. --- .../Entities/Locations/EmptyLocation.cs | 43 +++++++++++++++++++ .../Entities/Locations/GeneratedLocation.cs | 10 ++--- .../Extractor/Analyser.cs | 3 ++ 3 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/EmptyLocation.cs diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/EmptyLocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/EmptyLocation.cs new file mode 100644 index 000000000000..38b198102c8b --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/EmptyLocation.cs @@ -0,0 +1,43 @@ +using System.IO; + +namespace Semmle.Extraction.CSharp.Entities +{ + /// + /// Used to create a single canonical empty location entity. + /// + public class EmptyLocation : GeneratedLocation + { + private EmptyLocation(Context cx) : base(cx) { } + + public override void Populate(TextWriter trapFile) + { + trapFile.locations_default(this, GenFile, -1, -1, -1, -1); + } + + public override void WriteId(EscapingTextWriter trapFile) + { + if (Context.ExtractionContext.IsStandalone) + { + WriteStarId(trapFile); + return; + } + + trapFile.Write("loc,"); + trapFile.WriteSubId(GenFile); + trapFile.Write(",-1,-1,-1,-1"); + } + + public static new EmptyLocation Create(Context cx) => EmptyLocationFactory.Instance.CreateEntity(cx, typeof(EmptyLocation), null); + + private class EmptyLocationFactory : CachedEntityFactory + { + public static EmptyLocationFactory Instance { get; } = new EmptyLocationFactory(); + + /// + /// The QL library assumes the presence of a single empty location element. + /// + public override EmptyLocation Create(Context cx, string? init) => new EmptyLocation(cx); + } + } + +} diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/GeneratedLocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/GeneratedLocation.cs index 06d5b2fe4c2b..8badb31f21c2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/GeneratedLocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/GeneratedLocation.cs @@ -4,17 +4,17 @@ namespace Semmle.Extraction.CSharp.Entities { public class GeneratedLocation : SourceLocation { - private readonly File generatedFile; + protected File GenFile { get; init; } - private GeneratedLocation(Context cx) + protected GeneratedLocation(Context cx) : base(cx, null) { - generatedFile = GeneratedFile.Create(cx); + GenFile = GeneratedFile.Create(cx); } public override void Populate(TextWriter trapFile) { - trapFile.locations_default(this, generatedFile, 0, 0, 0, 0); + trapFile.locations_default(this, GenFile, 0, 0, 0, 0); } public override void WriteId(EscapingTextWriter trapFile) @@ -26,7 +26,7 @@ public override void WriteId(EscapingTextWriter trapFile) } trapFile.Write("loc,"); - trapFile.WriteSubId(generatedFile); + trapFile.WriteSubId(GenFile); trapFile.Write(",0,0,0,0"); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs index 3ea99a0d772c..0ee52501ba57 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs @@ -238,6 +238,9 @@ private void DoAnalyseCompilation() compilationEntity = Entities.Compilation.Create(cx); + // Add a single empty location + 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); From 7b1f9f1b70be3a4301fd253b0219a2010eda1895 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 22 Sep 2025 13:55:31 +0200 Subject: [PATCH 4/6] C#: Adjust the empty location to point to the unique generated empty location. --- csharp/ql/lib/semmle/code/csharp/Location.qll | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/Location.qll b/csharp/ql/lib/semmle/code/csharp/Location.qll index 9b2cea470ed7..960fa3246f3c 100644 --- a/csharp/ql/lib/semmle/code/csharp/Location.qll +++ b/csharp/ql/lib/semmle/code/csharp/Location.qll @@ -74,8 +74,20 @@ class Location extends @location { } /** An empty location. */ -class EmptyLocation extends Location { - EmptyLocation() { this.hasLocationInfo("", 0, 0, 0, 0) } +class EmptyLocation extends Location, @location_default { + EmptyLocation() { locations_default(this, _, -1, -1, -1, -1) } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } + + override string toString() { result = "empty location" } } /** @@ -83,6 +95,8 @@ class EmptyLocation extends Location { * within the file. */ class SourceLocation extends Location, @location_default { + SourceLocation() { not this instanceof EmptyLocation } + /** Gets the location that takes into account `#line` directives, if any. */ SourceLocation getMappedLocation() { locations_mapped(this, result) and From eba37a7fe04fa1fc276d5667655bb23b938541df Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 23 Sep 2025 11:26:40 +0200 Subject: [PATCH 5/6] Experiment with simpler EmptyLocation. --- csharp/ql/lib/semmle/code/csharp/Location.qll | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/Location.qll b/csharp/ql/lib/semmle/code/csharp/Location.qll index 960fa3246f3c..41f1254944f3 100644 --- a/csharp/ql/lib/semmle/code/csharp/Location.qll +++ b/csharp/ql/lib/semmle/code/csharp/Location.qll @@ -74,20 +74,8 @@ class Location extends @location { } /** An empty location. */ -class EmptyLocation extends Location, @location_default { - EmptyLocation() { locations_default(this, _, -1, -1, -1, -1) } - - override predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - filepath = "" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } - - override string toString() { result = "empty location" } +class EmptyLocation extends Location { + EmptyLocation() { this.hasLocationInfo("", -1, -1, -1, -1) } } /** @@ -95,8 +83,6 @@ class EmptyLocation extends Location, @location_default { * within the file. */ class SourceLocation extends Location, @location_default { - SourceLocation() { not this instanceof EmptyLocation } - /** Gets the location that takes into account `#line` directives, if any. */ SourceLocation getMappedLocation() { locations_mapped(this, result) and From 96e47fc7b05b37e00582688c7f1d8f12c75c1c6a Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Tue, 23 Sep 2025 13:59:11 +0200 Subject: [PATCH 6/6] C#: Adjust the location of the empty element. --- csharp/ql/lib/semmle/code/csharp/Location.qll | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/Location.qll b/csharp/ql/lib/semmle/code/csharp/Location.qll index 41f1254944f3..fc502545ba34 100644 --- a/csharp/ql/lib/semmle/code/csharp/Location.qll +++ b/csharp/ql/lib/semmle/code/csharp/Location.qll @@ -74,8 +74,20 @@ class Location extends @location { } /** An empty location. */ -class EmptyLocation extends Location { - EmptyLocation() { this.hasLocationInfo("", -1, -1, -1, -1) } +class EmptyLocation extends SourceLocation { + EmptyLocation() { locations_default(this, _, -1, -1, -1, -1) } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + filepath = "" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + } + + override string toString() { result = "empty location" } } /**