From d6b7424e2c5999ef2aebc08dcb2661b79f444c68 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Mon, 10 Nov 2025 15:06:28 +0100 Subject: [PATCH] C#: Add the same strategy in as in Java for XML element discarding. --- .../semmle/code/csharp/internal/Overlay.qll | 58 +++++++++++++++---- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/internal/Overlay.qll b/csharp/ql/lib/semmle/code/csharp/internal/Overlay.qll index 59b5359a1de..4d519bf15da 100644 --- a/csharp/ql/lib/semmle/code/csharp/internal/Overlay.qll +++ b/csharp/ql/lib/semmle/code/csharp/internal/Overlay.qll @@ -15,13 +15,28 @@ private string getLocationFilePath(@location_default loc) { exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result)) } +overlay[local] +private class DiscardableEntityBase extends @locatable { + /** Gets the path to the file in which this element occurs. */ + abstract string getFilePath(); + + /** Holds if this element exists in the base variant. */ + predicate existsInBase() { not isOverlay() } + + /** Holds if this element exists in the overlay variant. */ + predicate existsInOverlay() { isOverlay() } + + /** Gets a textual representation of this discardable element. */ + string toString() { none() } +} + /** * A class of elements that can be discarded from the base. */ overlay[local] -private class DiscardableEntity extends @locatable { +private class DiscardableEntity extends DiscardableEntityBase { /** Gets the path to the file in which this element occurs. */ - string getFilePath() { + override string getFilePath() { exists(@location_default loc | result = getLocationFilePath(loc) | expr_location(this, loc) or stmt_location(this, loc) or @@ -50,15 +65,6 @@ private class DiscardableEntity extends @locatable { extractor_messages(this, _, _, _, _, loc, _) ) } - - /** Holds if this element exists in the base variant. */ - predicate existsInBase() { not isOverlay() } - - /** Holds if this element exists in the overlay variant. */ - predicate existsInOverlay() { isOverlay() } - - /** Gets a textual representation of this discardable element. */ - string toString() { none() } } /** @@ -103,3 +109,33 @@ overlay[discard_entity] private predicate discardLocation(@location_default loc) { exists(string path | discardableLocation(loc, path) | overlayChangedFiles(path)) } + +/** + * A class of Xml locatables that can be discarded from the base. + */ +overlay[local] +private class DiscardableXmlEntity extends DiscardableEntityBase instanceof @xmllocatable { + /** Gets the path to the file in which this element occurs. */ + override string getFilePath() { + exists(@location_default loc | result = getLocationFilePath(loc) | xmllocations(this, loc)) + } +} + +overlay[local] +private predicate overlayXmlExtracted(string file) { + exists(DiscardableXmlEntity dxe | + dxe.existsInOverlay() and + file = dxe.getFilePath() and + not files(dxe, _) and + not xmlNs(dxe, _, _, _) + ) +} + +overlay[discard_entity] +private predicate discardXmlEntity(@xmllocatable xml) { + overlayChangedFiles(xml.(DiscardableXmlEntity).getFilePath()) + or + // The XML extractor is not incremental and may extract more + // XML files than those included in overlayChangedFiles. + overlayXmlExtracted(xml.(DiscardableXmlEntity).getFilePath()) +}