From 44de1133768d6577c73f1f1b5a941f44245cedae Mon Sep 17 00:00:00 2001 From: idrissrio Date: Tue, 3 Mar 2026 10:28:26 +0100 Subject: [PATCH 1/2] C/C++ overlay: use TRAP/tag-based entity discard strategy --- .../lib/semmle/code/cpp/internal/Overlay.qll | 140 +++++++----------- 1 file changed, 50 insertions(+), 90 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll b/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll index 3dea144bbf67..55b774a816f9 100644 --- a/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll +++ b/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll @@ -1,5 +1,12 @@ /** * Defines entity discard predicates for C++ overlay analysis. + * + * The discard strategy is based on TRAP file and tag tracking rather than + * source locations. The extractor records which TRAP file or tag each element + * is defined in (`in_trap_or_tag`), which source files use which TRAP files + * (`source_file_uses_trap`), and which TRAP files use which tags + * (`trap_uses_tag`). These relations allow us to precisely determine whether + * a base element should be discarded or retained in the combined database. */ private import OverlayXml @@ -12,111 +19,64 @@ private import OverlayXml overlay[local] predicate isOverlay() { databaseMetadata("isOverlay", "true") } -overlay[local] -private string getLocationFilePath(@location_default loc) { - exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result)) -} - -/** - * Gets the file path for an element with a single location. - */ -overlay[local] -private string getSingleLocationFilePath(@element e) { - exists(@location_default loc | - var_decls(e, _, _, _, loc) - or - fun_decls(e, _, _, _, loc) - or - type_decls(e, _, loc) - or - namespace_decls(e, _, loc, _) - or - macroinvocations(e, _, loc, _) - or - preprocdirects(e, _, loc) - or - diagnostics(e, _, _, _, _, loc) - or - usings(e, _, loc, _) - or - static_asserts(e, _, _, loc, _) - or - derivations(e, _, _, _, loc) - or - frienddecls(e, _, _, loc) - or - comments(e, _, loc) - or - exprs(e, _, loc) - or - stmts(e, _, loc) - or - initialisers(e, _, _, loc) - or - attributes(e, _, _, _, loc) - or - attribute_args(e, _, _, _, loc) - or - namequalifiers(e, _, _, loc) - or - enumconstants(e, _, _, _, _, loc) - or - type_mentions(e, _, loc, _) - or - lambda_capture(e, _, _, _, _, _, loc) - or - concept_templates(e, _, loc) - | - result = getLocationFilePath(loc) - ) -} - /** - * Gets the file path for an element with potentially multiple locations. + * Holds if TRAP file or tag `t` is reachable from a source file named + * `source_file` in the given variant (base or overlay). + * + * A TRAP file is directly reachable from its source file. A tag is reachable + * if the TRAP file that uses it is reachable. */ overlay[local] -private string getMultiLocationFilePath(@element e) { - exists(@location_default loc | - var_decls(_, e, _, _, loc) - or - fun_decls(_, e, _, _, loc) - or - type_decls(_, e, loc) - or - namespace_decls(_, e, loc, _) - | - result = getLocationFilePath(loc) +private predicate locallyReachableTrapOrTag( + boolean isOverlayVariant, string source_file, @trap_or_tag t +) { + exists(@source_file sf, string source_file_raw, @trap trap | + (if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and + source_file_uses_trap(sf, trap) and + source_file_name(sf, source_file_raw) and + source_file = source_file_raw.replaceAll("\\", "/") and + (t = trap or trap_uses_tag(trap, t)) ) } /** - * A local helper predicate that holds in the base variant and never in the - * overlay variant. - */ -overlay[local] -private predicate isBase() { not isOverlay() } - -/** - * Holds if `path` was extracted in the overlay database. + * Holds if element `e` is defined in TRAP file or tag `t` in the given + * variant (base or overlay). */ overlay[local] -private predicate overlayHasFile(string path) { - isOverlay() and - files(_, path) and - path != "" +private predicate locallyInTrapOrTag(boolean isOverlayVariant, @element e, @trap_or_tag t) { + (if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and + in_trap_or_tag(e, t) } /** * Discards an element from the base variant if: - * - It has a single location in a file extracted in the overlay, or - * - All of its locations are in files extracted in the overlay. + * - It is known to be in a base TRAP file or tag, and + * - It is not reachable from any overlay source file, and + * - Every base source file that reaches it has either changed or had its TRAP + * file redefined by the overlay. */ overlay[discard_entity] private predicate discardElement(@element e) { - isBase() and - ( - overlayHasFile(getSingleLocationFilePath(e)) - or - forex(string path | path = getMultiLocationFilePath(e) | overlayHasFile(path)) + // If we don't have any knowledge about what TRAP file something + // is in, then we don't want to discard it, so we only consider + // entities that are known to be in a base TRAP file. + locallyInTrapOrTag(false, e, _) and + // Anything that is reachable from an overlay source file should + // not be discarded. + not exists(@trap_or_tag t | locallyInTrapOrTag(true, e, t) | + locallyReachableTrapOrTag(true, _, t) + ) and + // Finally, we have to make sure that base shouldn't retain it. + // If it is reachable from a base source file, then that is + // sufficient unless either the base source file has changed (in + // particular, been deleted) or the overlay has redefined the TRAP + // file it is in. + forall(@trap_or_tag t, string source_file | + locallyInTrapOrTag(false, e, t) and + locallyReachableTrapOrTag(false, source_file, t) + | + overlayChangedFiles(source_file) or + locallyReachableTrapOrTag(true, _, t) ) } From 2ca7d8cb6db8fe5e2c01c075e6f042772fd2be57 Mon Sep 17 00:00:00 2001 From: idrissrio Date: Wed, 4 Mar 2026 15:28:37 +0100 Subject: [PATCH 2/2] C/C++ overlay: use new discard strategy --- .../lib/semmle/code/cpp/internal/Overlay.qll | 47 +++++-------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll b/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll index 55b774a816f9..15c3ecf78db5 100644 --- a/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll +++ b/cpp/ql/lib/semmle/code/cpp/internal/Overlay.qll @@ -1,20 +1,7 @@ -/** - * Defines entity discard predicates for C++ overlay analysis. - * - * The discard strategy is based on TRAP file and tag tracking rather than - * source locations. The extractor records which TRAP file or tag each element - * is defined in (`in_trap_or_tag`), which source files use which TRAP files - * (`source_file_uses_trap`), and which TRAP files use which tags - * (`trap_uses_tag`). These relations allow us to precisely determine whether - * a base element should be discarded or retained in the combined database. - */ - private import OverlayXml /** * Holds always for the overlay variant and never for the base variant. - * This local predicate is used to define local predicates that behave - * differently for the base and overlay variant. */ overlay[local] predicate isOverlay() { databaseMetadata("isOverlay", "true") } @@ -22,16 +9,11 @@ predicate isOverlay() { databaseMetadata("isOverlay", "true") } /** * Holds if TRAP file or tag `t` is reachable from a source file named * `source_file` in the given variant (base or overlay). - * - * A TRAP file is directly reachable from its source file. A tag is reachable - * if the TRAP file that uses it is reachable. */ overlay[local] -private predicate locallyReachableTrapOrTag( - boolean isOverlayVariant, string source_file, @trap_or_tag t -) { +private predicate locally_reachable_trap_or_tag(boolean is_overlay, string source_file, @trap_or_tag t) { exists(@source_file sf, string source_file_raw, @trap trap | - (if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and + (if isOverlay() then is_overlay = true else is_overlay = false) and source_file_uses_trap(sf, trap) and source_file_name(sf, source_file_raw) and source_file = source_file_raw.replaceAll("\\", "/") and @@ -44,28 +26,25 @@ private predicate locallyReachableTrapOrTag( * variant (base or overlay). */ overlay[local] -private predicate locallyInTrapOrTag(boolean isOverlayVariant, @element e, @trap_or_tag t) { - (if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and +private predicate locally_in_trap_or_tag(boolean is_overlay, @element e, @trap_or_tag t) { + (if isOverlay() then is_overlay = true else is_overlay = false) and in_trap_or_tag(e, t) } /** - * Discards an element from the base variant if: - * - It is known to be in a base TRAP file or tag, and - * - It is not reachable from any overlay source file, and - * - Every base source file that reaches it has either changed or had its TRAP - * file redefined by the overlay. + * Holds if element `e` from the base variant should be discarded because + * it has been redefined or is no longer reachable in the overlay. */ overlay[discard_entity] -private predicate discardElement(@element e) { +private predicate discard_element(@element e) { // If we don't have any knowledge about what TRAP file something // is in, then we don't want to discard it, so we only consider // entities that are known to be in a base TRAP file. - locallyInTrapOrTag(false, e, _) and + locally_in_trap_or_tag(false, e, _) and // Anything that is reachable from an overlay source file should // not be discarded. - not exists(@trap_or_tag t | locallyInTrapOrTag(true, e, t) | - locallyReachableTrapOrTag(true, _, t) + not exists(@trap_or_tag t | locally_in_trap_or_tag(true, e, t) | + locally_reachable_trap_or_tag(true, _, t) ) and // Finally, we have to make sure that base shouldn't retain it. // If it is reachable from a base source file, then that is @@ -73,10 +52,10 @@ private predicate discardElement(@element e) { // particular, been deleted) or the overlay has redefined the TRAP // file it is in. forall(@trap_or_tag t, string source_file | - locallyInTrapOrTag(false, e, t) and - locallyReachableTrapOrTag(false, source_file, t) + locally_in_trap_or_tag(false, e, t) and + locally_reachable_trap_or_tag(false, source_file, t) | overlayChangedFiles(source_file) or - locallyReachableTrapOrTag(true, _, t) + locally_reachable_trap_or_tag(true, _, t) ) }