diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs index 66e45387d87..fe01e0f9b58 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Accessor.cs @@ -63,8 +63,10 @@ namespace Semmle.Extraction.CSharp.Entities 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); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs index e4799c05507..0e1b756a37c 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Attribute.cs @@ -59,11 +59,11 @@ namespace Semmle.Extraction.CSharp.Entities { 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)); TypeMention.Create(Context, attributeSyntax.Name, this, type); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedEntity.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedEntity.cs index 96bef973211..2002fe0f1d7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedEntity.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedEntity.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using Microsoft.CodeAnalysis; @@ -52,6 +54,22 @@ namespace Semmle.Extraction.CSharp.Entities } } + 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 c39eb6076b5..92861e97fdd 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Base/CachedSymbol.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs index 3c4cdcffc0e..4e63f5535aa 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentBlock.cs @@ -11,7 +11,7 @@ namespace Semmle.Extraction.CSharp.Entities 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)); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs index 7638eefce12..13cd002da79 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/CommentLine.cs @@ -23,7 +23,7 @@ namespace Semmle.Extraction.CSharp.Entities { 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; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs index c3ce2bb6d29..04527778469 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs @@ -29,7 +29,7 @@ namespace Semmle.Extraction.CSharp.Entities ContainingType!.PopulateGenerics(); trapFile.constructors(this, Symbol.ContainingType.Name, ContainingType, (Constructor)OriginalDefinition); - trapFile.constructor_location(this, Location); + WriteLocationToTrap(trapFile.constructor_location, this, Location); if (MakeSynthetic) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs index b6a9c8e8f1b..3d07c7d42de 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Destructor.cs @@ -15,7 +15,7 @@ namespace Semmle.Extraction.CSharp.Entities 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) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs index 888e1ba7304..8828639820b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs @@ -51,8 +51,10 @@ namespace Semmle.Extraction.CSharp.Entities 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() diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs index 1df6be7a273..254e7c76956 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/EventAccessor.cs @@ -48,8 +48,10 @@ namespace Semmle.Extraction.CSharp.Entities 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); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs index a5cb6e316f4..93107fc6dab 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs @@ -40,6 +40,7 @@ namespace Semmle.Extraction.CSharp.Entities 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()) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs index 0a91eb57ecd..61b5c40e6e5 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs @@ -49,8 +49,7 @@ namespace Semmle.Extraction.CSharp.Entities } } - foreach (var l in Locations) - trapFile.field_location(this, l); + WriteLocationsToTrap(trapFile.field_location, this, Locations); if (!IsSourceDeclaration || !Symbol.FromSource()) return; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs index 1235b120253..e6a188a6ab1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Indexer.cs @@ -19,8 +19,10 @@ namespace Semmle.Extraction.CSharp.Entities 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; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs index f1c6813a66d..f16faa7f530 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/LocalVariable.cs @@ -41,7 +41,7 @@ namespace Semmle.Extraction.CSharp.Entities 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); } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/GeneratedLocation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/EmptyLocation.cs similarity index 54% rename from csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/GeneratedLocation.cs rename to csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/EmptyLocation.cs index d12f1ca51e0..f81db35fdbc 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/GeneratedLocation.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Locations/EmptyLocation.cs @@ -2,11 +2,11 @@ using System.IO; 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); @@ -26,15 +26,16 @@ namespace Semmle.Extraction.CSharp.Entities 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 + private class EmptyLocationFactory : CachedEntityFactory { - 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); } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs index 12684c304a4..09ba3cc02b2 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/NamespaceDeclaration.cs @@ -35,7 +35,7 @@ namespace Semmle.Extraction.CSharp.Entities 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); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs index bd3a637a624..af4cd1a10ce 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs @@ -43,8 +43,10 @@ namespace Semmle.Extraction.CSharp.Entities } } - foreach (var l in Locations) - trapFile.method_location(this, l); + if (Context.ExtractLocation(Symbol)) + { + WriteLocationsToTrap(trapFile.method_location, this, Locations); + } PopulateGenerics(trapFile); Overrides(trapFile); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs index 76d518776ca..a5d208fc86f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Parameter.cs @@ -116,14 +116,16 @@ namespace Semmle.Extraction.CSharp.Entities 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)) @@ -247,7 +249,6 @@ namespace Semmle.Extraction.CSharp.Entities var typeKey = VarargsType.Create(Context); // !! Maybe originaldefinition is wrong 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; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/PreprocessorDirective.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/PreprocessorDirective.cs index da39613e124..9520e80d245 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/PreprocessorDirective.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/PreprocessorDirectives/PreprocessorDirective.cs @@ -13,7 +13,7 @@ namespace Semmle.Extraction.CSharp.Entities 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); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs index f8845a667cc..ccffa1d9511 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs @@ -69,8 +69,10 @@ namespace Semmle.Extraction.CSharp.Entities 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()) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs index 5a840e3b9ef..a3372726ae3 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/DynamicType.cs @@ -16,7 +16,7 @@ namespace Semmle.Extraction.CSharp.Entities 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)); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs index 96c523d5bbd..d43ec686031 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NamedType.cs @@ -83,8 +83,7 @@ namespace Semmle.Extraction.CSharp.Entities // 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) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs index d97e4b9dad5..7e7d3df107f 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TupleType.cs @@ -55,7 +55,7 @@ namespace Semmle.Extraction.CSharp.Entities // 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 tupleElementsLazy; diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs index 25fda9eabe9..a74a547f87b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/TypeParameter.cs @@ -28,7 +28,7 @@ namespace Semmle.Extraction.CSharp.Entities foreach (var l in Symbol.Locations) { - trapFile.type_location(this, Context.CreateLocation(l)); + WriteLocationToTrap(trapFile.type_location, this, Context.CreateLocation(l)); } if (IsSourceDeclaration) diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs index 141bded87ac..e37d16567e1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/UserOperator.cs @@ -26,8 +26,7 @@ namespace Semmle.Extraction.CSharp.Entities returnType.TypeRef, (UserOperator)OriginalDefinition); - foreach (var l in Locations) - trapFile.operator_location(this, l); + WriteLocationsToTrap(trapFile.operator_location, this, Locations); if (IsSourceDeclaration) { diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs index 3ea99a0d772..4c8660c172a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Analyser.cs @@ -238,6 +238,9 @@ namespace Semmle.Extraction.CSharp 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); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs index f231c8238a9..b7160cd1f63 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Context.cs @@ -550,6 +550,10 @@ namespace Semmle.Extraction.CSharp !SymbolEqualityComparer.Default.Equals(symbol, symbol.OriginalDefinition) || scope.InScope(symbol); + public bool ExtractLocation(ISymbol symbol) => + SymbolEqualityComparer.Default.Equals(symbol, symbol.OriginalDefinition) && + scope.InScope(symbol); + /// /// Runs the given action , guarding for trap duplication /// based on key . @@ -582,14 +586,14 @@ namespace Semmle.Extraction.CSharp 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); diff --git a/csharp/ql/lib/change-notes/2025-10-02-entity-locations.md b/csharp/ql/lib/change-notes/2025-10-02-entity-locations.md new file mode 100644 index 00000000000..dd13aab6292 --- /dev/null +++ b/csharp/ql/lib/change-notes/2025-10-02-entity-locations.md @@ -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. diff --git a/csharp/ql/lib/semmle/code/csharp/Callable.qll b/csharp/ql/lib/semmle/code/csharp/Callable.qll index 7e17f853913..ef0d0673ce2 100644 --- a/csharp/ql/lib/semmle/code/csharp/Callable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Callable.qll @@ -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() } diff --git a/csharp/ql/lib/semmle/code/csharp/Event.qll b/csharp/ql/lib/semmle/code/csharp/Event.qll index a7079952478..39e2fdb7894 100644 --- a/csharp/ql/lib/semmle/code/csharp/Event.qll +++ b/csharp/ql/lib/semmle/code/csharp/Event.qll @@ -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" } } @@ -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) } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/Property.qll b/csharp/ql/lib/semmle/code/csharp/Property.qll index 60ea83c3011..e651639b631 100644 --- a/csharp/ql/lib/semmle/code/csharp/Property.qll +++ b/csharp/ql/lib/semmle/code/csharp/Property.qll @@ -196,7 +196,7 @@ class Property extends DeclarationWithGetSetAccessors, @property { override PropertyAccess getAnAccess() { result.getTarget() = this } - override Location getALocation() { property_location(this, result) } + override Location getALocation() { property_location(this.getUnboundDeclaration(), result) } override Expr getAnAssignedValue() { result = DeclarationWithGetSetAccessors.super.getAnAssignedValue() @@ -328,7 +328,7 @@ class Indexer extends DeclarationWithGetSetAccessors, Parameterizable, @indexer result = DeclarationWithGetSetAccessors.super.getAnUltimateImplementor() } - override Location getALocation() { indexer_location(this, result) } + override Location getALocation() { indexer_location(this.getUnboundDeclaration(), result) } override string toStringWithTypes() { result = this.getName() + "[" + this.parameterTypesToString() + "]" @@ -408,7 +408,7 @@ class Accessor extends Callable, Modifiable, Attributable, Overridable, @callabl override Accessor getUnboundDeclaration() { accessors(this, _, _, _, result) } - override Location getALocation() { accessor_location(this, result) } + override Location getALocation() { accessor_location(this.getUnboundDeclaration(), result) } override string toString() { result = this.getName() } } diff --git a/csharp/ql/test/library-tests/locations/A.cs b/csharp/ql/test/library-tests/locations/A.cs new file mode 100644 index 00000000000..7f641a0024e --- /dev/null +++ b/csharp/ql/test/library-tests/locations/A.cs @@ -0,0 +1,35 @@ +using System; + +public abstract class A +{ + public abstract T Prop { get; } + public abstract T this[int index] { get; set; } + public abstract event EventHandler Event; + public void Apply(T t) { } + public abstract object ToObject(T t); +} + +public class A2 : A +{ + public override string Prop => ""; + + public override string this[int i] + { + get { return ""; } + set { } + } + + public override event EventHandler Event + { + add { } + remove { } + } + + public override object ToObject(string t) => t; + + public void M() + { + A2 other = new(); + other.Apply(""); + } +} diff --git a/csharp/ql/test/library-tests/locations/B.cs b/csharp/ql/test/library-tests/locations/B.cs new file mode 100644 index 00000000000..c4cfa971116 --- /dev/null +++ b/csharp/ql/test/library-tests/locations/B.cs @@ -0,0 +1,20 @@ +using System; + +public class B : A +{ + public override int Prop => 0; + + public override int this[int i] + { + get { return 0; } + set { } + } + + public override event EventHandler Event + { + add { } + remove { } + } + + public override object ToObject(int t) => t; +} diff --git a/csharp/ql/test/library-tests/locations/C.cs b/csharp/ql/test/library-tests/locations/C.cs new file mode 100644 index 00000000000..a4063646d53 --- /dev/null +++ b/csharp/ql/test/library-tests/locations/C.cs @@ -0,0 +1,12 @@ +using System; + +class C +{ + public void M() + { + B b = new B(); + b.Apply(0); + A2 a2 = new A2(); + a2.Apply(""); + } +} diff --git a/csharp/ql/test/library-tests/locations/locations.expected b/csharp/ql/test/library-tests/locations/locations.expected new file mode 100644 index 00000000000..8b021b115d2 --- /dev/null +++ b/csharp/ql/test/library-tests/locations/locations.expected @@ -0,0 +1,52 @@ +member_locations +| A.cs:3:23:3:26 | A | A.cs:5:23:5:26 | Prop | A.cs:5:23:5:26 | A.cs:5:23:5:26 | +| A.cs:3:23:3:26 | A | A.cs:6:23:6:26 | Item | A.cs:6:23:6:26 | A.cs:6:23:6:26 | +| A.cs:3:23:3:26 | A | A.cs:7:40:7:44 | Event | A.cs:7:40:7:44 | A.cs:7:40:7:44 | +| A.cs:3:23:3:26 | A | A.cs:8:17:8:21 | Apply | A.cs:8:17:8:21 | A.cs:8:17:8:21 | +| A.cs:3:23:3:26 | A | A.cs:9:28:9:35 | ToObject | A.cs:9:28:9:35 | A.cs:9:28:9:35 | +| A.cs:3:23:3:26 | A | A.cs:5:23:5:26 | Prop | A.cs:5:23:5:26 | A.cs:5:23:5:26 | +| A.cs:3:23:3:26 | A | A.cs:6:23:6:26 | Item | A.cs:6:23:6:26 | A.cs:6:23:6:26 | +| A.cs:3:23:3:26 | A | A.cs:7:40:7:44 | Event | A.cs:7:40:7:44 | A.cs:7:40:7:44 | +| A.cs:3:23:3:26 | A | A.cs:8:17:8:21 | Apply | A.cs:8:17:8:21 | A.cs:8:17:8:21 | +| A.cs:3:23:3:26 | A | A.cs:9:28:9:35 | ToObject | A.cs:9:28:9:35 | A.cs:9:28:9:35 | +| A.cs:3:23:3:26 | A`1 | A.cs:5:23:5:26 | Prop | A.cs:5:23:5:26 | A.cs:5:23:5:26 | +| A.cs:3:23:3:26 | A`1 | A.cs:6:23:6:26 | Item | A.cs:6:23:6:26 | A.cs:6:23:6:26 | +| A.cs:3:23:3:26 | A`1 | A.cs:7:40:7:44 | Event | A.cs:7:40:7:44 | A.cs:7:40:7:44 | +| A.cs:3:23:3:26 | A`1 | A.cs:8:17:8:21 | Apply | A.cs:8:17:8:21 | A.cs:8:17:8:21 | +| A.cs:3:23:3:26 | A`1 | A.cs:9:28:9:35 | ToObject | A.cs:9:28:9:35 | A.cs:9:28:9:35 | +| A.cs:12:14:12:15 | A2 | A.cs:14:28:14:31 | Prop | A.cs:14:28:14:31 | A.cs:14:28:14:31 | +| A.cs:12:14:12:15 | A2 | A.cs:16:28:16:31 | Item | A.cs:16:28:16:31 | A.cs:16:28:16:31 | +| A.cs:12:14:12:15 | A2 | A.cs:22:40:22:44 | Event | A.cs:22:40:22:44 | A.cs:22:40:22:44 | +| A.cs:12:14:12:15 | A2 | A.cs:28:28:28:35 | ToObject | A.cs:28:28:28:35 | A.cs:28:28:28:35 | +| A.cs:12:14:12:15 | A2 | A.cs:30:17:30:17 | M | A.cs:30:17:30:17 | A.cs:30:17:30:17 | +| B.cs:3:14:3:14 | B | B.cs:5:25:5:28 | Prop | B.cs:5:25:5:28 | B.cs:5:25:5:28 | +| B.cs:3:14:3:14 | B | B.cs:7:25:7:28 | Item | B.cs:7:25:7:28 | B.cs:7:25:7:28 | +| B.cs:3:14:3:14 | B | B.cs:13:40:13:44 | Event | B.cs:13:40:13:44 | B.cs:13:40:13:44 | +| B.cs:3:14:3:14 | B | B.cs:19:28:19:35 | ToObject | B.cs:19:28:19:35 | B.cs:19:28:19:35 | +| 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 | +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 | +| A.cs:3:23:3:26 | A | A.cs:6:41:6:43 | get_Item | A.cs:6:41:6:43 | A.cs:6:41:6:43 | +| A.cs:3:23:3:26 | A | A.cs:6:46:6:48 | set_Item | A.cs:6:46:6:48 | A.cs:6:46:6:48 | +| A.cs:3:23:3:26 | A | A.cs:7:40:7:44 | add_Event | A.cs:7:40:7:44 | A.cs:7:40:7:44 | +| A.cs:3:23:3:26 | A | A.cs:7:40:7:44 | remove_Event | A.cs:7:40:7:44 | A.cs:7:40:7:44 | +| 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 | +| A.cs:3:23:3:26 | A | A.cs:6:41:6:43 | get_Item | A.cs:6:41:6:43 | A.cs:6:41:6:43 | +| A.cs:3:23:3:26 | A | A.cs:6:46:6:48 | set_Item | A.cs:6:46:6:48 | A.cs:6:46:6:48 | +| A.cs:3:23:3:26 | A | A.cs:7:40:7:44 | add_Event | A.cs:7:40:7:44 | A.cs:7:40:7:44 | +| A.cs:3:23:3:26 | A | A.cs:7:40:7:44 | remove_Event | A.cs:7:40:7:44 | A.cs:7:40:7:44 | +| A.cs:3:23:3:26 | A`1 | A.cs:5:30:5:32 | get_Prop | A.cs:5:30:5:32 | A.cs:5:30:5:32 | +| A.cs:3:23:3:26 | A`1 | A.cs:6:41:6:43 | get_Item | A.cs:6:41:6:43 | A.cs:6:41:6:43 | +| A.cs:3:23:3:26 | A`1 | A.cs:6:46:6:48 | set_Item | A.cs:6:46:6:48 | A.cs:6:46:6:48 | +| A.cs:3:23:3:26 | A`1 | A.cs:7:40:7:44 | add_Event | A.cs:7:40:7:44 | A.cs:7:40:7:44 | +| A.cs:3:23:3:26 | A`1 | A.cs:7:40:7:44 | remove_Event | A.cs:7:40:7:44 | A.cs:7:40:7:44 | +| A.cs:12:14:12:15 | A2 | A.cs:14:36:14:37 | get_Prop | A.cs:14:36:14:37 | A.cs:14:36:14:37 | +| A.cs:12:14:12:15 | A2 | A.cs:18:9:18:11 | get_Item | A.cs:18:9:18:11 | A.cs:18:9:18:11 | +| A.cs:12:14:12:15 | A2 | A.cs:19:9:19:11 | set_Item | A.cs:19:9:19:11 | A.cs:19:9:19:11 | +| A.cs:12:14:12:15 | A2 | A.cs:24:9:24:11 | add_Event | A.cs:24:9:24:11 | A.cs:24:9:24:11 | +| A.cs:12:14:12:15 | A2 | A.cs:25:9:25:14 | remove_Event | A.cs:25:9:25:14 | A.cs:25:9:25:14 | +| B.cs:3:14:3:14 | B | B.cs:5:33:5:33 | get_Prop | B.cs:5:33:5:33 | B.cs:5:33:5:33 | +| B.cs:3:14:3:14 | B | B.cs:9:9:9:11 | get_Item | B.cs:9:9:9:11 | B.cs:9:9:9:11 | +| B.cs:3:14:3:14 | B | B.cs:10:9:10:11 | set_Item | B.cs:10:9:10:11 | B.cs:10:9:10:11 | +| B.cs:3:14:3:14 | B | B.cs:15:9:15:11 | add_Event | B.cs:15:9:15:11 | B.cs:15:9:15:11 | +| B.cs:3:14:3:14 | B | B.cs:16:9:16:14 | remove_Event | B.cs:16:9:16:14 | B.cs:16:9:16:14 | diff --git a/csharp/ql/test/library-tests/locations/locations.ql b/csharp/ql/test/library-tests/locations/locations.ql new file mode 100644 index 00000000000..6c1dcc3e385 --- /dev/null +++ b/csharp/ql/test/library-tests/locations/locations.ql @@ -0,0 +1,14 @@ +import csharp + +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 +} + +query predicate accessor_location(Type t, Accessor a, SourceLocation l) { + t = a.getDeclaringType() and + l = a.getLocation() and + not l instanceof EmptyLocation +}