Merge branch 'main' into fix/path-injection-read-subkind

This commit is contained in:
MarkLee131
2026-05-04 18:56:12 +08:00
committed by GitHub
46 changed files with 1824 additions and 658 deletions

View File

@@ -7,7 +7,7 @@
* The "Multiplication result converted to larger type" (`cpp/integer-multiplication-cast-to-long`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite.
* The "Suspicious add with sizeof" (`cpp/suspicious-add-sizeof`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite.
* The "Wrong type of arguments to formatting function" (`cpp/wrong-type-format-argument`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite.
* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query has been upgraded to `high` precision. However, for `build mode: none` databases, it no longer produces any results. The results in this mode were found to be very noisy and fundamentally imprecise.
* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query has been upgraded to `high` precision. However, for `build-mode: none` databases, it no longer produces any results. The results in this mode were found to be very noisy and fundamentally imprecise.
## 1.6.0

View File

@@ -14,7 +14,7 @@ function may behave unpredictably.</p>
<p>This may indicate a misspelled function name, or that the required header containing
the function declaration has not been included.</p>
<p>Note: This query is not compatible with <code>build mode: none</code> databases, and produces
<p>Note: This query is not compatible with <code>build-mode: none</code> databases, and produces
no results on those databases.</p>
</overview>

View File

@@ -18,7 +18,7 @@ import TooManyArguments
import semmle.code.cpp.commons.Exclusions
/*
* This query is not compatible with build mode: none databases, and produces
* This query is not compatible with build-mode: none databases, and produces
* no results on those databases.
*/

View File

@@ -7,4 +7,4 @@
* The "Multiplication result converted to larger type" (`cpp/integer-multiplication-cast-to-long`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite.
* The "Suspicious add with sizeof" (`cpp/suspicious-add-sizeof`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite.
* The "Wrong type of arguments to formatting function" (`cpp/wrong-type-format-argument`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite.
* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query has been upgraded to `high` precision. However, for `build mode: none` databases, it no longer produces any results. The results in this mode were found to be very noisy and fundamentally imprecise.
* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query has been upgraded to `high` precision. However, for `build-mode: none` databases, it no longer produces any results. The results in this mode were found to be very noisy and fundamentally imprecise.

View File

@@ -64,9 +64,9 @@ No user-facing changes.
* When a code-scanning configuration specifies the `paths:` and/or `paths-ignore:` settings, these are now taken into account by the C# extractor's search for `.config`, `.props`, XML and project files.
* Updated the generated .NET “models as data” runtime models to cover .NET 10.
* C# 14: Support for *implicit* span conversions in the QL library.
* Basic extractor support for .NET 10 is now available. Extraction is supported for .NET 10 projects in both traced mode and `build mode: none`. However, code that uses language features new to C# 14 is not yet fully supported for extraction and analysis.
* Basic extractor support for .NET 10 is now available. Extraction is supported for .NET 10 projects in both traced mode and `build-mode: none`. However, code that uses language features new to C# 14 is not yet fully supported for extraction and analysis.
* Added autobuilder and `build-mode: none` support for `.slnx` solution files.
* In `build mode: none`, .NET 10 is now used by default unless a specific .NET version is specified elsewhere.
* In `build-mode: none`, .NET 10 is now used by default unless a specific .NET version is specified elsewhere.
* Added implicit reads of `System.Collections.Generic.KeyValuePair.Value` at taint-tracking sinks and at inputs to additional taint steps. As a result, taint-tracking queries will now produce more results when a container is tainted.
### Bug Fixes

View File

@@ -5,9 +5,9 @@
* When a code-scanning configuration specifies the `paths:` and/or `paths-ignore:` settings, these are now taken into account by the C# extractor's search for `.config`, `.props`, XML and project files.
* Updated the generated .NET “models as data” runtime models to cover .NET 10.
* C# 14: Support for *implicit* span conversions in the QL library.
* Basic extractor support for .NET 10 is now available. Extraction is supported for .NET 10 projects in both traced mode and `build mode: none`. However, code that uses language features new to C# 14 is not yet fully supported for extraction and analysis.
* Basic extractor support for .NET 10 is now available. Extraction is supported for .NET 10 projects in both traced mode and `build-mode: none`. However, code that uses language features new to C# 14 is not yet fully supported for extraction and analysis.
* Added autobuilder and `build-mode: none` support for `.slnx` solution files.
* In `build mode: none`, .NET 10 is now used by default unless a specific .NET version is specified elsewhere.
* In `build-mode: none`, .NET 10 is now used by default unless a specific .NET version is specified elsewhere.
* Added implicit reads of `System.Collections.Generic.KeyValuePair.Value` at taint-tracking sinks and at inputs to additional taint steps. As a result, taint-tracking queries will now produce more results when a container is tainted.
### Bug Fixes

View File

@@ -152,9 +152,9 @@ C#
* When a code-scanning configuration specifies the :code:`paths:` and/or :code:`paths-ignore:` settings, these are now taken into account by the C# extractor's search for :code:`.config`, :code:`.props`, XML and project files.
* Updated the generated .NET “models as data” runtime models to cover .NET 10.
* C# 14: Support for *implicit* span conversions in the QL library.
* Basic extractor support for .NET 10 is now available. Extraction is supported for .NET 10 projects in both traced mode and :code:`build mode: none`. However, code that uses language features new to C# 14 is not yet fully supported for extraction and analysis.
* Basic extractor support for .NET 10 is now available. Extraction is supported for .NET 10 projects in both traced mode and :code:`build-mode: none`. However, code that uses language features new to C# 14 is not yet fully supported for extraction and analysis.
* Added autobuilder and :code:`build-mode: none` support for :code:`.slnx` solution files.
* In :code:`build mode: none`, .NET 10 is now used by default unless a specific .NET version is specified elsewhere.
* In :code:`build-mode: none`, .NET 10 is now used by default unless a specific .NET version is specified elsewhere.
* Added implicit reads of :code:`System.Collections.Generic.KeyValuePair.Value` at taint-tracking sinks and at inputs to additional taint steps. As a result, taint-tracking queries will now produce more results when a container is tainted.
Golang

View File

@@ -0,0 +1,124 @@
.. _codeql-cli-2.25.3:
==========================
CodeQL 2.25.3 (2026-05-01)
==========================
.. contents:: Contents
:depth: 2
:local:
:backlinks: none
This is an overview of changes in the CodeQL CLI and relevant CodeQL query and library packs. For additional updates on changes to the CodeQL code scanning experience, check out the `code scanning section on the GitHub blog <https://github.blog/tag/code-scanning/>`__, `relevant GitHub Changelog updates <https://github.blog/changelog/label/application-security/>`__, `changes in the CodeQL extension for Visual Studio Code <https://marketplace.visualstudio.com/items/GitHub.vscode-codeql/changelog>`__, and the `CodeQL Action changelog <https://github.com/github/codeql-action/blob/main/CHANGELOG.md>`__.
Security Coverage
-----------------
CodeQL 2.25.3 runs a total of 496 security queries when configured with the Default suite (covering 169 CWE). The Extended suite enables an additional 131 queries (covering 32 more CWE).
CodeQL CLI
----------
Improvements
~~~~~~~~~~~~
* The :code:`codeql database finalize` command now accepts the :code:`--working-dir` flag. When specified, any extractor pre-finalize scripts will be run in that directory. If the flag is not used, the scripts will run in the source root directory (maintaining existing behavior). The flag will also be automatically passed through when running the higher-level
:code:`codeql database create` command.
Query Packs
-----------
Major Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
GitHub Actions
""""""""""""""
* Fixed alert messages in :code:`actions/artifact-poisoning/critical` and :code:`actions/artifact-poisoning/medium` as they previously included a redundant placeholder in the alert message that would on occasion contain a long block of yml that makes the alert difficult to understand. Also improved the wording to make it clearer that it is not the artifact that is being poisoned, but instead a potentially untrusted artifact that is consumed. Finally, changed the alert location to be the source, to align more with other queries reporting an artifact (e.g. zipslip) which is more useful.
Minor Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
C/C++
"""""
* Added :code:`AllocationFunction` models for :code:`aligned_alloc`, :code:`std::aligned_alloc`, and :code:`bsl::aligned_alloc`.
* The "Comparison of narrow type with wide type in loop condition" (:code:`cpp/comparison-with-wider-type`) query has been upgraded to :code:`high` precision. This query will now run in the default code scanning suite.
* The "Multiplication result converted to larger type" (:code:`cpp/integer-multiplication-cast-to-long`) query has been upgraded to :code:`high` precision. This query will now run in the default code scanning suite.
* The "Suspicious add with sizeof" (:code:`cpp/suspicious-add-sizeof`) query has been upgraded to :code:`high` precision. This query will now run in the default code scanning suite.
* The "Wrong type of arguments to formatting function" (:code:`cpp/wrong-type-format-argument`) query has been upgraded to :code:`high` precision. This query will now run in the default code scanning suite.
* The "Implicit function declaration" (:code:`cpp/implicit-function-declaration`) query has been upgraded to :code:`high` precision. However, for :code:`build-mode: none` databases, it no longer produces any results. The results in this mode were found to be very noisy and fundamentally imprecise.
C#
""
* The query :code:`cs/useless-tostring-call` has been updated to avoid false positive results in calls to :code:`StringBuilder.AppendLine` and calls of the form :code:`base.ToString()`. Moreover, the alert message has been made more precise.
JavaScript/TypeScript
"""""""""""""""""""""
* The query :code:`js/missing-rate-limiting` now takes Fastify per-route rate limiting into account.
Python
""""""
* The :code:`py/bind-socket-all-network-interfaces` query now uses the global data-flow library, leading to better precision and more results. Also, wrappers of :code:`socket.socket` in the :code:`eventlet` and :code:`gevent` libraries are now also recognized as socket binding operations.
GitHub Actions
""""""""""""""
* The query :code:`actions/missing-workflow-permissions` no longer produces false positive results on reusable workflows where all callers set permissions.
Language Libraries
------------------
Breaking Changes
~~~~~~~~~~~~~~~~
C/C++
"""""
* The deprecated :code:`NonThrowingFunction` class has been removed, use :code:`NonCppThrowingFunction` instead.
* The deprecated :code:`ThrowingFunction` class has been removed, use :code:`AlwaysSehThrowingFunction` instead.
Major Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Swift
"""""
* Upgraded to allow analysis of Swift 6.3.
Minor Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Java/Kotlin
"""""""""""
* The queries "Resolving XML external entity in user-controlled data" (:code:`java/xxe`) and "Resolving XML external entity in user-controlled data from local source" (:code:`java/xxe-local`) now recognize sinks in the Woodstox StAX library when :code:`com.ctc.wstx.stax.WstxInputFactory` or :code:`org.codehaus.stax2.XMLInputFactory2` are used directly.
Python
""""""
* The Python extractor now supports the new :code:`lazy import ...` and :code:`lazy from ... import ...` (as defined in `PEP-810 <https://peps.python.org/pep-0810/>`__) that will be part of Python 3.15.
GitHub Actions
""""""""""""""
* Removed false positive injection sink models for the :code:`context` input of :code:`docker/build-push-action` and the :code:`allowed-endpoints` input of :code:`step-security/harden-runner`.
Deprecated APIs
~~~~~~~~~~~~~~~
C#
""
* The predicates :code:`get[L|R]Value` in the class :code:`Assignment` have been deprecated. Use :code:`get[Left|Right]Operand` instead.
New Features
~~~~~~~~~~~~
C/C++
"""""
* Added a subclass :code:`AutoconfConfigureTestFile` of :code:`ConfigurationTestFile` that represents files created by GNU autoconf configure scripts to test the build configuration.

View File

@@ -11,6 +11,7 @@ A list of queries for each suite and language `is available here <https://docs.g
.. toctree::
:maxdepth: 1
codeql-cli-2.25.3
codeql-cli-2.25.2
codeql-cli-2.25.1
codeql-cli-2.25.0

View File

@@ -2732,11 +2732,6 @@ class PatternExpr extends Expr {
*/
LocalVariableDeclExpr asBindingOrUnnamedPattern() { result = this }
/**
* DEPRECATED: alias for `asBindingOrUnnamedPattern`.
*/
deprecated LocalVariableDeclExpr asBindingPattern() { result = this.asBindingOrUnnamedPattern() }
/**
* Gets this pattern cast to a record pattern.
*/

View File

@@ -810,14 +810,6 @@ class Field extends Member, ExprParent, @field, Variable {
)
}
/**
* DEPRECATED: The result is always `this`.
*/
deprecated Field getSourceDeclaration() { result = this }
/** DEPRECATED: This always holds. */
deprecated predicate isSourceDeclaration() { any() }
override predicate isPublic() {
Member.super.isPublic()
or

View File

@@ -558,11 +558,6 @@ class ConstCase extends SwitchCase {
class PatternCase extends SwitchCase {
PatternCase() { exists(PatternExpr pe | pe.isNthChildOf(this, _)) }
/**
* DEPRECATED: alias for getPattern(0)
*/
deprecated PatternExpr getPattern() { result = this.getPattern(0) }
/**
* Gets this case's `n`th pattern.
*/

View File

@@ -637,9 +637,6 @@ class RefType extends Type, Annotatable, Modifiable, @reftype {
this.(NestedType).getEnclosingType().getNestedName() + "$" + this.getName() = result
}
/** DEPRECATED: Alias for `getNestedName`. */
deprecated string nestedName() { result = this.getNestedName() }
/**
* Gets the source declaration of this type.
*

View File

@@ -10,57 +10,6 @@ import java
* Predicates for basic-block-level dominance.
*/
/**
* DEPRECATED: Use `BasicBlock::immediatelyDominates` instead.
*
* The immediate dominance relation for basic blocks.
*/
deprecated predicate bbIDominates(BasicBlock dom, BasicBlock node) {
dom.immediatelyDominates(node)
}
/** Exit points for basic-block control-flow. */
private predicate bbSink(BasicBlock exit) { exit.getLastNode() instanceof ControlFlow::ExitNode }
/** Reversed `bbSucc`. */
private predicate bbPred(BasicBlock post, BasicBlock pre) { post = pre.getASuccessor() }
/** The immediate post-dominance relation on basic blocks. */
deprecated predicate bbIPostDominates(BasicBlock dominator, BasicBlock node) =
idominance(bbSink/1, bbPred/2)(_, dominator, node)
/**
* DEPRECATED: Use `BasicBlock::strictlyDominates` instead.
*
* Holds if `dom` strictly dominates `node`.
*/
deprecated predicate bbStrictlyDominates(BasicBlock dom, BasicBlock node) {
dom.strictlyDominates(node)
}
/**
* DEPRECATED: Use `BasicBlock::dominates` instead.
*
* Holds if `dom` dominates `node`. (This is reflexive.)
*/
deprecated predicate bbDominates(BasicBlock dom, BasicBlock node) { dom.dominates(node) }
/**
* DEPRECATED: Use `BasicBlock::strictlyPostDominates` instead.
*
* Holds if `dom` strictly post-dominates `node`.
*/
deprecated predicate bbStrictlyPostDominates(BasicBlock dom, BasicBlock node) {
dom.strictlyPostDominates(node)
}
/**
* DEPRECATED: Use `BasicBlock::postDominates` instead.
*
* Holds if `dom` post-dominates `node`. (This is reflexive.)
*/
deprecated predicate bbPostDominates(BasicBlock dom, BasicBlock node) { dom.postDominates(node) }
/**
* The dominance frontier relation for basic blocks.
*

View File

@@ -43,14 +43,6 @@ abstract class SourceNode extends DataFlow::Node {
abstract string getThreatModel();
}
/**
* DEPRECATED: Use `ActiveThreatModelSource` instead.
*
* A class of data flow sources that respects the
* current threat model configuration.
*/
deprecated class ThreatModelFlowSource = ActiveThreatModelSource;
/**
* A data flow source that is enabled in the current threat model configuration.
*/

View File

@@ -8,14 +8,6 @@ import java
private import internal.FlowSummaryImpl as Impl
private import internal.DataFlowUtil
deprecated class SummaryComponent = Impl::Private::SummaryComponent;
deprecated module SummaryComponent = Impl::Private::SummaryComponent;
deprecated class SummaryComponentStack = Impl::Private::SummaryComponentStack;
deprecated module SummaryComponentStack = Impl::Private::SummaryComponentStack;
/** A synthetic callable with a set of concrete call sites and a flow summary. */
abstract class SyntheticCallable extends string {
bindingset[this]
@@ -147,5 +139,3 @@ private class SummarizedSyntheticCallableAdapter extends SummarizedCallable::Ran
)
}
}
deprecated class RequiredSummaryComponentStack = Impl::Private::RequiredSummaryComponentStack;

View File

@@ -196,18 +196,6 @@ Expr basicNullGuard(Expr e, boolean branch, boolean isnull) {
Guards_v3::nullGuard(result, any(GuardValue v | v.asBooleanValue() = branch), e, isnull)
}
/**
* DEPRECATED: Use `basicNullGuard` instead.
*
* Gets an expression that directly tests whether a given expression, `e`, is null or not.
*
* If `result` evaluates to `branch`, then `e` is guaranteed to be null if `isnull`
* is true, and non-null if `isnull` is false.
*/
deprecated Expr basicOrCustomNullGuard(Expr e, boolean branch, boolean isnull) {
result = basicNullGuard(e, branch, isnull)
}
/**
* Gets an expression that directly tests whether a given SSA variable is null or not.
*
@@ -218,18 +206,6 @@ Expr directNullGuard(SsaDefinition v, boolean branch, boolean isnull) {
result = basicNullGuard(sameValue(v, _), branch, isnull)
}
/**
* DEPRECATED: Use `nullGuardControls`/`nullGuardControlsBranchEdge` instead.
*
* Gets a `Guard` that tests (possibly indirectly) whether a given SSA variable is null or not.
*
* If `result` evaluates to `branch`, then `v` is guaranteed to be null if `isnull`
* is true, and non-null if `isnull` is false.
*/
deprecated Guard nullGuard(SsaDefinition v, boolean branch, boolean isnull) {
result = directNullGuard(v, branch, isnull)
}
/**
* Holds if there exists a null check on `v`, such that taking the branch edge
* from `bb1` to `bb2` implies that `v` is guaranteed to be null if `isnull` is

View File

@@ -198,19 +198,6 @@ module Public {
or
result = this.getType() and not exists(this.getImprovedTypeBound())
}
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
deprecated predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/**

View File

@@ -48,18 +48,6 @@ class MethodLdapNameAddAll extends Method {
}
}
/**
* DEPRECATED: No longer needed as clone steps are handled uniformly.
*
* A method with the name `clone` declared in `javax.naming.ldap.LdapName`.
*/
deprecated class MethodLdapNameClone extends Method {
MethodLdapNameClone() {
this.getDeclaringType() instanceof TypeLdapName and
this.hasName("clone")
}
}
/** A method with the name `getAll` declared in `javax.naming.ldap.LdapName`. */
class MethodLdapNameGetAll extends Method {
MethodLdapNameGetAll() {

View File

@@ -156,9 +156,6 @@ class SpringRequestMappingMethod extends SpringControllerMethod {
result = this.getProducesExpr().(CompileTimeConstantExpr).getStringValue()
}
/** DEPRECATED: Use `getAValue()` instead. */
deprecated string getValue() { result = requestMappingAnnotation.getStringValue("value") }
/**
* Gets a "value" @RequestMapping annotation string value, if present.
*

View File

@@ -20,13 +20,6 @@ class AndroidNetworkSecurityConfigFile extends XmlFile {
}
}
/**
* DEPRECATED. Use `semmle.code.java.frameworks.android.Android::inAndroidApplication` instead.
*
* Holds if this database contains an Android manifest file.
*/
deprecated predicate isAndroid() { exists(AndroidManifestXmlFile m) }
/** Holds if the given domain name is trusted by the Network Security Configuration XML file. */
private predicate trustedDomainViaXml(string domainName) {
exists(

View File

@@ -1,49 +1,5 @@
/** Provides taint-tracking configurations to reason about arithmetic using local-user-controlled data. */
overlay[local?]
deprecated module;
import java
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.security.ArithmeticCommon
/**
* DEPRECATED: Use `ArithmeticOverflowConfig` instead.
*
* A taint-tracking configuration to reason about arithmetic overflow using local-user-controlled data.
*/
deprecated module ArithmeticTaintedLocalOverflowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) { overflowSink(_, sink.asExpr()) }
predicate isBarrier(DataFlow::Node n) { overflowBarrier(n) }
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
/**
* DEPRECATED: Use `ArithmeticOverflow` instead and configure threat model sources to include `local`.
*
* Taint-tracking flow for arithmetic overflow using local-user-controlled data.
*/
deprecated module ArithmeticTaintedLocalOverflowFlow =
TaintTracking::Global<ArithmeticTaintedLocalOverflowConfig>;
/**
* A taint-tracking configuration to reason about arithmetic underflow using local-user-controlled data.
*/
deprecated module ArithmeticTaintedLocalUnderflowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) { underflowSink(_, sink.asExpr()) }
predicate isBarrier(DataFlow::Node n) { underflowBarrier(n) }
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
/**
* DEPRECATED: Use `ArithmeticUnderflow` instead and configure threat model sources to include `local`.
*
* Taint-tracking flow for arithmetic underflow using local-user-controlled data.
*/
deprecated module ArithmeticTaintedLocalUnderflowFlow =
TaintTracking::Global<ArithmeticTaintedLocalUnderflowConfig>;

View File

@@ -25,11 +25,6 @@ module ArithmeticOverflowConfig implements DataFlow::ConfigSig {
}
}
/**
* DEPRECATED: Use `ArithmeticOverflowConfig` instead.
*/
deprecated module RemoteUserInputOverflowConfig = ArithmeticOverflowConfig;
/** A taint-tracking configuration to reason about underflow from unvalidated input. */
module ArithmeticUnderflowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof ActiveThreatModelSource }
@@ -51,23 +46,8 @@ module ArithmeticUnderflowConfig implements DataFlow::ConfigSig {
}
}
/**
* DEPRECATED: Use `ArithmeticUnderflowConfig` instead.
*/
deprecated module RemoteUserInputUnderflowConfig = ArithmeticUnderflowConfig;
/** Taint-tracking flow for overflow from unvalidated input. */
module ArithmeticOverflow = TaintTracking::Global<ArithmeticOverflowConfig>;
/**
* DEPRECATED: Use `ArithmeticOverflow` instead.
*/
deprecated module RemoteUserInputOverflow = ArithmeticOverflow;
/** Taint-tracking flow for underflow from unvalidated input. */
module ArithmeticUnderflow = TaintTracking::Global<ArithmeticUnderflowConfig>;
/**
* DEPRECATED: Use `ArithmeticUnderflow` instead.
*/
deprecated module RemoteUserInputUnderflow = ArithmeticUnderflow;

View File

@@ -78,44 +78,11 @@ module InputToArgumentToExecFlowConfig implements DataFlow::ConfigSig {
}
}
/**
* DEPRECATED: Use `InputToArgumentToExecFlowConfig` instead.
*/
deprecated module RemoteUserInputToArgumentToExecFlowConfig = InputToArgumentToExecFlowConfig;
/**
* Taint-tracking flow for unvalidated input that is used to run an external process.
*/
module InputToArgumentToExecFlow = TaintTracking::Global<InputToArgumentToExecFlowConfig>;
/**
* DEPRECATED: Use `InputToArgumentToExecFlow` instead.
*/
deprecated module RemoteUserInputToArgumentToExecFlow = InputToArgumentToExecFlow;
/**
* A taint-tracking configuration for unvalidated local user input that is used to run an external process.
*/
deprecated module LocalUserInputToArgumentToExecFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) { sink instanceof CommandInjectionSink }
predicate isBarrier(DataFlow::Node node) { node instanceof CommandInjectionSanitizer }
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
any(CommandInjectionAdditionalTaintStep s).step(n1, n2)
}
}
/**
* DEPRECATED: Use `InputToArgumentToExecFlow` instead and configure threat model sources to include `local`.
*
* Taint-tracking flow for unvalidated local user input that is used to run an external process.
*/
deprecated module LocalUserInputToArgumentToExecFlow =
TaintTracking::Global<LocalUserInputToArgumentToExecFlowConfig>;
/**
* Implementation of `ExecTainted.ql`. It is extracted to a QLL
* so that it can be excluded from `ExecUnescaped.ql` to avoid

View File

@@ -1,27 +1,5 @@
/** Provides a taint-tracking configuration to reason about use of externally controlled strings for command injection vulnerabilities. */
overlay[local?]
deprecated module;
import java
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.security.ExternalProcess
private import semmle.code.java.security.CommandArguments
private import semmle.code.java.security.Sanitizers
/** A taint-tracking configuration to reason about use of externally controlled strings to make command line commands. */
deprecated module ExecTaintedLocalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ArgumentToExec }
predicate isBarrier(DataFlow::Node node) {
node instanceof SimpleTypeSanitizer
or
isSafeCommandArgument(node.asExpr())
}
}
/**
* DEPRCATED: Unused.
*
* Taint-tracking flow for use of externally controlled strings to make command line commands.
*/
deprecated module ExecTaintedLocalFlow = TaintTracking::Global<ExecTaintedLocalConfig>;

View File

@@ -1,26 +1,5 @@
/** Provides a taint-tracking configuration to reason about externally-controlled format strings from local sources. */
overlay[local?]
deprecated module;
import java
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.StringFormat
/** A taint-tracking configuration to reason about externally-controlled format strings from local sources. */
deprecated module ExternallyControlledFormatStringLocalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) {
sink.asExpr() = any(StringFormat formatCall).getFormatArgument()
}
predicate isBarrier(DataFlow::Node node) {
node.getType() instanceof NumericType or node.getType() instanceof BooleanType
}
}
/**
* DEPRECATED: Use `ExternallyControlledFormatStringFlow` instead and configure threat model sources to include `local`.
*
* Taint-tracking flow for externally-controlled format strings from local sources.
*/
deprecated module ExternallyControlledFormatStringLocalFlow =
TaintTracking::Global<ExternallyControlledFormatStringLocalConfig>;

View File

@@ -1,24 +1,5 @@
/** Provides a taint-tracking configuration to reason about improper validation of local user-provided size used for array construction. */
overlay[local?]
deprecated module;
import java
private import semmle.code.java.security.internal.ArraySizing
private import semmle.code.java.dataflow.FlowSources
/**
* A taint-tracking configuration to reason about improper validation of local user-provided size used for array construction.
*/
deprecated module ImproperValidationOfArrayConstructionLocalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) {
any(CheckableArrayAccess caa).canThrowOutOfBoundsDueToEmptyArray(sink.asExpr(), _)
}
}
/**
* DEPRECATED: Use `ImproperValidationOfArrayConstructionFlow` instead and configure threat model sources to include `local`.
*
* Taint-tracking flow for improper validation of local user-provided size used for array construction.
*/
deprecated module ImproperValidationOfArrayConstructionLocalFlow =
TaintTracking::Global<ImproperValidationOfArrayConstructionLocalConfig>;

View File

@@ -1,28 +1,5 @@
/** Provides a taint-tracking configuration to reason about improper validation of local user-provided array index. */
overlay[local?]
deprecated module;
import java
private import semmle.code.java.security.internal.ArraySizing
private import semmle.code.java.dataflow.FlowSources
/**
* A taint-tracking configuration to reason about improper validation of local user-provided array index.
*/
deprecated module ImproperValidationOfArrayIndexLocalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) {
any(CheckableArrayAccess caa).canThrowOutOfBounds(sink.asExpr())
}
predicate isBarrier(DataFlow::Node node) { node.getType() instanceof BooleanType }
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
/**
* DEPRECATED: Use `ImproperValidationOfArrayIndexFlow` instead and configure threat model sources to include `local`.
*
* Taint-tracking flow for improper validation of local user-provided array index.
*/
deprecated module ImproperValidationOfArrayIndexLocalFlow =
TaintTracking::Global<ImproperValidationOfArrayIndexLocalConfig>;

View File

@@ -115,34 +115,3 @@ module NumericCastFlowConfig implements DataFlow::ConfigSig {
* Taint-tracking flow for user input that is used in a numeric cast.
*/
module NumericCastFlow = TaintTracking::Global<NumericCastFlowConfig>;
/**
* A taint-tracking configuration for reasoning about local user input that is
* used in a numeric cast.
*/
deprecated module NumericCastLocalFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) {
sink.asExpr() = any(NumericNarrowingCastExpr cast).getExpr() and
sink.asExpr() instanceof VarAccess
}
predicate isBarrier(DataFlow::Node node) {
boundedRead(node.asExpr()) or
castCheck(node.asExpr()) or
node.getType() instanceof SmallType or
smallExpr(node.asExpr()) or
node.getEnclosingCallable() instanceof HashCodeMethod or
exists(RightShiftOp e | e.getShiftedVariable().getAnAccess() = node.asExpr())
}
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
/**
* DEPRECATED: Use `NumericCastFlow` instead and configure threat model sources to include `local`.
*
* Taint-tracking flow for local user input that is used in a numeric cast.
*/
deprecated module NumericCastLocalFlow = TaintTracking::Global<NumericCastLocalFlowConfig>;

View File

@@ -1,39 +1,5 @@
/** Provides a taint-tracking configuration to reason about response splitting vulnerabilities from local user input. */
overlay[local?]
deprecated module;
import java
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.security.ResponseSplitting
/**
* A taint-tracking configuration to reason about response splitting vulnerabilities from local user input.
*/
deprecated module ResponseSplittingLocalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) { sink instanceof HeaderSplittingSink }
predicate isBarrier(DataFlow::Node node) {
node.getType() instanceof PrimitiveType
or
node.getType() instanceof BoxedType
or
exists(MethodCall ma, string methodName, CompileTimeConstantExpr target |
node.asExpr() = ma and
ma.getMethod().hasQualifiedName("java.lang", "String", methodName) and
target = ma.getArgument(0) and
(
methodName = "replace" and target.getIntValue() = [10, 13] // 10 == "\n", 13 == "\r"
or
methodName = "replaceAll" and
target.getStringValue().regexpMatch(".*([\n\r]|\\[\\^[^\\]\r\n]*\\]).*")
)
)
}
}
/**
* DEPRECATED: Use `ResponseSplittingFlow` instead and configure threat model sources to include `local`.
*
* Taint-tracking flow for response splitting vulnerabilities from local user input.
*/
deprecated module ResponseSplittingLocalFlow = TaintTracking::Global<ResponseSplittingLocalConfig>;

View File

@@ -2,32 +2,7 @@
* Provides a taint-tracking configuration for reasoning about local user input
* that is used in a SQL query.
*/
overlay[local?]
deprecated module;
import java
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.security.SqlInjectionQuery
private import semmle.code.java.security.Sanitizers
/**
* A taint-tracking configuration for reasoning about local user input that is
* used in a SQL query.
*/
deprecated module LocalUserInputToQueryInjectionFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink }
predicate isBarrier(DataFlow::Node node) { node instanceof SimpleTypeSanitizer }
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
any(AdditionalQueryInjectionTaintStep s).step(node1, node2)
}
}
/**
* DEPRECATED: Use `QueryInjectionFlow` instead and configure threat model sources to include `local`.
*
* Taint-tracking flow for local user input that is used in a SQL query.
*/
deprecated module LocalUserInputToQueryInjectionFlow =
TaintTracking::Global<LocalUserInputToQueryInjectionFlowConfig>;

View File

@@ -78,28 +78,3 @@ module TaintedPathConfig implements DataFlow::ConfigSig {
/** Tracks flow from remote sources to the creation of a path. */
module TaintedPathFlow = TaintTracking::Global<TaintedPathConfig>;
/**
* A taint-tracking configuration for tracking flow from local user input to the creation of a path.
*/
deprecated module TaintedPathLocalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) { sink instanceof TaintedPathSink }
predicate isBarrier(DataFlow::Node sanitizer) {
sanitizer instanceof SimpleTypeSanitizer or
sanitizer instanceof PathInjectionSanitizer
}
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
any(TaintedPathAdditionalTaintStep s).step(n1, n2)
}
}
/**
* DEPRECATED: Use `TaintedPathFlow` instead and configure threat model sources to include `local`.
*
* Tracks flow from local user input to the creation of a path.
*/
deprecated module TaintedPathLocalFlow = TaintTracking::Global<TaintedPathLocalConfig>;

View File

@@ -1,21 +1,5 @@
/** Provides a taint-tracking configuration to reason about URL redirection from local sources. */
overlay[local?]
deprecated module;
import java
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.security.UrlRedirect
/**
* A taint-tracking configuration to reason about URL redirection from local sources.
*/
deprecated module UrlRedirectLocalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) { sink instanceof UrlRedirectSink }
}
/**
* DEPRECATED: Use `UrlRedirectFlow` instead and configure threat model sources to include `local`.
*
* Taint-tracking flow for URL redirection from local sources.
*/
deprecated module UrlRedirectLocalFlow = TaintTracking::Global<UrlRedirectLocalConfig>;

View File

@@ -1,30 +1,5 @@
/** Provides a taint-tracking configuration to reason about cross-site scripting from a local source. */
overlay[local?]
deprecated module;
import java
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.dataflow.TaintTracking
private import semmle.code.java.security.XSS
/**
* A taint-tracking configuration for reasoning about cross-site scripting vulnerabilities from a local source.
*/
deprecated module XssLocalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
predicate isBarrier(DataFlow::Node node) { node instanceof XssSanitizer }
predicate isBarrierOut(DataFlow::Node node) { node instanceof XssSinkBarrier }
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
any(XssAdditionalTaintStep s).step(node1, node2)
}
}
/**
* DEPRECATED: Use `XssFlow` instead and configure threat model sources to include `local`.
*
* Taint-tracking flow for cross-site scripting vulnerabilities from a local source.
*/
deprecated module XssLocalFlow = TaintTracking::Global<XssLocalConfig>;

View File

@@ -1,28 +1,5 @@
/** Provides taint tracking configurations to be used in local XXE queries. */
overlay[local?]
deprecated module;
import java
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.dataflow.TaintTracking
private import semmle.code.java.security.XxeQuery
/**
* A taint-tracking configuration for unvalidated local user input that is used in XML external entity expansion.
*/
deprecated module XxeLocalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) { sink instanceof XxeSink }
predicate isBarrier(DataFlow::Node sanitizer) { sanitizer instanceof XxeSanitizer }
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
any(XxeAdditionalTaintStep s).step(n1, n2)
}
}
/**
* DEPRECATED: Use `XxeFlow` instead and configure threat model sources to include `local`.
*
* Detect taint flow of unvalidated local user input that is used in XML external entity expansion.
*/
deprecated module XxeLocalFlow = TaintTracking::Global<XxeLocalConfig>;

View File

@@ -43,7 +43,7 @@ deprecated private predicate mayBeExploitable(Method m) {
// hence, here we check for the param type to be a Java `String`.
p.getType() instanceof TypeString and
// Exclude cases where a regex check is applied on a parameter to prevent false positives.
not m.(SpringRequestMappingMethod).getValue().matches("%{%:[%]%}%")
not m.(SpringRequestMappingMethod).getAValue().matches("%{%:[%]%}%")
) and
not maybeATestMethod(m)
}

View File

@@ -1,117 +0,0 @@
/**
* Provides classes for working with MyBatis mapper xml files and their content.
*/
deprecated module;
import java
/**
* MyBatis Mapper XML file.
*/
class MyBatisMapperXmlFile extends XmlFile {
MyBatisMapperXmlFile() {
count(XmlElement e | e = this.getAChild()) = 1 and
this.getAChild().getName() = "mapper"
}
}
/**
* An XML element in a `MyBatisMapperXMLFile`.
*/
class MyBatisMapperXmlElement extends XmlElement {
MyBatisMapperXmlElement() { this.getFile() instanceof MyBatisMapperXmlFile }
/**
* Gets the value for this element, with leading and trailing whitespace trimmed.
*/
string getValue() { result = this.allCharactersString().trim() }
/**
* Gets the reference type bound to MyBatis Mapper XML File.
*/
RefType getNamespaceRefType() {
result.getQualifiedName() = this.getAttribute("namespace").getValue()
}
}
/**
* An MyBatis Mapper sql operation element.
*/
abstract class MyBatisMapperSqlOperation extends MyBatisMapperXmlElement {
/**
* Gets the value of the `id` attribute of MyBatis Mapper sql operation element.
*/
string getId() { result = this.getAttribute("id").getValue() }
/**
* Gets the `<include>` element in a `MyBatisMapperSqlOperation`.
*/
MyBatisMapperInclude getInclude() { result = this.getAChild*() }
/**
* Gets the method bound to MyBatis Mapper XML File.
*/
Method getMapperMethod() {
result.getName() = this.getId() and
result.getDeclaringType() = this.getParent().(MyBatisMapperXmlElement).getNamespaceRefType()
}
}
/**
* A `<insert>` element in a `MyBatisMapperSqlOperation`.
*/
class MyBatisMapperInsert extends MyBatisMapperSqlOperation {
MyBatisMapperInsert() { this.getName() = "insert" }
}
/**
* A `<update>` element in a `MyBatisMapperSqlOperation`.
*/
class MyBatisMapperUpdate extends MyBatisMapperSqlOperation {
MyBatisMapperUpdate() { this.getName() = "update" }
}
/**
* A `<delete>` element in a `MyBatisMapperSqlOperation`.
*/
class MyBatisMapperDelete extends MyBatisMapperSqlOperation {
MyBatisMapperDelete() { this.getName() = "delete" }
}
/**
* A `<select>` element in a `MyBatisMapperSqlOperation`.
*/
class MyBatisMapperSelect extends MyBatisMapperSqlOperation {
MyBatisMapperSelect() { this.getName() = "select" }
}
/**
* A `<sql>` element in a `MyBatisMapperXMLElement`.
*/
class MyBatisMapperSql extends MyBatisMapperXmlElement {
MyBatisMapperSql() { this.getName() = "sql" }
/**
* Gets the value of the `id` attribute of this `<sql>`.
*/
string getId() { result = this.getAttribute("id").getValue() }
}
/**
* A `<include>` element in a `MyBatisMapperXMLElement`.
*/
class MyBatisMapperInclude extends MyBatisMapperXmlElement {
MyBatisMapperInclude() { this.getName() = "include" }
/**
* Gets the value of the `refid` attribute of this `<include>`.
*/
string getRefid() { result = this.getAttribute("refid").getValue() }
}
/**
* A `<foreach>` element in a `MyBatisMapperXMLElement`.
*/
class MyBatisMapperForeach extends MyBatisMapperXmlElement {
MyBatisMapperForeach() { this.getName() = "foreach" }
}

View File

@@ -0,0 +1,71 @@
var topLevelDecl : Int = 0
0
topLevelDecl + 1 // $ type=topLevelDecl:Int
class C {
var myInt : Int
// C.init
init(n: Int) {
myInt = n // $ type=n:Int
}
// C.getMyInt
func getMyInt() -> Int {
return myInt // $ type=.myInt:Int
}
}
class Derived : C {
// Derived.init
init() {
super.init(n: 0) // $ type=super:C target=C.init
}
// Derived.callGetMyInt
func callGetMyInt() -> Int {
let x = getMyInt(); // $ type=x:Int target=C.getMyInt
return x
}
}
class Generic<T> {
var value : T
// Generic.init
init(v: T) {
value = v // $ type=v:T
}
// Generic.getValue
func getValue() -> T {
return value // $ type=.value:T
}
}
class GenericDerived : Generic<Int> {
// GenericDerived.init
init() {
super.init(v: 0) // $ type=super@Generic<T>:Int target=Generic.init
}
}
func testGeneric() {
let g = Generic(v: 42) // $ type=g@Generic<T>:Int target=Generic.init
let x = g.getValue() // $ type=x:Int target=Generic.getValue
let gd = GenericDerived() // $ type=gd:GenericDerived target=GenericDerived.init
let y = gd.getValue() // $ type=y:Int target=Generic.getValue
}
// --- Extensions ---
extension C {
// C.doubled
func doubled() -> Int {
return myInt * 2 // $ type=.myInt:Int
}
}
func testExtension() {
let obj = C(n: 10) // $ target=C.init
let d = obj.doubled() // $ type=d:Int target=C.doubled
}

View File

@@ -0,0 +1,231 @@
// --- Static methods ---
class MathUtils {
// MathUtils.square
static func square(x: Int) -> Int {
return x * x // $ type=x:Int
}
// MathUtils.cube
class func cube(x: Int) -> Int {
return x * x * x // $ type=x:Int
}
}
func testStaticMethods() {
let s = MathUtils.square(x: 4) // $ type=s:Int target=MathUtils.square
let cu = MathUtils.cube(x: 3) // $ type=cu:Int target=MathUtils.cube
}
// --- Simple overloading ---
class Overloaded {
// Overloaded.process(_:Int)
func process(_ x: Int) -> Int {
return x + 1 // $ type=x:Int
}
// Overloaded.process(_:String)
func process(_ s: String) -> String {
return s // $ type=s:String
}
}
func testOverloading() {
let o = Overloaded() // $ target=init()
let r1 = o.process(42) // $ type=r1:Int target=Overloaded.process(_:Int)
let r2 = o.process("hello") // $ type=r2:String target=Overloaded.process(_:String)
}
// --- Structs and methods ---
struct Matrix {
var data : [[Int]]
// Matrix.init
init(data: [[Int]]) {
self.data = data
}
// Matrix.rowCount
func rowCount() -> Int {
return data.count
}
}
func testSubscripts() {
let m = Matrix(data: [[1, 2], [3, 4]]) // $ target=Matrix.init
let rc = m.rowCount() // $ type=rc:Int target=Matrix.rowCount
}
// --- Nested types ---
class Outer {
class Inner {
var value : Int
// Outer.Inner.init
init(value: Int) {
self.value = value // $ type=value:Int
}
// Outer.Inner.getValue
func getValue() -> Int {
return self.value // $ type=.value:Int
}
}
}
func testNestedTypes() {
let inner = Outer.Inner(value: 99) // $ type=inner:Inner target=Outer.Inner.init
let v = inner.getValue() // $ type=v:Int target=Outer.Inner.getValue
}
// --- Method chaining ---
class Builder {
var value : Int = 0
// Builder.init
init() {}
// Builder.set
func set(_ v: Int) -> Builder {
value = v
return self
}
// Builder.add
func add(_ v: Int) -> Builder {
value += v
return self
}
// Builder.build
func build() -> Int {
return value // $ type=.value:Int
}
}
func testChaining() {
let b = Builder() // $ type=b:Builder target=Builder.init
let result = b.set(10).add(5).build() // $ type=result:Int target=Builder.set target=Builder.add target=Builder.build
}
// --- Default parameter values ---
class Config {
// Config.init
init() {}
// Config.setup
func setup(retries: Int = 3, timeout: Double = 30.0) -> Int {
return retries // $ type=retries:Int
}
}
func testDefaultParams() {
let cfg = Config() // $ type=cfg:Config target=Config.init
let r1 = cfg.setup() // $ type=r1:Int target=Config.setup
let r2 = cfg.setup(retries: 5) // $ type=r2:Int target=Config.setup
let r3 = cfg.setup(retries: 2, timeout: 60.0) // $ type=r3:Int target=Config.setup
}
// --- Computed properties accessed via methods ---
class Temperature {
var celsius : Double
// Temperature.init
init(celsius: Double) {
self.celsius = celsius // $ type=celsius:Double
}
// Temperature.toCelsius
func toCelsius() -> Double {
return celsius // $ type=.celsius:Double
}
// Temperature.toFahrenheit
func toFahrenheit() -> Double {
return celsius * 9.0 / 5.0 + 32.0 // $ type=.celsius:Double
}
}
func testTemperature() {
let t = Temperature(celsius: 100.0) // $ type=t:Temperature target=Temperature.init
let c = t.toCelsius() // $ type=c:Double target=Temperature.toCelsius
let f = t.toFahrenheit() // $ type=f:Double target=Temperature.toFahrenheit
}
// --- Inheritance with overriding ---
class Animal {
// Animal.init
init() {}
// Animal.speak
func speak() -> String {
return "..."
}
}
class Dog : Animal {
// Dog.init
override init() {
super.init() // $ target=Animal.init
}
// Dog.speak
override func speak() -> String {
return "Woof"
}
// Dog.fetch
func fetch() -> String {
return "ball"
}
}
class Cat : Animal {
// Cat.init
override init() {
super.init() // $ target=Animal.init
}
// Cat.speak
override func speak() -> String {
return "Meow"
}
}
func testOverriding() {
let d = Dog() // $ type=d:Dog target=Dog.init
let ds = d.speak() // $ type=ds:String target=Dog.speak
let df = d.fetch() // $ type=df:String target=Dog.fetch
let ct = Cat() // $ type=ct:Cat target=Cat.init
let cs = ct.speak() // $ type=cs:String target=Cat.speak
}
// --- Mutating methods on structs ---
struct Counter {
var count : Int = 0
// Counter.increment
mutating func increment() {
count += 1
}
// Counter.getCount
func getCount() -> Int {
return count // $ type=.count:Int
}
}
func testMutating() {
var ctr = Counter() // $ type=ctr:Counter target=init()
ctr.increment() // $ target=Counter.increment
let val = ctr.getCount() // $ type=val:Int target=Counter.getCount
}

View File

@@ -0,0 +1,143 @@
// --- Generic functions ---
// identity
func identity<T>(_ x: T) -> T {
return x // $ type=x:T
}
// makePair
func makePair<A, B>(_ a: A, _ b: B) -> (A, B) {
return (a, b) // $ type=a:A
}
func testGenericFunctions() {
let i = identity(42) // $ type=i:Int target=identity
let s = identity("hello") // $ type=s:String target=identity
let p = makePair(1, "two") // $ target=makePair
}
// --- Generic structs ---
struct Pair<A, B> {
var first : A
var second : B
// Pair.init
init(first: A, second: B) {
self.first = first // $ type=first:A
self.second = second // $ type=second:B
}
// Pair.getFirst
func getFirst() -> A {
return first // $ type=.first:A
}
// Pair.getSecond
func getSecond() -> B {
return second // $ type=.second:B
}
}
func testGenericStruct() {
let p = Pair(first: 1, second: "x") // $ target=Pair.init type=p@Pair<A>:Int type=p@Pair<B>:String
let f = p.getFirst() // $ type=f:Int target=Pair.getFirst
let sc = p.getSecond() // $ type=sc:String target=Pair.getSecond
}
// --- Enums with associated values ---
enum Result<T> {
// Result.success
case success(T)
// Result.failure
case failure(String)
// Result.getValue
func getValue() -> T? {
switch self {
case .success(let v): // $ target=Result.success
return v // $ type=v:T
case .failure: // $ target=Result.failure
return nil
}
}
}
func testEnum() {
let r = Result.success(42) // $ type=r@Result<T>:Int target=Result.success
let v = r.getValue() // $ target=Result.getValue
let r2 : Result<Int> = .success(42) // $ type=r2@Result<T>:Int target=Result.success
let v2 = r2.getValue() // $ target=Result.getValue
}
// --- Closures and type inference ---
// applyTransform
func applyTransform<T, U>(_ value: T, _ transform: (T) -> U) -> U {
return transform(value)
}
func testClosures() {
let result = applyTransform(5, { x in x * 2 }) // $ target=applyTransform type=result:Int
let strings = applyTransform(10, { x in String(x) }) // $ target=applyTransform type=strings:String
}
// --- Generic class with constraints ---
protocol MyProtocol {
associatedtype MyType
// MyProtocol.foo
func foo() -> Self
// MyProtocol.bar
func bar() -> MyType
}
class Wrapper<T: MyProtocol> {
var inner : T
// Wrapper.init
init(_ inner: T) {
self.inner = inner // $ type=inner:T
}
// Wrapper.get
func get() -> T {
return inner // $ type=.inner:T
}
// Wrapper.callFoo
func callFoo() -> T {
let x = inner.foo(); // $ type=x:T target=MyProtocol.foo
return x
}
// Wrapper.callBar
func callBar() -> T.MyType {
let x = inner.bar(); // $ type=x:MyType target=MyProtocol.bar
return x
}
}
extension Int : MyProtocol {
typealias MyType = String
// Int.foo
func foo() -> Int {
return self * 2 // $ type=self:Int
}
// Int.bar
func bar() -> String {
return "number"
}
}
func testConstrainedGeneric() {
let w = Wrapper(42) // $ type=w@Wrapper<T>:Int target=Wrapper.init
let v = w.get() // $ type=v:Int target=Wrapper.get
let z = w.callFoo() // $ type=z:Int target=Wrapper.callFoo
}

View File

@@ -0,0 +1,220 @@
// --- Key-path expressions: basic property access ---
struct Point {
var x : Double
var y : Double
// Point.init
init(x: Double, y: Double) {
self.x = x // $ type=x:Double
self.y = y // $ type=y:Double
}
// Point.distanceFromOrigin
func distanceFromOrigin() -> Double {
return (x * x + y * y).squareRoot()
}
}
struct Line {
var start : Point
var end : Point
// Line.init
init(start: Point, end: Point) {
self.start = start
self.end = end
}
}
func testBasicKeyPaths() {
let kpX = \Point.x
let kpY = \Point.y
let p = Point(x: 3.0, y: 4.0) // $ type=p:Point target=Point.init
let xVal = p[keyPath: kpX] // $ type=xVal:Double
let yVal = p[keyPath: kpY] // $ type=yVal:Double
}
// --- Key-path expressions: nested property access ---
func testNestedKeyPaths() {
let kpStartX = \Line.start.x
let kpEndY = \Line.end.y
let s = Point(x: 0.0, y: 0.0) // $ target=Point.init
let e = Point(x: 1.0, y: 1.0) // $ target=Point.init
let line = Line(start: s, end: e) // $ target=Line.init
let startX = line[keyPath: kpStartX] // $ type=startX:Double
let endY = line[keyPath: kpEndY] // $ type=endY:Double
}
// --- Key-path expressions: identity (\.self) ---
func testSelfKeyPath() {
let kpSelf = \Int.self
let val = 42[keyPath: kpSelf] // $ type=val:Int
}
// --- Key-path expressions: optional chaining ---
struct Person {
var name : String
var address : Address?
// Person.init
init(name: String, address: Address?) {
self.name = name // $ type=name:String
self.address = address
}
}
struct Address {
var city : String
var zip : String
// Address.init
init(city: String, zip: String) {
self.city = city // $ type=city:String
self.zip = zip // $ type=zip:String
}
}
func testOptionalChainingKeyPaths() {
let kpCity = \Person.address?.city
let addr = Address(city: "NYC", zip: "10001") // $ target=Address.init
let person = Person(name: "Alice", address: addr) // $ target=Person.init
let city = person[keyPath: kpCity] // $ type=city:String?
}
// --- Key-path expressions: used as function arguments ---
struct Employee {
var name : String
var salary : Int
// Employee.init
init(name: String, salary: Int) {
self.name = name // $ type=name:String
self.salary = salary // $ type=salary:Int
}
}
// extractField(_:keyPath:)
func extractField<T, V>(_ items: [T], keyPath: KeyPath<T, V>) -> [V] {
return items.map { $0[keyPath: keyPath] }
}
func testKeyPathAsArgument() {
let e1 = Employee(name: "Alice", salary: 100) // $ target=Employee.init
let e2 = Employee(name: "Bob", salary: 200) // $ target=Employee.init
let employees = [e1, e2]
let names = extractField(employees, keyPath: \.name) // $ target=extractField(_:keyPath:)
let name = names[0] // $ type=name:String
let salaries = extractField(employees, keyPath: \.salary) // $ target=extractField(_:keyPath:)
let salary = salaries[0] // $ type=salary:Int
}
// --- Key-path expressions: with generics ---
class KPContainer<T> {
var item : T
// KPContainer.init
init(item: T) {
self.item = item // $ type=item:T
}
}
func testGenericKeyPaths() {
let kp = \KPContainer<Int>.item
let c = KPContainer(item: 42) // $ target=KPContainer.init
let v = c[keyPath: kp] // $ type=v:Int
}
// --- Key-path expressions: writable key paths and mutation ---
func testWritableKeyPaths() {
var p = Point(x: 1.0, y: 2.0) // $ type=p:Point target=Point.init
let kpX = \Point.x
p[keyPath: kpX] = 10.0
let newX = p[keyPath: kpX] // $ type=newX:Double
}
// --- Key-path expressions: appending key paths ---
func testKeyPathAppending() {
let kpStart = \Line.start
let kpX = \Point.x
let kpStartX = kpStart.appending(path: kpX)
let s = Point(x: 5.0, y: 6.0) // $ target=Point.init
let e = Point(x: 7.0, y: 8.0) // $ target=Point.init
let line = Line(start: s, end: e) // $ target=Line.init
let val = line[keyPath: kpStartX] // $ type=val:Double
}
// --- Key-path expressions: shorthand in higher-order functions ---
func testKeyPathInMap() {
let e1 = Employee(name: "Alice", salary: 100) // $ target=Employee.init
let e2 = Employee(name: "Bob", salary: 200) // $ target=Employee.init
let employees = [e1, e2]
let names = employees.map(\.name)
let name = names[0] // $ type=name:String
let salaries = employees.map(\.salary)
let salary = salaries[0] // $ type=salary:Int
}
// --- Key-path expressions: class hierarchy ---
class Shape2 {
var color : String
// Shape2.init
init(color: String) {
self.color = color // $ type=color:String
}
}
class Circle2 : Shape2 {
var radius : Double
// Circle2.init
init(color: String, radius: Double) {
self.radius = radius // $ type=radius:Double
super.init(color: color) // $ target=Shape2.init
}
}
func testInheritedKeyPaths() {
let kpColor = \Circle2.color
let kpRadius = \Circle2.radius
let c = Circle2(color: "red", radius: 5.0) // $ type=c:Circle2 target=Circle2.init
let col = c[keyPath: kpColor] // $ type=col:String
let rad = c[keyPath: kpRadius] // $ type=rad:Double
}
// --- Key-path expressions: tuple element access ---
func testTupleKeyPath() {
let kp0 = \(Int, String).0
let kp1 = \(Int, String).1
let tuple = (42, "hello")
let first = tuple[keyPath: kp0] // $ type=first:Int
let second = tuple[keyPath: kp1] // $ type=second:String
}
// --- Key-path expressions: array/dictionary subscript ---
func testSubscriptKeyPaths() {
let kpFirst = \[Int][0]
let arr = [10, 20, 30]
let first = arr[keyPath: kpFirst] // $ type=first:Int
let kpKey = \[String: Int]["x"]
let dict = ["x": 1, "y": 2]
let val = dict[keyPath: kpKey] // $ type=val:Int?
}

View File

@@ -0,0 +1,495 @@
// --- Overload by parameter type ---
class OverloadByType {
// OverloadByType.handle(_:Int)
func handle(_ x: Int) -> String {
return "int"
}
// OverloadByType.handle(_:Double)
func handle(_ x: Double) -> String {
return "double"
}
// OverloadByType.handle(_:String)
func handle(_ x: String) -> String {
return "string"
}
// OverloadByType.handle(_:Bool)
func handle(_ x: Bool) -> String {
return "bool"
}
}
func testOverloadByType() {
let o = OverloadByType() // $ target=init()
let r1 = o.handle(42) // $ type=r1:String target=OverloadByType.handle(_:Int)
let r2 = o.handle(3.14) // $ type=r2:String target=OverloadByType.handle(_:Double)
let r3 = o.handle("hi") // $ type=r3:String target=OverloadByType.handle(_:String)
let r4 = o.handle(true) // $ type=r4:String target=OverloadByType.handle(_:Bool)
}
// --- Overload by argument label ---
class OverloadByLabel {
// OverloadByLabel.configure(width:)
func configure(width: Int) -> String {
return "width"
}
// OverloadByLabel.configure(height:)
func configure(height: Int) -> String {
return "height"
}
// OverloadByLabel.configure(width:height:)
func configure(width: Int, height: Int) -> String {
return "both"
}
// OverloadByLabel.configure(size:)
func configure(size: Int) -> String {
return "size"
}
}
func testOverloadByLabel() {
let o = OverloadByLabel() // $ target=init()
let r1 = o.configure(width: 10) // $ type=r1:String target=OverloadByLabel.configure(width:)
let r2 = o.configure(height: 20) // $ type=r2:String target=OverloadByLabel.configure(height:)
let r3 = o.configure(width: 10, height: 20) // $ type=r3:String target=OverloadByLabel.configure(width:height:)
let r4 = o.configure(size: 30) // $ type=r4:String target=OverloadByLabel.configure(size:)
}
// --- Overload by arity (number of parameters) ---
class OverloadByArity {
// OverloadByArity.compute()
func compute() -> Int {
return 0
}
// OverloadByArity.compute(_:Int)
func compute(_ x: Int) -> Int {
return x
}
// OverloadByArity.compute(_:Int,_:Int)
func compute(_ x: Int, _ y: Int) -> Int {
return x + y
}
// OverloadByArity.compute(_:Int,_:Int,_:Int)
func compute(_ x: Int, _ y: Int, _ z: Int) -> Int {
return x + y + z
}
}
func testOverloadByArity() {
let o = OverloadByArity() // $ target=init()
let r0 = o.compute() // $ type=r0:Int target=OverloadByArity.compute()
let r1 = o.compute(1) // $ type=r1:Int target=OverloadByArity.compute(_:Int)
let r2 = o.compute(1, 2) // $ type=r2:Int target=OverloadByArity.compute(_:Int,_:Int)
let r3 = o.compute(1, 2, 3) // $ type=r3:Int target=OverloadByArity.compute(_:Int,_:Int,_:Int)
}
// --- Overload by return type (contextual type) ---
class OverloadByReturn {
// OverloadByReturn.create()->Int
func create() -> Int {
return 0
}
// OverloadByReturn.create()->String
func create() -> String {
return ""
}
// OverloadByReturn.create()->Double
func create() -> Double {
return 0.0
}
}
func testOverloadByReturn() {
let o = OverloadByReturn() // $ target=init()
let r1 : Int = o.create() // $ type=r1:Int target=OverloadByReturn.create()->Int
let r2 : String = o.create() // $ type=r2:String target=OverloadByReturn.create()->String
let r3 : Double = o.create() // $ type=r3:Double target=OverloadByReturn.create()->Double
}
// --- Overload: generic vs non-generic (non-generic preferred) ---
class OverloadGenericVsConcrete {
// OverloadGenericVsConcrete.process(_:Int)
func process(_ x: Int) -> String {
return "concrete"
}
// OverloadGenericVsConcrete.process<T>(_:)
func process<T>(_ x: T) -> String {
return "generic"
}
}
func testOverloadGenericVsConcrete() {
let o = OverloadGenericVsConcrete() // $ target=init()
let r1 = o.process(42) // $ type=r1:String target=OverloadGenericVsConcrete.process(_:Int)
let r2 = o.process("hello") // $ type=r2:String target=OverloadGenericVsConcrete.process<T>(_:)
let r3 = o.process(true) // $ type=r3:String target=OverloadGenericVsConcrete.process<T>(_:)
}
// --- Overload: free functions by parameter type ---
// freeOverload(_:Int)
func freeOverload(_ x: Int) -> String {
return "int"
}
// freeOverload(_:String)
func freeOverload(_ x: String) -> String {
return "string"
}
// freeOverload(_:Double)
func freeOverload(_ x: Double) -> String {
return "double"
}
func testFreeOverload() {
let r1 = freeOverload(42) // $ type=r1:String target=freeOverload(_:Int)
let r2 = freeOverload("hi") // $ type=r2:String target=freeOverload(_:String)
let r3 = freeOverload(1.5) // $ type=r3:String target=freeOverload(_:Double)
}
// --- Overload: init overloading ---
class MultiInit {
var value : String
// MultiInit.init()
init() {
value = "default"
}
// MultiInit.init(int:)
init(int: Int) {
value = "int"
}
// MultiInit.init(str:)
init(str: String) {
value = str // $ type=str:String
}
// MultiInit.init(x:y:)
init(x: Int, y: Int) {
value = "pair"
}
// MultiInit.getValue
func getValue() -> String {
return value // $ type=.value:String
}
}
func testInitOverloading() {
let m1 = MultiInit() // $ type=m1:MultiInit target=MultiInit.init()
let m2 = MultiInit(int: 5) // $ type=m2:MultiInit target=MultiInit.init(int:)
let m3 = MultiInit(str: "x") // $ type=m3:MultiInit target=MultiInit.init(str:)
let m4 = MultiInit(x: 1, y: 2) // $ type=m4:MultiInit target=MultiInit.init(x:y:)
let v = m1.getValue() // $ type=v:String target=MultiInit.getValue
}
// --- Overload: static vs instance method ---
class StaticVsInstance {
// StaticVsInstance.action()->instance
func action() -> String {
return "instance"
}
// StaticVsInstance.action()->static
static func action() -> String {
return "static"
}
// StaticVsInstance.init
init() {}
}
func testStaticVsInstance() {
let o = StaticVsInstance() // $ target=StaticVsInstance.init
let r1 = o.action() // $ type=r1:String target=StaticVsInstance.action()->instance
let r2 = StaticVsInstance.action() // $ type=r2:String target=StaticVsInstance.action()->static
}
// --- Overload: protocol extension default vs concrete implementation ---
protocol Describable {
// Describable.describe
func describe() -> String
}
extension Describable {
// Describable.describe(default)
func describe() -> String {
return "default"
}
// Describable.extra
func extra() -> String {
return "extra"
}
}
class DescribableImpl : Describable {
// DescribableImpl.init
init() {}
// DescribableImpl.describe
func describe() -> String {
return "concrete"
}
}
func testProtocolExtensionOverload() {
let d = DescribableImpl() // $ target=DescribableImpl.init
let r1 = d.describe() // $ type=r1:String target=DescribableImpl.describe
let r2 = d.extra() // $ type=r2:String target=Describable.extra
}
// --- Overload: subclass override resolution ---
class Base {
// Base.init
init() {}
// Base.action
func action() -> String {
return "base"
}
// Base.baseOnly
func baseOnly() -> String {
return "baseOnly"
}
}
class Sub : Base {
// Sub.init
override init() {
super.init() // $ target=Base.init
}
// Sub.action
override func action() -> String {
return "sub"
}
// Sub.subOnly
func subOnly() -> String {
return "subOnly"
}
}
class SubSub : Sub {
// SubSub.init
override init() {
super.init() // $ target=Sub.init
}
// SubSub.action
override func action() -> String {
return "subsub"
}
}
func testOverrideResolution() {
let b = Base() // $ target=Base.init
let rb = b.action() // $ type=rb:String target=Base.action
let s = Sub() // $ target=Sub.init
let rs = s.action() // $ type=rs:String target=Sub.action
let rbo = s.baseOnly() // $ type=rbo:String target=Base.baseOnly
let rso = s.subOnly() // $ type=rso:String target=Sub.subOnly
let ss = SubSub() // $ target=SubSub.init
let rss = ss.action() // $ type=rss:String target=SubSub.action
let rbo2 = ss.baseOnly() // $ type=rbo2:String target=Base.baseOnly
let rso2 = ss.subOnly() // $ type=rso2:String target=Sub.subOnly
}
// --- Overload: by external vs internal parameter names ---
class LabelVariants {
// LabelVariants.send(to:)
func send(to target: String) -> String {
return target // $ type=target:String
}
// LabelVariants.send(from:)
func send(from source: String) -> String {
return source // $ type=source:String
}
// LabelVariants.send(to:from:)
func send(to target: String, from source: String) -> String {
return target + source
}
}
func testLabelVariants() {
let o = LabelVariants() // $ target=init()
let r1 = o.send(to: "x") // $ type=r1:String target=LabelVariants.send(to:)
let r2 = o.send(from: "y") // $ type=r2:String target=LabelVariants.send(from:)
let r3 = o.send(to: "x", from: "y") // $ type=r3:String target=LabelVariants.send(to:from:)
}
// --- Overload: generic function with different constraint satisfaction ---
protocol Numeric2 : Equatable {
// Numeric2.zero
static func zero() -> Self
}
extension Int : Numeric2 {
// Int.zero
static func zero() -> Int { return 0 }
}
extension Double : Numeric2 {
// Double.zero
static func zero() -> Double { return 0.0 }
}
// constrainedId(_:Numeric2)
func constrainedId<T: Numeric2>(_ x: T) -> T {
return x
}
// constrainedId(_:Equatable)
func constrainedId<T: Equatable>(_ x: T) -> T {
return x
}
func testConstrainedOverload() {
let r1 = constrainedId(42) // $ type=r1:Int target=constrainedId(_:Numeric2)
let r2 = constrainedId("hello") // $ type=r2:String target=constrainedId(_:Equatable)
}
// --- Overload: methods on generic type specialized differently ---
class Box<T> {
var item : T
// Box.init
init(_ item: T) {
self.item = item // $ type=item:T
}
// Box.get
func get() -> T {
return item // $ type=.item:T
}
// Box.replace
func replace(_ newItem: T) {
item = newItem // $ type=newItem:T
}
}
func testGenericMethodResolution() {
let intBox = Box(10) // $ type=intBox@Box<T>:Int target=Box.init
let strBox = Box("hi") // $ type=strBox@Box<T>:String target=Box.init
let v1 = intBox.get() // $ type=v1:Int target=Box.get
let v2 = strBox.get() // $ type=v2:String target=Box.get
intBox.replace(20) // $ target=Box.replace
strBox.replace("bye") // $ target=Box.replace
}
// --- Overload: convenience init vs designated init ---
class Widget {
var name : String
var size : Int
// Widget.init(name:size:)
init(name: String, size: Int) {
self.name = name // $ type=name:String
self.size = size // $ type=size:Int
}
// Widget.init(name:)
convenience init(name: String) {
self.init(name: name, size: 1) // $ target=Widget.init(name:size:)
}
// Widget.init(size:)
convenience init(size: Int) {
self.init(name: "default", size: size) // $ target=Widget.init(name:size:)
}
}
func testConvenienceInit() {
let w1 = Widget(name: "a", size: 5) // $ type=w1:Widget target=Widget.init(name:size:)
let w2 = Widget(name: "b") // $ type=w2:Widget target=Widget.init(name:)
let w3 = Widget(size: 10) // $ type=w3:Widget target=Widget.init(size:)
}
// --- Overload: methods with closure parameters of different signatures ---
class Processor {
// Processor.init
init() {}
// Processor.apply(_:(Int)->Int)
func apply(_ f: (Int) -> Int) -> Int {
return f(0)
}
// Processor.apply(_:(String)->String)
func apply(_ f: (String) -> String) -> String {
return f("")
}
// Processor.apply(_:(Int,Int)->Int)
func apply(_ f: (Int, Int) -> Int) -> Int {
return f(0, 0)
}
}
func testClosureOverload() {
let p = Processor() // $ target=Processor.init
let r1 = p.apply({ x in x + 1 }) // $ type=r1:Int target=Processor.apply(_:(Int)->Int)
let r2 = p.apply({ s in s + "!" }) // $ type=r2:String target=Processor.apply(_:(String)->String)
let r3 = p.apply({ x, y in x + y }) // $ type=r3:Int target=Processor.apply(_:(Int,Int)->Int)
}
// --- Overload: subscript-like method overloading ---
class MultiSubscript {
var data : [Int] = [1, 2, 3]
var dict : [String: Int] = ["a": 1]
// MultiSubscript.init
init() {}
// MultiSubscript.get(_:)
func get(_ index: Int) -> Int {
return data[index]
}
// MultiSubscript.get(_:String)
func get(_ key: String) -> Int {
return dict[key] ?? 0
}
}
func testMethodSubscriptLike() {
let ms = MultiSubscript() // $ target=MultiSubscript.init
let r1 = ms.get(0) // $ type=r1:Int target=MultiSubscript.get(_:)
let r2 = ms.get("a") // $ type=r2:Int target=MultiSubscript.get(_:String)
}

View File

@@ -0,0 +1,119 @@
// --- Protocols and protocol conformance ---
protocol Shape {
// Shape.area
func area() -> Double
}
struct Circle : Shape {
var radius : Double
// Circle.init
init(radius: Double) {
self.radius = radius // $ type=radius:Double
}
// Circle.area
func area() -> Double {
return 3.14159 * radius * radius // $ type=.radius:Double
}
}
struct Rectangle : Shape {
var width : Double
var height : Double
// Rectangle.init
init(width: Double, height: Double) {
self.width = width // $ type=width:Double
self.height = height // $ type=height:Double
}
// Rectangle.area
func area() -> Double {
return width * height // $ type=.width:Double
}
}
func testProtocol() {
let c = Circle(radius: 5.0) // $ type=c:Circle target=Circle.init
let a1 = c.area() // $ type=a1:Double target=Circle.area
let r = Rectangle(width: 3.0, height: 4.0) // $ type=r:Rectangle target=Rectangle.init
let a2 = r.area() // $ type=a2:Double target=Rectangle.area
}
// --- Protocol with associated types ---
protocol Container {
associatedtype Item
// Container.getItem
func getItem() -> Item
// Container.count
func count() -> Int
}
struct IntContainer : Container {
typealias Item = Int
var items : [Int]
// IntContainer.init
init(items: [Int]) {
self.items = items
}
// IntContainer.getItem
func getItem() -> Int {
return items[0]
}
// IntContainer.count
func count() -> Int {
return items.count
}
}
func testAssociatedTypes() {
let ic = IntContainer(items: [1, 2, 3]) // $ type=ic:IntContainer target=IntContainer.init
let item = ic.getItem() // $ type=item:Int target=IntContainer.getItem
let cnt = ic.count() // $ type=cnt:Int target=IntContainer.count
}
// --- Multiple protocol conformance ---
protocol Printable {
// Printable.display
func display() -> String
}
protocol Identifiable {
// Identifiable.id
func id() -> Int
}
class Entity : Printable, Identifiable {
var name : String
var entityId : Int
// Entity.init
init(name: String, entityId: Int) {
self.name = name // $ type=name:String
self.entityId = entityId // $ type=entityId:Int
}
// Entity.display
func display() -> String {
return name // $ type=.name:String
}
// Entity.id
func id() -> Int {
return entityId // $ type=.entityId:Int
}
}
func testMultipleProtocols() {
let e = Entity(name: "test", entityId: 42) // $ type=e:Entity target=Entity.init
let d = e.display() // $ type=d:String target=Entity.display
let eid = e.id() // $ type=eid:Int target=Entity.id
}

View File

@@ -0,0 +1,89 @@
import swift
import TestUtils
import utils.test.InlineExpectationsTest
pragma[nomagic]
private predicate declHasPotentialCommentAt(Decl d, string path, int line) {
d.getLocation().hasLocationInfo(path, line + 1, _, _, _)
}
pragma[nomagic]
private SingleLineComment getPrecedingComment(Decl d) {
exists(string path, int line |
declHasPotentialCommentAt(d, path, line) and
result.getLocation().hasLocationInfo(path, line, _, _, _)
)
}
module ResolveTest implements TestSig {
string getARelevantTag() { result = "target" }
private predicate declHasName(Decl c, string value) {
exists(string s |
s = getPrecedingComment(c).getText() and
value = s.substring(3, s.length() - 1)
)
or
not exists(getPrecedingComment(c)) and
value = [c.(EnumElementDecl).getName(), c.(Function).getName()]
}
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(AstNode source, Decl target |
location = source.getLocation() and
element = source.toString() and
target =
[
source.(CallExpr).getStaticTarget().(Decl), source.(MethodLookupExpr).getMember(),
source.(EnumElementPattern).getElement()
] and
declHasName(target, value) and
tag = "target" and
toBeTested(source) and
toBeTested(target)
)
}
}
private Type getTypeAt(Type t, string path) {
path = "" and
result = t.getUnderlyingType()
or
exists(BoundGenericType b, GenericTypeDecl decl |
b = t and
decl = b.getDeclaration()
|
exists(string prefix, string suffix, int i |
result = getTypeAt(b.getArgType(i).getUnderlyingType(), suffix) and
prefix = decl.getName() + "<" + decl.getGenericTypeParam(i).getName() + ">" and
if suffix = "" then path = prefix else path = prefix + "." + suffix
)
)
}
module TypeTest implements TestSig {
string getARelevantTag() { result = "type" }
predicate hasActualResult(Location location, string element, string tag, string value) { none() }
predicate hasOptionalResult(Location location, string element, string tag, string value) {
exists(Locatable e, string path, Type t, Type t0 |
t = [e.(Expr).getType(), e.(VarDecl).getType()] and
tag = "type" and
location = e.getLocation() and
t0 = getTypeAt(t, path) and
exists(string name, string at |
name = t0.(AnyGenericType).getDeclaration().getName()
or
not t0 instanceof AnyGenericType and
name = t0.getName()
|
(if path = "" then at = "" else at = "@" + path) and
value = element + at + ":" + name and
element = e.toString()
)
)
}
}
import MakeTest<MergeTests<ResolveTest, TypeTest>>

View File

@@ -0,0 +1,300 @@
// --- where clause on generic function ---
// minOf(_:_:)
func minOf<T: Comparable>(_ a: T, _ b: T) -> T {
if a < b { return a } else { return b }
}
// maxOf(_:_:)
func maxOf<T>(_ a: T, _ b: T) -> T where T : Comparable {
if a > b { return a } else { return b }
}
func testWhereClause() {
let m1 = minOf(3, 7) // $ type=m1:Int target=minOf(_:_:)
let m2 = minOf("a", "z") // $ type=m2:String target=minOf(_:_:)
let m3 = maxOf(3, 7) // $ type=m3:Int target=maxOf(_:_:)
let m4 = maxOf("a", "z") // $ type=m4:String target=maxOf(_:_:)
}
// --- Multiple constraints on a single type parameter ---
protocol Displayable2 {
// Displayable2.display
func display() -> String
}
protocol Sortable {
// Sortable.sortKey
func sortKey() -> Int
}
struct TaggedItem : Displayable2, Sortable, Equatable {
var tag : String
var priority : Int
// TaggedItem.init
init(tag: String, priority: Int) {
self.tag = tag // $ type=tag:String
self.priority = priority // $ type=priority:Int
}
// TaggedItem.display
func display() -> String {
return tag // $ type=.tag:String
}
// TaggedItem.sortKey
func sortKey() -> Int {
return priority // $ type=.priority:Int
}
}
// showAndSort(_:)
func showAndSort<T: Displayable2 & Sortable>(_ item: T) -> String {
return item.display() // $ target=Displayable2.display
}
// showSortAndCompare(_:_:)
func showSortAndCompare<T>(_ a: T, _ b: T) -> Bool where T : Displayable2, T : Sortable, T : Equatable {
return a == b
}
func testMultipleConstraints() {
let item = TaggedItem(tag: "x", priority: 1) // $ target=TaggedItem.init
let s = showAndSort(item) // $ type=s:String target=showAndSort(_:)
let eq = showSortAndCompare(item, item) // $ type=eq:Bool target=showSortAndCompare(_:_:)
}
// --- Generic class with multiple constrained type parameters ---
class SortedPair<T: Comparable, U: Comparable> {
var first : T
var second : U
// SortedPair.init
init(first: T, second: U) {
self.first = first // $ type=first:T
self.second = second // $ type=second:U
}
// SortedPair.isFirstSmaller
func isFirstSmaller(than other: T) -> Bool {
return first < other // $ type=other:T
}
// SortedPair.isSecondSmaller
func isSecondSmaller(than other: U) -> Bool {
return second < other // $ type=other:U
}
}
func testMultiConstrainedParams() {
let sp = SortedPair(first: 3, second: "b") // $ target=SortedPair.init
let r1 = sp.isFirstSmaller(than: 5) // $ type=r1:Bool target=SortedPair.isFirstSmaller
let r2 = sp.isSecondSmaller(than: "z") // $ type=r2:Bool target=SortedPair.isSecondSmaller
}
// --- Associated type constraints (same-type constraint) ---
protocol ElementContainer {
associatedtype Element
// ElementContainer.first
func first() -> Element
}
struct IntArray : ElementContainer {
typealias Element = Int
var items : [Int]
// IntArray.init
init(items: [Int]) {
self.items = items
}
// IntArray.first
func first() -> Int {
return items[0]
}
}
struct StringArray : ElementContainer {
typealias Element = String
var items : [String]
// StringArray.init
init(items: [String]) {
self.items = items
}
// StringArray.first
func first() -> String {
return items[0]
}
}
// extractFirst(from:Int)
func extractFirst<C: ElementContainer>(from container: C) -> C.Element where C.Element == Int {
return container.first() // $ target=ElementContainer.first
}
// extractFirst(from:String)
func extractFirst<C: ElementContainer>(from container: C) -> C.Element where C.Element == String {
return container.first() // $ target=ElementContainer.first
}
func testSameTypeConstraint() {
let ia = IntArray(items: [10, 20]) // $ target=IntArray.init
let sa = StringArray(items: ["hi", "there"]) // $ target=StringArray.init
let r1 = extractFirst(from: ia) // $ type=r1:Int target=extractFirst(from:Int)
let r2 = extractFirst(from: sa) // $ type=r2:String target=extractFirst(from:String)
}
// --- Superclass constraint on type parameter ---
class Vehicle {
var speed : Int
// Vehicle.init
init(speed: Int) {
self.speed = speed // $ type=speed:Int
}
// Vehicle.describe
func describe() -> String {
return "vehicle"
}
}
class Car : Vehicle {
// Car.init
override init(speed: Int) {
super.init(speed: speed) // $ target=Vehicle.init
}
// Car.describe
override func describe() -> String {
return "car"
}
// Car.honk
func honk() -> String {
return "beep"
}
}
class Truck : Vehicle {
// Truck.init
override init(speed: Int) {
super.init(speed: speed) // $ target=Vehicle.init
}
// Truck.describe
override func describe() -> String {
return "truck"
}
// Truck.haul
func haul() -> String {
return "hauling"
}
}
// describeVehicle(_:)
func describeVehicle<T: Vehicle>(_ v: T) -> String {
return v.describe() // $ target=Vehicle.describe
}
func testSuperclassConstraint() {
let car = Car(speed: 100) // $ type=car:Car target=Car.init
let truck = Truck(speed: 60) // $ type=truck:Truck target=Truck.init
let d1 = describeVehicle(car) // $ type=d1:String target=describeVehicle(_:)
let d2 = describeVehicle(truck) // $ type=d2:String target=describeVehicle(_:)
let h = car.honk() // $ type=h:String target=Car.honk
let hl = truck.haul() // $ type=hl:String target=Truck.haul
}
// --- Constrained extension methods ---
protocol Summable {
// Summable.+
static func +(lhs: Self, rhs: Self) -> Self
}
extension Int : Summable {}
extension Double : Summable {}
extension String : Summable {}
struct Accumulator<T> {
var values : [T]
// Accumulator.init
init(values: [T]) {
self.values = values
}
// Accumulator.count
func count() -> Int {
return values.count
}
}
extension Accumulator where T : Summable {
// Accumulator.total
func total() -> T {
return values[0] + values[1] // $ target=Summable.+
}
}
extension Accumulator where T : Equatable {
// Accumulator.contains
func contains(_ item: T) -> Bool {
return values.contains(where: { $0 == item })
}
}
func testConstrainedExtensions() {
let intAcc = Accumulator(values: [1, 2, 3]) // $ target=Accumulator.init
let cnt = intAcc.count() // $ type=cnt:Int target=Accumulator.count
let tot = intAcc.total() // $ type=tot:Int target=Accumulator.total
let has = intAcc.contains(2) // $ type=has:Bool target=Accumulator.contains
}
// --- Generic method with its own constrained type parameter ---
class Transformer {
// Transformer.init
init() {}
// Transformer.transform
func transform<T: Summable>(_ items: [T]) -> T {
return items[0] + items[1] // $ target=Summable.+
}
// Transformer.merge
func merge<A: Equatable, B: Equatable>(_ a: A, _ b: B) -> Bool {
return true
}
}
func testMethodTypeParams() {
let t = Transformer() // $ target=Transformer.init
let r1 = t.transform([3, 1, 2]) // $ type=r1:Int target=Transformer.transform
let r2 = t.transform(["c", "a", "b"]) // $ type=r2:String target=Transformer.transform
let r3 = t.merge(1, "x") // $ type=r3:Bool target=Transformer.merge
}
// --- Recursive constraint (Comparable requiring Equatable) ---
// clamp(_:min:max:)
func clamp<T: Comparable>(_ value: T, min lower: T, max upper: T) -> T {
if value < lower { return lower }
if value > upper { return upper }
return value
}
func testRecursiveConstraint() {
let r1 = clamp(5, min: 0, max: 10) // $ type=r1:Int target=clamp(_:min:max:)
let r2 = clamp(3.5, min: 1.0, max: 2.0) // $ type=r2:Double target=clamp(_:min:max:)
let r3 = clamp("m", min: "a", max: "z") // $ type=r3:String target=clamp(_:min:max:)
}