From f58a6e5d3a3e76dd06de0472cc48ff67280ef33e Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 13 Mar 2026 10:01:02 +0000 Subject: [PATCH 01/11] Change @security-severity for XSS queries from 6.1 to 7.8 --- cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql | 2 +- csharp/ql/src/Security Features/CWE-079/XSS.ql | 2 +- go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.ql | 2 +- go/ql/src/Security/CWE-079/ReflectedXss.ql | 2 +- go/ql/src/Security/CWE-079/StoredXss.ql | 2 +- .../CWE/CWE-079/AndroidWebViewAddJavascriptInterface.ql | 2 +- .../CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql | 2 +- java/ql/src/Security/CWE/CWE-079/XSS.ql | 2 +- python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql | 2 +- python/ql/src/Security/CWE-079/ReflectedXss.ql | 2 +- ruby/ql/src/queries/security/cwe-079/ReflectedXSS.ql | 2 +- ruby/ql/src/queries/security/cwe-079/StoredXSS.ql | 2 +- ruby/ql/src/queries/security/cwe-079/UnsafeHtmlConstruction.ql | 2 +- rust/ql/src/queries/security/CWE-079/XSS.ql | 2 +- swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql index 994aba733d23..0e4a8f9741cd 100644 --- a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql +++ b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql @@ -4,7 +4,7 @@ * allows for a cross-site scripting vulnerability. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @precision high * @id cpp/cgi-xss * @tags security diff --git a/csharp/ql/src/Security Features/CWE-079/XSS.ql b/csharp/ql/src/Security Features/CWE-079/XSS.ql index 8735d89ef500..b819ed06bf83 100644 --- a/csharp/ql/src/Security Features/CWE-079/XSS.ql +++ b/csharp/ql/src/Security Features/CWE-079/XSS.ql @@ -4,7 +4,7 @@ * allows for a cross-site scripting vulnerability. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @precision high * @id cs/web/xss * @tags security diff --git a/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.ql b/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.ql index 15373ee85edf..f556630965c7 100644 --- a/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.ql +++ b/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.ql @@ -5,7 +5,7 @@ * scripting vulnerability. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @precision high * @id go/html-template-escaping-bypass-xss * @tags security diff --git a/go/ql/src/Security/CWE-079/ReflectedXss.ql b/go/ql/src/Security/CWE-079/ReflectedXss.ql index 0fca12ac2858..ebabb69f0a4e 100644 --- a/go/ql/src/Security/CWE-079/ReflectedXss.ql +++ b/go/ql/src/Security/CWE-079/ReflectedXss.ql @@ -4,7 +4,7 @@ * a cross-site scripting vulnerability. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @precision high * @id go/reflected-xss * @tags security diff --git a/go/ql/src/Security/CWE-079/StoredXss.ql b/go/ql/src/Security/CWE-079/StoredXss.ql index 83628b310421..dcae0a5f9c1d 100644 --- a/go/ql/src/Security/CWE-079/StoredXss.ql +++ b/go/ql/src/Security/CWE-079/StoredXss.ql @@ -4,7 +4,7 @@ * a stored cross-site scripting vulnerability. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @precision low * @id go/stored-xss * @tags security diff --git a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewAddJavascriptInterface.ql b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewAddJavascriptInterface.ql index 4368b537ab7c..3b4abcaa7f68 100644 --- a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewAddJavascriptInterface.ql +++ b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewAddJavascriptInterface.ql @@ -4,7 +4,7 @@ * @description Exposing a Java object in a WebView with a JavaScript interface can lead to malicious JavaScript controlling the application. * @kind problem * @problem.severity warning - * @security-severity 6.1 + * @security-severity 7.8 * @precision medium * @tags security * external/cwe/cwe-079 diff --git a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql index 561b2af8de02..3ea2b207c04b 100644 --- a/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql +++ b/java/ql/src/Security/CWE/CWE-079/AndroidWebViewSettingsEnabledJavaScript.ql @@ -4,7 +4,7 @@ * @kind problem * @id java/android/websettings-javascript-enabled * @problem.severity warning - * @security-severity 6.1 + * @security-severity 7.8 * @precision medium * @tags security * external/cwe/cwe-079 diff --git a/java/ql/src/Security/CWE/CWE-079/XSS.ql b/java/ql/src/Security/CWE/CWE-079/XSS.ql index 9ae92a7e362e..f1261ebff744 100644 --- a/java/ql/src/Security/CWE/CWE-079/XSS.ql +++ b/java/ql/src/Security/CWE/CWE-079/XSS.ql @@ -4,7 +4,7 @@ * allows for a cross-site scripting vulnerability. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @precision high * @id java/xss * @tags security diff --git a/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql index 97bbb72edec9..fd03ba433a10 100644 --- a/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql +++ b/python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql @@ -4,7 +4,7 @@ * cause a cross-site scripting vulnerability. * @kind problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @precision medium * @id py/jinja2/autoescape-false * @tags security diff --git a/python/ql/src/Security/CWE-079/ReflectedXss.ql b/python/ql/src/Security/CWE-079/ReflectedXss.ql index 11ebad00e375..286dbece126a 100644 --- a/python/ql/src/Security/CWE-079/ReflectedXss.ql +++ b/python/ql/src/Security/CWE-079/ReflectedXss.ql @@ -4,7 +4,7 @@ * allows for a cross-site scripting vulnerability. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @sub-severity high * @precision high * @id py/reflective-xss diff --git a/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.ql b/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.ql index 8cc60618cc5c..04eed164046d 100644 --- a/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.ql +++ b/ruby/ql/src/queries/security/cwe-079/ReflectedXSS.ql @@ -4,7 +4,7 @@ * allows for a cross-site scripting vulnerability. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @sub-severity high * @precision high * @id rb/reflected-xss diff --git a/ruby/ql/src/queries/security/cwe-079/StoredXSS.ql b/ruby/ql/src/queries/security/cwe-079/StoredXSS.ql index a621aee00b03..a2a1752f7f4f 100644 --- a/ruby/ql/src/queries/security/cwe-079/StoredXSS.ql +++ b/ruby/ql/src/queries/security/cwe-079/StoredXSS.ql @@ -4,7 +4,7 @@ * a stored cross-site scripting vulnerability. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @precision high * @id rb/stored-xss * @tags security diff --git a/ruby/ql/src/queries/security/cwe-079/UnsafeHtmlConstruction.ql b/ruby/ql/src/queries/security/cwe-079/UnsafeHtmlConstruction.ql index c1527783fc34..3fa40cd6f919 100644 --- a/ruby/ql/src/queries/security/cwe-079/UnsafeHtmlConstruction.ql +++ b/ruby/ql/src/queries/security/cwe-079/UnsafeHtmlConstruction.ql @@ -4,7 +4,7 @@ * user to perform a cross-site scripting attack. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @precision high * @id rb/html-constructed-from-input * @tags security diff --git a/rust/ql/src/queries/security/CWE-079/XSS.ql b/rust/ql/src/queries/security/CWE-079/XSS.ql index 3c43f5043c74..e7609196b3e7 100644 --- a/rust/ql/src/queries/security/CWE-079/XSS.ql +++ b/rust/ql/src/queries/security/CWE-079/XSS.ql @@ -4,7 +4,7 @@ * allows for a cross-site scripting vulnerability. * @kind path-problem * @problem.severity error - * @security-severity 6.1 + * @security-severity 7.8 * @precision high * @id rust/xss * @tags security diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql index 7243d2216a59..3a2de3fa80ae 100644 --- a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql @@ -3,7 +3,7 @@ * @description Fetching data in a WebView without restricting the base URL may allow an attacker to access sensitive local data, or enable cross-site scripting attack. * @kind path-problem * @problem.severity warning - * @security-severity 6.1 + * @security-severity 7.8 * @precision high * @id swift/unsafe-webview-fetch * @tags security From 056aa342fee99d9367f4041db89beee59417d3b7 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 13 Mar 2026 10:02:01 +0000 Subject: [PATCH 02/11] Change @security-severity for log injection queries from 7.8 to 6.1 --- csharp/ql/src/Security Features/CWE-117/LogForging.ql | 2 +- go/ql/src/Security/CWE-117/LogInjection.ql | 2 +- java/ql/src/Security/CWE/CWE-117/LogInjection.ql | 2 +- python/ql/src/Security/CWE-117/LogInjection.ql | 2 +- ruby/ql/src/queries/security/cwe-117/LogInjection.ql | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/csharp/ql/src/Security Features/CWE-117/LogForging.ql b/csharp/ql/src/Security Features/CWE-117/LogForging.ql index 9494af335705..a922f1c02f8b 100644 --- a/csharp/ql/src/Security Features/CWE-117/LogForging.ql +++ b/csharp/ql/src/Security Features/CWE-117/LogForging.ql @@ -4,7 +4,7 @@ * insertion of forged log entries by a malicious user. * @kind path-problem * @problem.severity error - * @security-severity 7.8 + * @security-severity 6.1 * @precision high * @id cs/log-forging * @tags security diff --git a/go/ql/src/Security/CWE-117/LogInjection.ql b/go/ql/src/Security/CWE-117/LogInjection.ql index 5b6586c8e4e2..08febfd842e8 100644 --- a/go/ql/src/Security/CWE-117/LogInjection.ql +++ b/go/ql/src/Security/CWE-117/LogInjection.ql @@ -4,7 +4,7 @@ * insertion of forged log entries by a malicious user. * @kind path-problem * @problem.severity error - * @security-severity 7.8 + * @security-severity 6.1 * @precision medium * @id go/log-injection * @tags security diff --git a/java/ql/src/Security/CWE/CWE-117/LogInjection.ql b/java/ql/src/Security/CWE/CWE-117/LogInjection.ql index dd4ffb6a10a1..f3efb578f76a 100644 --- a/java/ql/src/Security/CWE/CWE-117/LogInjection.ql +++ b/java/ql/src/Security/CWE/CWE-117/LogInjection.ql @@ -4,7 +4,7 @@ * insertion of forged log entries by malicious users. * @kind path-problem * @problem.severity error - * @security-severity 7.8 + * @security-severity 6.1 * @precision medium * @id java/log-injection * @tags security diff --git a/python/ql/src/Security/CWE-117/LogInjection.ql b/python/ql/src/Security/CWE-117/LogInjection.ql index f1b72faaccbd..64b29e142e08 100644 --- a/python/ql/src/Security/CWE-117/LogInjection.ql +++ b/python/ql/src/Security/CWE-117/LogInjection.ql @@ -4,7 +4,7 @@ * insertion of forged log entries by a malicious user. * @kind path-problem * @problem.severity error - * @security-severity 7.8 + * @security-severity 6.1 * @precision medium * @id py/log-injection * @tags security diff --git a/ruby/ql/src/queries/security/cwe-117/LogInjection.ql b/ruby/ql/src/queries/security/cwe-117/LogInjection.ql index 624c2f90e64d..50a4a718e32d 100644 --- a/ruby/ql/src/queries/security/cwe-117/LogInjection.ql +++ b/ruby/ql/src/queries/security/cwe-117/LogInjection.ql @@ -4,7 +4,7 @@ * insertion of forged log entries by a malicious user. * @kind path-problem * @problem.severity error - * @security-severity 7.8 + * @security-severity 6.1 * @precision medium * @id rb/log-injection * @tags security From 52809133f56a3ebf5a1bd1aae259fb024477492a Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 13 Mar 2026 11:10:43 +0000 Subject: [PATCH 03/11] Add change notes --- .../2026-03-13-adjust-xss-and-log-injection-severity.md | 4 ++++ .../2026-03-13-adjust-xss-and-log-injection-severity.md | 5 +++++ .../2026-03-13-adjust-xss-and-log-injection-severity.md | 5 +++++ .../2026-03-13-adjust-xss-and-log-injection-severity.md | 5 +++++ .../2026-03-13-adjust-xss-and-log-injection-severity.md | 5 +++++ .../2026-03-13-adjust-xss-and-log-injection-severity.md | 5 +++++ .../2026-03-13-adjust-xss-and-log-injection-severity.md | 4 ++++ .../2026-03-13-adjust-xss-and-log-injection-severity.md | 4 ++++ 8 files changed, 37 insertions(+) create mode 100644 cpp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md create mode 100644 csharp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md create mode 100644 go/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md create mode 100644 java/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md create mode 100644 python/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md create mode 100644 ruby/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md create mode 100644 rust/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md create mode 100644 swift/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md diff --git a/cpp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/cpp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md new file mode 100644 index 000000000000..0810e9c49bac --- /dev/null +++ b/cpp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md @@ -0,0 +1,4 @@ +--- +category: queryMetadata +--- +* The `@security-severity` metadata of `cpp/cgi-xss` has been increased from 6.1 (medium) to 7.8 (high). diff --git a/csharp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/csharp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md new file mode 100644 index 000000000000..c317194bc259 --- /dev/null +++ b/csharp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md @@ -0,0 +1,5 @@ +--- +category: queryMetadata +--- +* The `@security-severity` metadata of `cs/log-forging` has been reduced from 7.8 (high) to 6.1 (medium). +* The `@security-severity` metadata of `cs/web/xss` has been increased from 6.1 (medium) to 7.8 (high). diff --git a/go/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/go/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md new file mode 100644 index 000000000000..45320bcd719c --- /dev/null +++ b/go/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md @@ -0,0 +1,5 @@ +--- +category: queryMetadata +--- +* The `@security-severity` metadata of `go/log-injection` has been reduced from 7.8 (high) to 6.1 (medium). +* The `@security-severity` metadata of `go/html-template-escaping-bypass-xss`, `go/reflected-xss` and `go/stored-xss` has been increased from 6.1 (medium) to 7.8 (high). diff --git a/java/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/java/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md new file mode 100644 index 000000000000..fa1288af16eb --- /dev/null +++ b/java/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md @@ -0,0 +1,5 @@ +--- +category: queryMetadata +--- +* The `@security-severity` metadata of `java/log-injection` has been reduced from 7.8 (high) to 6.1 (medium). +* The `@security-severity` metadata of `java/android/webview-addjavascriptinterface`, `java/android/websettings-javascript-enabled` and `java/xss` has been increased from 6.1 (medium) to 7.8 (high). diff --git a/python/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/python/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md new file mode 100644 index 000000000000..4278d0171e34 --- /dev/null +++ b/python/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md @@ -0,0 +1,5 @@ +--- +category: queryMetadata +--- +* The `@security-severity` metadata of `py/log-injection` has been reduced from 7.8 (high) to 6.1 (medium). +* The `@security-severity` metadata of `py/jinja2/autoescape-false` and `py/reflective-xss` has been increased from 6.1 (medium) to 7.8 (high). diff --git a/ruby/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/ruby/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md new file mode 100644 index 000000000000..459c2ce7f916 --- /dev/null +++ b/ruby/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md @@ -0,0 +1,5 @@ +--- +category: queryMetadata +--- +* The `@security-severity` metadata of `rb/log-injection` has been reduced from 7.8 (high) to 6.1 (medium). +* The `@security-severity` metadata of `rb/reflected-xss`, `rb/stored-xss` and `rb/html-constructed-from-input` has been increased from 6.1 (medium) to 7.8 (high). diff --git a/rust/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/rust/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md new file mode 100644 index 000000000000..7c24d4147a5b --- /dev/null +++ b/rust/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md @@ -0,0 +1,4 @@ +--- +category: queryMetadata +--- +* The `@security-severity` metadata of `rust/xss` has been increased from 6.1 (medium) to 7.8 (high). diff --git a/swift/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/swift/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md new file mode 100644 index 000000000000..a46302ed1462 --- /dev/null +++ b/swift/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md @@ -0,0 +1,4 @@ +--- +category: queryMetadata +--- +* The `@security-severity` metadata of `swift/unsafe-webview-fetch` has been increased from 6.1 (medium) to 7.8 (high). From 3aaee9d981233098f1216ea10a7edd51878e5080 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 17 Mar 2026 12:01:05 +0000 Subject: [PATCH 04/11] Change @security-severity for rust/log-injection from 2.6 to 6.1 --- .../2026-03-13-adjust-xss-and-log-injection-severity.md | 1 + rust/ql/src/queries/security/CWE-117/LogInjection.ql | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/rust/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md index 7c24d4147a5b..8bfc5be15517 100644 --- a/rust/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md +++ b/rust/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md @@ -1,4 +1,5 @@ --- category: queryMetadata --- +* The `@security-severity` metadata of `rust/log-injection` has been increased from 2.6 (low) to 6.1 (medium). * The `@security-severity` metadata of `rust/xss` has been increased from 6.1 (medium) to 7.8 (high). diff --git a/rust/ql/src/queries/security/CWE-117/LogInjection.ql b/rust/ql/src/queries/security/CWE-117/LogInjection.ql index 64d9c47c7909..c00ac310ef67 100644 --- a/rust/ql/src/queries/security/CWE-117/LogInjection.ql +++ b/rust/ql/src/queries/security/CWE-117/LogInjection.ql @@ -4,7 +4,7 @@ * insertion of forged log entries by a malicious user. * @kind path-problem * @problem.severity error - * @security-severity 2.6 + * @security-severity 6.1 * @precision medium * @id rust/log-injection * @tags security From 19faf8f30bda96f32d7b2eb286b9c0d74ae75de6 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 17 Mar 2026 13:29:36 +0100 Subject: [PATCH 05/11] C#: Add ObjectInitMethod as enclosing callable for the instance initializers. --- csharp/ql/lib/semmle/code/csharp/Callable.qll | 27 +++++++++++++++ .../semmle/code/csharp/ExprOrStmtParent.qll | 2 ++ .../internal/ControlFlowGraphImpl.qll | 33 ++----------------- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/Callable.qll b/csharp/ql/lib/semmle/code/csharp/Callable.qll index f8346cfe01e2..611b578b859a 100644 --- a/csharp/ql/lib/semmle/code/csharp/Callable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Callable.qll @@ -336,6 +336,22 @@ class ExtensionTypeExtensionMethod extends ExtensionMethodImpl { ExtensionTypeExtensionMethod() { this.isInExtension() } } +/** + * A non-static member with an initializer, for example a field `int Field = 0`. + */ +private class InitializedInstanceMember extends Member { + private AssignExpr ae; + + InitializedInstanceMember() { + not this.isStatic() and + expr_parent_top_level(ae, _, this) and + not ae = getExpressionBody(_) + } + + /** Gets the initializer expression. */ + AssignExpr getInitializer() { result = ae } +} + /** * An object initializer method. * @@ -347,6 +363,17 @@ class ExtensionTypeExtensionMethod extends ExtensionMethodImpl { */ class ObjectInitMethod extends Method { ObjectInitMethod() { this.getName() = "" } + + /** + * Holds if this object initializer method performs the initialization + * of a member via assignment `init`. + */ + predicate initializes(AssignExpr init) { + exists(InitializedInstanceMember m | + this.getDeclaringType().getAMember() = m and + init = m.getInitializer() + ) + } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll b/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll index aa834ef91038..5afacf608a8c 100644 --- a/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll +++ b/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll @@ -214,6 +214,8 @@ private module Cached { parent*(enclosingStart(cfe), c.(Constructor).getInitializer()) or parent*(cfe, c.(Constructor).getObjectInitializerCall()) + or + parent*(cfe, any(AssignExpr init | c.(ObjectInitMethod).initializes(init))) } /** Holds if the enclosing statement of expression `e` is `s`. */ diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll index 1696869e5911..0bdf1f795db2 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll @@ -10,42 +10,15 @@ private import semmle.code.csharp.ExprOrStmtParent private import semmle.code.csharp.commons.Compilation private module Initializers { - /** - * A non-static member with an initializer, for example a field `int Field = 0`. - */ - class InitializedInstanceMember extends Member { - private AssignExpr ae; - - InitializedInstanceMember() { - not this.isStatic() and - expr_parent_top_level(ae, _, this) and - not ae = any(Callable c).getExpressionBody() - } - - /** Gets the initializer expression. */ - AssignExpr getInitializer() { result = ae } - } - - /** - * Holds if `obinit` is an object initializer method that performs the initialization - * of a member via assignment `init`. - */ - predicate obinitInitializes(ObjectInitMethod obinit, AssignExpr init) { - exists(InitializedInstanceMember m | - obinit.getDeclaringType().getAMember() = m and - init = m.getInitializer() - ) - } - /** * Gets the `i`th member initializer expression for object initializer method `obinit` * in compilation `comp`. */ AssignExpr initializedInstanceMemberOrder(ObjectInitMethod obinit, CompilationExt comp, int i) { - obinitInitializes(obinit, result) and + obinit.initializes(result) and result = rank[i + 1](AssignExpr ae0, Location l | - obinitInitializes(obinit, ae0) and + obinit.initializes(ae0) and l = ae0.getLocation() and getCompilation(l.getFile()) = comp | @@ -74,7 +47,7 @@ class CfgScope extends Element, @top_level_exprorstmt_parent { any(Callable c | c.(Constructor).hasInitializer() or - Initializers::obinitInitializes(c, _) + c.(ObjectInitMethod).initializes(_) or c.hasBody() ) From af63e636861038f59cceded11c38b5ffb46c35fa Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 17 Mar 2026 14:12:18 +0100 Subject: [PATCH 06/11] C#: Accept test changes. --- csharp/ql/test/library-tests/dispatch/CallGraph.expected | 1 + .../structuralcomparison/structuralComparison.expected | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/csharp/ql/test/library-tests/dispatch/CallGraph.expected b/csharp/ql/test/library-tests/dispatch/CallGraph.expected index 31e2a99ae24c..e7ebca868ba8 100644 --- a/csharp/ql/test/library-tests/dispatch/CallGraph.expected +++ b/csharp/ql/test/library-tests/dispatch/CallGraph.expected @@ -24,6 +24,7 @@ | ExactCallable.cs:15:25:15:35 | Run`2 | ExactCallable.cs:172:21:172:33 | MethodWithOut | | ExactCallable.cs:15:25:15:35 | Run`2 | ExactCallable.cs:177:21:177:34 | MethodWithOut2 | | ExactCallable.cs:182:21:182:22 | M1 | ExactCallable.cs:187:21:187:22 | M2 | +| TypeFlow.cs:3:7:3:14 | | TypeFlow.cs:22:20:22:22 | set_Prop | | TypeFlow.cs:5:5:5:12 | TypeFlow | TypeFlow.cs:24:10:24:12 | Run | | TypeFlow.cs:24:10:24:12 | Run | TypeFlow.cs:12:29:12:34 | Method | | TypeFlow.cs:24:10:24:12 | Run | TypeFlow.cs:17:30:17:35 | Method | diff --git a/csharp/ql/test/library-tests/structuralcomparison/structuralComparison.expected b/csharp/ql/test/library-tests/structuralcomparison/structuralComparison.expected index 0f131d8c25cb..d9b6636469ab 100644 --- a/csharp/ql/test/library-tests/structuralcomparison/structuralComparison.expected +++ b/csharp/ql/test/library-tests/structuralcomparison/structuralComparison.expected @@ -56,11 +56,11 @@ gvn | StructuralComparison.cs:3:14:3:18 | this access | (kind:Expr(12),false,Class) | | StructuralComparison.cs:3:14:3:18 | {...} | (kind:Stmt(1)) | | StructuralComparison.cs:5:26:5:26 | access to field x | (kind:Expr(16),true,x) | -| StructuralComparison.cs:5:26:5:26 | this access | (kind:Expr(12)) | +| StructuralComparison.cs:5:26:5:26 | this access | (kind:Expr(12),false,Class) | | StructuralComparison.cs:5:26:5:30 | ... = ... | ((kind:Expr(16),true,x) :: (0 :: (kind:Expr(63)))) | | StructuralComparison.cs:5:30:5:30 | 0 | 0 | | StructuralComparison.cs:6:26:6:26 | access to field y | (kind:Expr(16),true,y) | -| StructuralComparison.cs:6:26:6:26 | this access | (kind:Expr(12)) | +| StructuralComparison.cs:6:26:6:26 | this access | (kind:Expr(12),false,Class) | | StructuralComparison.cs:6:26:6:30 | ... = ... | ((kind:Expr(16),true,y) :: (1 :: (kind:Expr(63)))) | | StructuralComparison.cs:6:30:6:30 | 1 | 1 | | StructuralComparison.cs:8:24:8:24 | 0 | 0 | From 97670b36743f35e105dba31e04cfbe7d9efe2404 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Tue, 17 Mar 2026 16:32:04 +0100 Subject: [PATCH 07/11] Rust: Unify handling of struct and tuple constructors --- .../rust/elements/internal/StructExprImpl.qll | 5 + .../rust/elements/internal/StructImpl.qll | 6 + .../rust/elements/internal/StructPatImpl.qll | 7 + .../rust/elements/internal/VariantImpl.qll | 6 + .../internal/typeinference/TypeInference.qll | 425 ++++++------------ 5 files changed, 153 insertions(+), 296 deletions(-) diff --git a/rust/ql/lib/codeql/rust/elements/internal/StructExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/StructExprImpl.qll index d7704894c49a..897196b78cbe 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/StructExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/StructExprImpl.qll @@ -50,5 +50,10 @@ module Impl { or result = this.getVariant().getStructField(name) } + + /** Gets the `i`th struct field of the instantiated struct or variant. */ + StructField getNthStructField(int i) { + result = [this.getStruct().getNthStructField(i), this.getVariant().getNthStructField(i)] + } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll index cb4121b7224d..23fa1e76d9a8 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll @@ -35,6 +35,12 @@ module Impl { /** Gets a record field, if any. */ StructField getAStructField() { result = this.getStructField(_) } + /** Gets the `i`th struct field, if any. */ + pragma[nomagic] + StructField getNthStructField(int i) { + result = this.getFieldList().(StructFieldList).getField(i) + } + /** Gets the `i`th tuple field, if any. */ pragma[nomagic] TupleField getTupleField(int i) { result = this.getFieldList().(TupleFieldList).getField(i) } diff --git a/rust/ql/lib/codeql/rust/elements/internal/StructPatImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/StructPatImpl.qll index 28afc2a5b0d7..e649d2a57787 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/StructPatImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/StructPatImpl.qll @@ -42,6 +42,13 @@ module Impl { ) } + /** Gets the `i`th struct field of the instantiated struct or variant. */ + StructField getNthStructField(int i) { + exists(PathResolution::ItemNode item | item = this.getResolvedPath(_) | + result = [item.(Struct).getNthStructField(i), item.(Variant).getNthStructField(i)] + ) + } + /** Gets the struct pattern for the field `name`. */ pragma[nomagic] StructPatField getPatField(string name) { diff --git a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll index ed8b93f6c1d2..58b061049bdd 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll @@ -32,6 +32,12 @@ module Impl { result.getName().getText() = name } + /** Gets the `i`th struct field, if any. */ + pragma[nomagic] + StructField getNthStructField(int i) { + result = this.getFieldList().(StructFieldList).getField(i) + } + /** Gets the `i`th tuple field, if any. */ pragma[nomagic] TupleField getTupleField(int i) { result = this.getFieldList().(TupleFieldList).getField(i) } diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index c194531a0781..a4b338d7c692 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -872,188 +872,6 @@ private Type inferTypeEquality(AstNode n, TypePath path) { ) } -/** - * A matching configuration for resolving types of struct expressions - * like `Foo { bar = baz }`. - * - * This also includes nullary struct expressions like `None`. - */ -private module StructExprMatchingInput implements MatchingInputSig { - private newtype TPos = - TFieldPos(string name) { exists(any(Declaration decl).getField(name)) } or - TStructPos() - - class DeclarationPosition extends TPos { - string asFieldPos() { this = TFieldPos(result) } - - predicate isStructPos() { this = TStructPos() } - - string toString() { - result = this.asFieldPos() - or - this.isStructPos() and - result = "(struct)" - } - } - - abstract class Declaration extends AstNode { - final TypeParameter getTypeParameter(TypeParameterPosition ppos) { - typeParamMatchPosition(this.getTypeItem().getGenericParamList().getATypeParam(), result, ppos) - } - - abstract StructField getField(string name); - - abstract TypeItem getTypeItem(); - - Type getDeclaredType(DeclarationPosition dpos, TypePath path) { - // type of a field - exists(TypeMention tp | - tp = this.getField(dpos.asFieldPos()).getTypeRepr() and - result = tp.getTypeAt(path) - ) - or - // type parameter of the struct itself - dpos.isStructPos() and - result = this.getTypeParameter(_) and - path = TypePath::singleton(result) - or - // type of the struct or enum itself - dpos.isStructPos() and - path.isEmpty() and - result = TDataType(this.getTypeItem()) - } - } - - private class StructDecl extends Declaration, Struct { - StructDecl() { this.isStruct() or this.isUnit() } - - override StructField getField(string name) { result = this.getStructField(name) } - - override TypeItem getTypeItem() { result = this } - } - - private class StructVariantDecl extends Declaration, Variant { - StructVariantDecl() { this.isStruct() or this.isUnit() } - - override StructField getField(string name) { result = this.getStructField(name) } - - override TypeItem getTypeItem() { result = this.getEnum() } - } - - class AccessPosition = DeclarationPosition; - - abstract class Access extends AstNode { - pragma[nomagic] - abstract AstNode getNodeAt(AccessPosition apos); - - pragma[nomagic] - Type getInferredType(AccessPosition apos, TypePath path) { - result = inferType(this.getNodeAt(apos), path) - } - - pragma[nomagic] - abstract Path getStructPath(); - - pragma[nomagic] - Declaration getTarget() { result = resolvePath(this.getStructPath()) } - - pragma[nomagic] - Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { - // Handle constructions that use `Self {...}` syntax - exists(TypeMention tm, TypePath path0 | - tm = this.getStructPath() and - result = tm.getTypeAt(path0) and - path0.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) - ) - } - - /** - * Holds if the return type of this struct expression at `path` may have to - * be inferred from the context. - */ - pragma[nomagic] - predicate hasUnknownTypeAt(DeclarationPosition pos, TypePath path) { - exists(Declaration d, TypeParameter tp | - d = this.getTarget() and - pos.isStructPos() and - tp = d.getDeclaredType(pos, path) and - not exists(DeclarationPosition fieldPos | - not fieldPos.isStructPos() and - tp = d.getDeclaredType(fieldPos, _) - ) and - // check that no explicit type arguments have been supplied for `tp` - not exists(TypeArgumentPosition tapos | - exists(this.getTypeArgument(tapos, _)) and - TTypeParamTypeParameter(tapos.asTypeParam()) = tp - ) - ) - } - } - - private class StructExprAccess extends Access, StructExpr { - override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { - result = super.getTypeArgument(apos, path) - or - exists(TypePath suffix | - suffix.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) and - result = CertainTypeInference::inferCertainType(this, suffix) - ) - } - - override AstNode getNodeAt(AccessPosition apos) { - result = this.getFieldExpr(apos.asFieldPos()).getExpr() - or - result = this and - apos.isStructPos() - } - - override Path getStructPath() { result = this.getPath() } - } - - /** - * A potential nullary struct/variant construction such as `None`. - */ - private class PathExprAccess extends Access, PathExpr { - PathExprAccess() { not exists(CallExpr ce | this = ce.getFunction()) } - - override AstNode getNodeAt(AccessPosition apos) { - result = this and - apos.isStructPos() - } - - override Path getStructPath() { result = this.getPath() } - } - - predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { - apos = dpos - } -} - -private module StructExprMatching = Matching; - -pragma[nomagic] -private Type inferStructExprType0( - AstNode n, FunctionPosition pos, boolean hasReceiver, TypePath path -) { - exists(StructExprMatchingInput::Access a, StructExprMatchingInput::AccessPosition apos | - n = a.getNodeAt(apos) and - hasReceiver = false and - if apos.isStructPos() then pos.isReturn() else pos.asPosition() = 0 // the actual position doesn't matter, as long as it is positional - | - result = StructExprMatching::inferAccessType(a, apos, path) - or - a.hasUnknownTypeAt(apos, path) and - result = TUnknownType() - ) -} - -/** - * Gets the type of `n` at `path`, where `n` is either a struct expression or - * a field expression of a struct expression. - */ -private predicate inferStructExprType = - ContextTyping::CheckContextTyping::check/2; - pragma[nomagic] private TupleType inferTupleRootType(AstNode n) { // `typeEquality` handles the non-root cases @@ -3083,14 +2901,14 @@ private Type inferFunctionCallTypePreCheck( private predicate inferFunctionCallType = ContextTyping::CheckContextTyping::check/2; -abstract private class TupleLikeConstructor extends Addressable { +abstract private class Constructor extends Addressable { final TypeParameter getTypeParameter(TypeParameterPosition ppos) { typeParamMatchPosition(this.getTypeItem().getGenericParamList().getATypeParam(), result, ppos) } abstract TypeItem getTypeItem(); - abstract TupleField getTupleField(int i); + abstract TypeRepr getParameterTypeRepr(int pos); Type getReturnType(TypePath path) { result = TDataType(this.getTypeItem()) and @@ -3105,65 +2923,59 @@ abstract private class TupleLikeConstructor extends Addressable { or pos.isReturn() and result = this.getReturnType(path) - or - pos.isTypeQualifier() and - result = this.getReturnType(path) } Type getParameterType(int pos, TypePath path) { - result = this.getTupleField(pos).getTypeRepr().(TypeMention).getTypeAt(path) + result = this.getParameterTypeRepr(pos).(TypeMention).getTypeAt(path) } } -private class TupleLikeStruct extends TupleLikeConstructor instanceof Struct { - TupleLikeStruct() { this.isTuple() } - +private class StructConstructor extends Constructor instanceof Struct { override TypeItem getTypeItem() { result = this } - override TupleField getTupleField(int i) { result = Struct.super.getTupleField(i) } + override TypeRepr getParameterTypeRepr(int i) { + result = [super.getTupleField(i).getTypeRepr(), super.getNthStructField(i).getTypeRepr()] + } } -private class TupleLikeVariant extends TupleLikeConstructor instanceof Variant { - TupleLikeVariant() { this.isTuple() } - +private class VariantConstructor extends Constructor instanceof Variant { override TypeItem getTypeItem() { result = super.getEnum() } - override TupleField getTupleField(int i) { result = Variant.super.getTupleField(i) } + override TypeRepr getParameterTypeRepr(int i) { + result = [super.getTupleField(i).getTypeRepr(), super.getNthStructField(i).getTypeRepr()] + } } /** - * A matching configuration for resolving types of tuple-like variants and tuple - * structs such as `Result::Ok(42)`. + * A matching configuration for resolving types of constructors of enums and + * structs, such as `Result::Ok(42)`, `Foo { bar = 1 }` and `None`. */ -private module TupleLikeConstructionMatchingInput implements MatchingInputSig { +private module ConstructorMatchingInput implements MatchingInputSig { import FunctionPositionMatchingInput - class Declaration = TupleLikeConstructor; + class Declaration = Constructor; - class Access extends NonAssocCallExpr, ContextTyping::ContextTypedCallCand { - Access() { - this instanceof CallExprImpl::TupleStructExpr or - this instanceof CallExprImpl::TupleVariantExpr - } + abstract class Access extends AstNode { + abstract Type getInferredType(FunctionPosition pos, TypePath path); - override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { - result = NonAssocCallExpr.super.getTypeArgument(apos, path) - } + abstract Declaration getTarget(); - Declaration getTarget() { result = this.resolveCallTargetViaPathResolution() } + abstract AstNode getNodeAt(AccessPosition apos); + + abstract Type getTypeArgument(TypeArgumentPosition apos, TypePath path); /** - * Holds if the return type of this tuple-like construction at `path` may have to be inferred - * from the context, for example in `Result::Ok(42)` the error type has to be inferred from the - * context. + * Holds if the return type of this constructor expression at `path` may + * have to be inferred from the context. For example in `Result::Ok(42)` the + * error type has to be inferred from the context. */ pragma[nomagic] predicate hasUnknownTypeAt(FunctionPosition pos, TypePath path) { - exists(TupleLikeConstructor tc, TypeParameter tp | - tc = this.getTarget() and + exists(Declaration d, TypeParameter tp | + d = this.getTarget() and pos.isReturn() and - tp = tc.getReturnType(path) and - not tp = tc.getParameterType(_, _) and + tp = d.getDeclaredType(pos, path) and + not exists(FunctionPosition pos2 | not pos2.isReturn() and tp = d.getDeclaredType(pos2, _)) and // check that no explicit type arguments have been supplied for `tp` not exists(TypeArgumentPosition tapos | exists(this.getTypeArgument(tapos, _)) and @@ -3172,25 +2984,93 @@ private module TupleLikeConstructionMatchingInput implements MatchingInputSig { ) } } + + private class NonAssocCallAccess extends Access, NonAssocCallExpr, + ContextTyping::ContextTypedCallCand + { + NonAssocCallAccess() { + this instanceof CallExprImpl::TupleStructExpr or + this instanceof CallExprImpl::TupleVariantExpr + } + + override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { + result = NonAssocCallExpr.super.getTypeArgument(apos, path) + } + + override AstNode getNodeAt(AccessPosition apos) { + result = NonAssocCallExpr.super.getNodeAt(apos) + } + + override Type getInferredType(FunctionPosition pos, TypePath path) { + result = NonAssocCallExpr.super.getInferredType(pos, path) + } + + override Declaration getTarget() { result = this.resolveCallTargetViaPathResolution() } + } + + abstract private class StructAccess extends Access instanceof PathAstNode { + pragma[nomagic] + override Type getInferredType(AccessPosition apos, TypePath path) { + result = inferType(this.getNodeAt(apos), path) + } + + pragma[nomagic] + override Declaration getTarget() { result = resolvePath(super.getPath()) } + + pragma[nomagic] + override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { + // Handle constructions that use `Self {...}` syntax + exists(TypeMention tm, TypePath path0 | + tm = super.getPath() and + result = tm.getTypeAt(path0) and + path0.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) + ) + } + } + + private class StructExprAccess extends StructAccess, StructExpr { + override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { + result = super.getTypeArgument(apos, path) + or + exists(TypePath suffix | + suffix.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) and + result = CertainTypeInference::inferCertainType(this, suffix) + ) + } + + override AstNode getNodeAt(AccessPosition apos) { + result = + this.getFieldExpr(this.getNthStructField(apos.asPosition()).getName().getText()).getExpr() + or + result = this and apos.isReturn() + } + } + + /** A potential nullary struct/variant construction such as `None`. */ + private class PathExprAccess extends StructAccess, PathExpr { + PathExprAccess() { not exists(CallExpr ce | this = ce.getFunction()) } + + override AstNode getNodeAt(AccessPosition apos) { result = this and apos.isReturn() } + } } -private module TupleLikeConstructionMatching = Matching; +private module ConstructorMatching = Matching; pragma[nomagic] -private Type inferTupleLikeConstructionTypePreCheck( +private Type inferConstructorTypePreCheck( AstNode n, FunctionPosition pos, boolean hasReceiver, TypePath path ) { hasReceiver = false and - exists(TupleLikeConstructionMatchingInput::Access a | n = a.getNodeAt(pos) | - result = TupleLikeConstructionMatching::inferAccessType(a, pos, path) + exists(ConstructorMatchingInput::Access a | n = a.getNodeAt(pos) | + result = ConstructorMatching::inferAccessType(a, pos, path) or a.hasUnknownTypeAt(pos, path) and result = TUnknownType() ) } -private predicate inferTupleLikeConstructionType = - ContextTyping::CheckContextTyping::check/2; +private predicate inferConstructorType = + ContextTyping::CheckContextTyping::check/2; /** * A matching configuration for resolving types of operations like `a + b`. @@ -3676,71 +3556,27 @@ private Type inferDereferencedExprPtrType(AstNode n, TypePath path) { } /** - * A matching configuration for resolving types of struct patterns - * like `let Foo { bar } = ...`. + * A matching configuration for resolving types of constructor patterns like + * `let Foo { bar } = ...` or `let Some(x) = ...`. */ -private module StructPatMatchingInput implements MatchingInputSig { - class DeclarationPosition = StructExprMatchingInput::DeclarationPosition; +private module ConstructorPatMatchingInput implements MatchingInputSig { + import FunctionPositionMatchingInput - class Declaration = StructExprMatchingInput::Declaration; + class Declaration = ConstructorMatchingInput::Declaration; - class AccessPosition = DeclarationPosition; + class Access extends Pat instanceof PathAstNode { + Access() { this instanceof TupleStructPat or this instanceof StructPat } - class Access extends StructPat { Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } AstNode getNodeAt(AccessPosition apos) { - result = this.getPatField(apos.asFieldPos()).getPat() + this = + any(StructPat sp | + result = + sp.getPatField(sp.getNthStructField(apos.asPosition()).getName().getText()).getPat() + ) or - result = this and - apos.isStructPos() - } - - Type getInferredType(AccessPosition apos, TypePath path) { - result = inferType(this.getNodeAt(apos), path) - or - // The struct/enum type is supplied explicitly as a type qualifier, e.g. - // `let Foo::Variant { ... } = ...`. - apos.isStructPos() and - result = this.getPath().(TypeMention).getTypeAt(path) - } - - Declaration getTarget() { result = resolvePath(this.getPath()) } - } - - predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { - apos = dpos - } -} - -private module StructPatMatching = Matching; - -/** - * Gets the type of `n` at `path`, where `n` is either a struct pattern or - * a field pattern of a struct pattern. - */ -pragma[nomagic] -private Type inferStructPatType(AstNode n, TypePath path) { - exists(StructPatMatchingInput::Access a, StructPatMatchingInput::AccessPosition apos | - n = a.getNodeAt(apos) and - result = StructPatMatching::inferAccessType(a, apos, path) - ) -} - -/** - * A matching configuration for resolving types of tuple struct patterns - * like `let Some(x) = ...`. - */ -private module TupleStructPatMatchingInput implements MatchingInputSig { - import FunctionPositionMatchingInput - - class Declaration = TupleLikeConstructor; - - class Access extends TupleStructPat { - Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } - - AstNode getNodeAt(AccessPosition apos) { - result = this.getField(apos.asPosition()) + result = this.(TupleStructPat).getField(apos.asPosition()) or result = this and apos.isReturn() @@ -3750,26 +3586,27 @@ private module TupleStructPatMatchingInput implements MatchingInputSig { result = inferType(this.getNodeAt(apos), path) or // The struct/enum type is supplied explicitly as a type qualifier, e.g. + // `let Foo::Variant { ... } = ...` or // `let Option::::Some(x) = ...`. - apos.isTypeQualifier() and - result = this.getPath().(TypeMention).getTypeAt(path) + apos.isReturn() and + result = super.getPath().(TypeMention).getTypeAt(path) } - Declaration getTarget() { result = resolvePath(this.getPath()) } + Declaration getTarget() { result = resolvePath(super.getPath()) } } } -private module TupleStructPatMatching = Matching; +private module ConstructorPatMatching = Matching; /** - * Gets the type of `n` at `path`, where `n` is either a tuple struct pattern or - * a positional pattern of a tuple struct pattern. + * Gets the type of `n` at `path`, where `n` is a pattern for a constructor, + * either a struct pattern or a tuple-struct pattern. */ pragma[nomagic] -private Type inferTupleStructPatType(AstNode n, TypePath path) { - exists(TupleStructPatMatchingInput::Access a, TupleStructPatMatchingInput::AccessPosition apos | +private Type inferConstructorPatType(AstNode n, TypePath path) { + exists(ConstructorPatMatchingInput::Access a, FunctionPosition apos | n = a.getNodeAt(apos) and - result = TupleStructPatMatching::inferAccessType(a, apos, path) + result = ConstructorPatMatching::inferAccessType(a, apos, path) ) } @@ -4080,11 +3917,9 @@ private module Cached { or result = inferTypeEquality(n, path) or - result = inferStructExprType(n, path) - or result = inferFunctionCallType(n, path) or - result = inferTupleLikeConstructionType(n, path) + result = inferConstructorType(n, path) or result = inferOperationType(n, path) or @@ -4106,9 +3941,7 @@ private module Cached { or result = inferClosureExprType(n, path) or - result = inferStructPatType(n, path) - or - result = inferTupleStructPatType(n, path) + result = inferConstructorPatType(n, path) ) } } @@ -4157,9 +3990,9 @@ private module Debug { t = inferFunctionCallType(n, path) } - predicate debugInferTupleLikeConstructionType(AstNode n, TypePath path, Type t) { + predicate debugInferConstructorType(AstNode n, TypePath path, Type t) { n = getRelevantLocatable() and - t = inferTupleLikeConstructionType(n, path) + t = inferConstructorType(n, path) } predicate debugTypeMention(TypeMention tm, TypePath path, Type type) { From d180900ab4d01a0d00dab29e7e9dea0a1ebbad94 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Tue, 17 Mar 2026 19:01:22 +0100 Subject: [PATCH 08/11] Rust: Minor improvements to documentation comments Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../lib/codeql/rust/internal/typeinference/TypeInference.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index a4b338d7c692..41af0ec31f7f 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -2948,7 +2948,7 @@ private class VariantConstructor extends Constructor instanceof Variant { /** * A matching configuration for resolving types of constructors of enums and - * structs, such as `Result::Ok(42)`, `Foo { bar = 1 }` and `None`. + * structs, such as `Result::Ok(42)`, `Foo { bar: 1 }` and `None`. */ private module ConstructorMatchingInput implements MatchingInputSig { import FunctionPositionMatchingInput @@ -3586,7 +3586,7 @@ private module ConstructorPatMatchingInput implements MatchingInputSig { result = inferType(this.getNodeAt(apos), path) or // The struct/enum type is supplied explicitly as a type qualifier, e.g. - // `let Foo::Variant { ... } = ...` or + // `let Foo::::Variant { ... } = ...` or // `let Option::::Some(x) = ...`. apos.isReturn() and result = super.getPath().(TypeMention).getTypeAt(path) From 6efd844180536df09573491df1cd63cf4c176c9a Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Wed, 18 Mar 2026 14:04:49 +0100 Subject: [PATCH 09/11] Rust: Rename into "construction" and "deconstruction" --- .../internal/typeinference/TypeInference.qll | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 41af0ec31f7f..e88a69e910f3 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -2947,10 +2947,10 @@ private class VariantConstructor extends Constructor instanceof Variant { } /** - * A matching configuration for resolving types of constructors of enums and + * A matching configuration for resolving types of constructions of enums and * structs, such as `Result::Ok(42)`, `Foo { bar: 1 }` and `None`. */ -private module ConstructorMatchingInput implements MatchingInputSig { +private module ConstructionMatchingInput implements MatchingInputSig { import FunctionPositionMatchingInput class Declaration = Constructor; @@ -2965,7 +2965,7 @@ private module ConstructorMatchingInput implements MatchingInputSig { abstract Type getTypeArgument(TypeArgumentPosition apos, TypePath path); /** - * Holds if the return type of this constructor expression at `path` may + * Holds if the return type of this construction expression at `path` may * have to be inferred from the context. For example in `Result::Ok(42)` the * error type has to be inferred from the context. */ @@ -3054,23 +3054,23 @@ private module ConstructorMatchingInput implements MatchingInputSig { } } -private module ConstructorMatching = Matching; +private module ConstructionMatching = Matching; pragma[nomagic] -private Type inferConstructorTypePreCheck( +private Type inferConstructionTypePreCheck( AstNode n, FunctionPosition pos, boolean hasReceiver, TypePath path ) { hasReceiver = false and - exists(ConstructorMatchingInput::Access a | n = a.getNodeAt(pos) | - result = ConstructorMatching::inferAccessType(a, pos, path) + exists(ConstructionMatchingInput::Access a | n = a.getNodeAt(pos) | + result = ConstructionMatching::inferAccessType(a, pos, path) or a.hasUnknownTypeAt(pos, path) and result = TUnknownType() ) } -private predicate inferConstructorType = - ContextTyping::CheckContextTyping::check/2; +private predicate inferConstructionType = + ContextTyping::CheckContextTyping::check/2; /** * A matching configuration for resolving types of operations like `a + b`. @@ -3556,13 +3556,13 @@ private Type inferDereferencedExprPtrType(AstNode n, TypePath path) { } /** - * A matching configuration for resolving types of constructor patterns like + * A matching configuration for resolving types of deconstruction patterns like * `let Foo { bar } = ...` or `let Some(x) = ...`. */ -private module ConstructorPatMatchingInput implements MatchingInputSig { +private module DeconstructionPatMatchingInput implements MatchingInputSig { import FunctionPositionMatchingInput - class Declaration = ConstructorMatchingInput::Declaration; + class Declaration = ConstructionMatchingInput::Declaration; class Access extends Pat instanceof PathAstNode { Access() { this instanceof TupleStructPat or this instanceof StructPat } @@ -3596,17 +3596,17 @@ private module ConstructorPatMatchingInput implements MatchingInputSig { } } -private module ConstructorPatMatching = Matching; +private module DeconstructionPatMatching = Matching; /** * Gets the type of `n` at `path`, where `n` is a pattern for a constructor, * either a struct pattern or a tuple-struct pattern. */ pragma[nomagic] -private Type inferConstructorPatType(AstNode n, TypePath path) { - exists(ConstructorPatMatchingInput::Access a, FunctionPosition apos | +private Type inferDeconstructionPatType(AstNode n, TypePath path) { + exists(DeconstructionPatMatchingInput::Access a, FunctionPosition apos | n = a.getNodeAt(apos) and - result = ConstructorPatMatching::inferAccessType(a, apos, path) + result = DeconstructionPatMatching::inferAccessType(a, apos, path) ) } @@ -3919,7 +3919,7 @@ private module Cached { or result = inferFunctionCallType(n, path) or - result = inferConstructorType(n, path) + result = inferConstructionType(n, path) or result = inferOperationType(n, path) or @@ -3941,7 +3941,7 @@ private module Cached { or result = inferClosureExprType(n, path) or - result = inferConstructorPatType(n, path) + result = inferDeconstructionPatType(n, path) ) } } @@ -3990,9 +3990,9 @@ private module Debug { t = inferFunctionCallType(n, path) } - predicate debugInferConstructorType(AstNode n, TypePath path, Type t) { + predicate debugInferConstructionType(AstNode n, TypePath path, Type t) { n = getRelevantLocatable() and - t = inferConstructorType(n, path) + t = inferConstructionType(n, path) } predicate debugTypeMention(TypeMention tm, TypePath path, Type type) { From b8222167d2a74f55fd3466d15136d6269b62d9dc Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Wed, 18 Mar 2026 15:02:54 +0100 Subject: [PATCH 10/11] Rust: Ensure that `TPositionalArgumentPosition` is large enough for struct expressions --- .../codeql/rust/elements/internal/InvocationExprImpl.qll | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll index 685eee1e43a2..9fad85e756f4 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/InvocationExprImpl.qll @@ -9,7 +9,12 @@ module Impl { // For the type `FunctionPosition` used by type inference, we work with function-call syntax // adjusted positions, so a call like `x.m(a, b, c)` needs positions `0` through `3`; for this // reason, there is no `- 1` after `max(...)` below. - i in [0 .. max([any(ParamList l).getNumberOfParams(), any(ArgList l).getNumberOfArgs()])] + i in [0 .. max([ + any(ParamList l).getNumberOfParams(), + any(ArgList l).getNumberOfArgs(), + any(StructFieldList l).getNumberOfFields() // Positions are used for struct expressions in type inference + ] + )] } or TSelfArgumentPosition() or TTypeQualifierArgumentPosition() From f2a0724620371ebeb2043090f1cf0da256df2a8a Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Wed, 18 Mar 2026 15:05:33 +0100 Subject: [PATCH 11/11] Rust: Use `getReturnType` --- .../ql/lib/codeql/rust/internal/typeinference/TypeInference.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index e88a69e910f3..da150a3ef390 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -2974,7 +2974,7 @@ private module ConstructionMatchingInput implements MatchingInputSig { exists(Declaration d, TypeParameter tp | d = this.getTarget() and pos.isReturn() and - tp = d.getDeclaredType(pos, path) and + tp = d.getReturnType(path) and not exists(FunctionPosition pos2 | not pos2.isReturn() and tp = d.getDeclaredType(pos2, _)) and // check that no explicit type arguments have been supplied for `tp` not exists(TypeArgumentPosition tapos |