Compare commits

..

6 Commits

Author SHA1 Message Date
yoff
8189cd2133 Python: visit function parameter and return annotations in new CFG
The new (shared-CFG-based) Python control flow graph in
`semmle.python.controlflow.internal.Cfg` previously did not emit CFG
nodes for parameter type annotations (`def f(x: T): ...`) or for the
return type annotation (`-> T`). The legacy CFG emitted both, and a
small number of framework models rely on this: `LocalSources.qll`'s
`annotatedInstance` walks the parameter annotation expression by way
of its CFG node to track that a parameter receives an instance of the
annotated class.

After the dataflow flip to the new CFG/SSA this regression manifested
as lost flows in any test exercising annotation-based parameter
tracking: FastAPI `Depends()` receivers, Pydantic request bodies,
Starlette `WebSocket`, the call-graph type-annotation test, and so on.
Extend `FunctionDefExpr` to visit each annotation as a child of the
function-def expression, in CPython evaluation order: positional
parameter annotations, `*args` annotation, keyword-only parameter
annotations, `**kwargs` annotation, then the return annotation. (Lambda
expressions have no annotations in Python syntax, so `LambdaExpr` is
unchanged.) PEP 695 type parameters remain out of scope; they belong
to the inner annotation scope, not the enclosing CFG.

Restored test results across `framework/aiohttp`, `framework/fastapi`,
`framework/lxml`, the `CallGraph-type-annotations` test, and
`CWE-022-PathInjection`. Two FastAPI list-comprehension MISSING markers
become positive (`taint_test.py:41,55`). CPython CFG consistency
remains clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-24 08:41:24 +00:00
yoff
5081d8171e Python: switch dataflow library to new (shared) CFG + SSA
Flips the Python dataflow trunk from the legacy CFG (semmle/python/Flow.qll)
and legacy ESSA SSA (semmle/python/essa/*) to the new shared CFG facade
(semmle.python.controlflow.internal.Cfg) and the new SSA adapter
(semmle.python.dataflow.new.internal.SsaImpl), both introduced
additively in the preceding PRs in this stack.

This is the trunk-flip equivalent of the original draft PR #21894 (kept
around as documentation), rebased on top of the four preparatory PRs:

  P1: Remove AstNode.getAFlowNode() and rewrite callers (#21919).
  P2: Qualify Flow.qll's AST references with Py:: prefix (#21920).
  P3: Add new shared-CFG-backed control flow graph (#21921).
  P4: Add new shared-SSA-backed SSA adapter (#21923).

The Python dataflow library (semmle/python/dataflow/new/) now imports
the new CFG facade and SSA adapter. All CFG-typed predicates
(ControlFlowNode, CallNode, BasicBlock, NameNode, AttrNode, ...) are
qualified with the Cfg:: prefix; SSA references switch from
EssaVariable/EssaDefinition to SsaImpl::Definition/SourceVariable.

GuardNode is redesigned to use the new CFG's outcome-node model
(isAfterTrue / isAfterFalse) instead of the legacy ConditionBlock +
flipped indirection. Only BarrierGuard<...> is preserved as public
API.

Framework files (Bottle, FastApi, Django, Tornado, Pyramid, Stdlib,
...) are updated to take CFG nodes from the new facade.

A handful of dataflow consistency tweaks for the new CFG:
- Augmented-assignment targets are treated as both load and store.
- 'from X import *' produces uncertain SSA writes for unknown names.
- CFG nodes are canonicalised so dataflow does not see equivalent
  pre/post-order pairs as distinct nodes.

Two AST tweaks for the new CFG:
- AstNodeImpl: omit PEP 695 type-parameter names from
  FunctionDefExpr / ClassDefExpr children.
- ImportResolution: drop the legacy essa import.

Test churn (~175 files): reblessed library- and query-test .expected
files reflect slightly different CFG granularity, different toString
output, and a handful of true alert deltas in security queries.

Verification: all 367 lib + src + consistency-queries compile clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-24 08:41:23 +00:00
Copilot
e51acdc541 Python: add new shared-SSA-backed SSA adapter
Preparatory refactor for the shared-CFG dataflow migration. Adds the
new Python SSA adapter additively, without changing any production
behaviour.

Library additions:

- semmle.python.dataflow.new.internal.SsaImpl — Python SSA
  implementation built on the new (shared) CFG. Mirrors the Java SSA
  adapter (java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll):
  an InputSig is defined in terms of positional (BasicBlock, int)
  variable references, and the shared
  codeql.ssa.Ssa::Make<Location, Cfg, Input> module is then
  instantiated.

  SourceVariable is the AST-level Py::Variable. Variable references
  are looked up via the new CFG facade's NameNode.defines/uses/deletes
  predicates (added in the preceding PR), which themselves are
  one-line bridges to AST-level Name.defines/uses/deletes.

  Implicit-entry definitions are inserted for non-local/global/builtin
  reads, captured variables, and (when needed) parameters.

Test additions:

- library-tests/dataflow-new-ssa/ — exercises the new SSA over a
  representative test corpus and checks expected def/use chains.

- library-tests/dataflow-new-ssa-vs-legacy/ — runs both new SSA and
  legacy ESSA over the same corpus and diffs the results, so any
  semantic divergence shows up as a test failure.

Production impact:

None. The new SSA adapter has zero callers in lib/ and src/ — the
legacy ESSA SSA (semmle/python/essa/*) remains the default. The
dataflow library is not migrated yet; that lands in a follow-up PR.

Verified by:
- All 367 lib + src + consistency-queries compile clean.
- All 641 ControlFlow + PointsTo + dataflow + essa + consistency
  library-tests pass.
- Both new dataflow-new-ssa[/vs-legacy] test packs pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-24 08:40:16 +00:00
yoff
c95c3fe638 Python: model exception edges for raise-prone expressions inside try/with
The new CFG previously only emitted exception edges for explicit `raise`
and `assert` statements. As a result, code that became reachable only
via the exception path of an arbitrary expression (e.g., the body of an
`except` handler following a try-body whose `call()` could raise) was
classified as dead, breaking analyses like StackTraceExposure,
FileNotAlwaysClosed, ExceptionInfo, UseOfExit, and CatchingBaseException.

This commit adds a `mayThrow` predicate over expressions that are known
sources of implicit exceptions in Python (calls, attribute access,
subscripts, arithmetic/comparison operators, imports, await/yield/yield
from) plus `from m import *` at the statement level, and routes them
through the shared CFG's `beginAbruptCompletion(_, _, ExceptionSuccessor,
always=false)` hook.

The set of exception sources is restricted to nodes that are
syntactically inside a `try`/`with` statement in the same scope.
This mirrors Java's `ControlFlowGraph::mayThrow`, which only emits
exception edges where local handling can observe them — outside such
contexts, the edges add CFG complexity (weakening BarrierGuard
precision and breaking SSA continuity around augmented assignments and
subscript stores) without analysis benefit, since exceptions just
propagate to the function exit anyway.

Net effect on the test suite: ~100 alerts restored across the exception-
related query tests (StackTraceExposure +29, ExceptionInfo +17,
FileNotAlwaysClosed +52, UseOfExit +1, CatchingBaseException restored)
with no precision regressions. Affected `.expected` files and the
regression-guard `dead_under_no_raise.py` are updated accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-24 08:15:45 +00:00
yoff
6c03194092 Python: add new shared-CFG-backed control flow graph facade (Cfg)
Adds the public facade on top of the AstNodeImpl adapter from the
previous commit. Re-exposes the same API surface as
semmle/python/Flow.qll (ControlFlowNode, CallNode, BasicBlock,
NameNode, DefinitionNode, CompareNode, ...), backed by the shared
codeql.controlflow.ControlFlowGraph library.

- semmle.python.controlflow.internal.Cfg — public facade.
- ControlFlow/store-load/* — basic store/load coverage via the facade.

The new CFG library is added additively: it has zero callers in lib/
and src/, and the legacy CFG in semmle/python/Flow.qll remains the
default. Dataflow, SSA, and production query migration land in
follow-up PRs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-24 08:15:45 +00:00
yoff
39e6bfc894 Python: add shared-CFG AstSig adapter (AstNodeImpl)
Preparatory refactor for the shared-CFG dataflow migration. Adds the
adapter that mediates between the Python AST and the shared
codeql.controlflow.ControlFlowGraph signature, plus the test suites
that validate the new CFG directly against this adapter. The public
facade is added in the following commit.

Library additions:

- semmle.python.controlflow.internal.AstNodeImpl — wraps Python's
  Stmt/Expr/Scope/Pattern and adds two synthetic kinds of node
  (BlockStmt for body slots, intermediate nodes for multi-operand
  boolean expressions) to satisfy the shared CFG signature.

- lib/printCfgNew.ql — debug/visualisation query for the new CFG.

- consistency-queries/CfgConsistency.ql — consistency query running
  the shared CFG's standard checks against Python.

Test additions (all driven directly off AstNodeImpl):

- ControlFlow/bindings/* — annotation-driven SSA-binding tests
  (annassign, compound, comprehension, decorated, except_handler,
  imports, match_pattern, parameters, simple, type_params,
  walrus_starred, with_stmt, dead_under_no_raise).

- ControlFlow/evaluation-order/NewCfg*.ql — mirrors of the existing
  OldCfg evaluation-order self-validation suite, run against the
  new CFG via NewCfgImpl.qll.

- Minor extensions to existing test_if.py / test_boolean.py +
  cosmetic .expected churn on a handful of OldCfg tests.

No dataflow, SSA, or production query is migrated yet.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-24 08:15:33 +00:00
390 changed files with 11911 additions and 7912 deletions

View File

@@ -248,7 +248,6 @@ use_repo(
"kotlin-compiler-2.2.20-Beta2",
"kotlin-compiler-2.3.0",
"kotlin-compiler-2.3.20",
"kotlin-compiler-2.4.0",
"kotlin-compiler-embeddable-1.8.0",
"kotlin-compiler-embeddable-1.9.0-Beta",
"kotlin-compiler-embeddable-1.9.20-Beta",
@@ -260,7 +259,6 @@ use_repo(
"kotlin-compiler-embeddable-2.2.20-Beta2",
"kotlin-compiler-embeddable-2.3.0",
"kotlin-compiler-embeddable-2.3.20",
"kotlin-compiler-embeddable-2.4.0",
"kotlin-stdlib-1.8.0",
"kotlin-stdlib-1.9.0-Beta",
"kotlin-stdlib-1.9.20-Beta",
@@ -272,7 +270,6 @@ use_repo(
"kotlin-stdlib-2.2.20-Beta2",
"kotlin-stdlib-2.3.0",
"kotlin-stdlib-2.3.20",
"kotlin-stdlib-2.4.0",
)
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")

View File

@@ -1,413 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Semmle.Util;
using Semmle.Util.Logging;
namespace Semmle.Extraction.CSharp.DependencyFetching
{
internal sealed partial class FeedManager : IDisposable
{
internal const string PublicNugetOrgFeed = "https://api.nuget.org/v3/index.json";
private readonly ILogger logger;
private readonly IDotNet dotnet;
private readonly FileProvider fileProvider;
private readonly DependabotProxy? dependabotProxy;
private readonly DependencyDirectory emptyPackageDirectory;
public ImmutableHashSet<string> PrivateRegistryFeeds { get; }
public bool HasPrivateRegistryFeeds { get; }
public bool CheckNugetFeedResponsiveness { get; } = EnvironmentVariables.GetBooleanOptOut(EnvironmentVariableNames.CheckNugetFeedResponsiveness);
public FeedManager(ILogger logger, IDotNet dotnet, DependabotProxy? dependabotProxy, FileProvider fileProvider)
{
this.logger = logger;
this.dotnet = dotnet;
this.dependabotProxy = dependabotProxy;
this.fileProvider = fileProvider;
PrivateRegistryFeeds = dependabotProxy?.RegistryURLs.ToImmutableHashSet() ?? [];
HasPrivateRegistryFeeds = PrivateRegistryFeeds.Count > 0;
emptyPackageDirectory = new DependencyDirectory("empty", "empty package", logger);
}
private string? GetDirectoryName(string path)
{
try
{
return new FileInfo(path).Directory?.FullName;
}
catch (Exception exc)
{
logger.LogWarning($"Failed to get directory of '{path}': {exc}");
}
return null;
}
private IEnumerable<string> GetFeeds(Func<IList<string>> getNugetFeeds)
{
var results = getNugetFeeds();
var regex = EnabledNugetFeed();
foreach (var result in results)
{
var match = regex.Match(result);
if (!match.Success)
{
logger.LogError($"Failed to parse feed from '{result}'");
continue;
}
var url = match.Groups[1].Value;
if (!url.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase) &&
!url.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
{
logger.LogInfo($"Skipping feed '{url}' as it is not a valid URL.");
continue;
}
if (!string.IsNullOrWhiteSpace(url))
{
yield return url;
}
}
}
private IEnumerable<string> GetFeedsFromFolder(string folderPath) =>
GetFeeds(() => dotnet.GetNugetFeedsFromFolder(folderPath));
private IEnumerable<string> GetFeedsFromNugetConfig(string nugetConfigPath) =>
GetFeeds(() => dotnet.GetNugetFeeds(nugetConfigPath));
private string FeedsToRestoreArgument(IEnumerable<string> feeds)
{
// If there are no feeds, we want to override any default feeds that `dotnet restore` would use by passing a dummy source argument.
if (!feeds.Any())
{
return $" -s \"{emptyPackageDirectory.DirInfo.FullName}\"";
}
// Add package sources. If any are present, they override all sources specified in
// the configuration file(s).
var feedArgs = new StringBuilder();
foreach (var feed in feeds)
{
feedArgs.Append($" -s \"{feed}\"");
}
return feedArgs.ToString();
}
/// <summary>
/// Constructs the list of NuGet sources to use for this restore.
/// (1) Use the feeds we get from `dotnet nuget list source`
/// (2) Use private registries, if they are configured
/// </summary>
/// <param name="path">Path to project/solution</param>
/// <param name="reachableFeeds">The set of reachable NuGet feeds.</param>
/// <returns>A string representing the NuGet sources argument for the restore command.</returns>
public string? MakeRestoreSourcesArgument(string path, HashSet<string> reachableFeeds)
{
// Do not construct a set of explicit NuGet sources to use for restore.
if (!CheckNugetFeedResponsiveness && !HasPrivateRegistryFeeds)
{
return null;
}
// Find the path specific feeds.
var folder = GetDirectoryName(path);
var feedsToConsider = folder is not null ? GetFeedsFromFolder(folder).ToHashSet() : new HashSet<string>();
if (HasPrivateRegistryFeeds)
{
feedsToConsider.UnionWith(PrivateRegistryFeeds);
}
var feedsToUse = CheckNugetFeedResponsiveness
? feedsToConsider.Where(reachableFeeds.Contains)
: feedsToConsider;
return FeedsToRestoreArgument(feedsToUse);
}
private (int initialTimeout, int tryCount) GetFeedRequestSettings(bool isFallback)
{
int timeoutMilliSeconds = isFallback && int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessInitialTimeoutForFallback), out timeoutMilliSeconds)
? timeoutMilliSeconds
: int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessInitialTimeout), out timeoutMilliSeconds)
? timeoutMilliSeconds
: 1000;
logger.LogDebug($"Initial timeout for NuGet feed reachability check is {timeoutMilliSeconds}ms.");
int tryCount = isFallback && int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessRequestCountForFallback), out tryCount)
? tryCount
: int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessRequestCount), out tryCount)
? tryCount
: 4;
logger.LogDebug($"Number of tries for NuGet feed reachability check is {tryCount}.");
return (timeoutMilliSeconds, tryCount);
}
private static async Task<HttpResponseMessage> ExecuteGetRequest(string address, HttpClient httpClient, CancellationToken cancellationToken)
{
return await httpClient.GetAsync(address, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
}
private bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount, out bool isTimeout)
{
logger.LogInfo($"Checking if NuGet feed '{feed}' is reachable...");
// Configure the HttpClient to be aware of the Dependabot Proxy, if used.
HttpClientHandler httpClientHandler = new();
if (dependabotProxy != null)
{
httpClientHandler.Proxy = new WebProxy(dependabotProxy.Address);
if (dependabotProxy.Certificate != null)
{
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, _) =>
{
if (chain is null || cert is null)
{
var msg = cert is null && chain is null
? "certificate and chain"
: chain is null
? "chain"
: "certificate";
logger.LogWarning($"Dependabot proxy certificate validation failed due to missing {msg}");
return false;
}
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
chain.ChainPolicy.CustomTrustStore.Add(dependabotProxy.Certificate);
return chain.Build(cert);
};
}
}
using HttpClient client = new(httpClientHandler);
isTimeout = false;
for (var i = 0; i < tryCount; i++)
{
using var cts = new CancellationTokenSource();
cts.CancelAfter(timeoutMilliSeconds);
try
{
logger.LogInfo($"Attempt {i + 1}/{tryCount} to reach NuGet feed '{feed}'.");
using var response = ExecuteGetRequest(feed, client, cts.Token).GetAwaiter().GetResult();
response.EnsureSuccessStatusCode();
logger.LogInfo($"Querying NuGet feed '{feed}' succeeded.");
return true;
}
catch (Exception exc)
{
if (exc is TaskCanceledException tce &&
tce.CancellationToken == cts.Token &&
cts.Token.IsCancellationRequested)
{
logger.LogInfo($"Didn't receive answer from NuGet feed '{feed}' in {timeoutMilliSeconds}ms.");
timeoutMilliSeconds *= 2;
continue;
}
logger.LogInfo($"Querying NuGet feed '{feed}' failed. The reason for the failure: {exc.Message}");
return false;
}
}
logger.LogWarning($"Didn't receive answer from NuGet feed '{feed}'. Tried it {tryCount} times.");
isTimeout = true;
return false;
}
/// <summary>
/// Retrieves a list of excluded NuGet feeds from the corresponding environment variable.
/// </summary>
private HashSet<string> GetExcludedFeeds()
{
var excludedFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.ExcludedNugetFeedsFromResponsivenessCheck)
.ToHashSet();
if (excludedFeeds.Count > 0)
{
logger.LogInfo($"Excluded NuGet feeds from responsiveness check: {string.Join(", ", excludedFeeds.OrderBy(f => f))}");
}
return excludedFeeds;
}
/// <summary>
/// Checks that we can connect to the specified NuGet feeds.
/// </summary>
/// <param name="feeds">The set of package feeds to check.</param>
/// <param name="reachableFeeds">The list of feeds that were reachable.</param>
/// <returns>
/// True if there is a timeout when trying to reach the feeds (excluding any feeds that are configured
/// to be excluded from the check) or false otherwise.
/// </returns>
public bool CheckSpecifiedFeeds(HashSet<string> feeds, out HashSet<string> reachableFeeds)
{
// Exclude any feeds from the feed check that are configured by the corresponding environment variable.
// These feeds are always assumed to be reachable.
var excludedFeeds = GetExcludedFeeds();
HashSet<string> feedsToCheck = feeds.Where(feed =>
{
if (excludedFeeds.Contains(feed))
{
logger.LogInfo($"Not checking reachability of NuGet feed '{feed}' as it is in the list of excluded feeds.");
return false;
}
return true;
}).ToHashSet();
reachableFeeds = GetReachableNuGetFeeds(feedsToCheck, isFallback: false, out var isTimeout).ToHashSet();
// Always consider feeds excluded for the reachability check as reachable.
reachableFeeds.UnionWith(feeds.Where(feed => excludedFeeds.Contains(feed)));
return isTimeout;
}
public bool IsDefaultFeedReachable()
{
if (CheckNugetFeedResponsiveness)
{
var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback: false);
return IsFeedReachable(PublicNugetOrgFeed, initialTimeout, tryCount, out var _);
}
return true;
}
/// <summary>
/// Tests which of the feeds given by <paramref name="feedsToCheck"/> are reachable.
/// </summary>
/// <param name="feedsToCheck">The feeds to check.</param>
/// <param name="isFallback">Whether the feeds are fallback feeds or not.</param>
/// <param name="isTimeout">Whether a timeout occurred while checking the feeds.</param>
/// <returns>The list of feeds that could be reached.</returns>
private List<string> GetReachableNuGetFeeds(HashSet<string> feedsToCheck, bool isFallback, out bool isTimeout)
{
var fallbackStr = isFallback ? "fallback " : "";
logger.LogInfo($"Checking {fallbackStr}NuGet feed reachability on feeds: {string.Join(", ", feedsToCheck.OrderBy(f => f))}");
var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback);
var timeout = false;
var reachableFeeds = feedsToCheck
.Where(feed =>
{
var reachable = IsFeedReachable(feed, initialTimeout, tryCount, out var feedTimeout);
timeout |= feedTimeout;
return reachable;
})
.ToList();
if (reachableFeeds.Count == 0)
{
logger.LogWarning($"No {fallbackStr}NuGet feeds are reachable.");
}
else
{
logger.LogInfo($"Reachable {fallbackStr}NuGet feeds: {string.Join(", ", reachableFeeds.OrderBy(f => f))}");
}
isTimeout = timeout;
return reachableFeeds;
}
public List<string> GetReachableFallbackNugetFeeds(HashSet<string>? feedsFromNugetConfigs)
{
var fallbackFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.FallbackNugetFeeds).ToHashSet();
if (fallbackFeeds.Count == 0)
{
fallbackFeeds.Add(PublicNugetOrgFeed);
logger.LogInfo($"No fallback NuGet feeds specified. Adding default feed: {PublicNugetOrgFeed}");
var shouldAddNugetConfigFeeds = EnvironmentVariables.GetBooleanOptOut(EnvironmentVariableNames.AddNugetConfigFeedsToFallback);
logger.LogInfo($"Adding feeds from nuget.config to fallback restore: {shouldAddNugetConfigFeeds}");
if (shouldAddNugetConfigFeeds && feedsFromNugetConfigs?.Count > 0)
{
// There are some feeds in `feedsFromNugetConfigs` that have already been checked for reachability, we could skip those.
// But we might use different responsiveness testing settings when we try them in the fallback logic, so checking them again is safer.
fallbackFeeds.UnionWith(feedsFromNugetConfigs);
logger.LogInfo($"Using NuGet feeds from nuget.config files as fallback feeds: {string.Join(", ", feedsFromNugetConfigs.OrderBy(f => f))}");
}
}
return GetReachableNuGetFeeds(fallbackFeeds, isFallback: true, out var _);
}
public (HashSet<string> explicitFeeds, HashSet<string> allFeeds) GetAllFeeds()
{
var nugetConfigs = fileProvider.NugetConfigs;
// Find feeds that are explicitly configured in the NuGet configuration files that we found.
var explicitFeeds = nugetConfigs
.SelectMany(GetFeedsFromNugetConfig)
.ToHashSet();
if (explicitFeeds.Count > 0)
{
logger.LogInfo($"Found {explicitFeeds.Count} NuGet feeds in nuget.config files: {string.Join(", ", explicitFeeds.OrderBy(f => f))}");
}
else
{
logger.LogDebug("No NuGet feeds found in nuget.config files.");
}
// If private package registries are configured for C#, then consider those
// in addition to the ones that are configured in `nuget.config` files.
if (HasPrivateRegistryFeeds)
{
logger.LogInfo($"Found {PrivateRegistryFeeds.Count} private registry feeds configured for C#: {string.Join(", ", PrivateRegistryFeeds.OrderBy(f => f))}");
explicitFeeds.UnionWith(PrivateRegistryFeeds);
}
HashSet<string> allFeeds = [];
// Add all explicitFeeds to the set of all feeds.
allFeeds.UnionWith(explicitFeeds);
// Obtain the list of feeds from the root source directory.
// If a NuGet file is present it will be respected, otherwise we will just get the machine/environment specific feeds.
var nugetFeedsFromRoot = GetFeedsFromFolder(fileProvider.SourceDir.FullName);
allFeeds.UnionWith(nugetFeedsFromRoot);
if (nugetConfigs.Count > 0)
{
var nugetConfigFeeds = nugetConfigs
.Select(GetDirectoryName)
.Where(folder => folder != null)
.SelectMany(folder => GetFeedsFromFolder(folder!))
.ToHashSet();
allFeeds.UnionWith(nugetConfigFeeds);
}
logger.LogInfo($"Found {allFeeds.Count} NuGet feeds (with inherited ones) in nuget.config files: {string.Join(", ", allFeeds.OrderBy(f => f))}");
return (explicitFeeds, allFeeds);
}
[GeneratedRegex(@"^E\s(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
private static partial Regex EnabledNugetFeed();
public void Dispose()
{
emptyPackageDirectory.Dispose();
}
}
}

View File

@@ -4,6 +4,9 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
@@ -16,19 +19,24 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
{
internal sealed partial class NugetPackageRestorer : IDisposable
{
internal const string PublicNugetOrgFeed = "https://api.nuget.org/v3/index.json";
private readonly FileProvider fileProvider;
private readonly FileContent fileContent;
private readonly IDotNet dotnet;
private readonly DependabotProxy? dependabotProxy;
private readonly IDiagnosticsWriter diagnosticsWriter;
private readonly DependencyDirectory legacyPackageDirectory;
private readonly DependencyDirectory missingPackageDirectory;
private readonly DependencyDirectory emptyPackageDirectory;
private readonly ILogger logger;
private readonly ICompilationInfoContainer compilationInfoContainer;
private readonly FeedManager feedManager;
private readonly bool checkNugetFeedResponsiveness = EnvironmentVariables.GetBooleanOptOut(EnvironmentVariableNames.CheckNugetFeedResponsiveness);
private readonly ImmutableHashSet<string> privateRegistryFeeds;
private readonly bool hasPrivateRegistryFeeds;
public DependencyDirectory PackageDirectory { get; }
public NugetPackageRestorer(
FileProvider fileProvider,
FileContent fileContent,
@@ -41,6 +49,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
this.fileProvider = fileProvider;
this.fileContent = fileContent;
this.dotnet = dotnet;
this.dependabotProxy = dependabotProxy;
this.privateRegistryFeeds = dependabotProxy?.RegistryURLs.ToImmutableHashSet() ?? [];
this.hasPrivateRegistryFeeds = privateRegistryFeeds.Count > 0;
this.diagnosticsWriter = diagnosticsWriter;
this.logger = logger;
this.compilationInfoContainer = compilationInfoContainer;
@@ -48,7 +59,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
PackageDirectory = new DependencyDirectory("packages", "package", logger);
legacyPackageDirectory = new DependencyDirectory("legacypackages", "legacy package", logger);
missingPackageDirectory = new DependencyDirectory("missingpackages", "missing package", logger);
feedManager = new FeedManager(logger, dotnet, dependabotProxy, fileProvider);
emptyPackageDirectory = new DependencyDirectory("empty", "empty package", logger);
}
public string? TryRestore(string package)
@@ -107,22 +118,20 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
public HashSet<AssemblyLookupLocation> Restore()
{
var assemblyLookupLocations = new HashSet<AssemblyLookupLocation>();
logger.LogInfo($"Checking NuGet feed responsiveness: {feedManager.CheckNugetFeedResponsiveness}");
compilationInfoContainer.CompilationInfos.Add(("NuGet feed responsiveness checked", feedManager.CheckNugetFeedResponsiveness ? "1" : "0"));
logger.LogInfo($"Checking NuGet feed responsiveness: {checkNugetFeedResponsiveness}");
compilationInfoContainer.CompilationInfos.Add(("NuGet feed responsiveness checked", checkNugetFeedResponsiveness ? "1" : "0"));
HashSet<string> explicitFeeds = [];
HashSet<string> reachableFeeds = [];
try
{
EmitNugetConfigDiagnostics();
// Find feeds that are configured in NuGet.config files and divide them into ones that
// are explicitly configured for the project or by a private registry, and "all feeds"
// (including inherited ones) from other locations on the host outside of the working directory.
(explicitFeeds, var allFeeds) = feedManager.GetAllFeeds();
(explicitFeeds, var allFeeds) = GetAllFeeds();
if (feedManager.CheckNugetFeedResponsiveness)
if (checkNugetFeedResponsiveness)
{
var inheritedFeeds = allFeeds.Except(explicitFeeds).ToHashSet();
@@ -131,7 +140,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString()));
}
var timeout = feedManager.CheckSpecifiedFeeds(explicitFeeds, out var reachableExplicitFeeds);
var timeout = CheckSpecifiedFeeds(explicitFeeds, out var reachableExplicitFeeds);
reachableFeeds.UnionWith(reachableExplicitFeeds);
var allExplicitReachable = explicitFeeds.Count == reachableExplicitFeeds.Count;
@@ -148,11 +157,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
// Inherited feeds should only be used, if they are indeed reachable (as they may be environment specific).
feedManager.CheckSpecifiedFeeds(inheritedFeeds, out var reachableInheritedFeeds);
CheckSpecifiedFeeds(inheritedFeeds, out var reachableInheritedFeeds);
reachableFeeds.UnionWith(reachableInheritedFeeds);
}
using (var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager.IsDefaultFeedReachable))
using (var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, IsDefaultFeedReachable))
{
var count = packagesConfigRestore.InstallPackages();
@@ -206,7 +215,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
var usedPackageNames = GetAllUsedPackageDirNames(dependencies);
var missingPackageLocation = feedManager.CheckNugetFeedResponsiveness
var missingPackageLocation = checkNugetFeedResponsiveness
? DownloadMissingPackagesFromSpecificFeeds(usedPackageNames, explicitFeeds)
: DownloadMissingPackages(usedPackageNames);
@@ -217,6 +226,79 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
return assemblyLookupLocations;
}
/// <summary>
/// Tests which of the feeds given by <paramref name="feedsToCheck"/> are reachable.
/// </summary>
/// <param name="feedsToCheck">The feeds to check.</param>
/// <param name="isFallback">Whether the feeds are fallback feeds or not.</param>
/// <param name="isTimeout">Whether a timeout occurred while checking the feeds.</param>
/// <returns>The list of feeds that could be reached.</returns>
private List<string> GetReachableNuGetFeeds(HashSet<string> feedsToCheck, bool isFallback, out bool isTimeout)
{
var fallbackStr = isFallback ? "fallback " : "";
logger.LogInfo($"Checking {fallbackStr}NuGet feed reachability on feeds: {string.Join(", ", feedsToCheck.OrderBy(f => f))}");
var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback);
var timeout = false;
var reachableFeeds = feedsToCheck
.Where(feed =>
{
var reachable = IsFeedReachable(feed, initialTimeout, tryCount, out var feedTimeout);
timeout |= feedTimeout;
return reachable;
})
.ToList();
if (reachableFeeds.Count == 0)
{
logger.LogWarning($"No {fallbackStr}NuGet feeds are reachable.");
}
else
{
logger.LogInfo($"Reachable {fallbackStr}NuGet feeds: {string.Join(", ", reachableFeeds.OrderBy(f => f))}");
}
isTimeout = timeout;
return reachableFeeds;
}
private bool IsDefaultFeedReachable()
{
if (checkNugetFeedResponsiveness)
{
var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback: false);
return IsFeedReachable(PublicNugetOrgFeed, initialTimeout, tryCount, out var _);
}
return true;
}
private List<string> GetReachableFallbackNugetFeeds(HashSet<string>? feedsFromNugetConfigs)
{
var fallbackFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.FallbackNugetFeeds).ToHashSet();
if (fallbackFeeds.Count == 0)
{
fallbackFeeds.Add(PublicNugetOrgFeed);
logger.LogInfo($"No fallback NuGet feeds specified. Adding default feed: {PublicNugetOrgFeed}");
var shouldAddNugetConfigFeeds = EnvironmentVariables.GetBooleanOptOut(EnvironmentVariableNames.AddNugetConfigFeedsToFallback);
logger.LogInfo($"Adding feeds from nuget.config to fallback restore: {shouldAddNugetConfigFeeds}");
if (shouldAddNugetConfigFeeds && feedsFromNugetConfigs?.Count > 0)
{
// There are some feeds in `feedsFromNugetConfigs` that have already been checked for reachability, we could skip those.
// But we might use different responsiveness testing settings when we try them in the fallback logic, so checking them again is safer.
fallbackFeeds.UnionWith(feedsFromNugetConfigs);
logger.LogInfo($"Using NuGet feeds from nuget.config files as fallback feeds: {string.Join(", ", feedsFromNugetConfigs.OrderBy(f => f))}");
}
}
var reachableFallbackFeeds = GetReachableNuGetFeeds(fallbackFeeds, isFallback: true, out var _);
compilationInfoContainer.CompilationInfos.Add(("Reachable fallback NuGet feed count", reachableFallbackFeeds.Count.ToString()));
return reachableFallbackFeeds;
}
/// <summary>
/// Executes `dotnet restore` on all solution files in solutions.
@@ -239,7 +321,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
var projects = fileProvider.Solutions.SelectMany(solution =>
{
logger.LogInfo($"Restoring solution {solution}...");
var nugetSources = feedManager.MakeRestoreSourcesArgument(solution, reachableFeeds);
var nugetSources = MakeRestoreSourcesArgument(solution, reachableFeeds);
var res = dotnet.Restore(new(solution, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows));
if (res.Success)
{
@@ -264,6 +346,57 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
return projects;
}
private string FeedsToRestoreArgument(IEnumerable<string> feeds)
{
// If there are no feeds, we want to override any default feeds that `dotnet restore` would use by passing a dummy source argument.
if (!feeds.Any())
{
return $" -s \"{emptyPackageDirectory.DirInfo.FullName}\"";
}
// Add package sources. If any are present, they override all sources specified in
// the configuration file(s).
var feedArgs = new StringBuilder();
foreach (var feed in feeds)
{
feedArgs.Append($" -s \"{feed}\"");
}
return feedArgs.ToString();
}
/// <summary>
/// Constructs the list of NuGet sources to use for this restore.
/// (1) Use the feeds we get from `dotnet nuget list source`
/// (2) Use private registries, if they are configured
/// </summary>
/// <param name="path">Path to project/solution</param>
/// <param name="reachableFeeds">The set of reachable NuGet feeds.</param>
/// <returns>A string representing the NuGet sources argument for the restore command.</returns>
private string? MakeRestoreSourcesArgument(string path, HashSet<string> reachableFeeds)
{
// Do not construct an set of explicit NuGet sources to use for restore.
if (!checkNugetFeedResponsiveness && !hasPrivateRegistryFeeds)
{
return null;
}
// Find the path specific feeds.
var folder = GetDirectoryName(path);
var feedsToConsider = folder is not null ? GetFeeds(() => dotnet.GetNugetFeedsFromFolder(folder)).ToHashSet() : [];
if (hasPrivateRegistryFeeds)
{
feedsToConsider.UnionWith(privateRegistryFeeds);
}
var feedsToUse = checkNugetFeedResponsiveness
? feedsToConsider.Where(reachableFeeds.Contains)
: feedsToConsider;
return FeedsToRestoreArgument(feedsToUse);
}
/// <summary>
/// Executes `dotnet restore` on all projects in projects.
/// This is done in parallel for performance reasons.
@@ -288,7 +421,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
foreach (var project in projectGroup)
{
logger.LogInfo($"Restoring project {project}...");
var nugetSources = feedManager.MakeRestoreSourcesArgument(project, reachableFeeds);
var nugetSources = MakeRestoreSourcesArgument(project, reachableFeeds);
var res = dotnet.Restore(new(project, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows));
assets.AddDependenciesRange(res.AssetsFilePaths);
lock (sync)
@@ -317,9 +450,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
private AssemblyLookupLocation? DownloadMissingPackagesFromSpecificFeeds(IEnumerable<string> usedPackageNames, HashSet<string>? feedsFromNugetConfigs)
{
var reachableFallbackFeeds = feedManager.GetReachableFallbackNugetFeeds(feedsFromNugetConfigs);
compilationInfoContainer.CompilationInfos.Add(("Reachable fallback NuGet feed count", reachableFallbackFeeds.Count.ToString()));
var reachableFallbackFeeds = GetReachableFallbackNugetFeeds(feedsFromNugetConfigs);
if (reachableFallbackFeeds.Count > 0)
{
return DownloadMissingPackages(usedPackageNames, fallbackNugetFeeds: reachableFallbackFeeds);
@@ -605,6 +736,147 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
}
private static async Task<HttpResponseMessage> ExecuteGetRequest(string address, HttpClient httpClient, CancellationToken cancellationToken)
{
return await httpClient.GetAsync(address, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
}
private bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount, out bool isTimeout)
{
logger.LogInfo($"Checking if NuGet feed '{feed}' is reachable...");
// Configure the HttpClient to be aware of the Dependabot Proxy, if used.
HttpClientHandler httpClientHandler = new();
if (dependabotProxy != null)
{
httpClientHandler.Proxy = new WebProxy(dependabotProxy.Address);
if (dependabotProxy.Certificate != null)
{
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, _) =>
{
if (chain is null || cert is null)
{
var msg = cert is null && chain is null
? "certificate and chain"
: chain is null
? "chain"
: "certificate";
logger.LogWarning($"Dependabot proxy certificate validation failed due to missing {msg}");
return false;
}
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
chain.ChainPolicy.CustomTrustStore.Add(dependabotProxy.Certificate);
return chain.Build(cert);
};
}
}
using HttpClient client = new(httpClientHandler);
isTimeout = false;
for (var i = 0; i < tryCount; i++)
{
using var cts = new CancellationTokenSource();
cts.CancelAfter(timeoutMilliSeconds);
try
{
logger.LogInfo($"Attempt {i + 1}/{tryCount} to reach NuGet feed '{feed}'.");
using var response = ExecuteGetRequest(feed, client, cts.Token).GetAwaiter().GetResult();
response.EnsureSuccessStatusCode();
logger.LogInfo($"Querying NuGet feed '{feed}' succeeded.");
return true;
}
catch (Exception exc)
{
if (exc is TaskCanceledException tce &&
tce.CancellationToken == cts.Token &&
cts.Token.IsCancellationRequested)
{
logger.LogInfo($"Didn't receive answer from NuGet feed '{feed}' in {timeoutMilliSeconds}ms.");
timeoutMilliSeconds *= 2;
continue;
}
logger.LogInfo($"Querying NuGet feed '{feed}' failed. The reason for the failure: {exc.Message}");
return false;
}
}
logger.LogWarning($"Didn't receive answer from NuGet feed '{feed}'. Tried it {tryCount} times.");
isTimeout = true;
return false;
}
private (int initialTimeout, int tryCount) GetFeedRequestSettings(bool isFallback)
{
int timeoutMilliSeconds = isFallback && int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessInitialTimeoutForFallback), out timeoutMilliSeconds)
? timeoutMilliSeconds
: int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessInitialTimeout), out timeoutMilliSeconds)
? timeoutMilliSeconds
: 1000;
logger.LogDebug($"Initial timeout for NuGet feed reachability check is {timeoutMilliSeconds}ms.");
int tryCount = isFallback && int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessRequestCountForFallback), out tryCount)
? tryCount
: int.TryParse(Environment.GetEnvironmentVariable(EnvironmentVariableNames.NugetFeedResponsivenessRequestCount), out tryCount)
? tryCount
: 4;
logger.LogDebug($"Number of tries for NuGet feed reachability check is {tryCount}.");
return (timeoutMilliSeconds, tryCount);
}
/// <summary>
/// Retrieves a list of excluded NuGet feeds from the corresponding environment variable.
/// </summary>
private HashSet<string> GetExcludedFeeds()
{
var excludedFeeds = EnvironmentVariables.GetURLs(EnvironmentVariableNames.ExcludedNugetFeedsFromResponsivenessCheck)
.ToHashSet();
if (excludedFeeds.Count > 0)
{
logger.LogInfo($"Excluded NuGet feeds from responsiveness check: {string.Join(", ", excludedFeeds.OrderBy(f => f))}");
}
return excludedFeeds;
}
/// <summary>
/// Checks that we can connect to the specified NuGet feeds.
/// </summary>
/// <param name="feeds">The set of package feeds to check.</param>
/// <param name="reachableFeeds">The list of feeds that were reachable.</param>
/// <returns>
/// True if there is a timeout when trying to reach the feeds (excluding any feeds that are configured
/// to be excluded from the check) or false otherwise.
/// </returns>
private bool CheckSpecifiedFeeds(HashSet<string> feeds, out HashSet<string> reachableFeeds)
{
// Exclude any feeds from the feed check that are configured by the corresponding environment variable.
// These feeds are always assumed to be reachable.
var excludedFeeds = GetExcludedFeeds();
HashSet<string> feedsToCheck = feeds.Where(feed =>
{
if (excludedFeeds.Contains(feed))
{
logger.LogInfo($"Not checking reachability of NuGet feed '{feed}' as it is in the list of excluded feeds.");
return false;
}
return true;
}).ToHashSet();
reachableFeeds = GetReachableNuGetFeeds(feedsToCheck, isFallback: false, out var isTimeout).ToHashSet();
// Always consider feeds excluded for the reachability check as reachable.
reachableFeeds.UnionWith(feeds.Where(feed => excludedFeeds.Contains(feed)));
return isTimeout;
}
/// <summary>
/// If <paramref name="allFeedsReachable"/> is `false`, logs this and emits a diagnostic.
/// Adds a `CompilationInfos` entry either way.
@@ -627,15 +899,56 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
compilationInfoContainer.CompilationInfos.Add(("All NuGet feeds reachable", allFeedsReachable ? "1" : "0"));
}
private void EmitNugetConfigDiagnostics()
private IEnumerable<string> GetFeeds(Func<IList<string>> getNugetFeeds)
{
var results = getNugetFeeds();
var regex = EnabledNugetFeed();
foreach (var result in results)
{
var match = regex.Match(result);
if (!match.Success)
{
logger.LogError($"Failed to parse feed from '{result}'");
continue;
}
var url = match.Groups[1].Value;
if (!url.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase) &&
!url.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
{
logger.LogInfo($"Skipping feed '{url}' as it is not a valid URL.");
continue;
}
if (!string.IsNullOrWhiteSpace(url))
{
yield return url;
}
}
}
private string? GetDirectoryName(string path)
{
try
{
return new FileInfo(path).Directory?.FullName;
}
catch (Exception exc)
{
logger.LogWarning($"Failed to get directory of '{path}': {exc}");
}
return null;
}
private (HashSet<string> explicitFeeds, HashSet<string> allFeeds) GetAllFeeds()
{
var nugetConfigs = fileProvider.NugetConfigs;
// On systems with case-sensitive file systems (for simplicity, we assume that is Linux), the
// filenames of NuGet configuration files must be named correctly. For compatibility with projects
// that are typically built on Windows or macOS where this doesn't matter, we accept all variants
// of `nuget.config` ourselves. However, `dotnet` does not. If we detect that incorrectly-named
// files are present, we emit a diagnostic to warn the user.
var nugetConfigs = fileProvider.NugetConfigs;
if (SystemBuildActions.Instance.IsLinux())
{
string[] acceptedNugetConfigNames = ["nuget.config", "NuGet.config", "NuGet.Config"];
@@ -665,6 +978,53 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
));
}
}
// Find feeds that are explicitly configured in the NuGet configuration files that we found.
var explicitFeeds = nugetConfigs
.SelectMany(config => GetFeeds(() => dotnet.GetNugetFeeds(config)))
.ToHashSet();
if (explicitFeeds.Count > 0)
{
logger.LogInfo($"Found {explicitFeeds.Count} NuGet feeds in nuget.config files: {string.Join(", ", explicitFeeds.OrderBy(f => f))}");
}
else
{
logger.LogDebug("No NuGet feeds found in nuget.config files.");
}
// If private package registries are configured for C#, then consider those
// in addition to the ones that are configured in `nuget.config` files.
if (hasPrivateRegistryFeeds)
{
logger.LogInfo($"Found {privateRegistryFeeds.Count} private registry feeds configured for C#: {string.Join(", ", privateRegistryFeeds.OrderBy(f => f))}");
explicitFeeds.UnionWith(privateRegistryFeeds);
}
HashSet<string> allFeeds = [];
// Add all explicitFeeds to the set of all feeds.
allFeeds.UnionWith(explicitFeeds);
// Obtain the list of feeds from the root source directory.
// If a NuGet file is present it will be respected, otherwise we will just get the machine/environment specific feeds.
var nugetFeedsFromRoot = GetFeeds(() => dotnet.GetNugetFeedsFromFolder(fileProvider.SourceDir.FullName));
allFeeds.UnionWith(nugetFeedsFromRoot);
if (nugetConfigs.Count > 0)
{
var nugetConfigFeeds = nugetConfigs
.Select(GetDirectoryName)
.Where(folder => folder != null)
.SelectMany(folder => GetFeeds(() => dotnet.GetNugetFeedsFromFolder(folder!)))
.ToHashSet();
allFeeds.UnionWith(nugetConfigFeeds);
}
logger.LogInfo($"Found {allFeeds.Count} NuGet feeds (with inherited ones) in nuget.config files: {string.Join(", ", allFeeds.OrderBy(f => f))}");
return (explicitFeeds, allFeeds);
}
[GeneratedRegex(@"<TargetFramework>.*</TargetFramework>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
@@ -676,12 +1036,15 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
[GeneratedRegex(@"^(.+)\.(\d+\.\d+\.\d+(-(.+))?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
private static partial Regex LegacyNugetPackage();
[GeneratedRegex(@"^E\s(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
private static partial Regex EnabledNugetFeed();
public void Dispose()
{
PackageDirectory?.Dispose();
legacyPackageDirectory?.Dispose();
missingPackageDirectory?.Dispose();
feedManager.Dispose();
emptyPackageDirectory?.Dispose();
}
/// <summary>

View File

@@ -40,7 +40,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
return new NugetExeWrapper(fileProvider, packageDirectory, logger, useDefaultFeed);
}
return new NoOpPackagesConfig(fileProvider.PackagesConfigs, logger);
return new NoOpPackagesConfig(fileProvider, logger);
}
/// <summary>
@@ -302,7 +302,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
private void AddDefaultPackageSource(string nugetConfig)
{
logger.LogInfo("Adding default package source...");
RunMonoNugetCommand($"sources add -Name DefaultNugetOrg -Source {FeedManager.PublicNugetOrgFeed} -ConfigFile \"{nugetConfig}\"", out _);
RunMonoNugetCommand($"sources add -Name DefaultNugetOrg -Source {NugetPackageRestorer.PublicNugetOrgFeed} -ConfigFile \"{nugetConfig}\"", out _);
}
public void Dispose()
@@ -343,15 +343,15 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
private class NoOpPackagesConfig : IPackagesConfigRestore
{
private readonly Semmle.Util.Logging.ILogger logger;
private readonly ICollection<string> packagesConfigs;
private readonly FileProvider fileProvider;
public NoOpPackagesConfig(ICollection<string> packagesConfigs, Semmle.Util.Logging.ILogger logger)
public NoOpPackagesConfig(FileProvider fileProvider, Semmle.Util.Logging.ILogger logger)
{
this.packagesConfigs = packagesConfigs;
this.fileProvider = fileProvider;
this.logger = logger;
}
public int PackageCount => packagesConfigs.Count;
public int PackageCount => fileProvider.PackagesConfigs.Count;
public int InstallPackages()
{

View File

@@ -75,8 +75,7 @@ module Ast implements AstSig<Location> {
additional predicate skipControlFlow(AstNode e) {
e instanceof TypeAccess and
not e instanceof TypeAccessPatternExpr and
not any(CS::SpecificCatchClause sc).getTypeAccess() = e
not e instanceof TypeAccessPatternExpr
or
not e.getFile().fromSource()
}
@@ -221,12 +220,7 @@ module Ast implements AstSig<Location> {
final private class FinalCatchClause = CS::CatchClause;
class CatchClause extends FinalCatchClause {
AstNode getPattern() {
result = this.(CS::SpecificCatchClause).getVariableDeclExpr() or
result = this.(CS::SpecificCatchClause).getTypeAccess()
}
AstNode getVariable() { none() }
AstNode getVariable() { result = this.(CS::SpecificCatchClause).getVariableDeclExpr() }
Expr getCondition() { result = this.getFilterClause() }

View File

@@ -447,13 +447,13 @@
| ExitMethods.cs:20:10:20:11 | Entry | ExitMethods.cs:20:10:20:11 | Exit | 8 |
| ExitMethods.cs:26:10:26:11 | Entry | ExitMethods.cs:26:10:26:11 | Exit | 8 |
| ExitMethods.cs:32:10:32:11 | Entry | ExitMethods.cs:32:10:32:11 | Exit | 8 |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:16:44:32 | access to type ArgumentException | 10 |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:9:47:9 | catch (...) {...} | 9 |
| ExitMethods.cs:38:10:38:11 | Exit | ExitMethods.cs:38:10:38:11 | Exit | 1 |
| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:38:10:38:11 | Normal Exit | 1 |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:46:13:46:19 | return ...; | 4 |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:48:16:48:24 | access to type Exception | 4 |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:50:13:50:19 | return ...; | 4 |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:38:10:38:11 | Exceptional Exit | 3 |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:46:13:46:19 | return ...; | 4 |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | catch (...) {...} | 2 |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:50:13:50:19 | return ...; | 4 |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | Exceptional Exit | 2 |
| ExitMethods.cs:54:10:54:11 | Entry | ExitMethods.cs:54:10:54:11 | Exit | 7 |
| ExitMethods.cs:60:10:60:11 | Entry | ExitMethods.cs:60:10:60:11 | Exit | 7 |
| ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:68:13:68:13 | access to parameter b | 5 |
@@ -508,13 +508,13 @@
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:19:10:19:11 | Normal Exit | 1 |
| Finally.cs:21:9:51:9 | After try {...} ... | Finally.cs:20:5:52:5 | After {...} | 2 |
| Finally.cs:23:13:23:37 | After call to method WriteLine | Finally.cs:24:13:24:19 | return ...; | 4 |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | IOException ex | 2 |
| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:28:13:28:18 | throw ...; | 6 |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:30:41:30:42 | ArgumentException ex | 4 |
| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:38:17:38:44 | throw ...; | 16 |
| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:41:16:41:24 | access to type Exception | 4 |
| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:42:9:43:9 | {...} | 2 |
| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:46:13:46:19 | return ...; | 6 |
| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:28:13:28:18 | throw ...; | 7 |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | catch (...) {...} | 2 |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | catch (...) {...} | 1 |
| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:38:17:38:44 | throw ...; | 17 |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | catch (...) {...} | 2 |
| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:42:9:43:9 | {...} | 2 |
| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:46:13:46:19 | return ...; | 6 |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:49:9:51:9 | After {...} | 8 |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:58:13:58:37 | call to method WriteLine | 8 |
| Finally.cs:54:10:54:11 | Exceptional Exit | Finally.cs:54:10:54:11 | Exceptional Exit | 1 |
@@ -522,12 +522,11 @@
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:54:10:54:11 | Normal Exit | 1 |
| Finally.cs:56:9:71:9 | After try {...} ... | Finally.cs:55:5:72:5 | After {...} | 2 |
| Finally.cs:58:13:58:37 | After call to method WriteLine | Finally.cs:59:13:59:19 | return ...; | 4 |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | IOException ex | 2 |
| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:63:13:63:18 | throw ...; | 6 |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:26:65:26 | Exception e | 4 |
| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:63:13:63:18 | throw ...; | 7 |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | catch (...) {...} | 2 |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | catch (...) {...} | 1 |
| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:35:65:51 | ... != ... | 9 |
| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | 1 |
| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | ... != ... | 8 |
| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] | 1 |
| Finally.cs:65:35:65:51 | After ... != ... [false] | Finally.cs:65:35:65:51 | After ... != ... [false] | 1 |
| Finally.cs:65:35:65:51 | After ... != ... [true] | Finally.cs:66:9:67:9 | {...} | 2 |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:69:9:71:9 | After {...} | 8 |
@@ -590,10 +589,9 @@
| Finally.cs:158:21:158:36 | After ... == ... [false] | Finally.cs:157:13:160:13 | After {...} | 3 |
| Finally.cs:158:21:158:36 | After ... == ... [true] | Finally.cs:159:27:159:44 | object creation of type Exception | 5 |
| Finally.cs:159:27:159:44 | After object creation of type Exception | Finally.cs:159:21:159:45 | throw ...; | 2 |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:166:13:168:13 | After {...} | 10 |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | Exception e | 2 |
| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | ... == ... | 8 |
| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:161:30:161:30 | After Exception e [no-match] | 1 |
| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:39:161:54 | ... == ... | 9 |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:166:13:168:13 | After {...} | 11 |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | catch (...) {...} | 1 |
| Finally.cs:161:39:161:54 | After ... == ... [false] | Finally.cs:161:39:161:54 | After ... == ... [false] | 1 |
| Finally.cs:161:39:161:54 | After ... == ... [true] | Finally.cs:162:13:164:13 | After {...} | 13 |
| Finally.cs:172:11:172:20 | Entry | Finally.cs:172:11:172:20 | Exit | 11 |
@@ -611,10 +609,9 @@
| Finally.cs:186:21:186:22 | After access to parameter b2 [false] | Finally.cs:185:13:187:13 | After {...} | 3 |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:186:31:186:46 | object creation of type ExceptionB | 4 |
| Finally.cs:186:31:186:46 | After object creation of type ExceptionB | Finally.cs:186:25:186:47 | throw ...; | 2 |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | access to parameter b2 | 2 |
| Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | 1 |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | access to type ExceptionB | 2 |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | access to parameter b2 | 2 |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | 1 |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | catch (...) {...} | 1 |
| Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | 1 |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | access to parameter b1 | 4 |
| Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:189:13:191:13 | After {...} | 3 |
@@ -636,7 +633,7 @@
| Finally.cs:209:21:209:22 | After access to parameter b3 [true] | Finally.cs:209:25:209:47 | throw ...; | 6 |
| Finally.cs:216:10:216:12 | Entry | Finally.cs:220:13:220:36 | call to method WriteLine | 8 |
| Finally.cs:220:13:220:36 | After call to method WriteLine | Finally.cs:219:9:221:9 | After {...} | 3 |
| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:223:9:225:9 | After {...} | 9 |
| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:223:9:225:9 | After {...} | 10 |
| Finally.cs:227:9:229:9 | {...} | Finally.cs:216:10:216:12 | Exit | 18 |
| Finally.cs:233:10:233:12 | Entry | Finally.cs:239:21:239:22 | access to parameter b1 | 10 |
| Finally.cs:233:10:233:12 | Exceptional Exit | Finally.cs:233:10:233:12 | Exceptional Exit | 1 |

View File

@@ -264,12 +264,12 @@ conditionBlock
| DefaultParam.cs:3:12:3:13 | Entry | DefaultParam.cs:3:30:3:30 | After s [no-match] | false |
| DefaultParam.cs:3:42:3:42 | i | DefaultParam.cs:3:42:3:42 | After i [match] | true |
| DefaultParam.cs:3:42:3:42 | i | DefaultParam.cs:3:42:3:42 | After i [no-match] | false |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | true |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | false |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | false |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | false |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | true |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | false |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | true |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | false |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | false |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | false |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | true |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | false |
| ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:68:13:68:13 | After access to parameter b [false] | false |
| ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:68:13:68:13 | After access to parameter b [true] | true |
| ExitMethods.cs:72:17:72:27 | Entry | ExitMethods.cs:74:13:74:13 | After access to parameter b [false] | false |
@@ -280,31 +280,29 @@ conditionBlock
| ExitMethods.cs:115:16:115:34 | Entry | ExitMethods.cs:117:16:117:30 | After call to method Contains [true] | true |
| ExitMethods.cs:140:17:140:42 | Entry | ExitMethods.cs:142:13:142:13 | After access to parameter b [false] | false |
| ExitMethods.cs:140:17:140:42 | Entry | ExitMethods.cs:142:13:142:13 | After access to parameter b [true] | true |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | After IOException ex [match] | true |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | After IOException ex [no-match] | false |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | false |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | false |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:16:41:24 | After access to type Exception [match] | false |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | false |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | true |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | false |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [match] | false |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | false |
| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [match] | true |
| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | false |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | After IOException ex [match] | true |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | After IOException ex [no-match] | false |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | true |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | false |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | false |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | false |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | true |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | false |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | false |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | false |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | false |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | false |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | true |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | false |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | true |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] | true |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [true] | true |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | true |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | false |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | false |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | false |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:26:65:26 | After Exception e [match] | false |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:26:65:26 | After Exception e [no-match] | false |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:35:65:51 | After ... != ... [false] | false |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:35:65:51 | After ... != ... [true] | false |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:26:65:26 | After Exception e [match] | true |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] | false |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] | true |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:35:65:51 | After ... != ... [true] | true |
| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | After ... != ... [false] | false |
| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | After ... != ... [true] | true |
| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:35:65:51 | After ... != ... [false] | false |
| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:35:65:51 | After ... != ... [true] | true |
| Finally.cs:77:9:100:9 | [LoopHeader] while (...) ... | Finally.cs:74:10:74:11 | Exceptional Exit | true |
| Finally.cs:77:9:100:9 | [LoopHeader] while (...) ... | Finally.cs:77:16:77:20 | After ... > ... [false] | false |
| Finally.cs:77:9:100:9 | [LoopHeader] while (...) ... | Finally.cs:77:16:77:20 | After ... > ... [true] | true |
@@ -356,36 +354,33 @@ conditionBlock
| Finally.cs:158:21:158:31 | After access to property Length | Finally.cs:158:21:158:36 | After ... == ... [false] | false |
| Finally.cs:158:21:158:31 | After access to property Length | Finally.cs:158:21:158:36 | After ... == ... [true] | true |
| Finally.cs:158:21:158:31 | After access to property Length | Finally.cs:159:27:159:44 | After object creation of type Exception | true |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | After Exception e [match] | true |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | After Exception e [no-match] | false |
| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:39:161:54 | After ... == ... [false] | false |
| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:39:161:54 | After ... == ... [true] | true |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | true |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:39:161:54 | After ... == ... [false] | true |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:39:161:54 | After ... == ... [true] | true |
| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | After ... == ... [false] | false |
| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | After ... == ... [true] | true |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:180:17:180:18 | After access to parameter b1 [false] | false |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:180:17:180:18 | After access to parameter b1 [true] | true |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:180:27:180:42 | After object creation of type ExceptionA | true |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:186:21:186:22 | After access to parameter b2 [false] | false |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:186:21:186:22 | After access to parameter b2 [true] | true |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | true |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | true |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | true |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | catch (...) {...} | true |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | true |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | true |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | true |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | true |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | true |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | true |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | true |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | false |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | false |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | true |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | true |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | true |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | true |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | true |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | true |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | true |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | true |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | false |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] | true |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | true |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | true |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] | false |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] | true |
| Finally.cs:195:10:195:12 | Entry | Finally.cs:199:17:199:18 | After access to parameter b1 [false] | false |

View File

@@ -2816,20 +2816,16 @@ dominance
| ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | ExitMethods.cs:44:9:47:9 | catch (...) {...} |
| ExitMethods.cs:42:13:42:31 | ...; | ExitMethods.cs:42:13:42:30 | Before call to method ErrorAlways |
| ExitMethods.cs:42:25:42:29 | false | ExitMethods.cs:42:13:42:30 | call to method ErrorAlways |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:45:9:47:9 | {...} |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | catch (...) {...} |
| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:16:44:32 | access to type ArgumentException |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:45:9:47:9 | {...} |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] |
| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] |
| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] |
| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:46:13:46:19 | Before return ...; |
| ExitMethods.cs:46:13:46:19 | Before return ...; | ExitMethods.cs:46:13:46:19 | return ...; |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:49:9:51:9 | {...} |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | Exceptional Exit |
| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:16:48:24 | access to type Exception |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:49:9:51:9 | {...} |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] |
| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] |
| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] |
| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:50:13:50:19 | Before return ...; |
| ExitMethods.cs:50:13:50:19 | Before return ...; | ExitMethods.cs:50:13:50:19 | return ...; |
| ExitMethods.cs:54:10:54:11 | Entry | ExitMethods.cs:55:5:58:5 | {...} |
@@ -3128,22 +3124,20 @@ dominance
| Finally.cs:23:13:23:38 | After ...; | Finally.cs:24:13:24:19 | Before return ...; |
| Finally.cs:23:31:23:36 | "Try2" | Finally.cs:23:13:23:37 | call to method WriteLine |
| Finally.cs:24:13:24:19 | Before return ...; | Finally.cs:24:13:24:19 | return ...; |
| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:26:38:26:39 | IOException ex |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | catch (...) {...} |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | IOException ex |
| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:26:48:26:51 | true |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] |
| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:38:26:39 | After IOException ex [match] |
| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:38:26:39 | After IOException ex [no-match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] |
| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:48:26:51 | true |
| Finally.cs:26:48:26:51 | After true [true] | Finally.cs:27:9:29:9 | {...} |
| Finally.cs:26:48:26:51 | true | Finally.cs:26:48:26:51 | After true [true] |
| Finally.cs:27:9:29:9 | {...} | Finally.cs:28:13:28:18 | Before throw ...; |
| Finally.cs:28:13:28:18 | Before throw ...; | Finally.cs:28:13:28:18 | throw ...; |
| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:30:41:30:42 | ArgumentException ex |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | catch (...) {...} |
| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:41:30:42 | ArgumentException ex |
| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:31:9:40:9 | {...} |
| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] |
| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:41:30:42 | After ArgumentException ex [match] |
| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] |
| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [match] |
| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] |
| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:31:9:40:9 | {...} |
| Finally.cs:31:9:40:9 | {...} | Finally.cs:32:13:39:13 | try {...} ... |
| Finally.cs:32:13:39:13 | try {...} ... | Finally.cs:33:13:35:13 | {...} |
| Finally.cs:33:13:35:13 | {...} | Finally.cs:34:17:34:32 | if (...) ... |
@@ -3158,13 +3152,12 @@ dominance
| Finally.cs:38:23:38:43 | Before object creation of type Exception | Finally.cs:38:37:38:42 | "Boo!" |
| Finally.cs:38:23:38:43 | object creation of type Exception | Finally.cs:38:23:38:43 | After object creation of type Exception |
| Finally.cs:38:37:38:42 | "Boo!" | Finally.cs:38:23:38:43 | object creation of type Exception |
| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:42:9:43:9 | {...} |
| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:44:9:47:9 | catch {...} |
| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:16:41:24 | access to type Exception |
| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:42:9:43:9 | {...} |
| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] |
| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:16:41:24 | After access to type Exception [match] |
| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:16:41:24 | After access to type Exception [no-match] |
| Finally.cs:44:9:47:9 | catch {...} | Finally.cs:45:9:47:9 | {...} |
| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] |
| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] |
| Finally.cs:44:9:47:9 | After catch {...} [match] | Finally.cs:45:9:47:9 | {...} |
| Finally.cs:44:9:47:9 | catch {...} | Finally.cs:44:9:47:9 | After catch {...} [match] |
| Finally.cs:45:9:47:9 | {...} | Finally.cs:46:13:46:19 | Before return ...; |
| Finally.cs:46:13:46:19 | Before return ...; | Finally.cs:46:13:46:19 | return ...; |
| Finally.cs:49:9:51:9 | After {...} | Finally.cs:19:10:19:11 | Exceptional Exit |
@@ -3190,20 +3183,19 @@ dominance
| Finally.cs:58:13:58:38 | After ...; | Finally.cs:59:13:59:19 | Before return ...; |
| Finally.cs:58:31:58:36 | "Try3" | Finally.cs:58:13:58:37 | call to method WriteLine |
| Finally.cs:59:13:59:19 | Before return ...; | Finally.cs:59:13:59:19 | return ...; |
| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:61:38:61:39 | IOException ex |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | catch (...) {...} |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | IOException ex |
| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:61:48:61:51 | true |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] |
| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:38:61:39 | After IOException ex [match] |
| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:38:61:39 | After IOException ex [no-match] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [match] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] |
| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:48:61:51 | true |
| Finally.cs:61:48:61:51 | After true [true] | Finally.cs:62:9:64:9 | {...} |
| Finally.cs:61:48:61:51 | true | Finally.cs:61:48:61:51 | After true [true] |
| Finally.cs:62:9:64:9 | {...} | Finally.cs:63:13:63:18 | Before throw ...; |
| Finally.cs:63:13:63:18 | Before throw ...; | Finally.cs:63:13:63:18 | throw ...; |
| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:26:65:26 | Exception e |
| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | Before ... != ... |
| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:26:65:26 | After Exception e [match] |
| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:26:65:26 | After Exception e [no-match] |
| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:26:65:26 | Exception e |
| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [match] |
| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] |
| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:35:65:51 | Before ... != ... |
| Finally.cs:65:35:65:35 | access to local variable e | Finally.cs:65:35:65:43 | access to property Message |
| Finally.cs:65:35:65:43 | After access to property Message | Finally.cs:65:48:65:51 | null |
| Finally.cs:65:35:65:43 | Before access to property Message | Finally.cs:65:35:65:35 | access to local variable e |
@@ -3476,11 +3468,11 @@ dominance
| Finally.cs:159:27:159:44 | Before object creation of type Exception | Finally.cs:159:41:159:43 | "1" |
| Finally.cs:159:27:159:44 | object creation of type Exception | Finally.cs:159:27:159:44 | After object creation of type Exception |
| Finally.cs:159:41:159:43 | "1" | Finally.cs:159:27:159:44 | object creation of type Exception |
| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:30:161:30 | Exception e |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:165:13:168:13 | catch {...} |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | Exception e |
| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | Before ... == ... |
| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:30:161:30 | After Exception e [match] |
| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:30:161:30 | After Exception e [no-match] |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [match] |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] |
| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:39:161:54 | Before ... == ... |
| Finally.cs:161:39:161:39 | access to local variable e | Finally.cs:161:39:161:47 | access to property Message |
| Finally.cs:161:39:161:47 | After access to property Message | Finally.cs:161:52:161:54 | "1" |
| Finally.cs:161:39:161:47 | Before access to property Message | Finally.cs:161:39:161:39 | access to local variable e |
@@ -3501,7 +3493,8 @@ dominance
| Finally.cs:163:35:163:41 | Before access to array element | Finally.cs:163:35:163:38 | access to parameter args |
| Finally.cs:163:35:163:41 | access to array element | Finally.cs:163:35:163:41 | After access to array element |
| Finally.cs:163:40:163:40 | 0 | Finally.cs:163:35:163:41 | access to array element |
| Finally.cs:165:13:168:13 | catch {...} | Finally.cs:166:13:168:13 | {...} |
| Finally.cs:165:13:168:13 | After catch {...} [match] | Finally.cs:166:13:168:13 | {...} |
| Finally.cs:165:13:168:13 | catch {...} | Finally.cs:165:13:168:13 | After catch {...} [match] |
| Finally.cs:166:13:168:13 | {...} | Finally.cs:167:17:167:38 | ...; |
| Finally.cs:167:17:167:37 | After call to method WriteLine | Finally.cs:167:17:167:38 | After ...; |
| Finally.cs:167:17:167:37 | Before call to method WriteLine | Finally.cs:167:35:167:36 | "" |
@@ -3573,10 +3566,9 @@ dominance
| Finally.cs:186:31:186:46 | Before object creation of type ExceptionB | Finally.cs:186:31:186:46 | object creation of type ExceptionB |
| Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | access to type ExceptionB |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | access to parameter b2 |
| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | access to parameter b2 |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:189:13:191:13 | {...} |
| Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:38:188:39 | After access to parameter b2 [false] |
| Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
@@ -3671,7 +3663,8 @@ dominance
| Finally.cs:220:13:220:37 | ...; | Finally.cs:220:13:220:36 | Before call to method WriteLine |
| Finally.cs:220:13:220:37 | After ...; | Finally.cs:219:9:221:9 | After {...} |
| Finally.cs:220:31:220:35 | "Try" | Finally.cs:220:13:220:36 | call to method WriteLine |
| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:223:9:225:9 | {...} |
| Finally.cs:222:9:225:9 | After catch {...} [match] | Finally.cs:223:9:225:9 | {...} |
| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:222:9:225:9 | After catch {...} [match] |
| Finally.cs:223:9:225:9 | {...} | Finally.cs:224:13:224:39 | ...; |
| Finally.cs:224:13:224:38 | After call to method WriteLine | Finally.cs:224:13:224:39 | After ...; |
| Finally.cs:224:13:224:38 | Before call to method WriteLine | Finally.cs:224:31:224:37 | "Catch" |
@@ -10622,17 +10615,13 @@ postDominance
| ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | ExitMethods.cs:42:25:42:29 | false |
| ExitMethods.cs:42:13:42:31 | ...; | ExitMethods.cs:41:9:43:9 | {...} |
| ExitMethods.cs:42:25:42:29 | false | ExitMethods.cs:42:13:42:30 | Before call to method ErrorAlways |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] |
| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:42:13:42:30 | call to method ErrorAlways |
| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:9:47:9 | catch (...) {...} |
| ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] |
| ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] |
| ExitMethods.cs:46:13:46:19 | Before return ...; | ExitMethods.cs:45:9:47:9 | {...} |
| ExitMethods.cs:46:13:46:19 | return ...; | ExitMethods.cs:46:13:46:19 | Before return ...; |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:48:9:51:9 | catch (...) {...} |
| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:48:16:48:24 | access to type Exception |
| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:9:51:9 | catch (...) {...} |
| ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] |
| ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] |
| ExitMethods.cs:50:13:50:19 | Before return ...; | ExitMethods.cs:49:9:51:9 | {...} |
| ExitMethods.cs:50:13:50:19 | return ...; | ExitMethods.cs:50:13:50:19 | Before return ...; |
| ExitMethods.cs:54:10:54:11 | Exceptional Exit | ExitMethods.cs:56:9:56:22 | call to method ErrorAlways2 |
@@ -10922,17 +10911,15 @@ postDominance
| Finally.cs:23:31:23:36 | "Try2" | Finally.cs:23:13:23:37 | Before call to method WriteLine |
| Finally.cs:24:13:24:19 | Before return ...; | Finally.cs:23:13:23:38 | After ...; |
| Finally.cs:24:13:24:19 | return ...; | Finally.cs:24:13:24:19 | Before return ...; |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:26:38:26:39 | After IOException ex [no-match] |
| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:9:29:9 | catch (...) {...} |
| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:9:29:9 | After catch (...) {...} [match] |
| Finally.cs:26:48:26:51 | After true [true] | Finally.cs:26:48:26:51 | true |
| Finally.cs:26:48:26:51 | true | Finally.cs:26:38:26:39 | After IOException ex [match] |
| Finally.cs:26:48:26:51 | true | Finally.cs:26:38:26:39 | IOException ex |
| Finally.cs:27:9:29:9 | {...} | Finally.cs:26:48:26:51 | After true [true] |
| Finally.cs:28:13:28:18 | Before throw ...; | Finally.cs:27:9:29:9 | {...} |
| Finally.cs:28:13:28:18 | throw ...; | Finally.cs:28:13:28:18 | Before throw ...; |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] |
| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] |
| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:9:40:9 | catch (...) {...} |
| Finally.cs:31:9:40:9 | {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [match] |
| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:9:40:9 | After catch (...) {...} [match] |
| Finally.cs:31:9:40:9 | {...} | Finally.cs:30:41:30:42 | ArgumentException ex |
| Finally.cs:32:13:39:13 | try {...} ... | Finally.cs:31:9:40:9 | {...} |
| Finally.cs:33:13:35:13 | {...} | Finally.cs:32:13:39:13 | try {...} ... |
| Finally.cs:34:17:34:32 | if (...) ... | Finally.cs:33:13:35:13 | {...} |
@@ -10947,12 +10934,11 @@ postDominance
| Finally.cs:38:23:38:43 | Before object creation of type Exception | Finally.cs:38:17:38:44 | Before throw ...; |
| Finally.cs:38:23:38:43 | object creation of type Exception | Finally.cs:38:37:38:42 | "Boo!" |
| Finally.cs:38:37:38:42 | "Boo!" | Finally.cs:38:23:38:43 | Before object creation of type Exception |
| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] |
| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] |
| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:9:43:9 | catch (...) {...} |
| Finally.cs:42:9:43:9 | {...} | Finally.cs:41:16:41:24 | After access to type Exception [match] |
| Finally.cs:42:9:43:9 | {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] |
| Finally.cs:44:9:47:9 | After catch {...} [match] | Finally.cs:44:9:47:9 | catch {...} |
| Finally.cs:44:9:47:9 | catch {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] |
| Finally.cs:45:9:47:9 | {...} | Finally.cs:44:9:47:9 | catch {...} |
| Finally.cs:45:9:47:9 | {...} | Finally.cs:44:9:47:9 | After catch {...} [match] |
| Finally.cs:46:13:46:19 | Before return ...; | Finally.cs:45:9:47:9 | {...} |
| Finally.cs:46:13:46:19 | return ...; | Finally.cs:46:13:46:19 | Before return ...; |
| Finally.cs:49:9:51:9 | After {...} | Finally.cs:50:13:50:41 | After ...; |
@@ -10980,23 +10966,21 @@ postDominance
| Finally.cs:58:31:58:36 | "Try3" | Finally.cs:58:13:58:37 | Before call to method WriteLine |
| Finally.cs:59:13:59:19 | Before return ...; | Finally.cs:58:13:58:38 | After ...; |
| Finally.cs:59:13:59:19 | return ...; | Finally.cs:59:13:59:19 | Before return ...; |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:61:38:61:39 | After IOException ex [no-match] |
| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:9:64:9 | catch (...) {...} |
| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:9:64:9 | After catch (...) {...} [match] |
| Finally.cs:61:48:61:51 | After true [true] | Finally.cs:61:48:61:51 | true |
| Finally.cs:61:48:61:51 | true | Finally.cs:61:38:61:39 | After IOException ex [match] |
| Finally.cs:61:48:61:51 | true | Finally.cs:61:38:61:39 | IOException ex |
| Finally.cs:62:9:64:9 | {...} | Finally.cs:61:48:61:51 | After true [true] |
| Finally.cs:63:13:63:18 | Before throw ...; | Finally.cs:62:9:64:9 | {...} |
| Finally.cs:63:13:63:18 | throw ...; | Finally.cs:63:13:63:18 | Before throw ...; |
| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] |
| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] |
| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:9:67:9 | catch (...) {...} |
| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:9:67:9 | After catch (...) {...} [match] |
| Finally.cs:65:35:65:35 | access to local variable e | Finally.cs:65:35:65:43 | Before access to property Message |
| Finally.cs:65:35:65:43 | After access to property Message | Finally.cs:65:35:65:43 | access to property Message |
| Finally.cs:65:35:65:43 | Before access to property Message | Finally.cs:65:35:65:51 | Before ... != ... |
| Finally.cs:65:35:65:43 | access to property Message | Finally.cs:65:35:65:35 | access to local variable e |
| Finally.cs:65:35:65:51 | ... != ... | Finally.cs:65:48:65:51 | null |
| Finally.cs:65:35:65:51 | Before ... != ... | Finally.cs:65:26:65:26 | After Exception e [match] |
| Finally.cs:65:35:65:51 | Before ... != ... | Finally.cs:65:26:65:26 | Exception e |
| Finally.cs:65:48:65:51 | null | Finally.cs:65:35:65:43 | After access to property Message |
| Finally.cs:66:9:67:9 | {...} | Finally.cs:65:35:65:51 | After ... != ... [true] |
| Finally.cs:69:9:71:9 | After {...} | Finally.cs:70:13:70:41 | After ...; |
@@ -11253,17 +11237,16 @@ postDominance
| Finally.cs:159:27:159:44 | Before object creation of type Exception | Finally.cs:159:21:159:45 | Before throw ...; |
| Finally.cs:159:27:159:44 | object creation of type Exception | Finally.cs:159:41:159:43 | "1" |
| Finally.cs:159:41:159:43 | "1" | Finally.cs:159:27:159:44 | Before object creation of type Exception |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:30:161:30 | After Exception e [no-match] |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:39:161:54 | After ... == ... [false] |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:159:21:159:45 | throw ...; |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:159:27:159:44 | object creation of type Exception |
| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:13:164:13 | catch (...) {...} |
| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:13:164:13 | After catch (...) {...} [match] |
| Finally.cs:161:39:161:39 | access to local variable e | Finally.cs:161:39:161:47 | Before access to property Message |
| Finally.cs:161:39:161:47 | After access to property Message | Finally.cs:161:39:161:47 | access to property Message |
| Finally.cs:161:39:161:47 | Before access to property Message | Finally.cs:161:39:161:54 | Before ... == ... |
| Finally.cs:161:39:161:47 | access to property Message | Finally.cs:161:39:161:39 | access to local variable e |
| Finally.cs:161:39:161:54 | ... == ... | Finally.cs:161:52:161:54 | "1" |
| Finally.cs:161:39:161:54 | Before ... == ... | Finally.cs:161:30:161:30 | After Exception e [match] |
| Finally.cs:161:39:161:54 | Before ... == ... | Finally.cs:161:30:161:30 | Exception e |
| Finally.cs:161:52:161:54 | "1" | Finally.cs:161:39:161:47 | After access to property Message |
| Finally.cs:162:13:164:13 | After {...} | Finally.cs:163:17:163:43 | After ...; |
| Finally.cs:162:13:164:13 | {...} | Finally.cs:161:39:161:54 | After ... == ... [true] |
@@ -11277,9 +11260,10 @@ postDominance
| Finally.cs:163:35:163:41 | Before access to array element | Finally.cs:163:17:163:42 | Before call to method WriteLine |
| Finally.cs:163:35:163:41 | access to array element | Finally.cs:163:40:163:40 | 0 |
| Finally.cs:163:40:163:40 | 0 | Finally.cs:163:35:163:38 | access to parameter args |
| Finally.cs:165:13:168:13 | After catch {...} [match] | Finally.cs:165:13:168:13 | catch {...} |
| Finally.cs:165:13:168:13 | catch {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] |
| Finally.cs:166:13:168:13 | After {...} | Finally.cs:167:17:167:38 | After ...; |
| Finally.cs:166:13:168:13 | {...} | Finally.cs:165:13:168:13 | catch {...} |
| Finally.cs:166:13:168:13 | {...} | Finally.cs:165:13:168:13 | After catch {...} [match] |
| Finally.cs:167:17:167:37 | After call to method WriteLine | Finally.cs:167:17:167:37 | call to method WriteLine |
| Finally.cs:167:17:167:37 | Before call to method WriteLine | Finally.cs:167:17:167:38 | ...; |
| Finally.cs:167:17:167:37 | call to method WriteLine | Finally.cs:167:35:167:36 | "" |
@@ -11348,12 +11332,11 @@ postDominance
| Finally.cs:186:25:186:47 | throw ...; | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:186:31:186:46 | Before object creation of type ExceptionB | Finally.cs:186:25:186:47 | Before throw ...; |
| Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:186:31:186:46 | Before object creation of type ExceptionB |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:186:25:186:47 | throw ...; |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:186:31:186:46 | object creation of type ExceptionB |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:20:188:29 | access to type ExceptionB |
| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:38:188:39 | access to parameter b2 |
| Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:189:13:191:13 | After {...} | Finally.cs:190:17:190:47 | After if (...) ... |
| Finally.cs:189:13:191:13 | {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:190:17:190:47 | After if (...) ... | Finally.cs:190:21:190:22 | After access to parameter b1 [false] |
@@ -11443,8 +11426,9 @@ postDominance
| Finally.cs:220:13:220:37 | ...; | Finally.cs:219:9:221:9 | {...} |
| Finally.cs:220:13:220:37 | After ...; | Finally.cs:220:13:220:36 | After call to method WriteLine |
| Finally.cs:220:31:220:35 | "Try" | Finally.cs:220:13:220:36 | Before call to method WriteLine |
| Finally.cs:222:9:225:9 | After catch {...} [match] | Finally.cs:222:9:225:9 | catch {...} |
| Finally.cs:223:9:225:9 | After {...} | Finally.cs:224:13:224:39 | After ...; |
| Finally.cs:223:9:225:9 | {...} | Finally.cs:222:9:225:9 | catch {...} |
| Finally.cs:223:9:225:9 | {...} | Finally.cs:222:9:225:9 | After catch {...} [match] |
| Finally.cs:224:13:224:38 | After call to method WriteLine | Finally.cs:224:13:224:38 | call to method WriteLine |
| Finally.cs:224:13:224:38 | Before call to method WriteLine | Finally.cs:224:13:224:39 | ...; |
| Finally.cs:224:13:224:38 | call to method WriteLine | Finally.cs:224:31:224:37 | "Catch" |
@@ -17423,18 +17407,18 @@ blockDominance
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:38:10:38:11 | Entry |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:38:10:38:11 | Exit |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:38:10:38:11 | Normal Exit |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] |
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:38:10:38:11 | Exit | ExitMethods.cs:38:10:38:11 | Exit |
| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:38:10:38:11 | Normal Exit |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:54:10:54:11 | Entry | ExitMethods.cs:54:10:54:11 | Entry |
| ExitMethods.cs:60:10:60:11 | Entry | ExitMethods.cs:60:10:60:11 | Entry |
| ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:66:17:66:26 | Entry |
@@ -17518,38 +17502,38 @@ blockDominance
| Finally.cs:19:10:19:11 | Entry | Finally.cs:19:10:19:11 | Normal Exit |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:21:9:51:9 | After try {...} ... |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:23:13:23:37 | After call to method WriteLine |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:26:9:29:9 | After catch (...) {...} [match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:26:9:29:9 | catch (...) {...} |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:26:38:26:39 | After IOException ex [match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:26:38:26:39 | After IOException ex [no-match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:30:41:30:42 | After ArgumentException ex [match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:41:16:41:24 | After access to type Exception [match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:41:16:41:24 | After access to type Exception [no-match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:30:9:40:9 | After catch (...) {...} [match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:41:9:43:9 | After catch (...) {...} [match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] |
| Finally.cs:19:10:19:11 | Entry | Finally.cs:49:9:51:9 | {...} |
| Finally.cs:19:10:19:11 | Exceptional Exit | Finally.cs:19:10:19:11 | Exceptional Exit |
| Finally.cs:19:10:19:11 | Exit | Finally.cs:19:10:19:11 | Exit |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:19:10:19:11 | Normal Exit |
| Finally.cs:21:9:51:9 | After try {...} ... | Finally.cs:21:9:51:9 | After try {...} ... |
| Finally.cs:23:13:23:37 | After call to method WriteLine | Finally.cs:23:13:23:37 | After call to method WriteLine |
| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:26:9:29:9 | After catch (...) {...} [match] |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [match] |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | catch (...) {...} |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | After IOException ex [match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | After IOException ex [no-match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:16:41:24 | After access to type Exception [match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:16:41:24 | After access to type Exception [no-match] |
| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:26:38:26:39 | After IOException ex [match] |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:26:38:26:39 | After IOException ex [no-match] |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [match] |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [match] |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] |
| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:30:41:30:42 | After ArgumentException ex [match] |
| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] |
| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [match] |
| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] |
| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:41:16:41:24 | After access to type Exception [match] |
| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] |
| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:30:9:40:9 | After catch (...) {...} [match] |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] |
| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] |
| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:19:10:19:11 | Exceptional Exit |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:19:10:19:11 | Exit |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:19:10:19:11 | Normal Exit |
@@ -17561,12 +17545,11 @@ blockDominance
| Finally.cs:54:10:54:11 | Entry | Finally.cs:54:10:54:11 | Normal Exit |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:56:9:71:9 | After try {...} ... |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:58:13:58:37 | After call to method WriteLine |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:61:9:64:9 | After catch (...) {...} [match] |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:61:9:64:9 | catch (...) {...} |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:61:38:61:39 | After IOException ex [match] |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:61:38:61:39 | After IOException ex [no-match] |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:65:9:67:9 | After catch (...) {...} [match] |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:65:26:65:26 | After Exception e [match] |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:65:26:65:26 | After Exception e [no-match] |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:65:35:65:51 | After ... != ... [true] |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:69:9:71:9 | {...} |
@@ -17575,26 +17558,23 @@ blockDominance
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:54:10:54:11 | Normal Exit |
| Finally.cs:56:9:71:9 | After try {...} ... | Finally.cs:56:9:71:9 | After try {...} ... |
| Finally.cs:58:13:58:37 | After call to method WriteLine | Finally.cs:58:13:58:37 | After call to method WriteLine |
| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:61:9:64:9 | After catch (...) {...} [match] |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [match] |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [true] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [match] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | catch (...) {...} |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | After IOException ex [match] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | After IOException ex [no-match] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [match] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:26:65:26 | After Exception e [match] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:26:65:26 | After Exception e [no-match] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:65:35:65:51 | After ... != ... [true] |
| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:61:38:61:39 | After IOException ex [match] |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:61:38:61:39 | After IOException ex [no-match] |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:26:65:26 | After Exception e [match] |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:65:35:65:51 | After ... != ... [true] |
| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:9:67:9 | After catch (...) {...} [match] |
| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:35:65:51 | After ... != ... [true] |
| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] |
| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:26:65:26 | After Exception e [match] |
| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | After ... != ... [true] |
| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] |
| Finally.cs:65:35:65:51 | After ... != ... [false] | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:65:35:65:51 | After ... != ... [true] | Finally.cs:65:35:65:51 | After ... != ... [true] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:54:10:54:11 | Exceptional Exit |
@@ -17803,10 +17783,9 @@ blockDominance
| Finally.cs:147:10:147:11 | Entry | Finally.cs:158:21:158:36 | After ... == ... [false] |
| Finally.cs:147:10:147:11 | Entry | Finally.cs:158:21:158:36 | After ... == ... [true] |
| Finally.cs:147:10:147:11 | Entry | Finally.cs:159:27:159:44 | After object creation of type Exception |
| Finally.cs:147:10:147:11 | Entry | Finally.cs:161:13:164:13 | After catch (...) {...} [match] |
| Finally.cs:147:10:147:11 | Entry | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] |
| Finally.cs:147:10:147:11 | Entry | Finally.cs:161:13:164:13 | catch (...) {...} |
| Finally.cs:147:10:147:11 | Entry | Finally.cs:161:30:161:30 | After Exception e [match] |
| Finally.cs:147:10:147:11 | Entry | Finally.cs:161:30:161:30 | After Exception e [no-match] |
| Finally.cs:147:10:147:11 | Entry | Finally.cs:161:39:161:54 | After ... == ... [false] |
| Finally.cs:147:10:147:11 | Entry | Finally.cs:161:39:161:54 | After ... == ... [true] |
| Finally.cs:147:10:147:11 | Exceptional Exit | Finally.cs:147:10:147:11 | Exceptional Exit |
@@ -17825,10 +17804,9 @@ blockDominance
| Finally.cs:155:9:169:9 | {...} | Finally.cs:158:21:158:36 | After ... == ... [false] |
| Finally.cs:155:9:169:9 | {...} | Finally.cs:158:21:158:36 | After ... == ... [true] |
| Finally.cs:155:9:169:9 | {...} | Finally.cs:159:27:159:44 | After object creation of type Exception |
| Finally.cs:155:9:169:9 | {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [match] |
| Finally.cs:155:9:169:9 | {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] |
| Finally.cs:155:9:169:9 | {...} | Finally.cs:161:13:164:13 | catch (...) {...} |
| Finally.cs:155:9:169:9 | {...} | Finally.cs:161:30:161:30 | After Exception e [match] |
| Finally.cs:155:9:169:9 | {...} | Finally.cs:161:30:161:30 | After Exception e [no-match] |
| Finally.cs:155:9:169:9 | {...} | Finally.cs:161:39:161:54 | After ... == ... [false] |
| Finally.cs:155:9:169:9 | {...} | Finally.cs:161:39:161:54 | After ... == ... [true] |
| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:147:10:147:11 | Exceptional Exit |
@@ -17843,17 +17821,15 @@ blockDominance
| Finally.cs:158:21:158:36 | After ... == ... [true] | Finally.cs:158:21:158:36 | After ... == ... [true] |
| Finally.cs:158:21:158:36 | After ... == ... [true] | Finally.cs:159:27:159:44 | After object creation of type Exception |
| Finally.cs:159:27:159:44 | After object creation of type Exception | Finally.cs:159:27:159:44 | After object creation of type Exception |
| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:13:164:13 | After catch (...) {...} [match] |
| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:39:161:54 | After ... == ... [false] |
| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:39:161:54 | After ... == ... [true] |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [match] |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | catch (...) {...} |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | After Exception e [match] |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | After Exception e [no-match] |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:39:161:54 | After ... == ... [false] |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:39:161:54 | After ... == ... [true] |
| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:30:161:30 | After Exception e [match] |
| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | After ... == ... [false] |
| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | After ... == ... [true] |
| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:161:30:161:30 | After Exception e [no-match] |
| Finally.cs:161:39:161:54 | After ... == ... [false] | Finally.cs:161:39:161:54 | After ... == ... [false] |
| Finally.cs:161:39:161:54 | After ... == ... [true] | Finally.cs:161:39:161:54 | After ... == ... [true] |
| Finally.cs:172:11:172:20 | Entry | Finally.cs:172:11:172:20 | Entry |
@@ -17871,10 +17847,9 @@ blockDominance
| Finally.cs:176:10:176:11 | Entry | Finally.cs:186:21:186:22 | After access to parameter b2 [false] |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:186:21:186:22 | After access to parameter b2 [true] |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:188:38:188:39 | After access to parameter b2 [false] |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:176:10:176:11 | Entry | Finally.cs:190:21:190:22 | After access to parameter b1 [false] |
@@ -17894,10 +17869,9 @@ blockDominance
| Finally.cs:183:9:192:9 | {...} | Finally.cs:186:21:186:22 | After access to parameter b2 [false] |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:186:21:186:22 | After access to parameter b2 [true] |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [false] |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:183:9:192:9 | {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [false] |
@@ -17907,30 +17881,27 @@ blockDominance
| Finally.cs:186:21:186:22 | After access to parameter b2 [false] | Finally.cs:186:21:186:22 | After access to parameter b2 [false] |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] |
| Finally.cs:186:31:186:46 | After object creation of type ExceptionB | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] |
| Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [false] |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [false] |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:190:21:190:22 | After access to parameter b1 [true] |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] |
| Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] |
@@ -21697,14 +21668,14 @@ postBlockDominance
| ExitMethods.cs:38:10:38:11 | Exit | ExitMethods.cs:38:10:38:11 | Exit |
| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:38:10:38:11 | Entry |
| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:38:10:38:11 | Normal Exit |
| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] |
| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] |
| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] |
| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] |
| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] |
| ExitMethods.cs:54:10:54:11 | Entry | ExitMethods.cs:54:10:54:11 | Entry |
| ExitMethods.cs:60:10:60:11 | Entry | ExitMethods.cs:60:10:60:11 | Entry |
| ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:66:17:66:26 | Entry |
@@ -21772,32 +21743,32 @@ postBlockDominance
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:19:10:19:11 | Normal Exit |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:21:9:51:9 | After try {...} ... |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:23:13:23:37 | After call to method WriteLine |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:26:9:29:9 | After catch (...) {...} [match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:26:9:29:9 | catch (...) {...} |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:26:38:26:39 | After IOException ex [match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:26:38:26:39 | After IOException ex [no-match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:30:41:30:42 | After ArgumentException ex [match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:41:16:41:24 | After access to type Exception [match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:41:16:41:24 | After access to type Exception [no-match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:30:9:40:9 | After catch (...) {...} [match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:41:9:43:9 | After catch (...) {...} [match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] |
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:49:9:51:9 | {...} |
| Finally.cs:21:9:51:9 | After try {...} ... | Finally.cs:21:9:51:9 | After try {...} ... |
| Finally.cs:23:13:23:37 | After call to method WriteLine | Finally.cs:23:13:23:37 | After call to method WriteLine |
| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:26:9:29:9 | After catch (...) {...} [match] |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | catch (...) {...} |
| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:26:38:26:39 | After IOException ex [match] |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:26:38:26:39 | After IOException ex [no-match] |
| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:30:41:30:42 | After ArgumentException ex [match] |
| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] |
| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:41:16:41:24 | After access to type Exception [match] |
| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:41:16:41:24 | After access to type Exception [no-match] |
| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:30:9:40:9 | After catch (...) {...} [match] |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] |
| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:41:9:43:9 | After catch (...) {...} [match] |
| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:19:10:19:11 | Entry |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:23:13:23:37 | After call to method WriteLine |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:26:9:29:9 | catch (...) {...} |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:26:38:26:39 | After IOException ex [match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:26:38:26:39 | After IOException ex [no-match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:41:16:41:24 | After access to type Exception [match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:41:16:41:24 | After access to type Exception [no-match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:49:9:51:9 | {...} |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:54:10:54:11 | Entry |
| Finally.cs:54:10:54:11 | Exceptional Exit | Finally.cs:54:10:54:11 | Exceptional Exit |
@@ -21806,35 +21777,31 @@ postBlockDominance
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:54:10:54:11 | Normal Exit |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:56:9:71:9 | After try {...} ... |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:58:13:58:37 | After call to method WriteLine |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:61:9:64:9 | After catch (...) {...} [match] |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:61:9:64:9 | catch (...) {...} |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:61:38:61:39 | After IOException ex [match] |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:61:38:61:39 | After IOException ex [no-match] |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:9:67:9 | After catch (...) {...} [match] |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:26:65:26 | After Exception e [match] |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:26:65:26 | After Exception e [no-match] |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:65:35:65:51 | After ... != ... [true] |
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:69:9:71:9 | {...} |
| Finally.cs:56:9:71:9 | After try {...} ... | Finally.cs:56:9:71:9 | After try {...} ... |
| Finally.cs:58:13:58:37 | After call to method WriteLine | Finally.cs:58:13:58:37 | After call to method WriteLine |
| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:61:9:64:9 | After catch (...) {...} [match] |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | catch (...) {...} |
| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:61:38:61:39 | After IOException ex [match] |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:61:38:61:39 | After IOException ex [no-match] |
| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:9:67:9 | After catch (...) {...} [match] |
| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] |
| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] |
| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:26:65:26 | After Exception e [match] |
| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:65:26:65:26 | After Exception e [no-match] |
| Finally.cs:65:35:65:51 | After ... != ... [false] | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:65:35:65:51 | After ... != ... [true] | Finally.cs:65:35:65:51 | After ... != ... [true] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:54:10:54:11 | Entry |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:58:13:58:37 | After call to method WriteLine |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [match] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:61:9:64:9 | catch (...) {...} |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:61:38:61:39 | After IOException ex [match] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:61:38:61:39 | After IOException ex [no-match] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [match] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:65:26:65:26 | After Exception e [match] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:65:26:65:26 | After Exception e [no-match] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:65:35:65:51 | After ... != ... [false] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:65:35:65:51 | After ... != ... [true] |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:69:9:71:9 | {...} |
@@ -22006,10 +21973,9 @@ postBlockDominance
| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:158:21:158:36 | After ... == ... [false] |
| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:158:21:158:36 | After ... == ... [true] |
| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:159:27:159:44 | After object creation of type Exception |
| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:13:164:13 | After catch (...) {...} [match] |
| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] |
| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:13:164:13 | catch (...) {...} |
| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:30:161:30 | After Exception e [match] |
| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:30:161:30 | After Exception e [no-match] |
| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:39:161:54 | After ... == ... [false] |
| Finally.cs:149:9:169:9 | After try {...} ... | Finally.cs:161:39:161:54 | After ... == ... [true] |
| Finally.cs:151:17:151:28 | After ... == ... [false] | Finally.cs:151:17:151:28 | After ... == ... [false] |
@@ -22030,24 +21996,21 @@ postBlockDominance
| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:158:21:158:36 | After ... == ... [false] |
| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:158:21:158:36 | After ... == ... [true] |
| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:159:27:159:44 | After object creation of type Exception |
| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:13:164:13 | After catch (...) {...} [match] |
| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] |
| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:13:164:13 | catch (...) {...} |
| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:30:161:30 | After Exception e [match] |
| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:30:161:30 | After Exception e [no-match] |
| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:39:161:54 | After ... == ... [false] |
| Finally.cs:156:13:168:13 | After try {...} ... | Finally.cs:161:39:161:54 | After ... == ... [true] |
| Finally.cs:158:21:158:31 | After access to property Length | Finally.cs:158:21:158:31 | After access to property Length |
| Finally.cs:158:21:158:36 | After ... == ... [false] | Finally.cs:158:21:158:36 | After ... == ... [false] |
| Finally.cs:158:21:158:36 | After ... == ... [true] | Finally.cs:158:21:158:36 | After ... == ... [true] |
| Finally.cs:159:27:159:44 | After object creation of type Exception | Finally.cs:159:27:159:44 | After object creation of type Exception |
| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:13:164:13 | After catch (...) {...} [match] |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:30:161:30 | After Exception e [no-match] |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:161:39:161:54 | After ... == ... [false] |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:158:21:158:36 | After ... == ... [true] |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:159:27:159:44 | After object creation of type Exception |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | catch (...) {...} |
| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:30:161:30 | After Exception e [match] |
| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:161:30:161:30 | After Exception e [no-match] |
| Finally.cs:161:39:161:54 | After ... == ... [false] | Finally.cs:161:39:161:54 | After ... == ... [false] |
| Finally.cs:161:39:161:54 | After ... == ... [true] | Finally.cs:161:39:161:54 | After ... == ... [true] |
| Finally.cs:172:11:172:20 | Entry | Finally.cs:172:11:172:20 | Entry |
@@ -22066,8 +22029,8 @@ postBlockDominance
| Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:186:21:186:22 | After access to parameter b2 [false] |
| Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:186:21:186:22 | After access to parameter b2 [true] |
| Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:178:9:192:9 | After try {...} ... | Finally.cs:190:21:190:22 | After access to parameter b1 [false] |
| Finally.cs:180:17:180:18 | After access to parameter b1 [false] | Finally.cs:180:17:180:18 | After access to parameter b1 [false] |
@@ -22087,32 +22050,31 @@ postBlockDominance
| Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:186:21:186:22 | After access to parameter b2 [false] |
| Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:186:21:186:22 | After access to parameter b2 [true] |
| Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:184:13:191:13 | After try {...} ... | Finally.cs:190:21:190:22 | After access to parameter b1 [false] |
| Finally.cs:186:21:186:22 | After access to parameter b2 [false] | Finally.cs:186:21:186:22 | After access to parameter b2 [false] |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] |
| Finally.cs:186:31:186:46 | After object creation of type ExceptionB | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:186:21:186:22 | After access to parameter b2 [true] |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] |
| Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:188:38:188:39 | After access to parameter b2 [false] |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:186:21:186:22 | After access to parameter b2 [true] |
| Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:186:31:186:46 | After object creation of type ExceptionB |
| Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:188:13:191:13 | After catch (...) {...} [match] |
| Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] |
| Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:188:38:188:39 | After access to parameter b2 [true] |
| Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:190:21:190:22 | After access to parameter b1 [false] |
| Finally.cs:190:21:190:22 | After access to parameter b1 [true] | Finally.cs:190:21:190:22 | After access to parameter b1 [true] |

View File

@@ -3016,19 +3016,15 @@ nodeEnclosing
| ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:42:13:42:31 | ...; | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:42:25:42:29 | false | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:46:13:46:19 | Before return ...; | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:46:13:46:19 | return ...; | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:50:13:50:19 | Before return ...; | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:50:13:50:19 | return ...; | ExitMethods.cs:38:10:38:11 | M6 |
@@ -3362,20 +3358,18 @@ nodeEnclosing
| Finally.cs:23:31:23:36 | "Try2" | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:24:13:24:19 | Before return ...; | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:24:13:24:19 | return ...; | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:48:26:51 | After true [true] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:48:26:51 | true | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:27:9:29:9 | {...} | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:28:13:28:18 | Before throw ...; | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:28:13:28:18 | throw ...; | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:31:9:40:9 | {...} | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:32:13:39:13 | try {...} ... | Finally.cs:19:10:19:11 | M2 |
@@ -3392,12 +3386,11 @@ nodeEnclosing
| Finally.cs:38:23:38:43 | Before object creation of type Exception | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:38:23:38:43 | object creation of type Exception | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:38:37:38:42 | "Boo!" | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:42:9:43:9 | {...} | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:44:9:47:9 | After catch {...} [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:44:9:47:9 | catch {...} | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:45:9:47:9 | {...} | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:46:13:46:19 | Before return ...; | Finally.cs:19:10:19:11 | M2 |
@@ -3427,20 +3420,18 @@ nodeEnclosing
| Finally.cs:58:31:58:36 | "Try3" | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:59:13:59:19 | Before return ...; | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:59:13:59:19 | return ...; | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:48:61:51 | After true [true] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:48:61:51 | true | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:62:9:64:9 | {...} | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:63:13:63:18 | Before throw ...; | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:63:13:63:18 | throw ...; | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:26:65:26 | Exception e | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:35:65:35 | access to local variable e | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:35:65:43 | After access to property Message | Finally.cs:54:10:54:11 | M3 |
@@ -3728,10 +3719,9 @@ nodeEnclosing
| Finally.cs:159:27:159:44 | Before object creation of type Exception | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:159:27:159:44 | object creation of type Exception | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:159:41:159:43 | "1" | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:30:161:30 | Exception e | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:39:161:39 | access to local variable e | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:39:161:47 | After access to property Message | Finally.cs:147:10:147:11 | M8 |
@@ -3754,6 +3744,7 @@ nodeEnclosing
| Finally.cs:163:35:163:41 | Before access to array element | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:163:35:163:41 | access to array element | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:163:40:163:40 | 0 | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:165:13:168:13 | After catch {...} [match] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:165:13:168:13 | catch {...} | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:166:13:168:13 | After {...} | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:166:13:168:13 | {...} | Finally.cs:147:10:147:11 | M8 |
@@ -3834,11 +3825,9 @@ nodeEnclosing
| Finally.cs:186:31:186:46 | After object creation of type ExceptionB | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:186:31:186:46 | Before object creation of type ExceptionB | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:176:10:176:11 | M9 |
@@ -3940,6 +3929,7 @@ nodeEnclosing
| Finally.cs:220:13:220:37 | ...; | Finally.cs:216:10:216:12 | M11 |
| Finally.cs:220:13:220:37 | After ...; | Finally.cs:216:10:216:12 | M11 |
| Finally.cs:220:31:220:35 | "Try" | Finally.cs:216:10:216:12 | M11 |
| Finally.cs:222:9:225:9 | After catch {...} [match] | Finally.cs:216:10:216:12 | M11 |
| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:216:10:216:12 | M11 |
| Finally.cs:223:9:225:9 | After {...} | Finally.cs:216:10:216:12 | M11 |
| Finally.cs:223:9:225:9 | {...} | Finally.cs:216:10:216:12 | M11 |
@@ -8840,10 +8830,10 @@ blockEnclosing
| ExitMethods.cs:38:10:38:11 | Entry | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:38:10:38:11 | Exit | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:38:10:38:11 | Normal Exit | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | M6 |
| ExitMethods.cs:54:10:54:11 | Entry | ExitMethods.cs:54:10:54:11 | M7 |
| ExitMethods.cs:60:10:60:11 | Entry | ExitMethods.cs:60:10:60:11 | M8 |
| ExitMethods.cs:66:17:66:26 | Entry | ExitMethods.cs:66:17:66:26 | ErrorMaybe |
@@ -8898,13 +8888,13 @@ blockEnclosing
| Finally.cs:19:10:19:11 | Normal Exit | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:21:9:51:9 | After try {...} ... | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:23:13:23:37 | After call to method WriteLine | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:49:9:51:9 | {...} | Finally.cs:19:10:19:11 | M2 |
| Finally.cs:54:10:54:11 | Entry | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:54:10:54:11 | Exceptional Exit | Finally.cs:54:10:54:11 | M3 |
@@ -8912,12 +8902,11 @@ blockEnclosing
| Finally.cs:54:10:54:11 | Normal Exit | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:56:9:71:9 | After try {...} ... | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:58:13:58:37 | After call to method WriteLine | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:35:65:51 | After ... != ... [false] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:65:35:65:51 | After ... != ... [true] | Finally.cs:54:10:54:11 | M3 |
| Finally.cs:69:9:71:9 | {...} | Finally.cs:54:10:54:11 | M3 |
@@ -8980,10 +8969,9 @@ blockEnclosing
| Finally.cs:158:21:158:36 | After ... == ... [false] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:158:21:158:36 | After ... == ... [true] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:159:27:159:44 | After object creation of type Exception | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:39:161:54 | After ... == ... [false] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:161:39:161:54 | After ... == ... [true] | Finally.cs:147:10:147:11 | M8 |
| Finally.cs:172:11:172:20 | Entry | Finally.cs:172:11:172:20 | ExceptionA |
@@ -9001,10 +8989,9 @@ blockEnclosing
| Finally.cs:186:21:186:22 | After access to parameter b2 [false] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:186:21:186:22 | After access to parameter b2 [true] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:186:31:186:46 | After object creation of type ExceptionB | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:176:10:176:11 | M9 |
| Finally.cs:190:21:190:22 | After access to parameter b1 [false] | Finally.cs:176:10:176:11 | M9 |

View File

@@ -1398,11 +1398,9 @@
| ExitMethods.cs:42:13:42:31 | ...; | ExitMethods.cs:42:13:42:31 | ...; |
| ExitMethods.cs:42:25:42:29 | false | ExitMethods.cs:42:25:42:29 | false |
| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | catch (...) {...} |
| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:16:44:32 | access to type ArgumentException |
| ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:45:9:47:9 | {...} |
| ExitMethods.cs:46:13:46:19 | return ...; | ExitMethods.cs:46:13:46:19 | return ...; |
| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:9:51:9 | catch (...) {...} |
| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:16:48:24 | access to type Exception |
| ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:49:9:51:9 | {...} |
| ExitMethods.cs:50:13:50:19 | return ...; | ExitMethods.cs:50:13:50:19 | return ...; |
| ExitMethods.cs:55:5:58:5 | {...} | ExitMethods.cs:55:5:58:5 | {...} |
@@ -1571,7 +1569,6 @@
| Finally.cs:38:23:38:43 | object creation of type Exception | Finally.cs:38:37:38:42 | "Boo!" |
| Finally.cs:38:37:38:42 | "Boo!" | Finally.cs:38:37:38:42 | "Boo!" |
| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:9:43:9 | catch (...) {...} |
| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:16:41:24 | access to type Exception |
| Finally.cs:42:9:43:9 | {...} | Finally.cs:42:9:43:9 | {...} |
| Finally.cs:44:9:47:9 | catch {...} | Finally.cs:44:9:47:9 | catch {...} |
| Finally.cs:45:9:47:9 | {...} | Finally.cs:45:9:47:9 | {...} |
@@ -1769,7 +1766,6 @@
| Finally.cs:186:25:186:47 | throw ...; | Finally.cs:186:31:186:46 | object creation of type ExceptionB |
| Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:186:31:186:46 | object creation of type ExceptionB |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | catch (...) {...} |
| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:20:188:29 | access to type ExceptionB |
| Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:38:188:39 | access to parameter b2 |
| Finally.cs:189:13:191:13 | {...} | Finally.cs:189:13:191:13 | {...} |
| Finally.cs:190:17:190:47 | if (...) ... | Finally.cs:190:17:190:47 | if (...) ... |

View File

@@ -3062,21 +3062,17 @@
| ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | ExitMethods.cs:44:9:47:9 | catch (...) {...} | exception |
| ExitMethods.cs:42:13:42:31 | ...; | ExitMethods.cs:42:13:42:30 | Before call to method ErrorAlways | |
| ExitMethods.cs:42:25:42:29 | false | ExitMethods.cs:42:13:42:30 | call to method ErrorAlways | |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | ExitMethods.cs:45:9:47:9 | {...} | |
| ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | ExitMethods.cs:48:9:51:9 | catch (...) {...} | |
| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:16:44:32 | access to type ArgumentException | |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | ExitMethods.cs:45:9:47:9 | {...} | |
| ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | no-match |
| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [match] | match |
| ExitMethods.cs:44:16:44:32 | access to type ArgumentException | ExitMethods.cs:44:16:44:32 | After access to type ArgumentException [no-match] | no-match |
| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [match] | match |
| ExitMethods.cs:44:9:47:9 | catch (...) {...} | ExitMethods.cs:44:9:47:9 | After catch (...) {...} [no-match] | no-match |
| ExitMethods.cs:45:9:47:9 | {...} | ExitMethods.cs:46:13:46:19 | Before return ...; | |
| ExitMethods.cs:46:13:46:19 | Before return ...; | ExitMethods.cs:46:13:46:19 | return ...; | |
| ExitMethods.cs:46:13:46:19 | return ...; | ExitMethods.cs:38:10:38:11 | Normal Exit | return |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | ExitMethods.cs:49:9:51:9 | {...} | |
| ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | ExitMethods.cs:38:10:38:11 | Exceptional Exit | exception |
| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:16:48:24 | access to type Exception | |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | ExitMethods.cs:49:9:51:9 | {...} | |
| ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | no-match |
| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:16:48:24 | After access to type Exception [match] | match |
| ExitMethods.cs:48:16:48:24 | access to type Exception | ExitMethods.cs:48:16:48:24 | After access to type Exception [no-match] | no-match |
| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [match] | match |
| ExitMethods.cs:48:9:51:9 | catch (...) {...} | ExitMethods.cs:48:9:51:9 | After catch (...) {...} [no-match] | no-match |
| ExitMethods.cs:49:9:51:9 | {...} | ExitMethods.cs:50:13:50:19 | Before return ...; | |
| ExitMethods.cs:50:13:50:19 | Before return ...; | ExitMethods.cs:50:13:50:19 | return ...; | |
| ExitMethods.cs:50:13:50:19 | return ...; | ExitMethods.cs:38:10:38:11 | Normal Exit | return |
@@ -3397,23 +3393,21 @@
| Finally.cs:23:31:23:36 | "Try2" | Finally.cs:23:13:23:37 | call to method WriteLine | |
| Finally.cs:24:13:24:19 | Before return ...; | Finally.cs:24:13:24:19 | return ...; | |
| Finally.cs:24:13:24:19 | return ...; | Finally.cs:49:9:51:9 | {...} | return |
| Finally.cs:26:9:29:9 | After catch (...) {...} [match] | Finally.cs:26:38:26:39 | IOException ex | |
| Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | Finally.cs:30:9:40:9 | catch (...) {...} | |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:38:26:39 | IOException ex | |
| Finally.cs:26:38:26:39 | After IOException ex [match] | Finally.cs:26:48:26:51 | true | |
| Finally.cs:26:38:26:39 | After IOException ex [no-match] | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:38:26:39 | After IOException ex [match] | match |
| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:38:26:39 | After IOException ex [no-match] | no-match |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [match] | match |
| Finally.cs:26:9:29:9 | catch (...) {...} | Finally.cs:26:9:29:9 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:26:38:26:39 | IOException ex | Finally.cs:26:48:26:51 | true | |
| Finally.cs:26:48:26:51 | After true [true] | Finally.cs:27:9:29:9 | {...} | |
| Finally.cs:26:48:26:51 | true | Finally.cs:26:48:26:51 | After true [true] | true |
| Finally.cs:27:9:29:9 | {...} | Finally.cs:28:13:28:18 | Before throw ...; | |
| Finally.cs:28:13:28:18 | Before throw ...; | Finally.cs:28:13:28:18 | throw ...; | |
| Finally.cs:28:13:28:18 | throw ...; | Finally.cs:49:9:51:9 | {...} | exception |
| Finally.cs:30:9:40:9 | After catch (...) {...} [match] | Finally.cs:30:41:30:42 | ArgumentException ex | |
| Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | Finally.cs:41:9:43:9 | catch (...) {...} | |
| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:41:30:42 | ArgumentException ex | |
| Finally.cs:30:41:30:42 | After ArgumentException ex [match] | Finally.cs:31:9:40:9 | {...} | |
| Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:41:30:42 | After ArgumentException ex [match] | match |
| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:30:41:30:42 | After ArgumentException ex [no-match] | no-match |
| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [match] | match |
| Finally.cs:30:9:40:9 | catch (...) {...} | Finally.cs:30:9:40:9 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:30:41:30:42 | ArgumentException ex | Finally.cs:31:9:40:9 | {...} | |
| Finally.cs:31:9:40:9 | {...} | Finally.cs:32:13:39:13 | try {...} ... | |
| Finally.cs:32:13:39:13 | try {...} ... | Finally.cs:33:13:35:13 | {...} | |
| Finally.cs:33:13:35:13 | {...} | Finally.cs:34:17:34:32 | if (...) ... | |
@@ -3429,14 +3423,13 @@
| Finally.cs:38:23:38:43 | Before object creation of type Exception | Finally.cs:38:37:38:42 | "Boo!" | |
| Finally.cs:38:23:38:43 | object creation of type Exception | Finally.cs:38:23:38:43 | After object creation of type Exception | |
| Finally.cs:38:37:38:42 | "Boo!" | Finally.cs:38:23:38:43 | object creation of type Exception | |
| Finally.cs:41:9:43:9 | After catch (...) {...} [match] | Finally.cs:42:9:43:9 | {...} | |
| Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | Finally.cs:44:9:47:9 | catch {...} | |
| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:16:41:24 | access to type Exception | |
| Finally.cs:41:16:41:24 | After access to type Exception [match] | Finally.cs:42:9:43:9 | {...} | |
| Finally.cs:41:16:41:24 | After access to type Exception [no-match] | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:16:41:24 | After access to type Exception [match] | match |
| Finally.cs:41:16:41:24 | access to type Exception | Finally.cs:41:16:41:24 | After access to type Exception [no-match] | no-match |
| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [match] | match |
| Finally.cs:41:9:43:9 | catch (...) {...} | Finally.cs:41:9:43:9 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:42:9:43:9 | {...} | Finally.cs:49:9:51:9 | {...} | |
| Finally.cs:44:9:47:9 | catch {...} | Finally.cs:45:9:47:9 | {...} | |
| Finally.cs:44:9:47:9 | After catch {...} [match] | Finally.cs:45:9:47:9 | {...} | |
| Finally.cs:44:9:47:9 | catch {...} | Finally.cs:44:9:47:9 | After catch {...} [match] | match |
| Finally.cs:45:9:47:9 | {...} | Finally.cs:46:13:46:19 | Before return ...; | |
| Finally.cs:46:13:46:19 | Before return ...; | Finally.cs:46:13:46:19 | return ...; | |
| Finally.cs:46:13:46:19 | return ...; | Finally.cs:49:9:51:9 | {...} | return |
@@ -3467,23 +3460,21 @@
| Finally.cs:58:31:58:36 | "Try3" | Finally.cs:58:13:58:37 | call to method WriteLine | |
| Finally.cs:59:13:59:19 | Before return ...; | Finally.cs:59:13:59:19 | return ...; | |
| Finally.cs:59:13:59:19 | return ...; | Finally.cs:69:9:71:9 | {...} | return |
| Finally.cs:61:9:64:9 | After catch (...) {...} [match] | Finally.cs:61:38:61:39 | IOException ex | |
| Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | Finally.cs:65:9:67:9 | catch (...) {...} | |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:38:61:39 | IOException ex | |
| Finally.cs:61:38:61:39 | After IOException ex [match] | Finally.cs:61:48:61:51 | true | |
| Finally.cs:61:38:61:39 | After IOException ex [no-match] | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:38:61:39 | After IOException ex [match] | match |
| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:38:61:39 | After IOException ex [no-match] | no-match |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [match] | match |
| Finally.cs:61:9:64:9 | catch (...) {...} | Finally.cs:61:9:64:9 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:61:38:61:39 | IOException ex | Finally.cs:61:48:61:51 | true | |
| Finally.cs:61:48:61:51 | After true [true] | Finally.cs:62:9:64:9 | {...} | |
| Finally.cs:61:48:61:51 | true | Finally.cs:61:48:61:51 | After true [true] | true |
| Finally.cs:62:9:64:9 | {...} | Finally.cs:63:13:63:18 | Before throw ...; | |
| Finally.cs:63:13:63:18 | Before throw ...; | Finally.cs:63:13:63:18 | throw ...; | |
| Finally.cs:63:13:63:18 | throw ...; | Finally.cs:69:9:71:9 | {...} | exception |
| Finally.cs:65:9:67:9 | After catch (...) {...} [match] | Finally.cs:65:26:65:26 | Exception e | |
| Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | Finally.cs:69:9:71:9 | {...} | exception |
| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:26:65:26 | Exception e | |
| Finally.cs:65:26:65:26 | After Exception e [match] | Finally.cs:65:35:65:51 | Before ... != ... | |
| Finally.cs:65:26:65:26 | After Exception e [no-match] | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:26:65:26 | After Exception e [match] | match |
| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:26:65:26 | After Exception e [no-match] | no-match |
| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [match] | match |
| Finally.cs:65:9:67:9 | catch (...) {...} | Finally.cs:65:9:67:9 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:65:26:65:26 | Exception e | Finally.cs:65:35:65:51 | Before ... != ... | |
| Finally.cs:65:35:65:35 | access to local variable e | Finally.cs:65:35:65:43 | access to property Message | |
| Finally.cs:65:35:65:43 | After access to property Message | Finally.cs:65:48:65:51 | null | |
| Finally.cs:65:35:65:43 | Before access to property Message | Finally.cs:65:35:65:35 | access to local variable e | |
@@ -3796,12 +3787,11 @@
| Finally.cs:159:27:159:44 | object creation of type Exception | Finally.cs:159:27:159:44 | After object creation of type Exception | |
| Finally.cs:159:27:159:44 | object creation of type Exception | Finally.cs:161:13:164:13 | catch (...) {...} | exception |
| Finally.cs:159:41:159:43 | "1" | Finally.cs:159:27:159:44 | object creation of type Exception | |
| Finally.cs:161:13:164:13 | After catch (...) {...} [match] | Finally.cs:161:30:161:30 | Exception e | |
| Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | Finally.cs:165:13:168:13 | catch {...} | |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:30:161:30 | Exception e | |
| Finally.cs:161:30:161:30 | After Exception e [match] | Finally.cs:161:39:161:54 | Before ... == ... | |
| Finally.cs:161:30:161:30 | After Exception e [no-match] | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:30:161:30 | After Exception e [match] | match |
| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:30:161:30 | After Exception e [no-match] | no-match |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [match] | match |
| Finally.cs:161:13:164:13 | catch (...) {...} | Finally.cs:161:13:164:13 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:161:30:161:30 | Exception e | Finally.cs:161:39:161:54 | Before ... == ... | |
| Finally.cs:161:39:161:39 | access to local variable e | Finally.cs:161:39:161:47 | access to property Message | |
| Finally.cs:161:39:161:47 | After access to property Message | Finally.cs:161:52:161:54 | "1" | |
| Finally.cs:161:39:161:47 | Before access to property Message | Finally.cs:161:39:161:39 | access to local variable e | |
@@ -3824,7 +3814,8 @@
| Finally.cs:163:35:163:41 | Before access to array element | Finally.cs:163:35:163:38 | access to parameter args | |
| Finally.cs:163:35:163:41 | access to array element | Finally.cs:163:35:163:41 | After access to array element | |
| Finally.cs:163:40:163:40 | 0 | Finally.cs:163:35:163:41 | access to array element | |
| Finally.cs:165:13:168:13 | catch {...} | Finally.cs:166:13:168:13 | {...} | |
| Finally.cs:165:13:168:13 | After catch {...} [match] | Finally.cs:166:13:168:13 | {...} | |
| Finally.cs:165:13:168:13 | catch {...} | Finally.cs:165:13:168:13 | After catch {...} [match] | match |
| Finally.cs:166:13:168:13 | After {...} | Finally.cs:156:13:168:13 | After try {...} ... | |
| Finally.cs:166:13:168:13 | {...} | Finally.cs:167:17:167:38 | ...; | |
| Finally.cs:167:17:167:37 | After call to method WriteLine | Finally.cs:167:17:167:38 | After ...; | |
@@ -3905,12 +3896,10 @@
| Finally.cs:186:31:186:46 | Before object creation of type ExceptionB | Finally.cs:186:31:186:46 | object creation of type ExceptionB | |
| Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:186:31:186:46 | After object creation of type ExceptionB | |
| Finally.cs:186:31:186:46 | object creation of type ExceptionB | Finally.cs:188:13:191:13 | catch (...) {...} | exception |
| Finally.cs:188:13:191:13 | After catch (...) {...} [match] | Finally.cs:188:38:188:39 | access to parameter b2 | |
| Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | Finally.cs:176:10:176:11 | Exceptional Exit | exception |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:20:188:29 | access to type ExceptionB | |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | Finally.cs:188:38:188:39 | access to parameter b2 | |
| Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:20:188:29 | After access to type ExceptionB [match] | match |
| Finally.cs:188:20:188:29 | access to type ExceptionB | Finally.cs:188:20:188:29 | After access to type ExceptionB [no-match] | no-match |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [match] | match |
| Finally.cs:188:13:191:13 | catch (...) {...} | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:188:38:188:39 | After access to parameter b2 [false] | Finally.cs:188:13:191:13 | After catch (...) {...} [no-match] | no-match |
| Finally.cs:188:38:188:39 | After access to parameter b2 [true] | Finally.cs:189:13:191:13 | {...} | |
| Finally.cs:188:38:188:39 | access to parameter b2 | Finally.cs:188:38:188:39 | After access to parameter b2 [false] | false |
@@ -4020,7 +4009,8 @@
| Finally.cs:220:13:220:37 | ...; | Finally.cs:220:13:220:36 | Before call to method WriteLine | |
| Finally.cs:220:13:220:37 | After ...; | Finally.cs:219:9:221:9 | After {...} | |
| Finally.cs:220:31:220:35 | "Try" | Finally.cs:220:13:220:36 | call to method WriteLine | |
| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:223:9:225:9 | {...} | |
| Finally.cs:222:9:225:9 | After catch {...} [match] | Finally.cs:223:9:225:9 | {...} | |
| Finally.cs:222:9:225:9 | catch {...} | Finally.cs:222:9:225:9 | After catch {...} [match] | match |
| Finally.cs:223:9:225:9 | After {...} | Finally.cs:227:9:229:9 | {...} | |
| Finally.cs:223:9:225:9 | {...} | Finally.cs:224:13:224:39 | ...; | |
| Finally.cs:224:13:224:38 | After call to method WriteLine | Finally.cs:224:13:224:39 | After ...; | |

View File

@@ -336,12 +336,11 @@
| patterns.cs:142:26:142:34 | { ... } | patterns.cs:142:26:142:34 | After { ... } | semmle.label | successor |
| patterns.cs:142:31:142:32 | 10 | patterns.cs:142:26:142:34 | { ... } | semmle.label | successor |
| patterns.cs:142:41:142:41 | 6 | patterns.cs:136:17:143:13 | After ... switch { ... } | semmle.label | successor |
| patterns.cs:145:9:148:9 | After catch (...) {...} [match] | patterns.cs:145:41:145:42 | InvalidOperationException ex | semmle.label | successor |
| patterns.cs:145:9:148:9 | After catch (...) {...} [no-match] | patterns.cs:123:10:123:21 | Exceptional Exit | semmle.label | exception |
| patterns.cs:145:9:148:9 | catch (...) {...} | patterns.cs:145:41:145:42 | InvalidOperationException ex | semmle.label | successor |
| patterns.cs:145:41:145:42 | After InvalidOperationException ex [match] | patterns.cs:146:9:148:9 | {...} | semmle.label | successor |
| patterns.cs:145:41:145:42 | After InvalidOperationException ex [no-match] | patterns.cs:145:9:148:9 | After catch (...) {...} [no-match] | semmle.label | no-match |
| patterns.cs:145:41:145:42 | InvalidOperationException ex | patterns.cs:145:41:145:42 | After InvalidOperationException ex [match] | semmle.label | match |
| patterns.cs:145:41:145:42 | InvalidOperationException ex | patterns.cs:145:41:145:42 | After InvalidOperationException ex [no-match] | semmle.label | no-match |
| patterns.cs:145:9:148:9 | catch (...) {...} | patterns.cs:145:9:148:9 | After catch (...) {...} [match] | semmle.label | match |
| patterns.cs:145:9:148:9 | catch (...) {...} | patterns.cs:145:9:148:9 | After catch (...) {...} [no-match] | semmle.label | no-match |
| patterns.cs:145:41:145:42 | InvalidOperationException ex | patterns.cs:146:9:148:9 | {...} | semmle.label | successor |
| patterns.cs:146:9:148:9 | After {...} | patterns.cs:134:9:148:9 | After try {...} ... | semmle.label | successor |
| patterns.cs:146:9:148:9 | {...} | patterns.cs:147:13:147:51 | ...; | semmle.label | successor |
| patterns.cs:147:13:147:50 | After call to method WriteLine | patterns.cs:147:13:147:51 | After ...; | semmle.label | successor |

View File

@@ -21,7 +21,7 @@
Java,"Java 7 to 26 [6]_","javac (OpenJDK and Oracle JDK),
Eclipse compiler for Java (ECJ) [7]_",``.java``
Kotlin,"Kotlin 1.8.0 to 2.4.0\ *x*","kotlinc",``.kt``
Kotlin,"Kotlin 1.8.0 to 2.3.2\ *x*","kotlinc",``.kt``
JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [8]_"
Python [9]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13",Not applicable,``.py``
Ruby [10]_,"up to 3.3",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``"

View File

@@ -53,10 +53,6 @@ _extractor_name_prefix = "%s-%s" % (
"embeddable" if _for_embeddable else "standalone",
)
_compiler_plugin_registrar_service_source = "src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar"
_compiler_plugin_registrar_service_target = "META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar"
py_binary(
name = "generate_dbscheme",
srcs = ["generate_dbscheme.py"],
@@ -68,14 +64,8 @@ _resources = [
r[len("src/main/resources/"):],
)
for r in glob(["src/main/resources/**"])
if r != _compiler_plugin_registrar_service_source
]
_compiler_plugin_registrar_service = (
_compiler_plugin_registrar_service_source,
_compiler_plugin_registrar_service_target,
)
kt_javac_options(
name = "javac-options",
release = "8",
@@ -101,32 +91,19 @@ kt_javac_options(
# * `resource_strip_prefix` is unique per jar, so we must also put other resources under the same version prefix
genrule(
name = "resources-%s" % v,
srcs = [src for src, _ in _resources] + (
[_compiler_plugin_registrar_service[0]] if not version_less(v, "2.4.0") else []
),
srcs = [src for src, _ in _resources],
outs = [
"%s/com/github/codeql/extractor.name" % v,
] + [
"%s/%s" % (v, target)
for _, target in _resources
] + (
["%s/%s" % (
v,
_compiler_plugin_registrar_service[1],
)] if not version_less(v, "2.4.0") else []
),
],
cmd = "\n".join([
"echo %s-%s > $(RULEDIR)/%s/com/github/codeql/extractor.name" % (_extractor_name_prefix, v, v),
] + [
"cp $(execpath %s) $(RULEDIR)/%s/%s" % (source, v, target)
for source, target in _resources
] + (
["cp $(execpath %s) $(RULEDIR)/%s/%s" % (
_compiler_plugin_registrar_service[0],
v,
_compiler_plugin_registrar_service[1],
)] if not version_less(v, "2.4.0") else []
)),
]),
),
kt_jvm_library(
name = "%s-%s" % (_extractor_name_prefix, v),

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -27,7 +27,7 @@ import shutil
import io
import os
DEFAULT_VERSION = "2.4.0"
DEFAULT_VERSION = "2.3.20"
def options():

View File

@@ -3,21 +3,32 @@
package com.github.codeql
import com.intellij.mock.MockProject
import com.intellij.openapi.extensions.LoadingOrder
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.config.CompilerConfiguration
class KotlinExtractorComponentRegistrar : Kotlin2ComponentRegistrar() {
override fun doRegisterExtensions(configuration: CompilerConfiguration) {
override fun registerProjectComponents(
project: MockProject,
configuration: CompilerConfiguration
) {
val invocationTrapFile = configuration[KEY_INVOCATION_TRAP_FILE]
if (invocationTrapFile == null) {
throw Exception("Required argument for TRAP invocation file not given")
}
registerExtractorExtension(
// Register with LoadingOrder.LAST to ensure the extractor runs after other
// IR generation plugins (like kotlinx.serialization) have generated their code.
val extensionPoint = project.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName)
extensionPoint.registerExtension(
KotlinExtractorExtension(
invocationTrapFile,
configuration[KEY_CHECK_TRAP_IDENTICAL] ?: false,
configuration[KEY_COMPILATION_STARTTIME],
configuration[KEY_EXIT_AFTER_EXTRACTION] ?: false
)
),
LoadingOrder.LAST,
project
)
}
}

View File

@@ -173,9 +173,9 @@ open class KotlinFileExtractor(
when (d) {
is IrFunction ->
when (d.name.asString()) {
"toString" -> d.codeQlValueParameters.isEmpty()
"hashCode" -> d.codeQlValueParameters.isEmpty()
"equals" -> d.codeQlValueParameters.singleOrNull()?.type?.isNullableAny() ?: false
"toString" -> d.valueParameters.isEmpty()
"hashCode" -> d.valueParameters.isEmpty()
"equals" -> d.valueParameters.singleOrNull()?.type?.isNullableAny() ?: false
else -> false
} && isJavaBinaryDeclaration(d)
else -> false
@@ -721,7 +721,7 @@ open class KotlinFileExtractor(
(it.type as? IrSimpleType)?.classFqName?.asString() != "kotlin.Deprecated"
} +
// Note we lose any arguments to @java.lang.Deprecated that were written in source.
codeQlAnnotationFromSymbolOwner(
IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
jldConstructor.returnType,
@@ -781,13 +781,13 @@ open class KotlinFileExtractor(
val locId = tw.getLocation(constructorCall)
tw.writeHasLocation(id, locId)
for (i in 0 until constructorCall.codeQlValueArgumentsCount) {
val param = constructorCall.symbol.owner.codeQlValueParameters[i]
for (i in 0 until constructorCall.valueArgumentsCount) {
val param = constructorCall.symbol.owner.valueParameters[i]
val prop =
constructorCall.symbol.owner.parentAsClass.declarations
.filterIsInstance<IrProperty>()
.first { it.name == param.name }
val v = constructorCall.codeQlGetValueArgument(i) ?: param.defaultValue?.expression
val v = constructorCall.getValueArgument(i) ?: param.defaultValue?.expression
val getter = prop.getter
if (getter == null) {
logger.warnElement("Expected annotation property to define a getter", prop)
@@ -1115,9 +1115,9 @@ open class KotlinFileExtractor(
returnId,
0,
returnId,
f.codeQlValueParameters.size,
f.valueParameters.size,
{ argParent, idxOffset ->
f.codeQlValueParameters.forEachIndexed { idx, param ->
f.valueParameters.forEachIndexed { idx, param ->
val syntheticParamId = useValueParameter(param, proxyFunctionId)
extractVariableAccess(
syntheticParamId,
@@ -1695,9 +1695,9 @@ open class KotlinFileExtractor(
returnId,
0,
returnId,
f.codeQlValueParameters.size,
f.valueParameters.size,
{ argParentId, idxOffset ->
f.codeQlValueParameters.mapIndexed { idx, param ->
f.valueParameters.mapIndexed { idx, param ->
val syntheticParamId = useValueParameter(param, functionId)
extractVariableAccess(
syntheticParamId,
@@ -1792,7 +1792,7 @@ open class KotlinFileExtractor(
extractBody: Boolean,
extractMethodAndParameterTypeAccesses: Boolean
) {
if (f.codeQlValueParameters.none { it.defaultValue != null }) return
if (f.valueParameters.none { it.defaultValue != null }) return
val id = getDefaultsMethodLabel(f)
if (id == null) {
@@ -1800,7 +1800,7 @@ open class KotlinFileExtractor(
return
}
val locId = getLocation(f, null)
val extReceiver = f.codeQlExtensionReceiverParameter
val extReceiver = f.extensionReceiverParameter
val dispatchReceiver = if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter
val parameterTypes = getDefaultsMethodArgTypes(f)
val allParamTypeResults =
@@ -1869,7 +1869,7 @@ open class KotlinFileExtractor(
tw.writeCompiler_generated(id, CompilerGeneratedKinds.DEFAULT_ARGUMENTS_METHOD.kind)
if (extractBody) {
val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.codeQlValueParameters
val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.valueParameters
// This stack entry represents as if we're extracting the 'real' function `f`, giving
// the indices of its non-synthetic parameters
// such that when we extract the default expressions below, any reference to f's nth
@@ -1895,12 +1895,12 @@ open class KotlinFileExtractor(
val realParamsVarId = getValueParameterLabel(id, parameterTypes.size - 2)
val intType = pluginContext.irBuiltIns.intType
val paramIdxOffset =
listOf(dispatchReceiver, f.codeQlExtensionReceiverParameter).count { it != null }
listOf(dispatchReceiver, f.extensionReceiverParameter).count { it != null }
extractBlockBody(id, locId).also { blockId ->
var nextStmt = 0
// For each parameter with a default, sub in the default value if the caller
// hasn't supplied a value:
f.codeQlValueParameters.forEachIndexed { paramIdx, param ->
f.valueParameters.forEachIndexed { paramIdx, param ->
val defaultVal = param.defaultValue
if (defaultVal != null) {
extractIfStmt(locId, blockId, nextStmt++, id).also { ifId ->
@@ -1975,7 +1975,7 @@ open class KotlinFileExtractor(
id
)
tw.writeHasLocation(thisCallId, locId)
f.codeQlValueParameters.forEachIndexed { idx, param ->
f.valueParameters.forEachIndexed { idx, param ->
extractVariableAccess(
tw.getLabelFor<DbParam>(getValueParameterLabel(id, idx)),
param.type,
@@ -2003,9 +2003,9 @@ open class KotlinFileExtractor(
)
.also { thisCallId ->
val realFnIdxOffset =
if (f.codeQlExtensionReceiverParameter != null) 1 else 0
if (f.extensionReceiverParameter != null) 1 else 0
val paramMappings =
f.codeQlValueParameters.mapIndexed { idx, param ->
f.valueParameters.mapIndexed { idx, param ->
Triple(
param.type,
idx + paramIdxOffset,
@@ -2156,7 +2156,7 @@ open class KotlinFileExtractor(
val dispatchReceiver =
f.dispatchReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) }
val extensionReceiver =
f.codeQlExtensionReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) }
f.extensionReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) }
extractExpressionBody(overloadId, realFunctionLocId).also { returnId ->
extractsDefaultsCall(
@@ -2180,28 +2180,28 @@ open class KotlinFileExtractor(
if (!f.hasAnnotation(jvmOverloadsFqName)) {
if (
f is IrConstructor &&
f.codeQlValueParameters.isNotEmpty() &&
f.codeQlValueParameters.all { it.defaultValue != null } &&
f.valueParameters.isNotEmpty() &&
f.valueParameters.all { it.defaultValue != null } &&
f.parentClassOrNull?.let {
// Don't create a default constructor for an annotation class, or a class
// that explicitly declares a no-arg constructor.
!it.isAnnotationClass &&
it.declarations.none { d ->
d is IrConstructor && d.codeQlValueParameters.isEmpty()
d is IrConstructor && d.valueParameters.isEmpty()
}
} == true
) {
// Per https://kotlinlang.org/docs/classes.html#creating-instances-of-classes, a
// single default overload gets created specifically
// when we have all default parameters, regardless of `@JvmOverloads`.
extractGeneratedOverload(f.codeQlValueParameters.map { _ -> null })
extractGeneratedOverload(f.valueParameters.map { _ -> null })
}
return
}
val paramList: MutableList<IrValueParameter?> = f.codeQlValueParameters.toMutableList()
for (n in (f.codeQlValueParameters.size - 1) downTo 0) {
if (f.codeQlValueParameters[n].defaultValue != null) {
val paramList: MutableList<IrValueParameter?> = f.valueParameters.toMutableList()
for (n in (f.valueParameters.size - 1) downTo 0) {
if (f.valueParameters[n].defaultValue != null) {
paramList[n] = null // Remove this parameter, to be replaced by a default value
extractGeneratedOverload(paramList)
}
@@ -2327,7 +2327,7 @@ open class KotlinFileExtractor(
getClassByFqName(pluginContext, it)?.let { annotationClass ->
annotationClass.owner.declarations.firstIsInstanceOrNull<IrConstructor>()?.let {
annotationConstructor ->
codeQlAnnotationFromSymbolOwner(
IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
annotationConstructor.returnType,
@@ -2388,13 +2388,13 @@ open class KotlinFileExtractor(
id
}
val extReceiver = f.codeQlExtensionReceiverParameter
val extReceiver = f.extensionReceiverParameter
// The following parameter order is correct, because member $default methods (where
// the order would be [dispatchParam], [extensionParam], normalParams) are not
// extracted here
val fParameters =
listOfNotNull(extReceiver) +
(overriddenAttributes?.valueParameters ?: f.codeQlValueParameters)
(overriddenAttributes?.valueParameters ?: f.valueParameters)
val paramTypes =
fParameters.mapIndexed { i, vp ->
extractValueParameter(
@@ -3069,14 +3069,14 @@ open class KotlinFileExtractor(
logger.errorElement("Unexpected dispatch receiver found", c)
}
if (c.codeQlValueArgumentsCount < 1) {
if (c.valueArgumentsCount < 1) {
logger.errorElement("No arguments found", c)
return
}
extractArgument(id, c, callable, enclosingStmt, 0, "Operand null")
if (c.codeQlValueArgumentsCount > 1) {
if (c.valueArgumentsCount > 1) {
logger.errorElement("Extra arguments found", c)
}
}
@@ -3095,21 +3095,21 @@ open class KotlinFileExtractor(
logger.errorElement("Unexpected dispatch receiver found", c)
}
if (c.codeQlValueArgumentsCount < 1) {
if (c.valueArgumentsCount < 1) {
logger.errorElement("No arguments found", c)
return
}
extractArgument(id, c, callable, enclosingStmt, 0, "LHS null")
if (c.codeQlValueArgumentsCount < 2) {
if (c.valueArgumentsCount < 2) {
logger.errorElement("No RHS found", c)
return
}
extractArgument(id, c, callable, enclosingStmt, 1, "RHS null")
if (c.codeQlValueArgumentsCount > 2) {
if (c.valueArgumentsCount > 2) {
logger.errorElement("Extra arguments found", c)
}
}
@@ -3122,7 +3122,7 @@ open class KotlinFileExtractor(
idx: Int,
msg: String
) {
val op = c.codeQlGetValueArgument(idx)
val op = c.getValueArgument(idx)
if (op == null) {
logger.errorElement(msg, c)
} else {
@@ -3267,8 +3267,8 @@ open class KotlinFileExtractor(
// and which should be replaced by defaults. The final Object parameter is apparently always
// null.
(listOfNotNull(if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter?.type) +
listOfNotNull(f.codeQlExtensionReceiverParameter?.type) +
f.codeQlValueParameters.map { it.type } +
listOfNotNull(f.extensionReceiverParameter?.type) +
f.valueParameters.map { it.type } +
listOf(pluginContext.irBuiltIns.intType, getDefaultsMethodLastArgType(f)))
.map { erase(it) }
@@ -3345,7 +3345,7 @@ open class KotlinFileExtractor(
val overriddenCallTarget =
(callTarget as? IrSimpleFunction)?.allOverridden(includeSelf = true)?.firstOrNull {
it.overriddenSymbols.isEmpty() &&
it.codeQlValueParameters.any { p -> p.defaultValue != null }
it.valueParameters.any { p -> p.defaultValue != null }
} ?: callTarget
if (isExternalDeclaration(overriddenCallTarget)) {
// Likewise, ensure the overridden target gets extracted.
@@ -3419,7 +3419,7 @@ open class KotlinFileExtractor(
}
val valueArgsWithDummies =
valueArguments.zip(callTarget.codeQlValueParameters).map { (expr, param) ->
valueArguments.zip(callTarget.valueParameters).map { (expr, param) ->
expr ?: IrConstImpl.defaultValueForType(0, 0, param.type)
}
@@ -3529,7 +3529,7 @@ open class KotlinFileExtractor(
callTarget: IrFunction,
valueArguments: List<IrExpression?>
): Boolean {
val varargParam = callTarget.codeQlValueParameters.withIndex().find { it.value.isVararg }
val varargParam = callTarget.valueParameters.withIndex().find { it.value.isVararg }
// If the vararg param is the only one not specified, and it has no default value, then we
// don't need to call a $default method,
// as omitting it already implies passing an empty vararg array.
@@ -3805,7 +3805,7 @@ open class KotlinFileExtractor(
) =
extractCallValueArguments(
callId,
(0 until call.codeQlValueArgumentsCount).map { call.codeQlGetValueArgument(it) },
(0 until call.valueArgumentsCount).map { call.getValueArgument(it) },
enclosingStmt,
enclosingCallable,
idxOffset
@@ -3874,7 +3874,7 @@ open class KotlinFileExtractor(
(owner.parentClassOrNull?.fqNameWhenAvailable?.asString() == type ||
(owner.parent is IrExternalPackageFragment &&
getFileClassFqName(owner)?.asString() == type)) &&
owner.codeQlValueParameters
owner.valueParameters
.map { it.type.classFqName?.asString() }
.toTypedArray() contentEquals parameterTypes
}
@@ -3926,8 +3926,8 @@ open class KotlinFileExtractor(
val result =
javaLangString?.declarations?.findSubType<IrFunction> {
it.name.asString() == "valueOf" &&
it.codeQlValueParameters.size == 1 &&
it.codeQlValueParameters[0].type == pluginContext.irBuiltIns.anyNType
it.valueParameters.size == 1 &&
it.valueParameters[0].type == pluginContext.irBuiltIns.anyNType
}
if (result == null) {
logger.error("Couldn't find declaration java.lang.String.valueOf(Object)")
@@ -3951,7 +3951,7 @@ open class KotlinFileExtractor(
val kotlinNoWhenBranchMatchedConstructor by lazy {
val result =
kotlinNoWhenBranchMatchedExn?.declarations?.findSubType<IrConstructor> {
it.codeQlValueParameters.isEmpty()
it.valueParameters.isEmpty()
}
if (result == null) {
logger.error("Couldn't find no-arg constructor for kotlin.NoWhenBranchMatchedException")
@@ -3990,7 +3990,7 @@ open class KotlinFileExtractor(
verboseln("No match as function name is ${target.name.asString()} not $fName")
return false
}
val extensionReceiverParameter = target.codeQlExtensionReceiverParameter
val extensionReceiverParameter = target.extensionReceiverParameter
val targetClass =
if (extensionReceiverParameter == null) {
if (isNullable == true) {
@@ -4098,8 +4098,8 @@ open class KotlinFileExtractor(
) {
val typeArgs =
if (extractMethodTypeArguments)
(0 until c.codeQlTypeArgumentsCount)
.map { c.codeQlGetTypeArgument(it) }
(0 until c.typeArgumentsCount)
.map { c.getTypeArgument(it) }
.requireNoNullsOrNull()
else listOf()
@@ -4116,9 +4116,9 @@ open class KotlinFileExtractor(
parent,
idx,
enclosingStmt,
(0 until c.codeQlValueArgumentsCount).map { c.codeQlGetValueArgument(it) },
(0 until c.valueArgumentsCount).map { c.getValueArgument(it) },
c.dispatchReceiver,
c.codeQlExtensionReceiver,
c.extensionReceiver,
typeArgs,
extractClassTypeArguments,
c.superQualifierSymbol
@@ -4126,12 +4126,12 @@ open class KotlinFileExtractor(
}
fun extractSpecialEnumFunction(fnName: String) {
if (c.codeQlTypeArgumentsCount != 1) {
if (c.typeArgumentsCount != 1) {
logger.errorElement("Expected to find exactly one type argument", c)
return
}
val enumType = (c.codeQlGetTypeArgument(0) as? IrSimpleType)?.classifier?.owner
val enumType = (c.getTypeArgument(0) as? IrSimpleType)?.classifier?.owner
if (enumType == null) {
logger.errorElement("Couldn't find type of enum type", c)
return
@@ -4178,13 +4178,13 @@ open class KotlinFileExtractor(
} else {
extractExpressionExpr(receiver, callable, id, 0, enclosingStmt)
}
if (c.codeQlValueArgumentsCount < 1) {
if (c.valueArgumentsCount < 1) {
logger.errorElement("No RHS found", c)
} else {
if (c.codeQlValueArgumentsCount > 1) {
if (c.valueArgumentsCount > 1) {
logger.errorElement("Extra arguments found", c)
}
val arg = c.codeQlGetValueArgument(0)
val arg = c.getValueArgument(0)
if (arg == null) {
logger.errorElement("RHS null", c)
} else {
@@ -4205,7 +4205,7 @@ open class KotlinFileExtractor(
} else {
extractExpressionExpr(receiver, callable, id, 0, enclosingStmt)
}
if (c.codeQlValueArgumentsCount > 0) {
if (c.valueArgumentsCount > 0) {
logger.errorElement("Extra arguments found", c)
}
}
@@ -4219,7 +4219,7 @@ open class KotlinFileExtractor(
}
fun binopExt(id: Label<out DbExpr>) {
binopReceiver(id, c.codeQlExtensionReceiver, "Extension receiver")
binopReceiver(id, c.extensionReceiver, "Extension receiver")
}
fun unaryopDisp(id: Label<out DbExpr>) {
@@ -4227,7 +4227,7 @@ open class KotlinFileExtractor(
}
fun unaryopExt(id: Label<out DbExpr>) {
unaryopReceiver(id, c.codeQlExtensionReceiver, "Extension receiver")
unaryopReceiver(id, c.extensionReceiver, "Extension receiver")
}
val dr = c.dispatchReceiver
@@ -4249,7 +4249,7 @@ open class KotlinFileExtractor(
parent,
idx,
enclosingStmt,
listOf(c.codeQlExtensionReceiver, c.codeQlGetValueArgument(0)),
listOf(c.extensionReceiver, c.getValueArgument(0)),
null,
null
)
@@ -4350,7 +4350,7 @@ open class KotlinFileExtractor(
// != gets desugared into not and ==. Here we resugar it.
c.origin == IrStatementOrigin.EXCLEQ &&
isFunction(target, "kotlin", "Boolean", "not") &&
c.codeQlValueArgumentsCount == 0 &&
c.valueArgumentsCount == 0 &&
dr != null &&
dr is IrCall &&
isBuiltinCallInternal(dr, "EQEQ") -> {
@@ -4362,7 +4362,7 @@ open class KotlinFileExtractor(
}
c.origin == IrStatementOrigin.EXCLEQEQ &&
isFunction(target, "kotlin", "Boolean", "not") &&
c.codeQlValueArgumentsCount == 0 &&
c.valueArgumentsCount == 0 &&
dr != null &&
dr is IrCall &&
isBuiltinCallInternal(dr, "EQEQEQ") -> {
@@ -4374,7 +4374,7 @@ open class KotlinFileExtractor(
}
c.origin == IrStatementOrigin.EXCLEQ &&
isFunction(target, "kotlin", "Boolean", "not") &&
c.codeQlValueArgumentsCount == 0 &&
c.valueArgumentsCount == 0 &&
dr != null &&
dr is IrCall &&
isBuiltinCallInternal(dr, "ieee754equals") -> {
@@ -4576,7 +4576,7 @@ open class KotlinFileExtractor(
parent,
idx,
enclosingStmt,
listOf(c.codeQlExtensionReceiver),
listOf(c.extensionReceiver),
null,
null
)
@@ -4596,8 +4596,8 @@ open class KotlinFileExtractor(
val locId = tw.getLocation(c)
extractExprContext(id, locId, callable, enclosingStmt)
if (c.codeQlTypeArgumentsCount == 1) {
val typeArgument = c.codeQlGetTypeArgument(0)
if (c.typeArgumentsCount == 1) {
val typeArgument = c.getTypeArgument(0)
if (typeArgument == null) {
logger.errorElement("Type argument missing in an arrayOfNulls call", c)
} else {
@@ -4618,8 +4618,8 @@ open class KotlinFileExtractor(
)
}
if (c.codeQlValueArgumentsCount == 1) {
val dim = c.codeQlGetValueArgument(0)
if (c.valueArgumentsCount == 1) {
val dim = c.getValueArgument(0)
if (dim != null) {
extractExpressionExpr(dim, callable, id, 0, enclosingStmt)
} else {
@@ -4651,8 +4651,8 @@ open class KotlinFileExtractor(
c.type.getArrayElementTypeCodeQL(pluginContext.irBuiltIns)
} else {
// TODO: is there any reason not to always use getArrayElementTypeCodeQL?
if (c.codeQlTypeArgumentsCount == 1) {
c.codeQlGetTypeArgument(0).also {
if (c.typeArgumentsCount == 1) {
c.getTypeArgument(0).also {
if (it == null) {
logger.errorElement(
"Type argument missing in an arrayOf call",
@@ -4670,7 +4670,7 @@ open class KotlinFileExtractor(
}
val arg =
if (c.codeQlValueArgumentsCount == 1) c.codeQlGetValueArgument(0)
if (c.valueArgumentsCount == 1) c.getValueArgument(0)
else {
logger.errorElement(
"Expected to find only one (vararg) argument in ${c.symbol.owner.name.asString()} call",
@@ -4719,7 +4719,7 @@ open class KotlinFileExtractor(
return
}
val ext = c.codeQlExtensionReceiver
val ext = c.extensionReceiver
if (ext == null) {
logger.errorElement(
"No extension receiver found for `KClass::java` call",
@@ -4826,8 +4826,8 @@ open class KotlinFileExtractor(
c.origin == IrStatementOrigin.EQ &&
c.dispatchReceiver != null -> {
val array = c.dispatchReceiver
val arrayIdx = c.codeQlGetValueArgument(0)
val assignedValue = c.codeQlGetValueArgument(1)
val arrayIdx = c.getValueArgument(0)
val assignedValue = c.getValueArgument(1)
if (array != null && arrayIdx != null && assignedValue != null) {
@@ -4882,22 +4882,22 @@ open class KotlinFileExtractor(
}
isBuiltinCall(c, "<unsafe-coerce>", "kotlin.jvm.internal") -> {
if (c.codeQlValueArgumentsCount != 1) {
if (c.valueArgumentsCount != 1) {
logger.errorElement(
"Expected to find one argument for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.codeQlValueArgumentsCount}",
"Expected to find one argument for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.valueArgumentsCount}",
c
)
return
}
if (c.codeQlTypeArgumentsCount != 2) {
if (c.typeArgumentsCount != 2) {
logger.errorElement(
"Expected to find two type arguments for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.codeQlTypeArgumentsCount}",
"Expected to find two type arguments for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.typeArgumentsCount}",
c
)
return
}
val valueArg = c.codeQlGetValueArgument(0)
val valueArg = c.getValueArgument(0)
if (valueArg == null) {
logger.errorElement(
"Cannot find value argument for a kotlin.jvm.internal.<unsafe-coerce>() call",
@@ -4905,7 +4905,7 @@ open class KotlinFileExtractor(
)
return
}
val typeArg = c.codeQlGetTypeArgument(1)
val typeArg = c.getTypeArgument(1)
if (typeArg == null) {
logger.errorElement(
"Cannot find type argument for a kotlin.jvm.internal.<unsafe-coerce>() call",
@@ -4924,7 +4924,7 @@ open class KotlinFileExtractor(
extractExpressionExpr(valueArg, callable, id, 1, enclosingStmt)
}
isBuiltinCallInternal(c, "dataClassArrayMemberToString") -> {
val arrayArg = c.codeQlGetValueArgument(0)
val arrayArg = c.getValueArgument(0)
val realArrayClass = arrayArg?.type?.classOrNull
if (realArrayClass == null) {
logger.errorElement(
@@ -4936,8 +4936,8 @@ open class KotlinFileExtractor(
val realCallee =
javaUtilArrays?.declarations?.findSubType<IrFunction> { decl ->
decl.name.asString() == "toString" &&
decl.codeQlValueParameters.size == 1 &&
decl.codeQlValueParameters[0].type.classOrNull?.let {
decl.valueParameters.size == 1 &&
decl.valueParameters[0].type.classOrNull?.let {
it == realArrayClass
} == true
}
@@ -4962,7 +4962,7 @@ open class KotlinFileExtractor(
}
}
isBuiltinCallInternal(c, "dataClassArrayMemberHashCode") -> {
val arrayArg = c.codeQlGetValueArgument(0)
val arrayArg = c.getValueArgument(0)
val realArrayClass = arrayArg?.type?.classOrNull
if (realArrayClass == null) {
logger.errorElement(
@@ -4974,8 +4974,8 @@ open class KotlinFileExtractor(
val realCallee =
javaUtilArrays?.declarations?.findSubType<IrFunction> { decl ->
decl.name.asString() == "hashCode" &&
decl.codeQlValueParameters.size == 1 &&
decl.codeQlValueParameters[0].type.classOrNull?.let {
decl.valueParameters.size == 1 &&
decl.valueParameters[0].type.classOrNull?.let {
it == realArrayClass
} == true
}
@@ -5155,7 +5155,7 @@ open class KotlinFileExtractor(
val type = useType(eType)
val isAnonymous = eType.isAnonymous
val locId = tw.getLocation(e)
val valueArgs = (0 until e.codeQlValueArgumentsCount).map { e.codeQlGetValueArgument(it) }
val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) }
val id =
if (
@@ -5211,10 +5211,10 @@ open class KotlinFileExtractor(
realCallTarget is IrConstructor &&
realCallTarget.parentClassOrNull?.fqNameWhenAvailable?.asString() ==
"kotlin.Enum" &&
realCallTarget.codeQlValueParameters.size == 2 &&
realCallTarget.codeQlValueParameters[0].type ==
realCallTarget.valueParameters.size == 2 &&
realCallTarget.valueParameters[0].type ==
pluginContext.irBuiltIns.stringType &&
realCallTarget.codeQlValueParameters[1].type == pluginContext.irBuiltIns.intType
realCallTarget.valueParameters[1].type == pluginContext.irBuiltIns.intType
) {
val id0 =
@@ -5287,7 +5287,7 @@ open class KotlinFileExtractor(
}
val args =
(0 until e.codeQlTypeArgumentsCount).map { e.codeQlGetTypeArgument(it) }.requireNoNullsOrNull()
(0 until e.typeArgumentsCount).map { e.getTypeArgument(it) }.requireNoNullsOrNull()
if (args == null) {
logger.warnElement("Found null type argument in enum constructor call", e)
return
@@ -5365,7 +5365,7 @@ open class KotlinFileExtractor(
// Check for an expression like x = get(x).op(e):
val opReceiver = updateRhs.dispatchReceiver
if (isExpectedLhs(opReceiver)) {
updateRhs.codeQlGetValueArgument(0)
updateRhs.getValueArgument(0)
} else null
} else null
}
@@ -5560,7 +5560,7 @@ open class KotlinFileExtractor(
"set"
)
) {
val updateRhs0 = arraySetCall.codeQlGetValueArgument(1)
val updateRhs0 = arraySetCall.getValueArgument(1)
if (updateRhs0 == null) {
logger.errorElement("Update RHS not found", e)
return false
@@ -6403,12 +6403,12 @@ open class KotlinFileExtractor(
val ids = getLocallyVisibleFunctionLabels(e.function)
val locId = tw.getLocation(e)
val ext = e.function.codeQlExtensionReceiverParameter
val ext = e.function.extensionReceiverParameter
val parameters =
if (ext != null) {
listOf(ext) + e.function.codeQlValueParameters
listOf(ext) + e.function.valueParameters
} else {
e.function.codeQlValueParameters
e.function.valueParameters
}
var types = parameters.map { it.type }
@@ -6670,7 +6670,7 @@ open class KotlinFileExtractor(
is IrFunction -> {
if (
ownerParent.dispatchReceiverParameter == owner &&
ownerParent.codeQlExtensionReceiverParameter != null
ownerParent.extensionReceiverParameter != null
) {
val ownerParent2 = ownerParent.parent
@@ -7089,7 +7089,7 @@ open class KotlinFileExtractor(
makeReceiverInfo(callableReferenceExpr.dispatchReceiver, 0)
private val extensionReceiverInfo =
makeReceiverInfo(
callableReferenceExpr.codeQlExtensionReceiver,
callableReferenceExpr.extensionReceiver,
if (dispatchReceiverInfo == null) 0 else 1
)
@@ -7627,8 +7627,8 @@ open class KotlinFileExtractor(
}
val expressionTypeArguments =
(0 until propertyReferenceExpr.codeQlTypeArgumentsCount).mapNotNull {
propertyReferenceExpr.codeQlGetTypeArgument(it)
(0 until propertyReferenceExpr.typeArgumentsCount).mapNotNull {
propertyReferenceExpr.getTypeArgument(it)
}
val idPropertyRef = tw.getFreshIdLabel<DbPropertyref>()
@@ -7829,7 +7829,7 @@ open class KotlinFileExtractor(
if (
functionReferenceExpr.dispatchReceiver != null &&
functionReferenceExpr.codeQlExtensionReceiver != null
functionReferenceExpr.extensionReceiver != null
) {
logger.errorElement(
"Unexpected: dispatchReceiver and extensionReceiver are both non-null",
@@ -7840,7 +7840,7 @@ open class KotlinFileExtractor(
if (
target.owner.dispatchReceiverParameter != null &&
target.owner.codeQlExtensionReceiverParameter != null
target.owner.extensionReceiverParameter != null
) {
logger.errorElement(
"Unexpected: dispatch and extension parameters are both non-null",
@@ -7899,8 +7899,8 @@ open class KotlinFileExtractor(
null
}
expressionTypeArguments =
(0 until functionReferenceExpr.codeQlTypeArgumentsCount).mapNotNull {
functionReferenceExpr.codeQlGetTypeArgument(it)
(0 until functionReferenceExpr.typeArgumentsCount).mapNotNull {
functionReferenceExpr.getTypeArgument(it)
}
dispatchReceiverIdx = -1
}
@@ -7965,7 +7965,7 @@ open class KotlinFileExtractor(
functionReferenceExpr,
declarationParent,
null,
{ it.codeQlValueParameters.size == 1 }
{ it.valueParameters.size == 1 }
) {
// The argument to FunctionReference's constructor is the function arity.
extractConstantInteger(
@@ -8572,7 +8572,7 @@ open class KotlinFileExtractor(
reverse: Boolean = false
) {
val typeArguments =
(0 until c.codeQlTypeArgumentsCount).map { c.codeQlGetTypeArgument(it) }.requireNoNullsOrNull()
(0 until c.typeArgumentsCount).map { c.getTypeArgument(it) }.requireNoNullsOrNull()
if (typeArguments == null) {
logger.errorElement("Found a null type argument for a member access expression", c)
} else {
@@ -8923,11 +8923,11 @@ open class KotlinFileExtractor(
tw.writeVariableBinding(lhsId, fieldId)
val parameters = mutableListOf<IrValueParameter>()
val extParam = samMember.codeQlExtensionReceiverParameter
val extParam = samMember.extensionReceiverParameter
if (extParam != null) {
parameters.add(extParam)
}
parameters.addAll(samMember.codeQlValueParameters)
parameters.addAll(samMember.valueParameters)
fun extractArgument(
p: IrValueParameter,
@@ -9032,7 +9032,7 @@ open class KotlinFileExtractor(
elementToReportOn: IrElement,
declarationParent: IrDeclarationParent,
compilerGeneratedKindOverride: CompilerGeneratedKinds? = null,
superConstructorSelector: (IrFunction) -> Boolean = { it.codeQlValueParameters.isEmpty() },
superConstructorSelector: (IrFunction) -> Boolean = { it.valueParameters.isEmpty() },
extractSuperconstructorArgs: (Label<DbSuperconstructorinvocationstmt>) -> Unit = {},
): Label<out DbClassorinterface> {
// Write class

View File

@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.*
import com.github.codeql.utils.versions.codeQlAddAnnotations
import org.jetbrains.kotlin.ir.types.addAnnotations
import org.jetbrains.kotlin.ir.types.classFqName
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.classOrNull
@@ -355,7 +355,7 @@ open class KotlinUsesExtractor(
}
private fun propertySignature(p: IrProperty) =
((p.getter ?: p.setter)?.codeQlExtensionReceiverParameter?.let {
((p.getter ?: p.setter)?.extensionReceiverParameter?.let {
useType(erase(it.type)).javaResult.signature
} ?: "")
@@ -368,7 +368,7 @@ open class KotlinUsesExtractor(
// useDeclarationParent -> useFunction
// -> extractFunctionLaterIfExternalFileMember, which would result for `fun <T> f(t:
// T) { ... }` for example.
(listOfNotNull(d.codeQlExtensionReceiverParameter) + d.codeQlValueParameters)
(listOfNotNull(d.extensionReceiverParameter) + d.valueParameters)
.map { useType(erase(it.type)).javaResult.signature }
.joinToString(separator = ",", prefix = "(", postfix = ")")
is IrProperty -> propertySignature(d) + externalClassExtractor.propertySignature
@@ -488,8 +488,8 @@ open class KotlinUsesExtractor(
val result =
replacementClass.declarations.findSubType<IrSimpleFunction> { replacementDecl ->
replacementDecl.name == f.name &&
replacementDecl.codeQlValueParameters.size == f.codeQlValueParameters.size &&
replacementDecl.codeQlValueParameters.zip(f.codeQlValueParameters).all {
replacementDecl.valueParameters.size == f.valueParameters.size &&
replacementDecl.valueParameters.zip(f.valueParameters).all {
erase(it.first.type) == erase(it.second.type)
}
}
@@ -1265,7 +1265,7 @@ open class KotlinUsesExtractor(
private fun getWildcardSuppressionDirective(t: IrAnnotationContainer): Boolean? =
t.getAnnotation(jvmWildcardSuppressionAnnotation)?.let {
@Suppress("USELESS_CAST") // `as? Boolean` is not needed for Kotlin < 2.1
(it.codeQlGetValueArgument(0) as? CodeQLIrConst<Boolean>)?.value as? Boolean ?: true
(it.getValueArgument(0) as? CodeQLIrConst<Boolean>)?.value as? Boolean ?: true
}
private fun addJavaLoweringArgumentWildcards(
@@ -1376,9 +1376,9 @@ open class KotlinUsesExtractor(
f.parent,
parentId,
getFunctionShortName(f).nameInDB,
(maybeParameterList ?: f.codeQlValueParameters).map { it.type },
(maybeParameterList ?: f.valueParameters).map { it.type },
getAdjustedReturnType(f),
f.codeQlExtensionReceiverParameter?.type,
f.extensionReceiverParameter?.type,
getFunctionTypeParameters(f),
classTypeArgsIncludingOuterClasses,
overridesCollectionsMethodWithAlteredParameterTypes(f),
@@ -1401,12 +1401,12 @@ open class KotlinUsesExtractor(
// The name of the function; normally f.name.asString().
name: String,
// The types of the value parameters that the functions takes; normally
// f.codeQlValueParameters.map { it.type }.
// f.valueParameters.map { it.type }.
parameterTypes: List<IrType>,
// The return type of the function; normally f.returnType.
returnType: IrType,
// The extension receiver of the function, if any; normally
// f.codeQlExtensionReceiverParameter?.type.
// f.extensionReceiverParameter?.type.
extensionParamType: IrType?,
// The type parameters of the function. This does not include type parameters of enclosing
// classes.
@@ -1579,7 +1579,7 @@ open class KotlinUsesExtractor(
parentClass.fqNameWhenAvailable?.asString() !=
"java.util.concurrent.ConcurrentHashMap" ||
getFunctionShortName(f).nameInDB != "keySet" ||
f.codeQlValueParameters.isNotEmpty() ||
f.valueParameters.isNotEmpty() ||
f.returnType.classFqName?.asString() != "kotlin.collections.MutableSet"
) {
return f.returnType
@@ -1587,7 +1587,7 @@ open class KotlinUsesExtractor(
val otherKeySet =
parentClass.declarations.findSubType<IrFunction> {
it.name.asString() == "keySet" && it.codeQlValueParameters.size == 1
it.name.asString() == "keySet" && it.valueParameters.size == 1
} ?: return f.returnType
return otherKeySet.returnType.codeQlWithHasQuestionMark(false)
@@ -1695,8 +1695,8 @@ open class KotlinUsesExtractor(
javaClass.declarations.findSubType<IrFunction> { decl ->
!decl.isFakeOverride &&
decl.name.asString() == jvmName &&
decl.codeQlValueParameters.size == f.codeQlValueParameters.size &&
decl.codeQlValueParameters.zip(f.codeQlValueParameters).all { p ->
decl.valueParameters.size == f.valueParameters.size &&
decl.valueParameters.zip(f.valueParameters).all { p ->
erase(p.first.type).classifierOrNull ==
erase(p.second.type).classifierOrNull
}
@@ -2125,7 +2125,7 @@ open class KotlinUsesExtractor(
}
return if (t.arguments.isNotEmpty())
t.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
t.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
else t
}
}
@@ -2153,7 +2153,7 @@ open class KotlinUsesExtractor(
val idxOffset =
if (
declarationParent is IrFunction &&
declarationParent.codeQlExtensionReceiverParameter != null
declarationParent.extensionReceiverParameter != null
)
// For extension functions increase the index to match what the java extractor sees:
1
@@ -2187,7 +2187,7 @@ open class KotlinUsesExtractor(
// Gets a field's corresponding property's extension receiver type, if any
fun getExtensionReceiverType(f: IrField) =
f.correspondingPropertySymbol?.owner?.let {
(it.getter ?: it.setter)?.codeQlExtensionReceiverParameter?.type
(it.getter ?: it.setter)?.extensionReceiverParameter?.type
}
fun getFieldLabel(f: IrField): String {
@@ -2222,14 +2222,14 @@ open class KotlinUsesExtractor(
val setter = p.setter
val func = getter ?: setter
val ext = func?.codeQlExtensionReceiverParameter
val ext = func?.extensionReceiverParameter
return if (ext == null) {
"@\"property;{$parentId};${p.name.asString()}\""
} else {
val returnType =
getter?.returnType
?: setter?.codeQlValueParameters?.singleOrNull()?.type
?: setter?.valueParameters?.singleOrNull()?.type
?: pluginContext.irBuiltIns.unitType
val typeParams = getFunctionTypeParameters(func)

View File

@@ -1,10 +1,5 @@
package com.github.codeql
import com.github.codeql.utils.versions.codeQlAnnotationFromSymbolOwner
import com.github.codeql.utils.versions.codeQlGetValueArgument
import com.github.codeql.utils.versions.codeQlPutValueArgument
import com.github.codeql.utils.versions.codeQlSetAnnotations
import com.github.codeql.utils.versions.codeQlSetDispatchReceiverParameter
import com.github.codeql.utils.versions.createImplicitParameterDeclarationWithWrappedDescriptor
import java.lang.annotation.ElementType
import java.util.HashSet
@@ -100,7 +95,7 @@ class MetaAnnotationSupport(
JvmAnnotationNames.REPEATABLE_ANNOTATION
}
return if (jvmRepeatable != null) {
((jvmRepeatable.codeQlGetValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)
((jvmRepeatable.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)
?.owner
} else {
getOrCreateSyntheticRepeatableAnnotationContainer(annotationClass)
@@ -122,12 +117,12 @@ class MetaAnnotationSupport(
)
return null
} else {
return codeQlAnnotationFromSymbolOwner(
return IrConstructorCallImpl.fromSymbolOwner(
containerClass.defaultType,
containerConstructor.symbol
)
.apply {
codeQlPutValueArgument(
putValueArgument(
0,
IrVarargImpl(
UNDEFINED_OFFSET,
@@ -149,7 +144,7 @@ class MetaAnnotationSupport(
// Taken from AdditionalClassAnnotationLowering.kt
private fun loadAnnotationTargets(targetEntry: IrConstructorCall): Set<KotlinTarget>? {
val valueArgument = targetEntry.codeQlGetValueArgument(0) as? IrVararg ?: return null
val valueArgument = targetEntry.getValueArgument(0) as? IrVararg ?: return null
return valueArgument.elements
.filterIsInstance<IrGetEnumValue>()
.mapNotNull { KotlinTarget.valueOrNull(it.symbol.owner.name.asString()) }
@@ -235,14 +230,14 @@ class MetaAnnotationSupport(
)
}
return codeQlAnnotationFromSymbolOwner(
return IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
targetConstructor.returnType,
targetConstructor.symbol,
0
)
.apply { codeQlPutValueArgument(0, vararg) }
.apply { putValueArgument(0, vararg) }
}
private val javaAnnotationRetention by lazy {
@@ -268,7 +263,7 @@ class MetaAnnotationSupport(
// Taken from AnnotationCodegen.kt (not available in Kotlin < 1.6.20)
private fun IrClass.getAnnotationRetention(): KotlinRetention? {
val retentionArgument =
getAnnotation(StandardNames.FqNames.retention)?.codeQlGetValueArgument(0) as? IrGetEnumValue
getAnnotation(StandardNames.FqNames.retention)?.getValueArgument(0) as? IrGetEnumValue
?: return null
val retentionArgumentValue = retentionArgument.symbol.owner
return KotlinRetention.valueOf(retentionArgumentValue.name.asString())
@@ -288,7 +283,7 @@ class MetaAnnotationSupport(
val targetConstructor =
retentionType.declarations.firstIsInstanceOrNull<IrConstructor>() ?: return null
return codeQlAnnotationFromSymbolOwner(
return IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
targetConstructor.returnType,
@@ -296,7 +291,7 @@ class MetaAnnotationSupport(
0
)
.apply {
codeQlPutValueArgument(
putValueArgument(
0,
IrGetEnumValueImpl(
UNDEFINED_OFFSET,
@@ -338,7 +333,7 @@ class MetaAnnotationSupport(
return
}
val newParam = thisReceiever.copyTo(this)
codeQlSetDispatchReceiverParameter(newParam)
dispatchReceiverParameter = newParam
body =
factory
.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
@@ -411,7 +406,7 @@ class MetaAnnotationSupport(
val repeatableContainerAnnotation =
kotlinAnnotationRepeatableContainer?.constructors?.single()
codeQlSetAnnotations(containerClass,
containerClass.annotations =
annotationClass.annotations
.filter {
it.isAnnotationWithEqualFqName(StandardNames.FqNames.retention) ||
@@ -420,7 +415,7 @@ class MetaAnnotationSupport(
.map { it.deepCopyWithSymbols(containerClass) } +
listOfNotNull(
repeatableContainerAnnotation?.let {
codeQlAnnotationFromSymbolOwner(
IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
it.returnType,
@@ -429,7 +424,6 @@ class MetaAnnotationSupport(
)
}
)
)
containerClass
}
@@ -468,14 +462,14 @@ class MetaAnnotationSupport(
containerClass.symbol,
containerClass.defaultType
)
return codeQlAnnotationFromSymbolOwner(
return IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
repeatableConstructor.returnType,
repeatableConstructor.symbol,
0
)
.apply { codeQlPutValueArgument(0, containerReference) }
.apply { putValueArgument(0, containerReference) }
}
private val javaAnnotationDocumented by lazy {
@@ -494,7 +488,7 @@ class MetaAnnotationSupport(
javaAnnotationDocumented?.declarations?.firstIsInstanceOrNull<IrConstructor>()
?: return null
return codeQlAnnotationFromSymbolOwner(
return IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
documentedConstructor.returnType,

View File

@@ -1,7 +1,6 @@
package com.github.codeql
import com.github.codeql.KotlinUsesExtractor.LocallyVisibleFunctionLabels
import com.github.codeql.utils.versions.codeQlExtensionReceiver
import com.semmle.extractor.java.PopulateFile
import com.semmle.util.unicode.UTF8Util
import java.io.BufferedWriter
@@ -332,7 +331,7 @@ open class FileTrapWriter(
is IrCall -> {
// Calls have incorrect startOffset, so we adjust them:
val dr = e.dispatchReceiver?.let { getStartOffset(it) }
val er = e.codeQlExtensionReceiver?.let { getStartOffset(it) }
val er = e.extensionReceiver?.let { getStartOffset(it) }
offsetMinOf(e.startOffset, dr, er)
}
else -> e.startOffset

View File

@@ -2,7 +2,6 @@ package com.github.codeql.comments
import com.github.codeql.*
import com.github.codeql.utils.isLocalFunction
import com.github.codeql.utils.versions.codeQlExtensionReceiverParameter
import com.github.codeql.utils.versions.isDispatchReceiver
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
@@ -12,7 +11,7 @@ import org.jetbrains.kotlin.ir.util.parentClassOrNull
private fun IrValueParameter.isExtensionReceiver(): Boolean {
val parentFun = parent as? IrFunction ?: return false
return parentFun.codeQlExtensionReceiverParameter == this
return parentFun.extensionReceiverParameter == this
}
open class CommentExtractor(

View File

@@ -1,8 +1,6 @@
package com.github.codeql.utils
import com.github.codeql.utils.versions.CodeQLIrConst
import com.github.codeql.utils.versions.codeQlGetValueArgument
import com.github.codeql.utils.versions.codeQlValueArgumentsCount
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -78,9 +76,9 @@ private fun getSpecialJvmName(f: IrFunction): String? {
fun getJvmName(container: IrAnnotationContainer): String? {
for (a: IrConstructorCall in container.annotations) {
val t = a.type
if (t is IrSimpleType && a.codeQlValueArgumentsCount == 1) {
if (t is IrSimpleType && a.valueArgumentsCount == 1) {
val owner = t.classifier.owner
val v = a.codeQlGetValueArgument(0)
val v = a.getValueArgument(0)
if (owner is IrClass) {
val aPkg = owner.packageFqName?.asString()
val name = owner.name.asString()

View File

@@ -18,7 +18,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol
import com.github.codeql.utils.versions.codeQlAddAnnotations
import org.jetbrains.kotlin.ir.types.addAnnotations
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.makeNotNull
import org.jetbrains.kotlin.ir.types.makeNullable
@@ -192,7 +192,7 @@ object RawTypeAnnotation {
addConstructor { isPrimary = true }
}
val constructor = annoClass.constructors.single()
codeQlAnnotationFromSymbolOwner(constructor.constructedClassType, constructor.symbol)
IrConstructorCallImpl.fromSymbolOwner(constructor.constructedClassType, constructor.symbol)
}
}
@@ -202,7 +202,7 @@ fun IrType.toRawType(): IrType =
when (val owner = this.classifier.owner) {
is IrClass -> {
if (this.arguments.isNotEmpty())
this.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
this.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
else this
}
is IrTypeParameter -> owner.superTypes[0].toRawType()
@@ -215,7 +215,7 @@ fun IrType.toRawType(): IrType =
fun IrClass.toRawType(): IrType {
val result = this.typeWith(listOf())
return if (this.typeParameters.isNotEmpty())
result.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
result.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
else result
}

View File

@@ -1,70 +0,0 @@
package com.github.codeql.utils.versions
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.addAnnotations
/**
* Compatibility accessors for pre-2.4.0 API patterns.
* In pre-2.4.0 versions, these delegate directly to the existing APIs.
*/
// IrFunction: valueParameters
val IrFunction.codeQlValueParameters: List<IrValueParameter>
get() = valueParameters
// IrFunction: extensionReceiverParameter
val IrFunction.codeQlExtensionReceiverParameter: IrValueParameter?
get() = extensionReceiverParameter
// IrMemberAccessExpression: valueArgumentsCount
val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int
get() = valueArgumentsCount
// IrMemberAccessExpression: getValueArgument
fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = getValueArgument(index)
// IrMemberAccessExpression: putValueArgument
fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) {
putValueArgument(index, value)
}
// IrMemberAccessExpression: extensionReceiver
val IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression?
get() = extensionReceiver
// IrMemberAccessExpression: typeArgumentsCount
val IrMemberAccessExpression<*>.codeQlTypeArgumentsCount: Int
get() = typeArgumentsCount
// IrMemberAccessExpression: getTypeArgument
fun IrMemberAccessExpression<*>.codeQlGetTypeArgument(index: Int): IrType? = getTypeArgument(index)
// addAnnotations compat: in pre-2.4.0, addAnnotations expects List<IrConstructorCall>
fun IrType.codeQlAddAnnotations(annotations: List<IrConstructorCall>): IrType =
addAnnotations(annotations)
// IrMutableAnnotationContainer.annotations setter: in pre-2.4.0, annotations is var with List<IrConstructorCall>
fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List<IrConstructorCall>) {
container.annotations = annotations
}
// IrFunction: set dispatch receiver parameter (pre-2.4.0 it's a var)
fun IrFunction.codeQlSetDispatchReceiverParameter(param: IrValueParameter?) {
dispatchReceiverParameter = param
}
// In pre-2.4.0, annotations are List<IrConstructorCall> so IrConstructorCallImpl works directly.
fun codeQlAnnotationFromSymbolOwner(
startOffset: Int, endOffset: Int, type: IrType, symbol: IrConstructorSymbol, typeArgumentsCount: Int
): IrConstructorCall =
IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, type, symbol, typeArgumentsCount)
fun codeQlAnnotationFromSymbolOwner(type: IrType, symbol: IrConstructorSymbol): IrConstructorCall =
IrConstructorCallImpl.fromSymbolOwner(type, symbol)

View File

@@ -3,34 +3,10 @@
package com.github.codeql
import com.intellij.mock.MockProject
import com.intellij.openapi.extensions.LoadingOrder
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.jetbrains.kotlin.config.CompilerConfiguration
@OptIn(ExperimentalCompilerApi::class)
abstract class Kotlin2ComponentRegistrar : ComponentRegistrar {
/* Nothing to do; supportsK2 doesn't exist yet. */
private var project: MockProject? = null
override fun registerProjectComponents(
project: MockProject,
configuration: CompilerConfiguration
) {
this.project = project
doRegisterExtensions(configuration)
}
abstract fun doRegisterExtensions(configuration: CompilerConfiguration)
fun registerExtractorExtension(extension: IrGenerationExtension) {
val p = project ?: throw IllegalStateException("registerExtractorExtension called before registerProjectComponents")
// Register with LoadingOrder.LAST to ensure the extractor runs after other
// IR generation plugins (like kotlinx.serialization) have generated their code.
val extensionPoint = p.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName)
extensionPoint.registerExtension(extension, LoadingOrder.LAST, p)
}
}

View File

@@ -3,35 +3,11 @@
package com.github.codeql
import com.intellij.mock.MockProject
import com.intellij.openapi.extensions.LoadingOrder
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.jetbrains.kotlin.config.CompilerConfiguration
@OptIn(ExperimentalCompilerApi::class)
abstract class Kotlin2ComponentRegistrar : ComponentRegistrar {
override val supportsK2: Boolean
get() = true
private var project: MockProject? = null
override fun registerProjectComponents(
project: MockProject,
configuration: CompilerConfiguration
) {
this.project = project
doRegisterExtensions(configuration)
}
abstract fun doRegisterExtensions(configuration: CompilerConfiguration)
fun registerExtractorExtension(extension: IrGenerationExtension) {
val p = project ?: throw IllegalStateException("registerExtractorExtension called before registerProjectComponents")
// Register with LoadingOrder.LAST to ensure the extractor runs after other
// IR generation plugins (like kotlinx.serialization) have generated their code.
val extensionPoint = p.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName)
extensionPoint.registerExtension(extension, LoadingOrder.LAST, p)
}
}

View File

@@ -1,123 +0,0 @@
@file:Suppress("DEPRECATION")
package com.github.codeql.utils.versions
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrAnnotation
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrAnnotationImpl
import org.jetbrains.kotlin.ir.expressions.impl.fromSymbolOwner
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.addAnnotations
/**
* Compatibility accessors for pre-2.4.0 API patterns.
* In 2.4.0, valueParameters/extensionReceiverParameter/extensionReceiver/
* getValueArgument/putValueArgument/valueArgumentsCount/typeArgumentsCount/getTypeArgument
* have been removed. This file provides the 2.4.0 implementations.
*/
// IrFunction: valueParameters -> parameters filtered to Regular kind
val IrFunction.codeQlValueParameters: List<IrValueParameter>
get() = parameters.filter { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.Regular }
// IrFunction: extensionReceiverParameter
val IrFunction.codeQlExtensionReceiverParameter: IrValueParameter?
get() = parameters.firstOrNull { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.ExtensionReceiver }
// Helper: get the offset of value arguments in the arguments list
private fun IrMemberAccessExpression<*>.valueArgumentOffset(): Int {
val owner = symbol.owner as? IrFunction ?: return 0
return owner.parameters.count { it.kind != org.jetbrains.kotlin.ir.declarations.IrParameterKind.Regular }
}
// IrMemberAccessExpression: valueArgumentsCount
// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params
val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int
get() = arguments.size - valueArgumentOffset()
// IrMemberAccessExpression: getValueArgument
// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params
fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = arguments[index + valueArgumentOffset()]
// IrMemberAccessExpression: putValueArgument
// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params
fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) {
arguments[index + valueArgumentOffset()] = value
}
// Re-add accessor for the extensionReceiver property removed in Kotlin 2.4.0.
val IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression?
get() {
val erp = extensionReceiverParameterIndex() ?: return null
return arguments[erp]
}
// Find the argument index corresponding to the extension receiver parameter.
// Calls and function references expose an IrFunction owner directly; property
// references need to look through their getter or setter.
private fun IrMemberAccessExpression<*>.extensionReceiverParameterIndex(): Int? {
// Direct function owner (IrCall, IrFunctionReference, etc.)
(symbol.owner as? IrFunction)?.codeQlExtensionReceiverParameter?.let {
return it.indexInParameters
}
// Property reference: look at getter or setter function
(this as? org.jetbrains.kotlin.ir.expressions.IrPropertyReference)?.let { propRef ->
propRef.getter?.owner?.codeQlExtensionReceiverParameter?.let {
return it.indexInParameters
}
propRef.setter?.owner?.codeQlExtensionReceiverParameter?.let {
return it.indexInParameters
}
}
return null
}
// IrMemberAccessExpression: typeArgumentsCount
val IrMemberAccessExpression<*>.codeQlTypeArgumentsCount: Int
get() = typeArguments.size
// IrMemberAccessExpression: getTypeArgument
fun IrMemberAccessExpression<*>.codeQlGetTypeArgument(index: Int): IrType? = typeArguments[index]
// addAnnotations compat: in 2.4.0, addAnnotations expects List<IrAnnotation>
// IrConstructorCall implements IrAnnotation in 2.4.0, so filterIsInstance is identity
fun IrType.codeQlAddAnnotations(annotations: List<IrConstructorCall>): IrType =
addAnnotations(annotations.filterIsInstance<IrAnnotation>())
// IrMutableAnnotationContainer.annotations setter: in 2.4.0, expects List<IrAnnotation>
fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List<IrConstructorCall>) {
container.annotations = annotations.filterIsInstance<IrAnnotation>()
}
// IrFunction: set dispatch receiver parameter
// In 2.4.0, dispatchReceiverParameter is val; modify the parameters list directly.
fun IrFunction.codeQlSetDispatchReceiverParameter(param: IrValueParameter?) {
val existing = parameters.indexOfFirst { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.DispatchReceiver }
val mutableParams = parameters.toMutableList()
if (existing >= 0) {
if (param != null) {
mutableParams[existing] = param
} else {
mutableParams.removeAt(existing)
}
} else if (param != null) {
param.kind = org.jetbrains.kotlin.ir.declarations.IrParameterKind.DispatchReceiver
mutableParams.add(0, param)
}
parameters = mutableParams
}
// In 2.4.0, annotation lists require IrAnnotation instances.
// Use IrAnnotationImpl.fromSymbolOwner instead of IrConstructorCallImpl.fromSymbolOwner.
fun codeQlAnnotationFromSymbolOwner(
startOffset: Int, endOffset: Int, type: IrType, symbol: IrConstructorSymbol, typeArgumentsCount: Int
): IrConstructorCall =
IrAnnotationImpl.fromSymbolOwner(startOffset, endOffset, type, symbol, typeArgumentsCount)
fun codeQlAnnotationFromSymbolOwner(type: IrType, symbol: IrConstructorSymbol): IrConstructorCall =
IrAnnotationImpl.fromSymbolOwner(type, symbol)

View File

@@ -1,45 +0,0 @@
package com.github.codeql
import com.intellij.mock.MockProject
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.jetbrains.kotlin.config.CompilerConfiguration
@OptIn(ExperimentalCompilerApi::class)
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
abstract class Kotlin2ComponentRegistrar :
CompilerPluginRegistrar(),
org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar {
override val supportsK2: Boolean
get() = true
override val pluginId: String
get() = "kotlin-extractor"
// ComponentRegistrar implementation (legacy path, still called by Kotlin compiler)
override fun registerProjectComponents(
project: MockProject,
configuration: CompilerConfiguration
) {
// Registration is done via ExtensionStorage in Kotlin 2.4+.
// This legacy entry point remains for compatibility with service discovery.
}
private var extensionStorage: CompilerPluginRegistrar.ExtensionStorage? = null
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
this@Kotlin2ComponentRegistrar.extensionStorage = this
doRegisterExtensions(configuration)
}
abstract fun doRegisterExtensions(configuration: CompilerConfiguration)
protected fun registerExtractorExtension(extension: IrGenerationExtension) {
val storage = extensionStorage
?: throw IllegalStateException("registerExtractorExtension called before registerExtensions")
with(storage) {
IrGenerationExtension.registerExtension(extension)
}
}
}

View File

@@ -1,13 +0,0 @@
package com.github.codeql.utils.versions
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrParameterKind
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
fun parameterIndexExcludingReceivers(vp: IrValueParameter): Int {
val offset =
(vp.parent as? IrFunction)?.let { f ->
f.parameters.count { it.kind == IrParameterKind.DispatchReceiver || it.kind == IrParameterKind.ExtensionReceiver || it.kind == IrParameterKind.Context }
} ?: 0
return vp.indexInParameters - offset
}

View File

@@ -1 +0,0 @@
com.github.codeql.KotlinExtractorComponentRegistrar

View File

@@ -11,7 +11,6 @@ VERSIONS = [
"2.2.20-Beta2",
"2.3.0",
"2.3.20",
"2.4.0",
]
def _version_to_tuple(v):

View File

@@ -1,5 +1,5 @@
{
"markdownMessage": "The Kotlin version installed (`999.999.999`) is too recent for this version of CodeQL. Install a version lower than 2.4.10.",
"markdownMessage": "The Kotlin version installed (`999.999.999`) is too recent for this version of CodeQL. Install a version lower than 2.3.30.",
"severity": "error",
"source": {
"extractorName": "java",

View File

@@ -1,8 +1,6 @@
import pathlib
import pytest
@pytest.mark.kotlin1
def test(codeql, java_full):
java_srcs = " ".join([str(s) for s in pathlib.Path().glob("*.java")])
codeql.database.create(

View File

@@ -1,8 +1,6 @@
import commands
import pytest
@pytest.mark.kotlin1
def test(codeql, java_full):
commands.run("kotlinc -language-version 1.9 test.kt -d lib")
codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp lib")

View File

@@ -1,6 +1,2 @@
import pytest
@pytest.mark.kotlin1
def test(codeql, java_full):
codeql.database.create(command="kotlinc -J-Xmx2G -language-version 1.9 SomeClass.kt")

View File

@@ -1,8 +1,6 @@
import commands
import pytest
@pytest.mark.kotlin1
def test(codeql, java_full):
commands.run("kotlinc -language-version 1.9 A.kt")
codeql.database.create(command="kotlinc -cp . -language-version 1.9 B.kt C.kt")

View File

@@ -1,8 +1,6 @@
import commands
import pytest
@pytest.mark.kotlin1
def test(codeql, java_full):
commands.run(["javac", "Test.java", "-d", "bin"])
codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp bin")

View File

@@ -1,8 +1,6 @@
import commands
import pytest
@pytest.mark.kotlin1
def test(codeql, java_full):
# Compile the JavaDefns2 copy outside tracing, to make sure the Kotlin view of it matches the Java view seen by the traced javac compilation of JavaDefns.java below.
commands.run(["javac", "JavaDefns2.java"])

View File

@@ -1,4 +0,0 @@
---
category: feature
---
* Kotlin 2.4.0 can now be analysed.

View File

@@ -138,9 +138,7 @@ private module Ast implements AstSig<Location> {
final private class FinalCatchClause = J::CatchClause;
class CatchClause extends FinalCatchClause {
AstNode getPattern() { result = super.getVariable() }
AstNode getVariable() { none() }
AstNode getVariable() { result = super.getVariable() }
Expr getCondition() { none() }

View File

@@ -235,16 +235,15 @@
| Test.kt:84:11:84:18 | After (...)... | 4 | Test.kt:85:3:85:10 | Before return ... |
| Test.kt:84:11:84:18 | After (...)... | 5 | Test.kt:85:10:85:10 | 1 |
| Test.kt:84:11:84:18 | After (...)... | 6 | Test.kt:85:3:85:10 | return ... |
| Test.kt:86:4:88:2 | After catch (...) [match] | 0 | Test.kt:86:4:88:2 | After catch (...) [match] |
| Test.kt:86:4:88:2 | After catch (...) [match] | 1 | Test.kt:86:11:86:31 | e |
| Test.kt:86:4:88:2 | After catch (...) [match] | 2 | Test.kt:86:34:88:2 | { ... } |
| Test.kt:86:4:88:2 | After catch (...) [match] | 3 | Test.kt:87:3:87:10 | Before return ... |
| Test.kt:86:4:88:2 | After catch (...) [match] | 4 | Test.kt:87:10:87:10 | 2 |
| Test.kt:86:4:88:2 | After catch (...) [match] | 5 | Test.kt:87:3:87:10 | return ... |
| Test.kt:86:4:88:2 | After catch (...) [no-match] | 0 | Test.kt:86:4:88:2 | After catch (...) [no-match] |
| Test.kt:86:4:88:2 | After catch (...) [no-match] | 1 | Test.kt:82:1:89:1 | Exceptional Exit |
| Test.kt:86:4:88:2 | catch (...) | 0 | Test.kt:86:4:88:2 | catch (...) |
| Test.kt:86:4:88:2 | catch (...) | 1 | Test.kt:86:11:86:31 | e |
| Test.kt:86:11:86:31 | After e [match] | 0 | Test.kt:86:11:86:31 | After e [match] |
| Test.kt:86:11:86:31 | After e [match] | 1 | Test.kt:86:34:88:2 | { ... } |
| Test.kt:86:11:86:31 | After e [match] | 2 | Test.kt:87:3:87:10 | Before return ... |
| Test.kt:86:11:86:31 | After e [match] | 3 | Test.kt:87:10:87:10 | 2 |
| Test.kt:86:11:86:31 | After e [match] | 4 | Test.kt:87:3:87:10 | return ... |
| Test.kt:86:11:86:31 | After e [no-match] | 0 | Test.kt:86:11:86:31 | After e [no-match] |
| Test.kt:86:11:86:31 | After e [no-match] | 1 | Test.kt:86:4:88:2 | After catch (...) [no-match] |
| Test.kt:86:11:86:31 | After e [no-match] | 2 | Test.kt:82:1:89:1 | Exceptional Exit |
| Test.kt:91:1:98:1 | Entry | 0 | Test.kt:91:1:98:1 | Entry |
| Test.kt:91:1:98:1 | Entry | 1 | Test.kt:91:22:98:1 | { ... } |
| Test.kt:91:1:98:1 | Entry | 2 | Test.kt:92:2:97:2 | try ... |
@@ -263,16 +262,15 @@
| Test.kt:93:12:93:13 | After ...!! | 4 | Test.kt:94:3:94:10 | Before return ... |
| Test.kt:93:12:93:13 | After ...!! | 5 | Test.kt:94:10:94:10 | 1 |
| Test.kt:93:12:93:13 | After ...!! | 6 | Test.kt:94:3:94:10 | return ... |
| Test.kt:95:4:97:2 | After catch (...) [match] | 0 | Test.kt:95:4:97:2 | After catch (...) [match] |
| Test.kt:95:4:97:2 | After catch (...) [match] | 1 | Test.kt:95:11:95:33 | e |
| Test.kt:95:4:97:2 | After catch (...) [match] | 2 | Test.kt:95:36:97:2 | { ... } |
| Test.kt:95:4:97:2 | After catch (...) [match] | 3 | Test.kt:96:3:96:10 | Before return ... |
| Test.kt:95:4:97:2 | After catch (...) [match] | 4 | Test.kt:96:10:96:10 | 2 |
| Test.kt:95:4:97:2 | After catch (...) [match] | 5 | Test.kt:96:3:96:10 | return ... |
| Test.kt:95:4:97:2 | After catch (...) [no-match] | 0 | Test.kt:95:4:97:2 | After catch (...) [no-match] |
| Test.kt:95:4:97:2 | After catch (...) [no-match] | 1 | Test.kt:91:1:98:1 | Exceptional Exit |
| Test.kt:95:4:97:2 | catch (...) | 0 | Test.kt:95:4:97:2 | catch (...) |
| Test.kt:95:4:97:2 | catch (...) | 1 | Test.kt:95:11:95:33 | e |
| Test.kt:95:11:95:33 | After e [match] | 0 | Test.kt:95:11:95:33 | After e [match] |
| Test.kt:95:11:95:33 | After e [match] | 1 | Test.kt:95:36:97:2 | { ... } |
| Test.kt:95:11:95:33 | After e [match] | 2 | Test.kt:96:3:96:10 | Before return ... |
| Test.kt:95:11:95:33 | After e [match] | 3 | Test.kt:96:10:96:10 | 2 |
| Test.kt:95:11:95:33 | After e [match] | 4 | Test.kt:96:3:96:10 | return ... |
| Test.kt:95:11:95:33 | After e [no-match] | 0 | Test.kt:95:11:95:33 | After e [no-match] |
| Test.kt:95:11:95:33 | After e [no-match] | 1 | Test.kt:95:4:97:2 | After catch (...) [no-match] |
| Test.kt:95:11:95:33 | After e [no-match] | 2 | Test.kt:91:1:98:1 | Exceptional Exit |
| Test.kt:100:1:110:1 | Entry | 0 | Test.kt:100:1:110:1 | Entry |
| Test.kt:100:1:110:1 | Entry | 1 | Test.kt:100:25:110:1 | { ... } |
| Test.kt:100:1:110:1 | Entry | 2 | Test.kt:101:5:103:5 | <Expr>; |

View File

@@ -32,17 +32,17 @@
| Test.kt:82:21:89:1 | { ... } | Test.kt:82:1:89:1 | Normal Exit |
| Test.kt:82:21:89:1 | { ... } | Test.kt:84:7:84:7 | x |
| Test.kt:82:21:89:1 | { ... } | Test.kt:86:4:88:2 | catch (...) |
| Test.kt:82:21:89:1 | { ... } | Test.kt:86:34:88:2 | { ... } |
| Test.kt:82:21:89:1 | { ... } | Test.kt:86:11:86:31 | e |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:82:1:89:1 | Exceptional Exit |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:34:88:2 | { ... } |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:11:86:31 | e |
| Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Exceptional Exit |
| Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Exit |
| Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Normal Exit |
| Test.kt:91:22:98:1 | { ... } | Test.kt:93:7:93:7 | x |
| Test.kt:91:22:98:1 | { ... } | Test.kt:95:4:97:2 | catch (...) |
| Test.kt:91:22:98:1 | { ... } | Test.kt:95:36:97:2 | { ... } |
| Test.kt:91:22:98:1 | { ... } | Test.kt:95:11:95:33 | e |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:91:1:98:1 | Exceptional Exit |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:36:97:2 | { ... } |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:11:95:33 | e |
| Test.kt:100:25:110:1 | { ... } | Test.kt:100:1:110:1 | Exit |
| Test.kt:100:25:110:1 | { ... } | Test.kt:100:1:110:1 | Normal Exit |
| Test.kt:100:25:110:1 | { ... } | Test.kt:101:22:101:22 | y |

View File

@@ -20,16 +20,16 @@
| Test.kt:82:21:89:1 | { ... } | Test.kt:86:4:88:2 | catch (...) |
| Test.kt:84:7:84:7 | x | Test.kt:82:1:89:1 | Normal Exit |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:82:1:89:1 | Exceptional Exit |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:34:88:2 | { ... } |
| Test.kt:86:34:88:2 | { ... } | Test.kt:82:1:89:1 | Normal Exit |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:11:86:31 | e |
| Test.kt:86:11:86:31 | e | Test.kt:82:1:89:1 | Normal Exit |
| Test.kt:91:1:98:1 | Exceptional Exit | Test.kt:91:1:98:1 | Exit |
| Test.kt:91:1:98:1 | Normal Exit | Test.kt:91:1:98:1 | Exit |
| Test.kt:91:22:98:1 | { ... } | Test.kt:93:7:93:7 | x |
| Test.kt:91:22:98:1 | { ... } | Test.kt:95:4:97:2 | catch (...) |
| Test.kt:93:7:93:7 | x | Test.kt:91:1:98:1 | Normal Exit |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:91:1:98:1 | Exceptional Exit |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:36:97:2 | { ... } |
| Test.kt:95:36:97:2 | { ... } | Test.kt:91:1:98:1 | Normal Exit |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:11:95:33 | e |
| Test.kt:95:11:95:33 | e | Test.kt:91:1:98:1 | Normal Exit |
| Test.kt:100:1:110:1 | Normal Exit | Test.kt:100:1:110:1 | Exit |
| Test.kt:100:25:110:1 | { ... } | Test.kt:101:9:101:17 | After ... (value equals) ... [false] |
| Test.kt:100:25:110:1 | { ... } | Test.kt:101:22:101:22 | y |

View File

@@ -136,8 +136,8 @@
| Test.kt:84:11:84:18 | (...)... | CastExpr | Test.kt:86:4:88:2 | catch (...) | CatchClause |
| Test.kt:85:3:85:10 | return ... | ReturnStmt | Test.kt:82:1:89:1 | Normal Exit | Method |
| Test.kt:85:10:85:10 | 1 | IntegerLiteral | Test.kt:85:3:85:10 | return ... | ReturnStmt |
| Test.kt:86:4:88:2 | catch (...) | CatchClause | Test.kt:82:1:89:1 | Exceptional Exit | Method |
| Test.kt:86:4:88:2 | catch (...) | CatchClause | Test.kt:86:11:86:31 | e | LocalVariableDeclExpr |
| Test.kt:86:11:86:31 | e | LocalVariableDeclExpr | Test.kt:82:1:89:1 | Exceptional Exit | Method |
| Test.kt:86:11:86:31 | e | LocalVariableDeclExpr | Test.kt:86:34:88:2 | { ... } | BlockStmt |
| Test.kt:86:34:88:2 | { ... } | BlockStmt | Test.kt:87:10:87:10 | 2 | IntegerLiteral |
| Test.kt:87:3:87:10 | return ... | ReturnStmt | Test.kt:82:1:89:1 | Normal Exit | Method |
@@ -155,8 +155,8 @@
| Test.kt:93:12:93:13 | ...!! | NotNullExpr | Test.kt:95:4:97:2 | catch (...) | CatchClause |
| Test.kt:94:3:94:10 | return ... | ReturnStmt | Test.kt:91:1:98:1 | Normal Exit | Method |
| Test.kt:94:10:94:10 | 1 | IntegerLiteral | Test.kt:94:3:94:10 | return ... | ReturnStmt |
| Test.kt:95:4:97:2 | catch (...) | CatchClause | Test.kt:91:1:98:1 | Exceptional Exit | Method |
| Test.kt:95:4:97:2 | catch (...) | CatchClause | Test.kt:95:11:95:33 | e | LocalVariableDeclExpr |
| Test.kt:95:11:95:33 | e | LocalVariableDeclExpr | Test.kt:91:1:98:1 | Exceptional Exit | Method |
| Test.kt:95:11:95:33 | e | LocalVariableDeclExpr | Test.kt:95:36:97:2 | { ... } | BlockStmt |
| Test.kt:95:36:97:2 | { ... } | BlockStmt | Test.kt:96:10:96:10 | 2 | IntegerLiteral |
| Test.kt:96:3:96:10 | return ... | ReturnStmt | Test.kt:91:1:98:1 | Normal Exit | Method |

View File

@@ -235,16 +235,15 @@
| Test.kt:84:11:84:18 | After (...)... | 4 | Test.kt:85:3:85:10 | Before return ... |
| Test.kt:84:11:84:18 | After (...)... | 5 | Test.kt:85:10:85:10 | 1 |
| Test.kt:84:11:84:18 | After (...)... | 6 | Test.kt:85:3:85:10 | return ... |
| Test.kt:86:4:88:2 | After catch (...) [match] | 0 | Test.kt:86:4:88:2 | After catch (...) [match] |
| Test.kt:86:4:88:2 | After catch (...) [match] | 1 | Test.kt:86:11:86:31 | e |
| Test.kt:86:4:88:2 | After catch (...) [match] | 2 | Test.kt:86:34:88:2 | { ... } |
| Test.kt:86:4:88:2 | After catch (...) [match] | 3 | Test.kt:87:3:87:10 | Before return ... |
| Test.kt:86:4:88:2 | After catch (...) [match] | 4 | Test.kt:87:10:87:10 | 2 |
| Test.kt:86:4:88:2 | After catch (...) [match] | 5 | Test.kt:87:3:87:10 | return ... |
| Test.kt:86:4:88:2 | After catch (...) [no-match] | 0 | Test.kt:86:4:88:2 | After catch (...) [no-match] |
| Test.kt:86:4:88:2 | After catch (...) [no-match] | 1 | Test.kt:82:1:89:1 | Exceptional Exit |
| Test.kt:86:4:88:2 | catch (...) | 0 | Test.kt:86:4:88:2 | catch (...) |
| Test.kt:86:4:88:2 | catch (...) | 1 | Test.kt:86:11:86:31 | e |
| Test.kt:86:11:86:31 | After e [match] | 0 | Test.kt:86:11:86:31 | After e [match] |
| Test.kt:86:11:86:31 | After e [match] | 1 | Test.kt:86:34:88:2 | { ... } |
| Test.kt:86:11:86:31 | After e [match] | 2 | Test.kt:87:3:87:10 | Before return ... |
| Test.kt:86:11:86:31 | After e [match] | 3 | Test.kt:87:10:87:10 | 2 |
| Test.kt:86:11:86:31 | After e [match] | 4 | Test.kt:87:3:87:10 | return ... |
| Test.kt:86:11:86:31 | After e [no-match] | 0 | Test.kt:86:11:86:31 | After e [no-match] |
| Test.kt:86:11:86:31 | After e [no-match] | 1 | Test.kt:86:4:88:2 | After catch (...) [no-match] |
| Test.kt:86:11:86:31 | After e [no-match] | 2 | Test.kt:82:1:89:1 | Exceptional Exit |
| Test.kt:91:1:98:1 | Entry | 0 | Test.kt:91:1:98:1 | Entry |
| Test.kt:91:1:98:1 | Entry | 1 | Test.kt:91:22:98:1 | { ... } |
| Test.kt:91:1:98:1 | Entry | 2 | Test.kt:92:2:97:2 | try ... |
@@ -263,16 +262,15 @@
| Test.kt:93:11:93:13 | After ...!! | 4 | Test.kt:94:3:94:10 | Before return ... |
| Test.kt:93:11:93:13 | After ...!! | 5 | Test.kt:94:10:94:10 | 1 |
| Test.kt:93:11:93:13 | After ...!! | 6 | Test.kt:94:3:94:10 | return ... |
| Test.kt:95:4:97:2 | After catch (...) [match] | 0 | Test.kt:95:4:97:2 | After catch (...) [match] |
| Test.kt:95:4:97:2 | After catch (...) [match] | 1 | Test.kt:95:11:95:33 | e |
| Test.kt:95:4:97:2 | After catch (...) [match] | 2 | Test.kt:95:36:97:2 | { ... } |
| Test.kt:95:4:97:2 | After catch (...) [match] | 3 | Test.kt:96:3:96:10 | Before return ... |
| Test.kt:95:4:97:2 | After catch (...) [match] | 4 | Test.kt:96:10:96:10 | 2 |
| Test.kt:95:4:97:2 | After catch (...) [match] | 5 | Test.kt:96:3:96:10 | return ... |
| Test.kt:95:4:97:2 | After catch (...) [no-match] | 0 | Test.kt:95:4:97:2 | After catch (...) [no-match] |
| Test.kt:95:4:97:2 | After catch (...) [no-match] | 1 | Test.kt:91:1:98:1 | Exceptional Exit |
| Test.kt:95:4:97:2 | catch (...) | 0 | Test.kt:95:4:97:2 | catch (...) |
| Test.kt:95:4:97:2 | catch (...) | 1 | Test.kt:95:11:95:33 | e |
| Test.kt:95:11:95:33 | After e [match] | 0 | Test.kt:95:11:95:33 | After e [match] |
| Test.kt:95:11:95:33 | After e [match] | 1 | Test.kt:95:36:97:2 | { ... } |
| Test.kt:95:11:95:33 | After e [match] | 2 | Test.kt:96:3:96:10 | Before return ... |
| Test.kt:95:11:95:33 | After e [match] | 3 | Test.kt:96:10:96:10 | 2 |
| Test.kt:95:11:95:33 | After e [match] | 4 | Test.kt:96:3:96:10 | return ... |
| Test.kt:95:11:95:33 | After e [no-match] | 0 | Test.kt:95:11:95:33 | After e [no-match] |
| Test.kt:95:11:95:33 | After e [no-match] | 1 | Test.kt:95:4:97:2 | After catch (...) [no-match] |
| Test.kt:95:11:95:33 | After e [no-match] | 2 | Test.kt:91:1:98:1 | Exceptional Exit |
| Test.kt:100:1:110:1 | Entry | 0 | Test.kt:100:1:110:1 | Entry |
| Test.kt:100:1:110:1 | Entry | 1 | Test.kt:100:25:110:1 | { ... } |
| Test.kt:100:1:110:1 | Entry | 2 | Test.kt:101:5:103:5 | <Expr>; |

View File

@@ -32,17 +32,17 @@
| Test.kt:82:21:89:1 | { ... } | Test.kt:82:1:89:1 | Normal Exit |
| Test.kt:82:21:89:1 | { ... } | Test.kt:84:3:84:18 | x |
| Test.kt:82:21:89:1 | { ... } | Test.kt:86:4:88:2 | catch (...) |
| Test.kt:82:21:89:1 | { ... } | Test.kt:86:34:88:2 | { ... } |
| Test.kt:82:21:89:1 | { ... } | Test.kt:86:11:86:31 | e |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:82:1:89:1 | Exceptional Exit |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:34:88:2 | { ... } |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:11:86:31 | e |
| Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Exceptional Exit |
| Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Exit |
| Test.kt:91:22:98:1 | { ... } | Test.kt:91:1:98:1 | Normal Exit |
| Test.kt:91:22:98:1 | { ... } | Test.kt:93:3:93:13 | x |
| Test.kt:91:22:98:1 | { ... } | Test.kt:95:4:97:2 | catch (...) |
| Test.kt:91:22:98:1 | { ... } | Test.kt:95:36:97:2 | { ... } |
| Test.kt:91:22:98:1 | { ... } | Test.kt:95:11:95:33 | e |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:91:1:98:1 | Exceptional Exit |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:36:97:2 | { ... } |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:11:95:33 | e |
| Test.kt:100:25:110:1 | { ... } | Test.kt:100:1:110:1 | Exit |
| Test.kt:100:25:110:1 | { ... } | Test.kt:100:1:110:1 | Normal Exit |
| Test.kt:100:25:110:1 | { ... } | Test.kt:101:22:101:22 | y |

View File

@@ -20,16 +20,16 @@
| Test.kt:82:21:89:1 | { ... } | Test.kt:86:4:88:2 | catch (...) |
| Test.kt:84:3:84:18 | x | Test.kt:82:1:89:1 | Normal Exit |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:82:1:89:1 | Exceptional Exit |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:34:88:2 | { ... } |
| Test.kt:86:34:88:2 | { ... } | Test.kt:82:1:89:1 | Normal Exit |
| Test.kt:86:4:88:2 | catch (...) | Test.kt:86:11:86:31 | e |
| Test.kt:86:11:86:31 | e | Test.kt:82:1:89:1 | Normal Exit |
| Test.kt:91:1:98:1 | Exceptional Exit | Test.kt:91:1:98:1 | Exit |
| Test.kt:91:1:98:1 | Normal Exit | Test.kt:91:1:98:1 | Exit |
| Test.kt:91:22:98:1 | { ... } | Test.kt:93:3:93:13 | x |
| Test.kt:91:22:98:1 | { ... } | Test.kt:95:4:97:2 | catch (...) |
| Test.kt:93:3:93:13 | x | Test.kt:91:1:98:1 | Normal Exit |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:91:1:98:1 | Exceptional Exit |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:36:97:2 | { ... } |
| Test.kt:95:36:97:2 | { ... } | Test.kt:91:1:98:1 | Normal Exit |
| Test.kt:95:4:97:2 | catch (...) | Test.kt:95:11:95:33 | e |
| Test.kt:95:11:95:33 | e | Test.kt:91:1:98:1 | Normal Exit |
| Test.kt:100:1:110:1 | Normal Exit | Test.kt:100:1:110:1 | Exit |
| Test.kt:100:25:110:1 | { ... } | Test.kt:101:9:101:17 | After ... (value equals) ... [false] |
| Test.kt:100:25:110:1 | { ... } | Test.kt:101:22:101:22 | y |

View File

@@ -136,8 +136,8 @@
| Test.kt:84:11:84:18 | (...)... | CastExpr | Test.kt:86:4:88:2 | catch (...) | CatchClause |
| Test.kt:85:3:85:10 | return ... | ReturnStmt | Test.kt:82:1:89:1 | Normal Exit | Method |
| Test.kt:85:10:85:10 | 1 | IntegerLiteral | Test.kt:85:3:85:10 | return ... | ReturnStmt |
| Test.kt:86:4:88:2 | catch (...) | CatchClause | Test.kt:82:1:89:1 | Exceptional Exit | Method |
| Test.kt:86:4:88:2 | catch (...) | CatchClause | Test.kt:86:11:86:31 | e | LocalVariableDeclExpr |
| Test.kt:86:11:86:31 | e | LocalVariableDeclExpr | Test.kt:82:1:89:1 | Exceptional Exit | Method |
| Test.kt:86:11:86:31 | e | LocalVariableDeclExpr | Test.kt:86:34:88:2 | { ... } | BlockStmt |
| Test.kt:86:34:88:2 | { ... } | BlockStmt | Test.kt:87:10:87:10 | 2 | IntegerLiteral |
| Test.kt:87:3:87:10 | return ... | ReturnStmt | Test.kt:82:1:89:1 | Normal Exit | Method |
@@ -155,8 +155,8 @@
| Test.kt:93:11:93:13 | ...!! | NotNullExpr | Test.kt:95:4:97:2 | catch (...) | CatchClause |
| Test.kt:94:3:94:10 | return ... | ReturnStmt | Test.kt:91:1:98:1 | Normal Exit | Method |
| Test.kt:94:10:94:10 | 1 | IntegerLiteral | Test.kt:94:3:94:10 | return ... | ReturnStmt |
| Test.kt:95:4:97:2 | catch (...) | CatchClause | Test.kt:91:1:98:1 | Exceptional Exit | Method |
| Test.kt:95:4:97:2 | catch (...) | CatchClause | Test.kt:95:11:95:33 | e | LocalVariableDeclExpr |
| Test.kt:95:11:95:33 | e | LocalVariableDeclExpr | Test.kt:91:1:98:1 | Exceptional Exit | Method |
| Test.kt:95:11:95:33 | e | LocalVariableDeclExpr | Test.kt:95:36:97:2 | { ... } | BlockStmt |
| Test.kt:95:36:97:2 | { ... } | BlockStmt | Test.kt:96:10:96:10 | 2 | IntegerLiteral |
| Test.kt:96:3:96:10 | return ... | ReturnStmt | Test.kt:91:1:98:1 | Normal Exit | Method |

View File

@@ -14,8 +14,8 @@
| MultiCatch.java:12:11:12:27 | new IOException(...) | MultiCatch.java:12:5:12:28 | throw ... |
| MultiCatch.java:14:5:14:29 | throw ... | MultiCatch.java:15:5:15:37 | catch (...) |
| MultiCatch.java:14:11:14:28 | new SQLException(...) | MultiCatch.java:14:5:14:29 | throw ... |
| MultiCatch.java:15:5:15:37 | catch (...) | MultiCatch.java:7:14:7:23 | Exceptional Exit |
| MultiCatch.java:15:5:15:37 | catch (...) | MultiCatch.java:15:36:15:36 | e |
| MultiCatch.java:15:36:15:36 | e | MultiCatch.java:7:14:7:23 | Exceptional Exit |
| MultiCatch.java:15:36:15:36 | e | MultiCatch.java:16:3:19:3 | { ... } |
| MultiCatch.java:16:3:19:3 | { ... } | MultiCatch.java:17:4:17:23 | <Expr>; |
| MultiCatch.java:17:4:17:4 | e | MultiCatch.java:17:4:17:22 | printStackTrace(...) |
@@ -41,8 +41,8 @@
| MultiCatch.java:29:11:29:28 | new SQLException(...) | MultiCatch.java:29:5:29:29 | throw ... |
| MultiCatch.java:30:4:30:25 | throw ... | MultiCatch.java:31:5:31:37 | catch (...) |
| MultiCatch.java:30:10:30:24 | new Exception(...) | MultiCatch.java:30:4:30:25 | throw ... |
| MultiCatch.java:31:5:31:37 | catch (...) | MultiCatch.java:22:14:22:24 | Exceptional Exit |
| MultiCatch.java:31:5:31:37 | catch (...) | MultiCatch.java:31:36:31:36 | e |
| MultiCatch.java:31:36:31:36 | e | MultiCatch.java:22:14:22:24 | Exceptional Exit |
| MultiCatch.java:31:36:31:36 | e | MultiCatch.java:32:3:32:4 | { ... } |
| MultiCatch.java:32:3:32:4 | { ... } | MultiCatch.java:22:14:22:24 | Normal Exit |
| MultiCatch.java:35:14:35:26 | Entry | MultiCatch.java:36:2:42:2 | { ... } |

View File

@@ -28,8 +28,8 @@
| CloseReaderTest.java:19:11:19:15 | stdin | CloseReaderTest.java:19:11:19:26 | readLine(...) |
| CloseReaderTest.java:19:11:19:26 | readLine(...) | CloseReaderTest.java:19:4:19:27 | return ... |
| CloseReaderTest.java:19:11:19:26 | readLine(...) | CloseReaderTest.java:20:5:20:26 | catch (...) |
| CloseReaderTest.java:20:5:20:26 | catch (...) | CloseReaderTest.java:9:23:9:34 | Exceptional Exit |
| CloseReaderTest.java:20:5:20:26 | catch (...) | CloseReaderTest.java:20:24:20:25 | ex |
| CloseReaderTest.java:20:24:20:25 | ex | CloseReaderTest.java:9:23:9:34 | Exceptional Exit |
| CloseReaderTest.java:20:24:20:25 | ex | CloseReaderTest.java:21:3:23:3 | { ... } |
| CloseReaderTest.java:21:3:23:3 | { ... } | CloseReaderTest.java:22:11:22:14 | null |
| CloseReaderTest.java:22:4:22:15 | return ... | CloseReaderTest.java:9:23:9:34 | Normal Exit |

View File

@@ -50,16 +50,16 @@
| SchackTest.java:16:4:16:41 | <Expr>; | SchackTest.java:16:4:16:13 | System.out |
| SchackTest.java:16:23:16:39 | "false successor" | SchackTest.java:16:4:16:40 | println(...) |
| SchackTest.java:17:5:17:17 | catch (...) | SchackTest.java:17:16:17:16 | e |
| SchackTest.java:17:5:17:17 | catch (...) | SchackTest.java:19:5:19:17 | catch (...) |
| SchackTest.java:17:16:17:16 | e | SchackTest.java:17:19:19:3 | { ... } |
| SchackTest.java:17:16:17:16 | e | SchackTest.java:19:5:19:17 | catch (...) |
| SchackTest.java:17:19:19:3 | { ... } | SchackTest.java:18:4:18:41 | <Expr>; |
| SchackTest.java:18:4:18:13 | System.out | SchackTest.java:18:23:18:39 | "false successor" |
| SchackTest.java:18:4:18:40 | println(...) | SchackTest.java:21:13:23:3 | { ... } |
| SchackTest.java:18:4:18:41 | <Expr>; | SchackTest.java:18:4:18:13 | System.out |
| SchackTest.java:18:23:18:39 | "false successor" | SchackTest.java:18:4:18:40 | println(...) |
| SchackTest.java:19:5:19:17 | catch (...) | SchackTest.java:19:16:19:16 | e |
| SchackTest.java:19:5:19:17 | catch (...) | SchackTest.java:21:13:23:3 | { ... } |
| SchackTest.java:19:16:19:16 | e | SchackTest.java:19:19:21:3 | { ... } |
| SchackTest.java:19:16:19:16 | e | SchackTest.java:21:13:23:3 | { ... } |
| SchackTest.java:19:19:21:3 | { ... } | SchackTest.java:20:4:20:74 | <Expr>; |
| SchackTest.java:20:4:20:13 | System.out | SchackTest.java:20:23:20:72 | "successor (but neither true nor false successor)" |
| SchackTest.java:20:4:20:73 | println(...) | SchackTest.java:21:13:23:3 | { ... } |

View File

@@ -18,8 +18,8 @@
| TestThrow.java:20:10:20:31 | new RuntimeException(...) | TestThrow.java:20:4:20:32 | throw ... |
| TestThrow.java:20:10:20:31 | new RuntimeException(...) | TestThrow.java:21:5:21:30 | catch (...) |
| TestThrow.java:21:5:21:30 | catch (...) | TestThrow.java:21:29:21:29 | e |
| TestThrow.java:21:5:21:30 | catch (...) | TestThrow.java:24:5:24:23 | catch (...) |
| TestThrow.java:21:29:21:29 | e | TestThrow.java:22:3:24:3 | { ... } |
| TestThrow.java:21:29:21:29 | e | TestThrow.java:24:5:24:23 | catch (...) |
| TestThrow.java:22:3:24:3 | { ... } | TestThrow.java:23:4:23:9 | <Expr>; |
| TestThrow.java:23:4:23:4 | z | TestThrow.java:23:8:23:8 | 1 |
| TestThrow.java:23:4:23:8 | ...=... | TestThrow.java:29:3:29:9 | <Expr>; |
@@ -71,8 +71,8 @@
| TestThrow.java:44:5:44:13 | thrower(...) | TestThrow.java:50:3:52:3 | { ... } |
| TestThrow.java:44:5:44:14 | <Expr>; | TestThrow.java:44:5:44:13 | thrower(...) |
| TestThrow.java:46:5:46:30 | catch (...) | TestThrow.java:46:29:46:29 | e |
| TestThrow.java:46:5:46:30 | catch (...) | TestThrow.java:50:3:52:3 | { ... } |
| TestThrow.java:46:29:46:29 | e | TestThrow.java:47:3:49:3 | { ... } |
| TestThrow.java:46:29:46:29 | e | TestThrow.java:50:3:52:3 | { ... } |
| TestThrow.java:47:3:49:3 | { ... } | TestThrow.java:48:4:48:9 | <Expr>; |
| TestThrow.java:48:4:48:4 | z | TestThrow.java:48:8:48:8 | 1 |
| TestThrow.java:48:4:48:8 | ...=... | TestThrow.java:50:3:52:3 | { ... } |
@@ -113,8 +113,8 @@
| TestThrow.java:67:5:67:13 | thrower(...) | TestThrow.java:69:5:69:30 | catch (...) |
| TestThrow.java:67:5:67:13 | thrower(...) | TestThrow.java:74:3:74:9 | <Expr>; |
| TestThrow.java:67:5:67:14 | <Expr>; | TestThrow.java:67:5:67:13 | thrower(...) |
| TestThrow.java:69:5:69:30 | catch (...) | TestThrow.java:15:14:15:14 | Exceptional Exit |
| TestThrow.java:69:5:69:30 | catch (...) | TestThrow.java:69:29:69:29 | e |
| TestThrow.java:69:29:69:29 | e | TestThrow.java:15:14:15:14 | Exceptional Exit |
| TestThrow.java:69:29:69:29 | e | TestThrow.java:70:3:72:3 | { ... } |
| TestThrow.java:70:3:72:3 | { ... } | TestThrow.java:71:4:71:9 | <Expr>; |
| TestThrow.java:71:4:71:4 | z | TestThrow.java:71:8:71:8 | 1 |
@@ -171,8 +171,8 @@
| TestThrow.java:97:28:97:36 | "Foo bar" | TestThrow.java:97:39:97:42 | null |
| TestThrow.java:97:39:97:42 | null | TestThrow.java:97:12:97:43 | new IOException(...) |
| TestThrow.java:99:6:99:31 | catch (...) | TestThrow.java:99:30:99:30 | e |
| TestThrow.java:99:6:99:31 | catch (...) | TestThrow.java:119:5:119:25 | catch (...) |
| TestThrow.java:99:30:99:30 | e | TestThrow.java:100:4:102:4 | { ... } |
| TestThrow.java:99:30:99:30 | e | TestThrow.java:119:5:119:25 | catch (...) |
| TestThrow.java:100:4:102:4 | { ... } | TestThrow.java:101:5:101:10 | <Expr>; |
| TestThrow.java:101:5:101:5 | z | TestThrow.java:101:9:101:9 | 1 |
| TestThrow.java:101:5:101:9 | ...=... | TestThrow.java:103:4:118:4 | try ... |
@@ -216,8 +216,8 @@
| TestThrow.java:116:28:116:36 | "Foo bar" | TestThrow.java:116:39:116:42 | null |
| TestThrow.java:116:39:116:42 | null | TestThrow.java:116:12:116:43 | new IOException(...) |
| TestThrow.java:119:5:119:25 | catch (...) | TestThrow.java:119:24:119:24 | e |
| TestThrow.java:119:5:119:25 | catch (...) | TestThrow.java:124:3:126:3 | { ... } |
| TestThrow.java:119:24:119:24 | e | TestThrow.java:120:3:122:3 | { ... } |
| TestThrow.java:119:24:119:24 | e | TestThrow.java:124:3:126:3 | { ... } |
| TestThrow.java:120:3:122:3 | { ... } | TestThrow.java:121:4:121:9 | <Expr>; |
| TestThrow.java:121:4:121:4 | z | TestThrow.java:121:8:121:8 | 2 |
| TestThrow.java:121:4:121:8 | ...=... | TestThrow.java:124:3:126:3 | { ... } |

View File

@@ -105,8 +105,8 @@
| TestTryCatch.java:34:9:34:9 | y | TestTryCatch.java:34:13:34:13 | 1 |
| TestTryCatch.java:34:9:34:13 | ... + ... | TestTryCatch.java:34:5:34:13 | ...=... |
| TestTryCatch.java:34:13:34:13 | 1 | TestTryCatch.java:34:9:34:13 | ... + ... |
| TestTryCatch.java:35:6:35:31 | catch (...) | TestTryCatch.java:4:14:4:14 | Exceptional Exit |
| TestTryCatch.java:35:6:35:31 | catch (...) | TestTryCatch.java:35:30:35:30 | e |
| TestTryCatch.java:35:30:35:30 | e | TestTryCatch.java:4:14:4:14 | Exceptional Exit |
| TestTryCatch.java:35:30:35:30 | e | TestTryCatch.java:36:4:40:4 | { ... } |
| TestTryCatch.java:36:4:40:4 | { ... } | TestTryCatch.java:37:5:37:14 | var ...; |
| TestTryCatch.java:37:5:37:14 | var ...; | TestTryCatch.java:37:13:37:13 | 1 |

View File

@@ -28,8 +28,8 @@
| TestTryWithResources.java:10:4:10:32 | <Expr>; | TestTryWithResources.java:10:4:10:13 | System.out |
| TestTryWithResources.java:10:23:10:30 | "worked" | TestTryWithResources.java:10:4:10:31 | println(...) |
| TestTryWithResources.java:11:5:11:35 | catch (...) | TestTryWithResources.java:11:34:11:34 | e |
| TestTryWithResources.java:11:5:11:35 | catch (...) | TestTryWithResources.java:13:13:15:3 | { ... } |
| TestTryWithResources.java:11:34:11:34 | e | TestTryWithResources.java:11:37:13:3 | { ... } |
| TestTryWithResources.java:11:34:11:34 | e | TestTryWithResources.java:13:13:15:3 | { ... } |
| TestTryWithResources.java:11:37:13:3 | { ... } | TestTryWithResources.java:12:4:12:40 | <Expr>; |
| TestTryWithResources.java:12:4:12:13 | System.out | TestTryWithResources.java:12:23:12:38 | "file not found" |
| TestTryWithResources.java:12:4:12:39 | println(...) | TestTryWithResources.java:13:13:15:3 | { ... } |

View File

@@ -0,0 +1,2 @@
import semmle.python.controlflow.internal.AstNodeImpl
import ControlFlow::Consistency

View File

@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
private import semmle.python.dataflow.new.internal.DataFlowDispatch
private import semmle.python.dataflow.new.internal.TaintTrackingImplSpecific
private import codeql.dataflow.internal.DataFlowImplConsistency
private import semmle.python.controlflow.internal.Cfg as Cfg
private module Input implements InputSig<Location, PythonDataFlow> {
private import Private
@@ -74,7 +75,7 @@ private module Input implements InputSig<Location, PythonDataFlow> {
// resolve to multiple functions), but we only make _one_ ArgumentNode for each
// argument in the CallNode, we end up violating this consistency check in those
// cases. (see `getCallArg` in DataFlowDispatch.qll)
exists(DataFlowCall other, CallNode cfgCall | other != call |
exists(DataFlowCall other, Cfg::CallNode cfgCall | other != call |
call.getNode() = cfgCall and
other.getNode() = cfgCall and
isArgumentNode(arg, call, _) and
@@ -90,16 +91,16 @@ private module Input implements InputSig<Location, PythonDataFlow> {
// allow it instead.
(
call.getScope() = attr.getScope() and
any(CfgNode n | n.asCfgNode() = call.getNode().(CallNode).getFunction()).getALocalSource() =
attr
any(CfgNode n | n.asCfgNode() = call.getNode().(Cfg::CallNode).getFunction())
.getALocalSource() = attr
or
not exists(call.getScope().(Function).getDefinition()) and
call.getScope().getScope+() = attr.getScope()
) and
(
other.getScope() = attr.getScope() and
any(CfgNode n | n.asCfgNode() = other.getNode().(CallNode).getFunction()).getALocalSource() =
attr
any(CfgNode n | n.asCfgNode() = other.getNode().(Cfg::CallNode).getFunction())
.getALocalSource() = attr
or
not exists(other.getScope().(Function).getDefinition()) and
other.getScope().getScope+() = attr.getScope()

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* A new Python control flow graph implementation has been added under `semmle.python.controlflow.internal.Cfg` (backed by `AstNodeImpl.qll`), built on the shared `codeql.controlflow.ControlFlowGraph` library. It is not yet used by the dataflow library or any production query; the legacy CFG in `semmle/python/Flow.qll` remains the default. The new library is exposed for tests and for upcoming migrations.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* A new SSA adapter has been added under `semmle.python.dataflow.new.internal.SsaImpl`, built on the shared `codeql.ssa.Ssa` library and the new shared CFG (`semmle.python.controlflow.internal.Cfg`). It is not yet used by the dataflow library or any production query; the legacy ESSA SSA in `semmle/python/essa/*` remains the default. The new SSA adapter is exposed for tests and for the upcoming dataflow migration.

View File

@@ -0,0 +1,4 @@
---
category: breaking
---
* The deprecated `AstNode.getAFlowNode()` and `Function.getAReturnValueFlowNode()` predicates now return nodes from the new shared CFG (`Cfg::ControlFlowNode`) rather than from the legacy CFG (`ControlFlowNode`). Callers that still rely on these deprecated APIs and feed the result into legacy-CFG-aware predicates will no longer type-check; migrate to `n.getNode() = e` (or, for return values, the explicit `Return` pattern shown in the deprecation message) to get nodes from the dataflow library's current CFG.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The new (shared-CFG-based) Python control flow graph now visits parameter and return type annotations as CFG nodes for function definitions, matching the legacy CFG. This restores annotation-based type tracking through framework models such as FastAPI's `Depends()`, Pydantic request models, Starlette `WebSocket` handlers, and any other models that flow a class reference through `Parameter.getAnnotation()` to identify instances of the annotated class.

View File

@@ -0,0 +1,45 @@
/**
* @name Print CFG (New)
* @description Produces a representation of a file's Control Flow Graph
* using the new shared control flow library.
* This query is used by the VS Code extension.
* @id python/print-cfg
* @kind graph
* @tags ide-contextual-queries/print-cfg
*/
private import python as Py
import semmle.python.controlflow.internal.AstNodeImpl
external string selectedSourceFile();
private predicate selectedSourceFileAlias = selectedSourceFile/0;
external int selectedSourceLine();
private predicate selectedSourceLineAlias = selectedSourceLine/0;
external int selectedSourceColumn();
private predicate selectedSourceColumnAlias = selectedSourceColumn/0;
module ViewCfgQueryInput implements ControlFlow::ViewCfgQueryInputSig<Py::File> {
predicate selectedSourceFile = selectedSourceFileAlias/0;
predicate selectedSourceLine = selectedSourceLineAlias/0;
predicate selectedSourceColumn = selectedSourceColumnAlias/0;
predicate cfgScopeSpan(
Ast::Callable callable, Py::File file, int startLine, int startColumn, int endLine,
int endColumn
) {
exists(Py::Scope scope |
scope = callable.asScope() and
file = scope.getLocation().getFile() and
scope.getLocation().hasLocationInfo(_, startLine, startColumn, endLine, endColumn)
)
}
}
import ControlFlow::ViewCfgQuery<Py::File, ViewCfgQueryInput>

View File

@@ -6,8 +6,9 @@
* directed and labeled; they specify how the components represented by nodes relate to each other.
*/
// Importing python under the `py` namespace to avoid importing `CallNode` from `Flow.qll` and thereby having a naming conflict with `API::CallNode`.
// Importing python under the `PY` namespace to avoid pulling in `CallNode` from `Flow.qll` (via `import python`) and thereby having a naming conflict with `API::CallNode`.
private import python as PY
private import semmle.python.controlflow.internal.Cfg as Cfg
import semmle.python.dataflow.new.DataFlow
private import semmle.python.internal.CachedStages
@@ -282,7 +283,7 @@ module API {
index = this.getIndex() and
(
// subscripting
exists(PY::SubscriptNode subscript |
exists(Cfg::SubscriptNode subscript |
subscript.getObject() = this.getAValueReachableFromSource().asCfgNode() and
subscript.getIndex() = index.asSink().asCfgNode()
|
@@ -290,7 +291,7 @@ module API {
subscript = result.asSource().asCfgNode()
or
// writing
subscript.(PY::DefinitionNode).getValue() = result.asSink().asCfgNode()
subscript.(Cfg::DefinitionNode).getValue() = result.asSink().asCfgNode()
)
or
// dictionary literals
@@ -684,7 +685,7 @@ module API {
* Ignores relative imports, such as `from ..foo.bar import baz`.
*/
private predicate imports(DataFlow::CfgNode imp, string name) {
exists(PY::ImportExprNode iexpr |
exists(Cfg::ImportExprNode iexpr |
imp.getNode() = iexpr and
not iexpr.getNode().isRelative() and
name = iexpr.getNode().getImportedModuleName()
@@ -775,7 +776,7 @@ module API {
// list literals, from `x` to `[x]`
// TODO: once convenient, this should be done at a higher level than the AST,
// at least at the CFG layer, to take splitting into account.
// Also consider `SequenceNode for generality.
// Also consider `Cfg::SequenceNode` for generality.
exists(PY::List list | list = pred.(DataFlow::ExprNode).getNode().getNode() |
rhs.(DataFlow::ExprNode).getNode().getNode() = list.getAnElt() and
lbl = Label::subscript()
@@ -805,7 +806,7 @@ module API {
subscript = trackUseNode(src).getSubscript(index)
|
// from `x` to a definition of `x[...]`
rhs.asCfgNode() = subscript.asCfgNode().(PY::DefinitionNode).getValue() and
rhs.asCfgNode() = subscript.asCfgNode().(Cfg::DefinitionNode).getValue() and
lbl = Label::subscript()
or
// from `x` to `"key"` in `x["key"]`

View File

@@ -3,6 +3,7 @@ module;
import python
private import semmle.python.internal.CachedStages
private import semmle.python.controlflow.internal.Cfg as Cfg
/** A syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
abstract class AstNode extends AstNode_ {
@@ -19,17 +20,16 @@ abstract class AstNode extends AstNode_ {
/**
* DEPRECATED: use `ControlFlowNode.getNode()` from the other direction instead;
* that is, replace `e.getAFlowNode() = n` with `n.getNode() = e`. This API is
* being removed to untangle the AST and CFG hierarchies in preparation for
* migrating the dataflow library off the legacy CFG.
* being removed to untangle the AST and CFG hierarchies.
*
* Gets a flow node corresponding directly to this node.
* NOTE: For some statements and other purely syntactic elements,
* there may not be a `ControlFlowNode`.
* Gets a flow node corresponding directly to this node, from the new
* (shared) CFG. NOTE: For some statements and other purely syntactic
* elements, there may not be a `ControlFlowNode`.
*/
cached
deprecated ControlFlowNode getAFlowNode() {
deprecated Cfg::ControlFlowNode getAFlowNode() {
Stages::AST::ref() and
py_flow_bb_node(result, this, _, _)
result.getNode() = this
}
/** Gets the location for this AST node */

View File

@@ -5,6 +5,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
private import semmle.python.dataflow.new.RemoteFlowSources
@@ -214,7 +215,7 @@ module Path {
SafeAccessCheck() { this = DataFlow::BarrierGuard<safeAccessCheck/3>::getABarrierNode() }
}
private predicate safeAccessCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
private predicate safeAccessCheck(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) {
g.(SafeAccessCheck::Range).checks(node, branch)
}
@@ -223,7 +224,7 @@ module Path {
/** A data-flow node that checks that a path is safe to access in some way, for example by having a controlled prefix. */
abstract class Range extends DataFlow::GuardNode {
/** Holds if this guard validates `node` upon evaluating to `branch`. */
abstract predicate checks(ControlFlowNode node, boolean branch);
abstract predicate checks(Cfg::ControlFlowNode node, boolean branch);
}
}
}

View File

@@ -3,6 +3,7 @@ module;
private import python
private import semmle.python.internal.CachedStages
private import semmle.python.controlflow.internal.Cfg as Cfg
/** An expression */
class Expr extends Expr_, AstNode {
@@ -70,7 +71,7 @@ class Attribute extends Attribute_ {
/* syntax: Expr.name */
override Expr getASubExpression() { result = this.getObject() }
deprecated override AttrNode getAFlowNode() { result = super.getAFlowNode() }
deprecated override Cfg::AttrNode getAFlowNode() { result = super.getAFlowNode() }
/** Gets the name of this attribute. That is the `name` in `obj.name` */
string getName() { result = Attribute_.super.getAttr() }
@@ -99,7 +100,7 @@ class Subscript extends Subscript_ {
Expr getObject() { result = Subscript_.super.getValue() }
deprecated override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
deprecated override Cfg::SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
}
/** A call expression, such as `func(...)` */
@@ -115,7 +116,7 @@ class Call extends Call_ {
override string toString() { result = this.getFunc().toString() + "()" }
deprecated override CallNode getAFlowNode() { result = super.getAFlowNode() }
deprecated override Cfg::CallNode getAFlowNode() { result = super.getAFlowNode() }
/** Gets a tuple (*) argument of this call. */
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
@@ -203,7 +204,7 @@ class IfExp extends IfExp_ {
result = this.getTest() or result = this.getBody() or result = this.getOrelse()
}
deprecated override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
deprecated override Cfg::IfExprNode getAFlowNode() { result = super.getAFlowNode() }
}
/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
@@ -413,7 +414,7 @@ class PlaceHolder extends PlaceHolder_ {
override string toString() { result = "$" + this.getId() }
deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
deprecated override Cfg::NameNode getAFlowNode() { result = super.getAFlowNode() }
}
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
@@ -480,7 +481,7 @@ class Name extends Name_ {
override string toString() { result = this.getId() }
deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
deprecated override Cfg::NameNode getAFlowNode() { result = super.getAFlowNode() }
override predicate isArtificial() {
/* Artificial variable names in comprehensions all start with "." */
@@ -587,7 +588,7 @@ abstract class NameConstant extends Name, ImmutableLiteral {
override predicate isConstant() { any() }
deprecated override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
deprecated override Cfg::NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
override predicate isArtificial() { none() }
}

View File

@@ -2,6 +2,7 @@ overlay[local]
module;
import python
private import semmle.python.controlflow.internal.Cfg as Cfg
/**
* A function, independent of defaults and binding.
@@ -157,12 +158,12 @@ class Function extends Function_, Scope, AstNode {
* DEPRECATED: bind a `Return` node explicitly instead, e.g.
* `exists(Return ret | ret.getScope() = this and n.getNode() = ret.getValue())`.
* This API is being phased out together with `AstNode.getAFlowNode()` to
* untangle the AST and CFG hierarchies in preparation for migrating the
* dataflow library off the legacy CFG.
* untangle the AST and CFG hierarchies.
*
* Gets a control flow node for a return value of this function.
* Gets a control flow node for a return value of this function, from the
* new (shared) CFG.
*/
deprecated ControlFlowNode getAReturnValueFlowNode() {
deprecated Cfg::ControlFlowNode getAReturnValueFlowNode() {
exists(Return ret |
ret.getScope() = this and
ret.getValue() = result.getNode()

View File

@@ -4,6 +4,7 @@ module;
import python
private import semmle.python.types.Builtins
private import semmle.python.internal.CachedStages
private import semmle.python.controlflow.internal.Cfg as Cfg
/**
* An alias in an import statement, the `mod as name` part of `import mod as name`. May be artificial;
@@ -163,7 +164,7 @@ class ImportMember extends ImportMember_ {
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
}
deprecated override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
deprecated override Cfg::ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
}
/** An import statement */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,12 @@
/** Provides commonly used BarrierGuards. */
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
exists(CompareNode cn | cn = g |
exists(ImmutableLiteral const, Cmpop op, ControlFlowNode c |
private predicate constCompare(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) {
exists(Cfg::CompareNode cn | cn = g |
exists(ImmutableLiteral const, Cmpop op, Cfg::ControlFlowNode c |
c.getNode() = const and
(
op = any(Eq eq) and branch = true
@@ -18,7 +19,7 @@ private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, bool
cn.operands(node, op, c)
)
or
exists(NameConstant const, Cmpop op, ControlFlowNode c |
exists(NameConstant const, Cmpop op, Cfg::ControlFlowNode c |
c.getNode() = const and
(
op = any(Is is_) and branch = true
@@ -31,12 +32,12 @@ private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, bool
cn.operands(node, op, c)
)
or
exists(IterableNode const_iterable, Cmpop op |
exists(Cfg::IterableNode const_iterable, Cmpop op |
op = any(In in_) and branch = true
or
op = any(NotIn ni) and branch = false
|
forall(ControlFlowNode elem | elem = const_iterable.getAnElement() |
forall(Cfg::ControlFlowNode elem | elem = const_iterable.getAnElement() |
elem.getNode() instanceof ImmutableLiteral
) and
cn.operands(node, op, const_iterable)

View File

@@ -4,6 +4,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
// Need to import `semmle.python.Frameworks` since frameworks can extend `SensitiveDataSource::Range`
private import semmle.python.Frameworks
@@ -105,7 +106,7 @@ private module SensitiveDataModeling {
or
// to cover functions that we don't have the definition for, and where the
// reference to the function has not already been marked as being sensitive
this.getFunction().asCfgNode().(NameNode).getId() = sensitiveString(classification)
this.getFunction().asCfgNode().(Cfg::NameNode).getId() = sensitiveString(classification)
}
override SensitiveDataClassification getClassification() { result = classification }
@@ -251,12 +252,12 @@ private module SensitiveDataModeling {
SensitiveDataClassification classification;
SensitiveVariableAssignment() {
exists(DefinitionNode def |
def.(NameNode).getId() = sensitiveString(classification) and
exists(Cfg::DefinitionNode def |
def.(Cfg::NameNode).getId() = sensitiveString(classification) and
(
this.asCfgNode() = def.getValue()
or
this.asCfgNode() = def.getValue().(ForNode).getSequence()
this.asCfgNode() = def.getValue().(Cfg::ForNode).getSequence()
) and
not this.asExpr() instanceof FunctionExpr and
not this.asExpr() instanceof ClassExpr
@@ -293,7 +294,7 @@ private module SensitiveDataModeling {
SensitiveDataClassification classification;
SensitiveSubscript() {
this.asCfgNode().(SubscriptNode).getIndex() =
this.asCfgNode().(Cfg::SubscriptNode).getIndex() =
sensitiveLookupStringConst(classification).asCfgNode()
}

View File

@@ -3,6 +3,7 @@ overlay[local]
module;
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import DataFlowUtil
import DataFlowPublic
private import DataFlowPrivate
@@ -83,9 +84,9 @@ abstract class AttrWrite extends AttrRef {
* ```python
* object.attr = value
* ```
* Also gives access to the `value` being written, by extending `DefinitionNode`.
* Also gives access to the `value` being written, by extending `Cfg::DefinitionNode`.
*/
private class AttributeAssignmentNode extends DefinitionNode, AttrNode { }
private class AttributeAssignmentNode extends Cfg::DefinitionNode, Cfg::AttrNode { }
/** A simple attribute assignment: `object.attr = value`. */
private class AttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode {
@@ -131,13 +132,13 @@ private class GlobalAttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode {
override string getAttributeName() { result = node.getName() }
}
/** Represents `CallNode`s that may refer to calls to built-in functions or classes. */
private class BuiltInCallNode extends CallNode {
/** Represents `Cfg::CallNode`s that may refer to calls to built-in functions or classes. */
private class BuiltInCallNode extends Cfg::CallNode {
string name;
BuiltInCallNode() {
// TODO disallow instances where the name of the built-in may refer to an in-scope variable of that name.
exists(NameNode id |
exists(Cfg::NameNode id |
name = Builtins::getBuiltinName() and
this.getFunction() = id and
id.getId() = name and
@@ -145,7 +146,7 @@ private class BuiltInCallNode extends CallNode {
)
}
/** Gets the name of the built-in function that is called at this `CallNode` */
/** Gets the name of the built-in function that is called at this `Cfg::CallNode` */
string getBuiltinName() { result = name }
}
@@ -157,20 +158,20 @@ private class BuiltinAttrCallNode extends BuiltInCallNode {
BuiltinAttrCallNode() { name in ["setattr", "getattr", "hasattr", "delattr"] }
/** Gets the control flow node for object on which the attribute is accessed. */
ControlFlowNode getObject() { result in [this.getArg(0), this.getArgByName("object")] }
Cfg::ControlFlowNode getObject() { result in [this.getArg(0), this.getArgByName("object")] }
/**
* Gets the control flow node for the value that is being written to the attribute.
* Only relevant for `setattr` calls.
*/
ControlFlowNode getValue() {
Cfg::ControlFlowNode getValue() {
// only valid for `setattr`
name = "setattr" and
result in [this.getArg(2), this.getArgByName("value")]
}
/** Gets the control flow node that defines the name of the attribute being accessed. */
ControlFlowNode getName() { result in [this.getArg(1), this.getArgByName("name")] }
Cfg::ControlFlowNode getName() { result in [this.getArg(1), this.getArgByName("name")] }
}
/** Represents calls to the built-in `setattr`. */
@@ -205,10 +206,10 @@ private class SetAttrCallAsAttrWrite extends AttrWrite, CfgNode {
* attr = value
* ...
* ```
* Instances of this class correspond to the `NameNode` for `attr`, and also gives access to `value` by
* virtue of being a `DefinitionNode`.
* Instances of this class correspond to the `Cfg::NameNode` for `attr`, and also gives access to `value` by
* virtue of being a `Cfg::DefinitionNode`.
*/
private class ClassAttributeAssignmentNode extends DefinitionNode, NameNode {
private class ClassAttributeAssignmentNode extends Cfg::DefinitionNode, Cfg::NameNode {
ClassAttributeAssignmentNode() { this.getScope() = any(ClassExpr c).getInnerScope() }
}
@@ -248,7 +249,7 @@ abstract class AttrRead extends AttrRef, Node, LocalSourceNode {
/** A simple attribute read, e.g. `object.attr` */
private class AttributeReadAsAttrRead extends AttrRead, CfgNode {
override AttrNode node;
override Cfg::AttrNode node;
AttributeReadAsAttrRead() { node.isLoad() }
@@ -285,7 +286,7 @@ private class GetAttrCallAsAttrRead extends AttrRead, CfgNode {
* is treated as if it is a read of the attribute `module.attr`, even if `module` is not imported directly.
*/
private class ModuleAttributeImportAsAttrRead extends AttrRead, CfgNode {
override ImportMemberNode node;
override Cfg::ImportMemberNode node;
override Node getObject() { result.asCfgNode() = node.getModule(_) }

View File

@@ -3,6 +3,7 @@ overlay[local]
module;
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.ImportStar
@@ -67,7 +68,7 @@ module Builtins {
DataFlow::CfgNode likelyBuiltin(string name) {
exists(Module m |
result.getNode() =
any(NameNode n |
any(Cfg::NameNode n |
possible_builtin_accessed_in_module(n, name, m) and
not possible_builtin_defined_in_module(name, m)
)
@@ -87,7 +88,7 @@ module Builtins {
* Holds if `n` is an access of a global variable called `name` (which is also the name of a
* built-in) inside the module `m`.
*/
private predicate possible_builtin_accessed_in_module(NameNode n, string name, Module m) {
private predicate possible_builtin_accessed_in_module(Cfg::NameNode n, string name, Module m) {
n.isGlobal() and
n.isLoad() and
name = n.getId() and

View File

@@ -25,7 +25,7 @@
* what callable this call might end up targeting.
*
* Specifically this means that we cannot use type-backtrackers from the function of a
* `CallNode`, since there is no `CallNode` to backtrack from for `func` in the example
* `Cfg::CallNode`, since there is no `Cfg::CallNode` to backtrack from for `func` in the example
* above.
*
* Note: This hasn't been 100% realized yet, so we don't currently expose a predicate to
@@ -35,6 +35,7 @@ overlay[local?]
module;
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import DataFlowPublic
private import DataFlowPrivate
private import FlowSummaryImpl as FlowSummaryImpl
@@ -162,7 +163,7 @@ newtype TArgumentPosition =
*/
TLambdaSelfArgumentPosition() or
TPositionalArgumentPosition(int index) {
exists(any(CallNode c).getArg(index))
exists(any(Cfg::CallNode c).getArg(index))
or
// since synthetic calls within a summarized callable could use a unique argument
// position, we need to ensure we make these available (these are specified as
@@ -174,7 +175,7 @@ newtype TArgumentPosition =
index = 0
} or
TKeywordArgumentPosition(string name) {
exists(any(CallNode c).getArgByName(name))
exists(any(Cfg::CallNode c).getArgByName(name))
or
// see comment for TPositionalArgumentPosition
FlowSummaryImpl::ParsePositions::isParsedKeywordParameterPosition(_, name)
@@ -297,10 +298,12 @@ predicate hasPropertyDecorator(Function func) {
*/
overlay[local]
predicate hasContextmanagerDecorator(Function func) {
exists(ControlFlowNode contextmanager |
contextmanager.(NameNode).getId() = "contextmanager" and contextmanager.(NameNode).isGlobal()
exists(Cfg::ControlFlowNode contextmanager |
contextmanager.(Cfg::NameNode).getId() = "contextmanager" and
contextmanager.(Cfg::NameNode).isGlobal()
or
contextmanager.(AttrNode).getObject("contextmanager").(NameNode).getId() = "contextlib"
contextmanager.(Cfg::AttrNode).getObject("contextmanager").(Cfg::NameNode).getId() =
"contextlib"
|
func.getADecorator() = contextmanager.getNode()
)
@@ -316,10 +319,10 @@ predicate hasContextmanagerDecorator(Function func) {
*/
overlay[local]
private predicate hasOverloadDecorator(Function func) {
exists(ControlFlowNode overload |
overload.(NameNode).getId() = "overload" and overload.(NameNode).isGlobal()
exists(Cfg::ControlFlowNode overload |
overload.(Cfg::NameNode).getId() = "overload" and overload.(Cfg::NameNode).isGlobal()
or
overload.(AttrNode).getObject("overload").(NameNode).isGlobal()
overload.(Cfg::AttrNode).getObject("overload").(Cfg::NameNode).isGlobal()
|
func.getADecorator() = overload.getNode()
)
@@ -538,7 +541,7 @@ class LibraryCallableValue extends DataFlowCallable, TLibraryCallable {
// =============================================================================
/** Gets a call to `type`. */
private CallCfgNode getTypeCall() {
exists(NameNode id | id.getId() = "type" and id.isGlobal() |
exists(Cfg::NameNode id | id.getId() = "type" and id.isGlobal() |
result.getFunction().asCfgNode() = id
)
}
@@ -550,7 +553,7 @@ private CallCfgNode getSuperCall() {
// link below), but otherwise only 2 edgecases. Overall it seems ok to ignore this complexity.
//
// https://github.com/python/cpython/blob/18b1782192f85bd26db89f5bc850f8bee4247c1a/Lib/unittest/mock.py#L48-L50
exists(NameNode id | id.getId() = "super" and id.isGlobal() |
exists(Cfg::NameNode id | id.getId() = "super" and id.isGlobal() |
result.getFunction().asCfgNode() = id
)
}
@@ -1036,7 +1039,7 @@ private module MethodCalls {
*/
pragma[nomagic]
private predicate directCall(
CallNode call, Function target, string functionName, Class cls, AttrRead attr, Node self
Cfg::CallNode call, Function target, string functionName, Class cls, AttrRead attr, Node self
) {
target = findFunctionAccordingToMroKnownStartingClass(cls, functionName) and
directCall_join(call, functionName, cls, attr, self)
@@ -1045,7 +1048,7 @@ private module MethodCalls {
/** Extracted to give good join order */
pragma[nomagic]
private predicate directCall_join(
CallNode call, string functionName, Class cls, AttrRead attr, Node self
Cfg::CallNode call, string functionName, Class cls, AttrRead attr, Node self
) {
call.getFunction() = attrReadTracker(attr).asCfgNode() and
attr.accesses(self, functionName) and
@@ -1062,7 +1065,7 @@ private module MethodCalls {
*/
pragma[nomagic]
private predicate callWithinMethodImplicitSelfOrCls(
CallNode call, Function target, string functionName, Class classWithMethod, AttrRead attr,
Cfg::CallNode call, Function target, string functionName, Class classWithMethod, AttrRead attr,
Node self
) {
target = findFunctionAccordingToMro(getADirectSubclass*(classWithMethod), functionName) and
@@ -1072,7 +1075,7 @@ private module MethodCalls {
/** Extracted to give good join order */
pragma[nomagic]
private predicate callWithinMethodImplicitSelfOrCls_join(
CallNode call, string functionName, Class classWithMethod, AttrRead attr, Node self
Cfg::CallNode call, string functionName, Class classWithMethod, AttrRead attr, Node self
) {
call.getFunction() = attrReadTracker(attr).asCfgNode() and
attr.accesses(self, functionName) and
@@ -1084,7 +1087,7 @@ private module MethodCalls {
* resolve the call to a known target (since the only super class might be the
* builtin `object`, so we never have the implementation of `__new__` in the DB).
*/
predicate fromSuperNewCall(CallNode call, Class classUsedInSuper, AttrRead attr, Node self) {
predicate fromSuperNewCall(Cfg::CallNode call, Class classUsedInSuper, AttrRead attr, Node self) {
fromSuper_join(call, "__new__", classUsedInSuper, attr, self) and
self in [classTracker(_), clsArgumentTracker(_)]
}
@@ -1106,7 +1109,7 @@ private module MethodCalls {
*/
pragma[nomagic]
predicate fromSuper(
CallNode call, Function target, string functionName, Class classUsedInSuper, AttrRead attr,
Cfg::CallNode call, Function target, string functionName, Class classUsedInSuper, AttrRead attr,
Node self
) {
target = findFunctionAccordingToMro(getNextClassInMro(classUsedInSuper), functionName) and
@@ -1116,7 +1119,7 @@ private module MethodCalls {
/** Extracted to give good join order */
pragma[nomagic]
private predicate fromSuper_join(
CallNode call, string functionName, Class classUsedInSuper, AttrRead attr, Node self
Cfg::CallNode call, string functionName, Class classUsedInSuper, AttrRead attr, Node self
) {
call.getFunction() = attrReadTracker(attr).asCfgNode() and
(
@@ -1135,7 +1138,7 @@ private module MethodCalls {
)
}
predicate resolveMethodCall(CallNode call, Function target, CallType type, Node self) {
predicate resolveMethodCall(Cfg::CallNode call, Function target, CallType type, Node self) {
(
directCall(call, target, _, _, _, self)
or
@@ -1182,7 +1185,7 @@ import MethodCalls
* NOTE: We have this predicate mostly to be able to compare with old point-to
* call-graph resolution. So it could be removed in the future.
*/
predicate resolveClassCall(CallNode call, Class cls) {
predicate resolveClassCall(Cfg::CallNode call, Class cls) {
call.getFunction() = classTracker(cls).asCfgNode()
or
// `cls()` inside a classmethod (which also contains `type(self)()` inside a method)
@@ -1212,7 +1215,7 @@ Function invokedFunctionFromClassConstruction(Class cls, string funcName) {
*
* See https://docs.python.org/3/reference/datamodel.html#object.__call__
*/
predicate resolveClassInstanceCall(CallNode call, Function target, Node self) {
predicate resolveClassInstanceCall(Cfg::CallNode call, Function target, Node self) {
exists(Class cls |
call.getFunction() = classInstanceTracker(cls).asCfgNode() and
target = findFunctionAccordingToMroKnownStartingClass(cls, "__call__")
@@ -1231,7 +1234,7 @@ predicate resolveClassInstanceCall(CallNode call, Function target, Node self) {
* Holds if `call` is a call to the `target`, with call-type `type`.
*/
cached
predicate resolveCall(CallNode call, Function target, CallType type) {
predicate resolveCall(Cfg::CallNode call, Function target, CallType type) {
Stages::DataFlow::ref() and
(
type instanceof CallTypePlainFunction and
@@ -1256,11 +1259,11 @@ predicate resolveCall(CallNode call, Function target, CallType type) {
// =============================================================================
/**
* Holds if the argument of `call` at position `apos` is `arg`. This is just a helper
* predicate that maps ArgumentPositions to the arguments of the underlying `CallNode`.
* predicate that maps ArgumentPositions to the arguments of the underlying `Cfg::CallNode`.
*/
overlay[local]
cached
predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) {
predicate normalCallArg(Cfg::CallNode call, Node arg, ArgumentPosition apos) {
exists(int index |
apos.isPositional(index) and
arg.asCfgNode() = call.getArg(index)
@@ -1275,7 +1278,7 @@ predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) {
exists(int index |
apos.isStarArgs(index) and
arg.asCfgNode() = call.getStarArg() and
// since `CallNode.getArg` doesn't include `*args`, we need to drop to the AST level
// since `Cfg::CallNode.getArg` doesn't include `*args`, we need to drop to the AST level
// to get the index. Notice that we only use the AST for getting the index, so we
// don't need to check for dominance in regards to splitting.
call.getStarArg().getNode() = call.getNode().getPositionalArg(index).(Starred).getValue()
@@ -1349,7 +1352,9 @@ predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) {
* translated into `l.clear()`, and we can still have use-use flow.
*/
cached
predicate getCallArg(CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos) {
predicate getCallArg(
Cfg::CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos
) {
Stages::DataFlow::ref() and
resolveCall(call, target, type) and
(
@@ -1442,10 +1447,12 @@ private predicate sameEnclosingCallable(Node node1, Node node2) {
// DataFlowCall
// =============================================================================
newtype TDataFlowCall =
TNormalCall(CallNode call, Function target, CallType type) { resolveCall(call, target, type) } or
TNormalCall(Cfg::CallNode call, Function target, CallType type) {
call.injects(_) and resolveCall(call, target, type)
} or
/** A call to the generated function inside a comprehension */
TComprehensionCall(Comp c) or
TPotentialLibraryCall(CallNode call) or
TPotentialLibraryCall(Cfg::CallNode call) { call.injects(_) } or
/** A synthesized call inside a summarized callable */
TSummaryCall(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
@@ -1465,7 +1472,7 @@ abstract class DataFlowCall extends TDataFlowCall {
abstract ArgumentNode getArgument(ArgumentPosition apos);
/** Get the control flow node representing this call, if any. */
abstract ControlFlowNode getNode();
abstract Cfg::ControlFlowNode getNode();
/** Gets the enclosing callable of this call. */
DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) }
@@ -1496,28 +1503,28 @@ abstract class ExtractedDataFlowCall extends DataFlowCall {
}
/**
* A resolved call in source code with an underlying `CallNode`.
* A resolved call in source code with an underlying `Cfg::CallNode`.
*
* This is considered normal, compared with special calls such as `obj[0]` calling the
* `__getitem__` method on the object. However, this also includes calls that go to the
* `__call__` special method.
*/
class NormalCall extends ExtractedDataFlowCall, TNormalCall {
CallNode call;
Cfg::CallNode call;
Function target;
CallType type;
NormalCall() { this = TNormalCall(call, target, type) }
override string toString() {
// note: if we used toString directly on the CallNode we would get
// `ControlFlowNode for func()`
// but the `ControlFlowNode` part is just clutter, so we go directly to the AST node
// note: if we used toString directly on the Cfg::CallNode we would get
// `Cfg::ControlFlowNode for func()`
// but the `Cfg::ControlFlowNode` part is just clutter, so we go directly to the AST node
// instead.
result = call.getNode().toString()
}
override ControlFlowNode getNode() { result = call }
override Cfg::ControlFlowNode getNode() { result = call }
override Scope getScope() { result = call.getScope() }
@@ -1545,7 +1552,7 @@ class ComprehensionCall extends ExtractedDataFlowCall, TComprehensionCall {
override string toString() { result = "comprehension call" }
override ControlFlowNode getNode() { result.getNode() = c }
override Cfg::ControlFlowNode getNode() { result.getNode() = c }
override Scope getScope() { result = c.getScope() }
@@ -1568,14 +1575,14 @@ class ComprehensionCall extends ExtractedDataFlowCall, TComprehensionCall {
* in this class.
*/
class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall {
CallNode call;
Cfg::CallNode call;
PotentialLibraryCall() { this = TPotentialLibraryCall(call) }
override string toString() {
// note: if we used toString directly on the CallNode we would get
// `ControlFlowNode for func()`
// but the `ControlFlowNode` part is just clutter, so we go directly to the AST node
// note: if we used toString directly on the Cfg::CallNode we would get
// `Cfg::ControlFlowNode for func()`
// but the `Cfg::ControlFlowNode` part is just clutter, so we go directly to the AST node
// instead.
result = call.getNode().toString()
}
@@ -1592,10 +1599,10 @@ class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall
// potential self argument, from `foo.bar()` -- note that this could also just be a
// module reference, but we really don't have a good way of knowing :|
apos.isSelf() and
result.asCfgNode() = call.getFunction().(AttrNode).getObject()
result.asCfgNode() = call.getFunction().(Cfg::AttrNode).getObject()
}
override ControlFlowNode getNode() { result = call }
override Cfg::ControlFlowNode getNode() { result = call }
override Scope getScope() { result = call.getScope() }
}
@@ -1627,7 +1634,7 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
override ArgumentNode getArgument(ArgumentPosition apos) { none() }
override ControlFlowNode getNode() { none() }
override Cfg::ControlFlowNode getNode() { none() }
override string toString() { result = "[summary] call to " + receiver + " in " + c }
@@ -1769,12 +1776,12 @@ private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNodeImpl
* This is used for tracking flow through captured variables.
*/
class SynthCapturedVariablesArgumentNode extends Node, TSynthCapturedVariablesArgumentNode {
ControlFlowNode callable;
Cfg::ControlFlowNode callable;
SynthCapturedVariablesArgumentNode() { this = TSynthCapturedVariablesArgumentNode(callable) }
/** Gets the `CallNode` corresponding to this captured variables argument node. */
CallNode getCallNode() { result.getFunction() = callable }
/** Gets the `Cfg::CallNode` corresponding to this captured variables argument node. */
Cfg::CallNode getCallNode() { result.getFunction() = callable }
/** Gets the `CfgNode` that corresponds to this synthetic node. */
CfgNode getUnderlyingNode() { result.asCfgNode() = callable }
@@ -1792,7 +1799,7 @@ class CapturedVariablesArgumentNodeAsArgumentNode extends ArgumentNode,
{
overlay[global]
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
exists(CallNode callNode | callNode = this.getCallNode() |
exists(Cfg::CallNode callNode | callNode = this.getCallNode() |
callNode = call.getNode() and
exists(Function target | resolveCall(callNode, target, _) |
target = any(VariableCapture::CapturedVariable v).getACapturingScope()
@@ -1806,7 +1813,7 @@ class CapturedVariablesArgumentNodeAsArgumentNode extends ArgumentNode,
class SynthCapturedVariablesArgumentPostUpdateNode extends PostUpdateNodeImpl,
TSynthCapturedVariablesArgumentPostUpdateNode
{
ControlFlowNode callable;
Cfg::ControlFlowNode callable;
SynthCapturedVariablesArgumentPostUpdateNode() {
this = TSynthCapturedVariablesArgumentPostUpdateNode(callable)

View File

@@ -2,8 +2,9 @@ overlay[local?]
module;
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import DataFlowPublic
private import semmle.python.essa.SsaCompute
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import semmle.python.dataflow.new.internal.ImportResolution
private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.python.frameworks.data.ModelsAsData
@@ -43,13 +44,28 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
// Nodes
//--------
overlay[local]
predicate isExpressionNode(ControlFlowNode node) { node.getNode() instanceof Expr }
predicate isExpressionNode(Cfg::ControlFlowNode node) {
// Restrict to the `injects` representative so the dataflow layer creates
// exactly one `TCfgNode` per AST expression.
node.injects(_) and
(
node.getNode() instanceof Expr
or
// `Cfg::ForNode` wraps a `For` statement's iter position, but
// overrides `.getNode()` to return the `Py::For` statement (for
// legacy parity). The underlying AST is still an `Expr` (the iter
// expression); we want a dataflow node here so that for-loop
// content reads (`for y in l`) have a source expression node to
// read content from.
node instanceof Cfg::ForNode
)
}
// =============================================================================
// SyntheticPreUpdateNode
// =============================================================================
class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode {
CallNode node;
Cfg::CallNode node;
SyntheticPreUpdateNode() { this = TSyntheticPreUpdateNode(node) }
@@ -151,7 +167,7 @@ predicate synthStarArgsElementParameterNodeStoreStep(
* been passed in a `**kwargs` argument.
*/
class SynthDictSplatArgumentNode extends Node, TSynthDictSplatArgumentNode {
CallNode node;
Cfg::CallNode node;
SynthDictSplatArgumentNode() { this = TSynthDictSplatArgumentNode(node) }
@@ -165,7 +181,7 @@ class SynthDictSplatArgumentNode extends Node, TSynthDictSplatArgumentNode {
private predicate synthDictSplatArgumentNodeStoreStep(
ArgumentNode nodeFrom, DictionaryElementContent c, SynthDictSplatArgumentNode nodeTo
) {
exists(string name, CallNode call, ArgumentPosition keywordPos |
exists(string name, Cfg::CallNode call, ArgumentPosition keywordPos |
nodeTo = TSynthDictSplatArgumentNode(call) and
getCallArg(call, _, _, nodeFrom, keywordPos) and
keywordPos.isKeyword(name) and
@@ -289,7 +305,7 @@ abstract class PostUpdateNodeImpl extends Node {
* Synthetic post-update nodes for synthetic nodes need to be listed one by one.
*/
class SyntheticPostUpdateNode extends PostUpdateNodeImpl, TSyntheticPostUpdateNode {
ControlFlowNode node;
Cfg::ControlFlowNode node;
SyntheticPostUpdateNode() { this = TSyntheticPostUpdateNode(node) }
@@ -333,16 +349,42 @@ module LocalFlow {
// `x = f(42)`
// nodeFrom is `f(42)`
// nodeTo is `x`
exists(AssignmentDefinition def |
//
// We use the CFG-level `DefinitionNode.getValue()` directly rather
// than going through SSA, because the new SSA library prunes write
// definitions that have no subsequent read in the same scope (e.g.
// a module-level `def f():` whose `f` is only read inside other
// functions). The CFG-level link is unconditional.
//
// The Name-target restriction mirrors legacy ESSA's
// `SsaDefinitions::assignment_definition`, which required
// `defn.(NameNode).defines(v)`. Subscript and attribute writes
// (`x[i] = 42`, `obj.attr = 42`) are intentionally excluded — their
// value flow is handled by the content-flow / `AttrWrite` machinery,
// not by a local-flow step *into* the Subscript/Attribute expression.
// Excluding them is essential for keeping augmented-assignment
// targets (`x[i] += 42`) classifiable as `LocalSourceNode` on the
// read side: the single canonical CFG node is both a load and a
// store, and any incoming local-flow step would disqualify it from
// being a local source.
exists(Cfg::DefinitionNode def |
nodeFrom.(CfgNode).getNode() = def.getValue() and
nodeTo.(CfgNode).getNode() = def.getDefiningNode()
nodeTo.(CfgNode).getNode() = def and
def instanceof Cfg::NameNode and
// Parameter defaults are evaluated in the enclosing scope, while the
// parameter itself lives in the function's scope. The cross-scope
// edge is provided by `runtimeJumpStep` instead.
not exists(Py::Parameter param | def.getNode() = param.asName())
)
or
// With definition
// `with f(42) as x:`
// nodeFrom is `f(42)`
// nodeTo is `x`
exists(With with, ControlFlowNode contextManager, WithDefinition withDef, ControlFlowNode var |
exists(
With with, Cfg::ControlFlowNode contextManager, SsaImpl::WithDefinition withDef,
Cfg::ControlFlowNode var
|
var = withDef.getDefiningNode()
|
nodeFrom.(CfgNode).getNode() = contextManager and
@@ -361,13 +403,13 @@ module LocalFlow {
predicate expressionFlowStep(Node nodeFrom, Node nodeTo) {
// If expressions
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::IfExprNode).getAnOperand()
or
// Assignment expressions
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(AssignmentExprNode).getValue()
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::AssignmentExprNode).getValue()
or
// boolean inline expressions such as `x or y` or `x and y`
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(BoolExprNode).getAnOperand()
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::BoolExprNode).getAnOperand()
or
// Flow inside an unpacking assignment
iterableUnpackingFlowStep(nodeFrom, nodeTo)
@@ -376,12 +418,25 @@ module LocalFlow {
matchFlowStep(nodeFrom, nodeTo)
}
predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
AdjacentUses::adjacentUseUse(nodeFrom, nodeTo)
predicate useToNextUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) {
// The SSA-level adjacent-use predicate works on specific CFG variants
// (e.g. boolean-outcome `[true]`/`[false]` or emptiness `[empty]`/`[non-empty]`
// splits of the same AST node), but dataflow values are insensitive to
// those splits — there is at most one `CfgNode` per AST. Project both
// ends through `.getNode()` so all variants contribute their use-use
// edges to the canonical pair.
exists(Cfg::NameNode fromVariant, Cfg::NameNode toVariant |
SsaImpl::AdjacentUses::adjacentUseUse(fromVariant, toVariant) and
fromVariant.getNode() = nodeFrom.getNode() and
toVariant.getNode() = nodeTo.getNode()
)
}
predicate defToFirstUse(EssaVariable var, NameNode nodeTo) {
AdjacentUses::firstUse(var.getDefinition(), nodeTo)
predicate defToFirstUse(SsaImpl::EssaVariable var, Cfg::NameNode nodeTo) {
exists(Cfg::NameNode toVariant |
SsaImpl::AdjacentUses::firstUse(var.getDefinition(), toVariant) and
toVariant.getNode() = nodeTo.getNode()
)
}
predicate useUseFlowStep(Node nodeFrom, Node nodeTo) {
@@ -390,12 +445,13 @@ module LocalFlow {
// `x = f(y)`
// nodeFrom is `y` on first line
// nodeTo is `y` on second line
exists(EssaDefinition def |
nodeFrom.(CfgNode).getNode() = def.(EssaNodeDefinition).getDefiningNode()
exists(SsaImpl::EssaDefinition def, Cfg::NameNode toVariant |
nodeFrom.(CfgNode).getNode() = def.(SsaImpl::EssaNodeDefinition).getDefiningNode()
or
nodeFrom.(ScopeEntryDefinitionNode).getDefinition() = def
|
AdjacentUses::firstUse(def, nodeTo.(CfgNode).getNode())
SsaImpl::AdjacentUses::firstUse(def, toVariant) and
toVariant.getNode() = nodeTo.(CfgNode).getNode().getNode()
)
or
// Next use after use
@@ -557,9 +613,9 @@ predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) {
// a parameter with a default value, since the parameter will be in the scope of the
// function, while the default value itself will be in the scope that _defines_ the
// function.
exists(ParameterDefinition param |
exists(SsaImpl::ParameterDefinition param |
// note: we go to the _control-flow node_ of the parameter, and not the ESSA node of the parameter, since for type-tracking, the ESSA node is not a LocalSourceNode, so we would get in trouble.
nodeFrom.asCfgNode() = param.getDefault() and
nodeFrom.asCfgNode().getNode() = param.getParameter().(Parameter).getDefault() and
nodeTo.asCfgNode() = param.getDefiningNode()
)
or
@@ -663,7 +719,7 @@ predicate neverSkipInPathGraph(Node n) {
// ```
// we would end up saying that the path MUST not skip the x in `y = x`, which is just
// annoying and doesn't help the path explanation become clearer.
n.asCfgNode() = any(EssaNodeDefinition def).getDefiningNode()
n.asCfgNode() = any(SsaImpl::EssaNodeDefinition def).getDefiningNode()
}
/**
@@ -874,7 +930,7 @@ predicate listStoreStep(CfgNode nodeFrom, ListElementContent c, CfgNode nodeTo)
// nodeFrom is `42`, cfg node
// nodeTo is the list, `[..., 42, ...]`, cfg node
// c denotes element of list
nodeTo.getNode().(ListNode).getAnElement() = nodeFrom.getNode() and
nodeTo.getNode().(Cfg::ListNode).getAnElement() = nodeFrom.getNode() and
not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and
// Suppress unused variable warning
c = c
@@ -887,7 +943,7 @@ predicate setStoreStep(CfgNode nodeFrom, SetElementContent c, CfgNode nodeTo) {
// nodeFrom is `42`, cfg node
// nodeTo is the set, `{..., 42, ...}`, cfg node
// c denotes element of list
nodeTo.getNode().(SetNode).getAnElement() = nodeFrom.getNode() and
nodeTo.getNode().(Cfg::SetNode).getAnElement() = nodeFrom.getNode() and
// Suppress unused variable warning
c = c
}
@@ -900,7 +956,7 @@ predicate tupleStoreStep(CfgNode nodeFrom, TupleElementContent c, CfgNode nodeTo
// nodeTo is the tuple, `(..., 42, ...)`, cfg node
// c denotes element of tuple and index of nodeFrom
exists(int n |
nodeTo.getNode().(TupleNode).getElement(n) = nodeFrom.getNode() and
nodeTo.getNode().(Cfg::TupleNode).getElement(n) = nodeFrom.getNode() and
not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and
c.getIndex() = n
)
@@ -914,7 +970,7 @@ predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeT
// nodeTo is the dict, `{..., "key" = 42, ...}`, cfg node
// c denotes element of dictionary and the key `"key"`
exists(KeyValuePair item |
item = nodeTo.asCfgNode().(DictNode).getNode().(Dict).getAnItem() and
item = nodeTo.asCfgNode().(Cfg::DictNode).getNode().(Dict).getAnItem() and
nodeFrom.getNode().getNode() = item.getValue() and
c.getKey() = item.getKey().(StringLiteral).getS()
)
@@ -929,9 +985,9 @@ predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeT
private predicate moreDictStoreSteps(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) {
// NOTE: It's important to add logic to the newtype definition of
// DictionaryElementContent if you add new cases here.
exists(SubscriptNode subscript |
exists(Cfg::SubscriptNode subscript |
nodeTo.(PostUpdateNode).getPreUpdateNode().asCfgNode() = subscript.getObject() and
nodeFrom.asCfgNode() = subscript.(DefinitionNode).getValue() and
nodeFrom.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText()
)
or
@@ -944,8 +1000,8 @@ private predicate moreDictStoreSteps(CfgNode nodeFrom, DictionaryElementContent
}
predicate dictClearStep(Node node, DictionaryElementContent c) {
exists(SubscriptNode subscript |
subscript instanceof DefinitionNode and
exists(Cfg::SubscriptNode subscript |
subscript instanceof Cfg::DefinitionNode and
node.asCfgNode() = subscript.getObject() and
c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText()
)
@@ -1024,7 +1080,7 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
// nodeFrom is `l`, cfg node
// nodeTo is `l[3]`, cfg node
// c is compatible with 3
nodeFrom.getNode() = nodeTo.getNode().(SubscriptNode).getObject() and
nodeFrom.getNode() = nodeTo.getNode().(Cfg::SubscriptNode).getObject() and
(
c instanceof ListElementContent
or
@@ -1033,10 +1089,10 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
c instanceof DictionaryElementAnyContent
or
c.(TupleElementContent).getIndex() =
nodeTo.getNode().(SubscriptNode).getIndex().getNode().(IntegerLiteral).getValue()
nodeTo.getNode().(Cfg::SubscriptNode).getIndex().getNode().(IntegerLiteral).getValue()
or
c.(DictionaryElementContent).getKey() =
nodeTo.getNode().(SubscriptNode).getIndex().getNode().(StringLiteral).getS()
nodeTo.getNode().(Cfg::SubscriptNode).getIndex().getNode().(StringLiteral).getS()
)
}
@@ -1091,7 +1147,7 @@ module Conversions {
predicate formatReadStep(Node nodeFrom, ContentSet c, Node nodeTo) {
// % formatting
exists(BinaryExprNode fmt | fmt = nodeTo.asCfgNode() |
exists(Cfg::BinaryExprNode fmt | fmt = nodeTo.asCfgNode() |
fmt.getOp() instanceof Mod and
fmt.getRight() = nodeFrom.asCfgNode()
) and

View File

@@ -5,11 +5,14 @@ overlay[local]
module;
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
private import codeql.controlflow.SuccessorType
private import DataFlowPrivate
import semmle.python.dataflow.new.TypeTracking
import Attributes
import LocalSources
private import semmle.python.essa.SsaCompute
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import semmle.python.dataflow.new.internal.ImportStar
private import semmle.python.frameworks.data.ModelsAsData
private import FlowSummaryImpl as FlowSummaryImpl
@@ -27,16 +30,18 @@ private import semmle.python.frameworks.data.ModelsAsData
overlay[local]
newtype TNode =
/** A node corresponding to a control flow node. */
TCfgNode(ControlFlowNode node) {
TCfgNode(Cfg::ControlFlowNode node) {
isExpressionNode(node)
or
node.getNode() instanceof Pattern
node.injects(_) and node.getNode() instanceof Pattern
} or
/**
* A node corresponding to a scope entry definition. That is, the value of a variable
* as it enters a scope.
*/
TScopeEntryDefinitionNode(ScopeEntryDefinition def) { not def.getScope() instanceof Module } or
TScopeEntryDefinitionNode(SsaImpl::ScopeEntryDefinition def) {
not def.getScope() instanceof Module
} or
/**
* A synthetic node representing the value of an object before a state change.
*
@@ -47,13 +52,15 @@ newtype TNode =
// NOTE: since we can't rely on the call graph, but we want to have synthetic
// pre-update nodes for class calls, we end up getting synthetic pre-update nodes for
// ALL calls :|
TSyntheticPreUpdateNode(CallNode call) or
TSyntheticPreUpdateNode(Cfg::CallNode call) { call.injects(_) } or
/**
* A synthetic node representing the value of an object after a state change.
* See QLDoc for `PostUpdateNode`.
*/
TSyntheticPostUpdateNode(ControlFlowNode node) {
exists(CallNode call |
TSyntheticPostUpdateNode(Cfg::ControlFlowNode node) {
node.injects(_) and
(
exists(Cfg::CallNode call |
node = call.getArg(_)
or
node = call.getArgByName(_)
@@ -62,12 +69,12 @@ newtype TNode =
node = call.getFunction()
)
or
node = any(AttrNode a).getObject()
node = any(Cfg::AttrNode a).getObject()
or
node = any(SubscriptNode s).getObject()
node = any(Cfg::SubscriptNode s).getObject()
or
// self parameter when used implicitly in `super()`
exists(Class cls, Function func, ParameterDefinition def |
exists(Class cls, Function func, SsaImpl::ParameterDefinition def |
func = cls.getAMethod() and
not isStaticmethod(func) and
// this matches what we do in ExtractedParameterNode
@@ -77,6 +84,7 @@ newtype TNode =
or
// the iterable argument to the implicit comprehension function
node.getNode() = any(Comp c).getIterable()
)
} or
/** A node representing a global (module-level) variable in a specific module. */
TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m } or
@@ -112,7 +120,9 @@ newtype TNode =
exists(ParameterPosition ppos | ppos.isStarArgs(_) | exists(callable.getParameter(ppos)))
} or
/** A synthetic node to capture keyword arguments that are passed to a `**kwargs` parameter. */
TSynthDictSplatArgumentNode(CallNode call) { exists(call.getArgByName(_)) } or
TSynthDictSplatArgumentNode(Cfg::CallNode call) {
call.injects(_) and exists(call.getArgByName(_))
} or
/** A synthetic node to allow flow to keyword parameters from a `**kwargs` argument. */
TSynthDictSplatParameterNode(DataFlowCallable callable) {
exists(ParameterPosition ppos | ppos.isKeyword(_) | exists(callable.getParameter(ppos)))
@@ -128,15 +138,15 @@ newtype TNode =
* A synthetic node representing the values of the variables captured
* by the callable being called.
*/
TSynthCapturedVariablesArgumentNode(ControlFlowNode callable) {
callable = any(CallNode c).getFunction()
TSynthCapturedVariablesArgumentNode(Cfg::ControlFlowNode callable) {
callable.injects(_) and callable = any(Cfg::CallNode c).getFunction()
} or
/**
* A synthetic node representing the values of the variables captured
* by the callable being called, after the output has been computed.
*/
TSynthCapturedVariablesArgumentPostUpdateNode(ControlFlowNode callable) {
callable = any(CallNode c).getFunction()
TSynthCapturedVariablesArgumentPostUpdateNode(Cfg::ControlFlowNode callable) {
callable.injects(_) and callable = any(Cfg::CallNode c).getFunction()
} or
/** A synthetic node representing the values of variables captured by a comprehension. */
TSynthCompCapturedVariablesArgumentNode(Comp comp) {
@@ -194,7 +204,7 @@ class Node extends TNode {
}
/** Gets the control-flow node corresponding to this node, if any. */
ControlFlowNode asCfgNode() { none() }
Cfg::ControlFlowNode asCfgNode() { none() }
/** Gets the expression corresponding to this node, if any. */
Expr asExpr() { none() }
@@ -207,14 +217,14 @@ class Node extends TNode {
/** A data-flow node corresponding to a control-flow node. */
class CfgNode extends Node, TCfgNode {
ControlFlowNode node;
Cfg::ControlFlowNode node;
CfgNode() { this = TCfgNode(node) }
/** Gets the `ControlFlowNode` represented by this data-flow node. */
ControlFlowNode getNode() { result = node }
/** Gets the `Cfg::ControlFlowNode` represented by this data-flow node. */
Cfg::ControlFlowNode getNode() { result = node }
override ControlFlowNode asCfgNode() { result = node }
override Cfg::ControlFlowNode asCfgNode() { result = node }
/** Gets a textual representation of this element. */
override string toString() { result = node.toString() }
@@ -224,9 +234,9 @@ class CfgNode extends Node, TCfgNode {
override Location getLocation() { result = node.getLocation() }
}
/** A data-flow node corresponding to a `CallNode` in the control-flow graph. */
/** A data-flow node corresponding to a `Cfg::CallNode` in the control-flow graph. */
class CallCfgNode extends CfgNode, LocalSourceNode {
override CallNode node;
override Cfg::CallNode node;
/**
* Gets the data-flow node for the function component of the call corresponding to this data-flow
@@ -307,15 +317,15 @@ ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e }
* as it enters a scope.
*/
class ScopeEntryDefinitionNode extends Node, TScopeEntryDefinitionNode {
ScopeEntryDefinition def;
SsaImpl::ScopeEntryDefinition def;
ScopeEntryDefinitionNode() { this = TScopeEntryDefinitionNode(def) }
/** Gets the `ScopeEntryDefinition` associated with this node. */
ScopeEntryDefinition getDefinition() { result = def }
/** Gets the `SsaImpl::ScopeEntryDefinition` associated with this node. */
SsaImpl::ScopeEntryDefinition getDefinition() { result = def }
/** Gets the source variable represented by this node. */
SsaSourceVariable getVariable() { result = def.getSourceVariable() }
SsaImpl::SsaSourceVariable getVariable() { result = def.getSourceVariable() }
override Location getLocation() { result = def.getLocation() }
@@ -337,7 +347,7 @@ class ParameterNode extends Node instanceof ParameterNodeImpl {
/** A parameter node found in the source code (not in a summary). */
class ExtractedParameterNode extends ParameterNodeImpl, CfgNode {
//, LocalSourceNode {
ParameterDefinition def;
SsaImpl::ParameterDefinition def;
ExtractedParameterNode() { node = def.getDefiningNode() }
@@ -368,10 +378,10 @@ Node getCallArgApproximation() {
exists(Class c | result.asExpr() = c.getAMethod().getArg(0))
or
// the object part of an attribute expression (which might be a bound method)
result.asCfgNode() = any(AttrNode a).getObject()
result.asCfgNode() = any(Cfg::AttrNode a).getObject()
or
// the function part of any call
result.asCfgNode() = any(CallNode c).getFunction()
result.asCfgNode() = any(Cfg::CallNode c).getFunction()
}
/** Gets the extracted argument nodes that do not rely on `getCallArg`. */
@@ -380,7 +390,7 @@ private Node implicitArgumentNode() {
normalCallArg(_, result, _)
or
// and self arguments
result.asCfgNode() = any(CallNode c).getFunction().(AttrNode).getObject()
result.asCfgNode() = any(Cfg::CallNode c).getFunction().(Cfg::AttrNode).getObject()
or
// for comprehensions, we allow the synthetic `iterable` argument
result.asExpr() = any(Comp c).getIterable()
@@ -489,17 +499,20 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
not result.getScope() = mod
}
/** Gets an `EssaNode` that corresponds to an assignment of this global variable. */
/** Gets a CFG node that corresponds to an assignment of this global variable. */
Node getAWrite() {
any(EssaNodeDefinition def).definedBy(var, result.asCfgNode().(DefinitionNode))
exists(Cfg::NameNode n |
n.defines(var) and
result.asCfgNode() = n
)
}
/** Gets the possible values of the variable at the end of import time */
CfgNode getADefiningWrite() {
exists(SsaVariable def |
def = any(SsaVariable ssa_var).getAnUltimateDefinition() and
def.getDefinition() = result.asCfgNode() and
def.getVariable() = var
exists(SsaImpl::EssaVariable def |
def = any(SsaImpl::EssaVariable ssa_var).getAnUltimateDefinition() and
def.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode() = result.asCfgNode() and
def.getSourceVariable().getVariable() = var
)
}
@@ -516,7 +529,7 @@ private ModuleVariableNode import_star_read(Node n) {
overlay[global]
pragma[nomagic]
private predicate resolved_import_star_module(Module m, string name, Node n) {
exists(NameNode nn | nn = n.asCfgNode() |
exists(Cfg::NameNode nn | nn = n.asCfgNode() |
ImportStar::importStarResolvesTo(pragma[only_bind_into](nn), m) and
nn.getId() = name
)
@@ -574,88 +587,88 @@ class StarPatternElementNode extends Node, TStarPatternElementNode {
}
/**
* Gets a node that controls whether other nodes are evaluated.
* A node that participates in a conditional split: a CFG node whose
* evaluation outcome (true/false) is used to choose between two
* successor basic blocks. In the shared CFG, branching is detected
* via typed successor edges (boolean successor types) on the unique
* `injects` node for each AST expression.
*
* In the base case, this is the last node of `conditionBlock`, and `flipped` is `false`.
* This definition accounts for (short circuting) `and`- and `or`-expressions, as the structure
* of basic blocks will reflect their semantics.
*
* However, in the program
* ```python
* if not is_safe(path):
* return
* ```
* the last node in the `ConditionBlock` is `not is_safe(path)`.
*
* We would like to consider also `is_safe(path)` a guard node, albeit with `flipped` being `true`.
* Thus we recurse through `not`-expressions.
* Users typically obtain a `GuardNode` by casting from a more specific
* Cfg type: `g.(Cfg::CallNode)` for a call-based check, etc.
*/
ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) {
// Base case: the last node truly does determine which successor is chosen
result = conditionBlock.getLastNode() and
flipped = false
or
// Recursive cases:
// if a guard node is a `not`-expression,
// the operand is also a guard node, but with inverted polarity.
exists(UnaryExprNode notNode |
result = notNode.getOperand() and
notNode.getNode().getOp() instanceof Not
|
notNode = guardNode(conditionBlock, flipped.booleanNot())
)
or
// if a guard node is compared to a boolean literal,
// the other operand is also a guard node,
// but with polarity depending on the literal (and on the comparison).
exists(CompareNode cmpNode, Cmpop op, ControlFlowNode b, boolean should_flip |
(
cmpNode.operands(result, op, b) or
cmpNode.operands(b, op, result)
) and
not result.getNode() instanceof BooleanLiteral and
(
// comparing to the boolean
(op instanceof Eq or op instanceof Is) and
// we should flip if the value compared against, here the value of `b`, is false
should_flip = b.getNode().(BooleanLiteral).booleanValue().booleanNot()
or
// comparing to the negation of the boolean
(op instanceof NotEq or op instanceof IsNot) and
// again, we should flip if the value compared against, here the value of `not b`, is false.
// That is, if the value of `b` is true.
should_flip = b.getNode().(BooleanLiteral).booleanValue()
class GuardNode extends Cfg::ControlFlowNode {
GuardNode() {
// This node has boolean successor edges (directly or via wrapping).
outcomeOfGuard(this, _, _)
}
/** Holds if this guard controls block `b` upon evaluating to `branch`. */
predicate controlsBlock(Cfg::BasicBlock b, boolean branch) {
exists(CfgImpl::BasicBlock outcomeBB |
outcomeOfGuard(this, outcomeBB, branch) and
outcomeBB.dominates(b)
)
|
// we flip `flipped` according to `should_flip` via the formula `flipped xor should_flip`.
flipped in [true, false] and
cmpNode = guardNode(conditionBlock, flipped.booleanXor(should_flip))
)
}
}
/**
* A node that controls whether other nodes are evaluated.
* Holds if `outcomeBB` is the basic block entered when `guard` evaluates
* to `branch`.
*
* The field `flipped` allows us to match `GuardNode`s underneath
* `not`-expressions and still choose the appropriate branch.
* For a direct guard `if g:`, the outcome BB starts at the after-value
* node for the matching branch. For wrapped guards like `not g` or
* `g == True`, we follow those wrappers up the AST to find the
* outermost expression that actually branches, with an appropriate
* polarity transform.
*/
class GuardNode extends ControlFlowNode {
ConditionBlock conditionBlock;
boolean flipped;
GuardNode() { this = guardNode(conditionBlock, flipped) }
/** Holds if this guard controls block `b` upon evaluating to `branch`. */
predicate controlsBlock(BasicBlock b, boolean branch) {
branch in [true, false] and
conditionBlock.controls(b, branch.booleanXor(flipped))
}
private predicate outcomeOfGuard(
Cfg::ControlFlowNode guard, CfgImpl::BasicBlock outcomeBB, boolean branch
) {
// Base case: the guard has boolean successor edges.
// Only the canonical representative (injects) can act as a guard base.
guard.injects(_) and
exists(BooleanSuccessor t |
t.getValue() = branch and
outcomeBB = guard.(CfgImpl::ControlFlowNode).getASuccessor(t).getBasicBlock()
)
or
// Recursive: `not guard` — same outcome split as `guard`, flipped.
exists(Cfg::UnaryExprNode notNode, boolean notBranch |
notNode.injects(_) and
notNode.getOperand().getNode() = guard.getNode() and
notNode.getNode().getOp() instanceof Not and
outcomeOfGuard(notNode, outcomeBB, notBranch) and
branch = notBranch.booleanNot()
)
or
// Recursive: comparisons against a boolean literal.
exists(
Cfg::CompareNode cmpNode, Cmpop op, Cfg::ControlFlowNode otherOperand,
Cfg::ControlFlowNode guardOperand, boolean polarity, boolean cmpBranch
|
cmpNode.injects(_) and
guardOperand.getNode() = guard.getNode() and
(
cmpNode.operands(guardOperand, op, otherOperand) or
cmpNode.operands(otherOperand, op, guardOperand)
) and
not guard.getNode() instanceof BooleanLiteral and
(
(op instanceof Eq or op instanceof Is) and
polarity = otherOperand.getNode().(BooleanLiteral).booleanValue()
or
(op instanceof NotEq or op instanceof IsNot) and
polarity = otherOperand.getNode().(BooleanLiteral).booleanValue().booleanNot()
) and
outcomeOfGuard(cmpNode, outcomeBB, cmpBranch) and
branch = cmpBranch.booleanXor(polarity.booleanNot())
)
}
/**
* Holds if the guard `g` validates `node` upon evaluating to `branch`.
*/
signature predicate guardChecksSig(GuardNode g, ControlFlowNode node, boolean branch);
signature predicate guardChecksSig(GuardNode g, Cfg::ControlFlowNode node, boolean branch);
/**
* Provides a set of barrier nodes for a guard that validates a node.
@@ -670,7 +683,9 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
result = ParameterizedBarrierGuard<Unit, extendedGuardChecks/4>::getABarrierNode(_)
}
private predicate extendedGuardChecks(GuardNode g, ControlFlowNode node, boolean branch, Unit u) {
private predicate extendedGuardChecks(
GuardNode g, Cfg::ControlFlowNode node, boolean branch, Unit u
) {
guardChecks(g, node, branch) and
u = u
}
@@ -680,7 +695,7 @@ bindingset[this]
private signature class ParamSig;
private module WithParam<ParamSig P> {
signature predicate guardChecksSig(GuardNode g, ControlFlowNode node, boolean branch, P param);
signature predicate guardChecksSig(GuardNode g, Cfg::ControlFlowNode node, boolean branch, P param);
}
/**
@@ -693,10 +708,16 @@ module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guar
/** Gets a node that is safely guarded by the given guard check with parameter `param`. */
overlay[global]
ExprNode getABarrierNode(P param) {
exists(GuardNode g, EssaDefinition def, ControlFlowNode node, boolean branch |
AdjacentUses::useOfDef(def, node) and
exists(GuardNode g, SsaImpl::EssaDefinition def, Cfg::ControlFlowNode node, boolean branch |
SsaImpl::AdjacentUses::useOfDef(def, node) and
guardChecks(g, node, branch, param) and
AdjacentUses::useOfDef(def, result.asCfgNode()) and
SsaImpl::AdjacentUses::useOfDef(def, result.asCfgNode()) and
// The protected use must be a different SSA position than the test
// position itself: `controlsBlock` is reflexive on dominance, and
// the test expression is an SSA-use position on the def-use chain.
// Without this guard, the test position would be returned as a
// barrier and block flow before it can reach genuine branch uses.
node != result.asCfgNode() and
g.controlsBlock(result.asCfgNode().getBasicBlock(), branch)
)
}
@@ -712,7 +733,7 @@ module ExternalBarrierGuard {
private import semmle.python.ApiGraphs
overlay[global]
private predicate guardCheck(GuardNode g, ControlFlowNode node, boolean branch, string kind) {
private predicate guardCheck(GuardNode g, Cfg::ControlFlowNode node, boolean branch, string kind) {
exists(API::CallNode call, API::Node parameter |
parameter = call.getAParameter() and
parameter = ModelOutput::getABarrierGuardNode(kind, branch)
@@ -748,10 +769,10 @@ newtype TContent =
TSetElementContent() or
/** An element of a tuple at a specific index. */
TTupleElementContent(int index) {
exists(any(TupleNode tn).getElement(index))
exists(any(Cfg::TupleNode tn).getElement(index))
or
// Arguments can overflow and end up in the starred parameter tuple.
exists(any(CallNode cn).getArg(index))
exists(any(Cfg::CallNode cn).getArg(index))
or
// since flow summaries might use tuples, we ensure that we at least have valid
// TTupleElementContent for the 0..7 (7 was picked to match `small_tuple` in
@@ -768,10 +789,14 @@ newtype TContent =
or
// d["key"] = ...
key =
any(SubscriptNode sub | sub.isStore() | sub.getIndex().getNode().(StringLiteral).getText())
any(Cfg::SubscriptNode sub |
sub.isStore()
|
sub.getIndex().getNode().(StringLiteral).getText()
)
or
// d.setdefault("key", ...)
exists(CallNode call | call.getFunction().(AttrNode).getName() = "setdefault" |
exists(Cfg::CallNode call | call.getFunction().(Cfg::AttrNode).getName() = "setdefault" |
key = call.getArg(0).getNode().(StringLiteral).getText()
)
} or

View File

@@ -5,17 +5,18 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.ImportStar
private import semmle.python.dataflow.new.TypeTracking
private import semmle.python.dataflow.new.internal.DataFlowPrivate
/**
* Holds if `init` is a package's `__init__.py` and `var` is a global variable in
* `init` whose name matches a submodule of the package.
*
* Inlined from `SsaSource::init_module_submodule_defn` to avoid pulling
* `semmle.python.essa.SsaDefinitions` into the new dataflow stack.
* Holds if the name of `var` refers to a submodule of a package and `init` is
* the `__init__` module of that package. Locally inlined replacement for the
* legacy `SsaSource::init_module_submodule_defn` so that this module has no
* direct dependency on `semmle.python.essa.SsaDefinitions`.
*/
private predicate initModuleSubmoduleDefn(GlobalVariable var, Module init) {
init.isPackageInit() and
@@ -81,13 +82,19 @@ module ImportResolution {
* Holds if there is an ESSA step from `defFrom` to `defTo`, which should be allowed
* for import resolution.
*/
private predicate allowedEssaImportStep(EssaDefinition defFrom, EssaDefinition defTo) {
private predicate allowedEssaImportStep(
SsaImpl::EssaDefinition defFrom, SsaImpl::EssaDefinition defTo
) {
// to handle definitions guarded by if-then-else
defFrom = defTo.(PhiFunction).getAnInput()
defFrom = defTo.(SsaImpl::PhiFunction).getAnInput()
or
// refined variable
// example: https://github.com/nvbn/thefuck/blob/ceeaeab94b5df5a4fe9d94d61e4f6b0bbea96378/thefuck/utils.py#L25-L45
defFrom = defTo.(EssaNodeRefinement).getInput().getDefinition()
// to handle uncertain writes such as `from X import *`, which create an
// uncertain SSA definition for every name in the importing scope. The
// immediately preceding definition is still potentially the value of the
// module export.
SsaImpl::Ssa::uncertainWriteDefinitionInput(defTo, defFrom)
// Note: legacy ESSA refinement-step (e.g. for `foo.bar = X`) is
// not modelled in the new SSA beyond the cases handled above.
}
/**
@@ -104,30 +111,32 @@ module ImportResolution {
// Definitions made inside `m` itself
//
// for code such as `foo = ...; foo.bar = ...` there will be TWO
// EssaDefinition/EssaVariable. One for `foo = ...` (AssignmentDefinition) and one
// SsaImpl::EssaDefinition/SsaImpl::EssaVariable. One for `foo = ...` (SsaImpl::AssignmentDefinition) and one
// for `foo.bar = ...`. The one for `foo.bar = ...` (EssaNodeRefinement). The
// EssaNodeRefinement is the one that will reach the end of the module (normal
// exit).
//
// However, we cannot just use the EssaNodeRefinement as the `val`, because the
// normal data-flow depends on use-use flow, and use-use flow targets CFG nodes not
// EssaNodes. So we need to go back from the EssaDefinition/EssaVariable that
// EssaNodes. So we need to go back from the SsaImpl::EssaDefinition/SsaImpl::EssaVariable that
// reaches the end of the module, to the first definition of the variable, and then
// track forwards using use-use flow to find a suitable CFG node that has flow into
// it from use-use flow.
exists(EssaVariable lastUseVar, EssaVariable firstDef |
exists(SsaImpl::EssaVariable lastUseVar, SsaImpl::EssaVariable firstDef |
lastUseVar.getName() = name and
// we ignore special variable $ introduced by our analysis (not used for anything)
// we ignore special variable * introduced by `from <pkg> import *` -- TODO: understand why we even have this?
not name in ["$", "*"] and
lastUseVar.getAUse() = m.getANormalExit() and
exists(Cfg::ControlFlowNode exit |
exit.isNormalExit() and exit.getScope() = m and lastUseVar.getAUse() = exit
) and
allowedEssaImportStep*(firstDef, lastUseVar) and
not allowedEssaImportStep(_, firstDef)
|
not LocalFlow::defToFirstUse(firstDef, _) and
val.asCfgNode() = firstDef.getDefinition().(EssaNodeDefinition).getDefiningNode()
val.asCfgNode() = firstDef.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode()
or
exists(ControlFlowNode mid, ControlFlowNode end |
exists(Cfg::ControlFlowNode mid, Cfg::ControlFlowNode end |
LocalFlow::defToFirstUse(firstDef, mid) and
LocalFlow::useToNextUse*(mid, end) and
not LocalFlow::useToNextUse(end, _) and
@@ -155,9 +164,9 @@ module ImportResolution {
* handles simple cases where we can statically tell that this is the case.
*/
private predicate all_mentions_name(Module m, string name) {
exists(DefinitionNode def, SequenceNode n |
exists(Cfg::DefinitionNode def, Cfg::SequenceNode n |
def.getValue() = n and
def.(NameNode).getId() = "__all__" and
def.(Cfg::NameNode).getId() = "__all__" and
def.getScope() = m and
any(StringLiteral s | s.getText() = name) = n.getAnElement().getNode()
)
@@ -170,18 +179,20 @@ module ImportResolution {
*/
private predicate no_or_complicated_all(Module m) {
// No mention of `__all__` in the module
not exists(DefinitionNode def | def.getScope() = m and def.(NameNode).getId() = "__all__")
not exists(Cfg::DefinitionNode def |
def.getScope() = m and def.(Cfg::NameNode).getId() = "__all__"
)
or
// `__all__` is set to a non-sequence value
exists(DefinitionNode def |
def.(NameNode).getId() = "__all__" and
exists(Cfg::DefinitionNode def |
def.(Cfg::NameNode).getId() = "__all__" and
def.getScope() = m and
not def.getValue() instanceof SequenceNode
not def.getValue() instanceof Cfg::SequenceNode
)
or
// `__all__` is used in some way that doesn't involve storing a value in it. This usually means
// it is being mutated through `append` or `extend`, which we don't handle.
exists(NameNode n | n.getId() = "__all__" and n.getScope() = m and n.isLoad())
exists(Cfg::NameNode n | n.getId() = "__all__" and n.getScope() = m and n.isLoad())
}
private predicate potential_module_export(Module m, string name) {
@@ -189,7 +200,7 @@ module ImportResolution {
or
no_or_complicated_all(m) and
(
exists(NameNode n | n.getId() = name and n.getScope() = m and name.charAt(0) != "_")
exists(Cfg::NameNode n | n.getId() = name and n.getScope() = m and name.charAt(0) != "_")
or
exists(Alias a | a.getAsname().(Name).getId() = name and a.getValue().getScope() = m)
)
@@ -219,12 +230,12 @@ module ImportResolution {
/** Gets a module that may have been added to `sys.modules`. */
private Module sys_modules_module_with_name(string name) {
exists(ControlFlowNode n, DataFlow::Node mod |
exists(SubscriptNode sub |
exists(Cfg::ControlFlowNode n, DataFlow::Node mod |
exists(Cfg::SubscriptNode sub |
sub.getObject() = sys_modules_reference().asCfgNode() and
sub.getIndex() = n and
n.getNode().(StringLiteral).getText() = name and
sub.(DefinitionNode).getValue() = mod.asCfgNode() and
sub.(Cfg::DefinitionNode).getValue() = mod.asCfgNode() and
mod = getModuleReference(result)
)
)
@@ -336,11 +347,11 @@ module ImportResolution {
// name as a submodule, we always consider that this attribute _could_ be a
// reference to the submodule, even if we don't know that the submodule has been
// imported yet.
exists(string submodule, Module package, EssaVariable var |
exists(string submodule, Module package, SsaImpl::EssaVariable var |
submodule = var.getName() and
initModuleSubmoduleDefn(var.getSourceVariable(), package) and
initModuleSubmoduleDefn(var.getSourceVariable().getVariable(), package) and
m = getModuleFromName(package.getPackageName() + "." + submodule) and
result.asCfgNode() = var.getDefinition().(EssaNodeDefinition).getDefiningNode()
result.asCfgNode() = var.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode()
)
}

View File

@@ -3,6 +3,7 @@ overlay[local]
module;
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.internal.Builtins
private import semmle.python.dataflow.new.internal.ImportResolution
private import semmle.python.dataflow.new.DataFlow
@@ -15,7 +16,7 @@ module ImportStar {
*/
overlay[local]
cached
predicate namePossiblyDefinedInImportStar(NameNode n, string name, Scope s) {
predicate namePossiblyDefinedInImportStar(Cfg::NameNode n, string name, Scope s) {
n.isLoad() and
name = n.getId() and
s = n.getScope().getEnclosingScope*() and
@@ -52,7 +53,7 @@ module ImportStar {
/** Holds if a global variable called `name` is assigned a value in the module `m`. */
cached
predicate globalNameDefinedInModule(string name, Module m) {
exists(NameNode n |
exists(Cfg::NameNode n |
not exists(LocalVariable v | n.defines(v)) and
n.isStore() and
name = n.getId() and
@@ -66,7 +67,7 @@ module ImportStar {
*/
overlay[global]
cached
predicate importStarResolvesTo(NameNode n, Module m) {
predicate importStarResolvesTo(Cfg::NameNode n, Module m) {
m = getStarImported+(n.getEnclosingModule()) and
globalNameDefinedInModule(n.getId(), m) and
not isDefinedLocally(n.getNode())
@@ -99,7 +100,7 @@ module ImportStar {
*/
overlay[local]
cached
ControlFlowNode potentialImportStarBase(Scope s) {
result = any(ImportStarNode n | n.getScope() = s).getModule()
Cfg::ControlFlowNode potentialImportStarBase(Scope s) {
result = any(Cfg::ImportStarNode n | n.getScope() = s).getModule()
}
}

View File

@@ -170,6 +170,8 @@ overlay[local]
module;
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import DataFlowPublic
/**
@@ -178,7 +180,7 @@ private import DataFlowPublic
* This class abstracts away the differing representations of comprehensions and
* for statements.
*/
class ForTarget extends ControlFlowNode {
class ForTarget extends Cfg::ControlFlowNode {
Expr source;
ForTarget() {
@@ -198,7 +200,7 @@ class ForTarget extends ControlFlowNode {
}
/** The LHS of an assignment, it also records the assigned value. */
class AssignmentTarget extends ControlFlowNode {
class AssignmentTarget extends Cfg::ControlFlowNode {
Expr value;
AssignmentTarget() {
@@ -209,7 +211,7 @@ class AssignmentTarget extends ControlFlowNode {
}
/** A direct (or top-level) target of an unpacking assignment. */
class UnpackingAssignmentDirectTarget extends ControlFlowNode instanceof SequenceNode {
class UnpackingAssignmentDirectTarget extends Cfg::ControlFlowNode instanceof Cfg::SequenceNode {
Expr value;
UnpackingAssignmentDirectTarget() {
@@ -222,7 +224,7 @@ class UnpackingAssignmentDirectTarget extends ControlFlowNode instanceof Sequenc
}
/** A (possibly recursive) target of an unpacking assignment. */
class UnpackingAssignmentTarget extends ControlFlowNode {
class UnpackingAssignmentTarget extends Cfg::ControlFlowNode {
UnpackingAssignmentTarget() {
this instanceof UnpackingAssignmentDirectTarget
or
@@ -231,10 +233,11 @@ class UnpackingAssignmentTarget extends ControlFlowNode {
}
/** A (possibly recursive) target of an unpacking assignment which is also a sequence. */
class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instanceof SequenceNode {
ControlFlowNode getElement(int i) { result = super.getElement(i) }
class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instanceof Cfg::SequenceNode
{
Cfg::ControlFlowNode getElement(int i) { result = super.getElement(i) }
ControlFlowNode getAnElement() { result = this.getElement(_) }
Cfg::ControlFlowNode getAnElement() { result = this.getElement(_) }
}
/**
@@ -255,7 +258,7 @@ predicate iterableUnpackingAssignmentFlowStep(Node nodeFrom, Node nodeTo) {
predicate iterableUnpackingForReadStep(CfgNode nodeFrom, Content c, Node nodeTo) {
exists(ForTarget target |
nodeFrom.getNode().getNode() = target.getSource() and
target instanceof SequenceNode and
target instanceof Cfg::SequenceNode and
nodeTo = TIterableSequenceNode(target)
) and
(
@@ -323,11 +326,11 @@ predicate iterableUnpackingConvertingStoreStep(Node nodeFrom, Content c, Node no
*/
predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo) {
exists(
UnpackingAssignmentSequenceTarget target, int index, ControlFlowNode element, int starIndex
UnpackingAssignmentSequenceTarget target, int index, Cfg::ControlFlowNode element, int starIndex
|
target.getElement(starIndex) instanceof StarredNode
target.getElement(starIndex) instanceof Cfg::StarredNode
or
not exists(target.getAnElement().(StarredNode)) and
not exists(target.getAnElement().(Cfg::StarredNode)) and
starIndex = -1
|
nodeFrom.(CfgNode).getNode() = target and
@@ -342,18 +345,18 @@ predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo
else c.(TupleElementContent).getIndex() >= index - 1
) and
(
if element instanceof SequenceNode
if element instanceof Cfg::SequenceNode
then
// Step 5b
nodeTo = TIterableSequenceNode(element)
else
if element instanceof StarredNode
if element instanceof Cfg::StarredNode
then
// Step 5c
nodeTo = TIterableElementNode(element)
else
// Step 5a
exists(MultiAssignmentDefinition mad | element = mad.getDefiningNode() |
exists(SsaImpl::MultiAssignmentDefinition mad | element = mad.getDefiningNode() |
nodeTo.(CfgNode).getNode() = element
)
)
@@ -366,7 +369,7 @@ predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo
* content type `ListElementContent`.
*/
predicate iterableUnpackingStarredElementStoreStep(Node nodeFrom, Content c, Node nodeTo) {
exists(ControlFlowNode starred, MultiAssignmentDefinition mad |
exists(Cfg::ControlFlowNode starred, SsaImpl::MultiAssignmentDefinition mad |
starred.getNode() instanceof Starred and
starred = mad.getDefiningNode()
|

View File

@@ -9,6 +9,7 @@ overlay[local]
module;
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import DataFlowPublic
private import DataFlowPrivate
private import semmle.python.internal.CachedStages
@@ -314,7 +315,7 @@ private module Cached {
*/
cached
predicate subscript(LocalSourceNode node, CfgNode subscript, CfgNode index) {
exists(CfgNode seq, SubscriptNode subscriptNode | subscriptNode = subscript.getNode() |
exists(CfgNode seq, Cfg::SubscriptNode subscriptNode | subscriptNode = subscript.getNode() |
node.flowsTo(seq) and
seq.getNode() = subscriptNode.getObject() and
index.getNode() = subscriptNode.getIndex()

View File

@@ -91,9 +91,7 @@ predicate matchAsFlowStep(Node nodeFrom, Node nodeTo) {
or
// the interior pattern flows to the alias
nodeFrom.(CfgNode).getNode().getNode() = subject.getPattern() and
exists(PatternAliasDefinition pad | pad.getDefiningNode().getNode() = alias |
nodeTo.(CfgNode).getNode() = pad.getDefiningNode()
)
nodeTo.(CfgNode).getNode().getNode() = alias
)
}
@@ -124,11 +122,9 @@ predicate matchLiteralFlowStep(Node nodeFrom, Node nodeTo) {
* syntax (toplevel): `case var:`
*/
predicate matchCaptureFlowStep(Node nodeFrom, Node nodeTo) {
exists(MatchCapturePattern capture, Name var | capture.getVariable() = var |
exists(MatchCapturePattern capture |
nodeFrom.(CfgNode).getNode().getNode() = capture and
exists(PatternCaptureDefinition pcd | pcd.getDefiningNode().getNode() = var |
nodeTo.(CfgNode).getNode() = pcd.getDefiningNode()
)
nodeTo.(CfgNode).getNode().getNode() = capture.getVariable()
)
}

View File

@@ -0,0 +1,548 @@
/**
* Provides the Python SSA implementation built on the new (shared) CFG.
*
* Mirrors the Java SSA adapter at
* `java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll`:
* an `InputSig` is defined in terms of positional `(BasicBlock, int)`
* variable references, and the shared
* `codeql.ssa.Ssa::Make<Location, Cfg, Input>` module is then
* instantiated.
*
* `SourceVariable` is the AST-level `Py::Variable`. Variable references
* are looked up via the CFG facade's `NameNode.defines`/`uses`/`deletes`
* predicates, which themselves are one-line bridges to AST-level
* `Name.defines`/`uses`/`deletes`.
*
* Implicit-entry definitions are inserted for:
* - non-local / global / builtin variables that are read in the scope
* but never assigned (no enclosing CFG node defines them),
* - captured variables (variables defined in an enclosing scope that
* are read inside the scope), and
* - parameters, but only if the corresponding parameter name is *not*
* itself a CFG node. With the C#-style parameter wiring already
* installed in `AstNodeImpl.qll`, parameter names *are* CFG nodes,
* so the regular `variableWrite` path handles them — no `i = -1`
* entry is needed for ordinary parameters.
*/
overlay[local?]
module;
private import python as Py
private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
private import semmle.python.controlflow.internal.Cfg as Cfg
private import codeql.ssa.Ssa as SsaImplCommon
private import codeql.controlflow.BasicBlock as BB
/**
* Adapts the Python `Cfg` facade to the shared SSA library's `CfgSig`.
* All members are inherited from `Cfg::ControlFlowNode` and
* `Cfg::BasicBlock`.
*/
private module CfgForSsa implements BB::CfgSig<Py::Location> {
class ControlFlowNode = CfgImpl::ControlFlowNode;
class BasicBlock = CfgImpl::BasicBlock;
class EntryBasicBlock = CfgImpl::Cfg::EntryBasicBlock;
predicate dominatingEdge = CfgImpl::Cfg::dominatingEdge/2;
}
/**
* A source variable for SSA, wrapping a Python AST `Variable`.
*
* We only track variables that are read at least once in their scope —
* tracking write-only variables would be unnecessary work — *except*
* for module-scope globals, where the "read" can be external (e.g.
* `import mymodule; mymodule.x`). Such globals are tracked
* unconditionally so that import-resolution can find their defining
* write.
*/
private newtype TSsaSourceVariable =
TPyVar(Py::Variable v) {
// Has a use somewhere — read-relevant for SSA.
exists(Cfg::NameNode n | n.uses(v))
or
// Or has a deletion (treated as a write that destroys the value).
exists(Cfg::NameNode n | n.deletes(v))
or
// Or is a module-scope global written in this module — must be
// tracked even if never read locally, because importers may read
// it as an attribute on the module object.
v.getScope() instanceof Py::Module and
exists(Cfg::NameNode n | n.defines(v))
or
// Or is a parameter — parameters must always have a
// `ParameterDefinition` for dataflow argument-routing to work,
// even if the parameter is never read in its scope. Mirrors
// legacy ESSA's `ParameterDefinition` (which fired for every
// parameter binding regardless of liveness).
exists(Py::Parameter p | p.asName() = v.getAStore())
}
/**
* A source variable for SSA, wrapping a Python AST `Variable`.
*/
class SsaSourceVariable extends TSsaSourceVariable {
/** Gets the underlying Python AST variable. */
Py::Variable getVariable() { this = TPyVar(result) }
/** Gets the (textual) name of this variable. */
string getName() { result = this.getVariable().getId() }
/** Gets a textual representation of this source variable. */
string toString() { result = this.getVariable().toString() }
/** Gets the location of this source variable. */
Py::Location getLocation() { result = this.getVariable().getScope().getLocation() }
/** Gets the scope in which this variable lives. */
Py::Scope getScope() { result = this.getVariable().getScope() }
/**
* Gets a use of this variable as it appears in the source — a `NameNode`
* that loads or deletes the variable. Mirrors legacy
* `SsaSourceVariable.getASourceUse()`.
*/
Cfg::ControlFlowNode getASourceUse() {
exists(Cfg::NameNode n | result = n |
n.uses(this.getVariable()) or n.deletes(this.getVariable())
)
}
/**
* Gets an implicit use of this variable. The new SSA does not have
* implicit-use refinements, but we keep this for API parity — every
* normal-exit of the variable's scope counts as a sink, ensuring
* variables stay live to scope exit for taint-tracking.
*/
Cfg::ControlFlowNode getAnImplicitUse() {
result.isNormalExit() and result.getScope() = this.getScope()
}
/**
* Gets a use of this variable — either an explicit source use or an
* implicit use at scope exit. Mirrors legacy `SsaSourceVariable.getAUse()`.
*/
Cfg::ControlFlowNode getAUse() {
result = this.getASourceUse() or result = this.getAnImplicitUse()
}
}
/**
* Holds if `v` is a non-local read in scope `s`, in the sense that `s`
* uses `v` but does not write it within `s`. This includes globals,
* builtins, and variables captured from an enclosing function scope.
*
* The `Py::Variable` `v` lives in some defining scope (the module for
* globals, an outer function for closures, etc.); the reading scope
* `s` is the scope where the use of `v` occurs.
*/
private predicate nonLocalReadIn(Py::Variable v, Py::Scope s) {
exists(Cfg::NameNode n |
n.uses(v) and
n.getScope() = s and
not exists(Cfg::NameNode def | def.defines(v) and def.getScope() = s)
) and
// Match legacy ESSA: only create entry defs for variables that have
// at least one defining store somewhere — otherwise the entry def
// represents "nothing reaches here", which is the default anyway and
// introduces no useful flow. (Legacy's `ModuleVariable` required a
// store; this is the closure-aware generalisation.)
exists(Cfg::NameNode store | store.defines(v))
}
/**
* Holds if `bb` is the entry basic block of a scope where `v` should
* have an implicit entry definition. This covers:
* - non-local / global / builtin variables read in `s`, and
* - captured variables (defined in an enclosing scope but read in `s`).
*
* Each reading scope gets its own entry def, so a closure variable can
* have multiple entry defs across all functions/methods that read it.
*
* Parameters are *not* included: their bound `Name` is itself a CFG
* node (per the C#-style parameter wiring), so `variableWrite` fires at
* the parameter's natural CFG index.
*/
private predicate hasEntryDefIn(SsaSourceVariable v, CfgImpl::BasicBlock bb) {
exists(Py::Scope s |
nonLocalReadIn(v.getVariable(), s) and
bb = entryBlock(s)
)
}
/**
* Gets the entry basic block of scope `s`, where implicit entry
* definitions are placed (at synthetic index `-1`).
*/
private CfgImpl::BasicBlock entryBlock(Py::Scope s) {
exists(CfgImpl::ControlFlowNode entry |
entry instanceof CfgImpl::ControlFlow::EntryNode and
entry.getEnclosingCallable().asScope() = s and
result = entry.getBasicBlock()
)
}
/**
* The SSA `InputSig` for Python. References are positional
* `(BasicBlock, int)` pairs into the new CFG.
*/
private module SsaImplInput implements SsaImplCommon::InputSig<Py::Location, CfgImpl::BasicBlock> {
class SourceVariable = SsaSourceVariable;
predicate variableWrite(CfgImpl::BasicBlock bb, int i, SourceVariable v, boolean certain) {
// Explicit binding at a CFG node — includes assignments,
// parameter Names (wired in via the C# pattern), exception-handler
// `as`-bindings, import aliases, and match-pattern captures.
exists(Cfg::NameNode n |
bb.getNode(i) = n and
n.defines(v.getVariable()) and
certain = true
)
or
// `del x` — removes the binding. Modelled as a certain write that
// makes any subsequent read invalid.
exists(Cfg::NameNode n |
bb.getNode(i) = n and
n.deletes(v.getVariable()) and
certain = true
)
or
// Implicit entry definition for non-local / captured / global /
// builtin variables read in some scope. Each reading scope's entry
// block gets one such write, allowing closures: e.g. when `x` is a
// parameter of an outer function and read inside a nested
// function, both scopes get entry defs for `x`.
hasEntryDefIn(v, bb) and
i = -1 and
certain = true
or
// `from X import *` — possibly rebinds every name in the importing
// scope. Modelled as an uncertain write at the import-star's CFG
// position for every variable that lives in (or is referenced
// from) the same scope as the import-star. Mirrors legacy ESSA's
// `ImportStarRefinement` (see `essa/SsaDefinitions.qll`'s
// `import_star_refinement` predicate). The write is uncertain so
// that prior definitions of the variable remain available — the
// shared-SSA `SsaUncertainWrite` merges the new value with the
// immediately preceding definition.
exists(Cfg::ImportStarNode imp |
imp.injects(_) and
bb.getNode(i) = imp and
certain = false and
(
v.getVariable().getScope() = imp.getScope()
or
// Variable is defined in some other scope but referenced in
// the same scope as the import-star (matches legacy clause 2:
// `other.uses(v) and def.getScope() = other.getScope()`).
exists(Cfg::NameNode other |
other.uses(v.getVariable()) and
imp.getScope() = other.getScope()
)
)
)
}
predicate variableRead(CfgImpl::BasicBlock bb, int i, SourceVariable v, boolean certain) {
// Explicit source use — a `Name` load or a `del x` of the variable.
exists(Cfg::NameNode n |
bb.getNode(i) = n and
n.uses(v.getVariable()) and
certain = true
)
or
// Synthetic use at the normal exit of the variable's defining scope.
// This keeps every variable live to scope exit so that callers (e.g.
// `module_export` in ImportResolution.qll, or taint-tracking pass-through
// through unread locals) can ask "which definition reaches end of
// scope?". Mirrors legacy ESSA's `SsaSourceVariable.getAUse()` which
// included `getScope().getANormalExit()`.
exists(Cfg::ControlFlowNode exit |
exit.isNormalExit() and
exit.getScope() = v.getVariable().getScope() and
bb.getNode(i) = exit and
certain = true
)
}
}
/**
* The shared SSA instantiation for Python.
*
* Members:
* - `Definition` — the union of explicit, uncertain, and phi definitions
* - `WriteDefinition`, `UncertainWriteDefinition`, `PhiNode`
* - the standard SSA predicates (`getAUse`, `getAnUltimateDefinition`, ...).
*/
module Ssa = SsaImplCommon::Make<Py::Location, CfgForSsa, SsaImplInput>;
final class Definition = Ssa::Definition;
final class WriteDefinition = Ssa::WriteDefinition;
final class UncertainWriteDefinition = Ssa::UncertainWriteDefinition;
final class PhiNode = Ssa::PhiNode;
// ===========================================================================
// ESSA-shaped adapter layer
//
// The dataflow library (`python/ql/lib/semmle/python/dataflow/new/`) and
// related modules (`ApiGraphs.qll`, etc.) consume the legacy ESSA API
// (`EssaVariable`, `EssaDefinition`, `AssignmentDefinition`,
// `ScopeEntryDefinition`, `ParameterDefinition`, `WithDefinition`,
// `PhiFunction`, plus the `AdjacentUses` module). To migrate them off
// the legacy CFG, we expose the same API surface on top of the
// shared SSA built above.
//
// This adapter is intentionally narrow: it covers only the predicates
// that new dataflow consumes. The richer legacy ESSA — refinement
// nodes, attribute refinements, edge refinements — stays available
// via `semmle.python.essa.Essa` for points-to / legacy code.
// ===========================================================================
/**
* Gets the CFG node at which a write definition's binding takes place.
*
* For ordinary writes (assignment, deletion, parameter) this is the
* canonical CFG node of the bound Name. For implicit entry definitions
* (synthesised at position `-1` of a scope's entry BB) this is the
* scope's entry node.
*/
private Cfg::ControlFlowNode writeDefNode(Ssa::WriteDefinition def) {
exists(CfgImpl::BasicBlock bb, int i | def.definesAt(_, bb, i) |
i >= 0 and result = bb.getNode(i)
or
i = -1 and result = bb.getNode(0)
)
}
/**
* A write definition whose binding has a corresponding CFG node — i.e.
* everything that's not a phi node. Mirrors legacy ESSA's
* `EssaNodeDefinition`.
*/
class EssaNodeDefinition extends Ssa::WriteDefinition {
/** Gets the CFG node where this definition's binding takes place. */
Cfg::ControlFlowNode getDefiningNode() { result = writeDefNode(this) }
/** Gets the variable defined here (legacy name). */
SsaSourceVariable getVariable() { result = this.getSourceVariable() }
/** Gets the enclosing scope. */
Py::Scope getScope() {
exists(Cfg::ControlFlowNode n | n = this.getDefiningNode() | result = n.getScope())
}
/**
* Holds if this definition defines source variable `v` at CFG node
* `defNode`. Flatter form of `getSourceVariable()` +
* `getDefiningNode()`, matching legacy ESSA's `definedBy`.
*/
predicate definedBy(SsaSourceVariable v, Cfg::ControlFlowNode defNode) {
v = this.getSourceVariable() and defNode = this.getDefiningNode()
}
}
/**
* An assignment definition: any binding where the value being assigned
* is statically known via `Cfg::DefinitionNode.getValue()`. Includes
* plain assignments, walrus, annotated assignments, augmented
* assignments, import aliases (`import x` / `from m import x [as y]`),
* `with ... as x`, and for-target bindings (where `getValue()` returns
* the iter expression's CFG node). Excludes parameter bindings —
* those are modelled by `ParameterDefinition`.
*/
class AssignmentDefinition extends EssaNodeDefinition {
AssignmentDefinition() {
exists(Cfg::NameNode n | n = this.getDefiningNode() |
exists(n.(Cfg::DefinitionNode).getValue()) and
not n.(Cfg::ControlFlowNode).isParameter()
)
}
/** Gets the CFG node for the value being assigned, if statically known. */
Cfg::ControlFlowNode getValue() {
result = this.getDefiningNode().(Cfg::DefinitionNode).getValue()
}
}
/**
* A parameter definition — the binding of a parameter name in a
* function's scope.
*/
class ParameterDefinition extends EssaNodeDefinition {
ParameterDefinition() { this.getDefiningNode().isParameter() }
/** Gets the AST `Parameter` (a `Py::Name` in param context). */
Py::Name getParameter() { result = this.getDefiningNode().getNode() }
}
/**
* A definition introduced by a `with ... as x:` clause.
*/
class WithDefinition extends EssaNodeDefinition {
WithDefinition() {
exists(Cfg::NameNode n, Py::With w |
n = this.getDefiningNode() and
w.getOptionalVars() = n.getNode()
)
}
}
/**
* An assignment where the LHS is a tuple/list and the RHS is unpacked:
* `a, b = (1, 2)` or `a, *rest = xs`. The SSA def lives at the inner
* `Name` CFG node, but for IterableUnpacking integration we expose
* the enclosing `StarredNode` as the `getDefiningNode()` for `*rest`
* patterns — mirroring legacy ESSA's `multi_assignment_definition`,
* which placed the def at the StarredNode CFG node.
*/
class MultiAssignmentDefinition extends EssaNodeDefinition {
MultiAssignmentDefinition() {
exists(Cfg::NameNode n | n = super.getDefiningNode() |
exists(Py::Assign a, Py::Expr lhs |
a.getATarget() = lhs and
(lhs instanceof Py::Tuple or lhs instanceof Py::List) and
lhs.getASubExpression+() = n.getNode()
)
or
// For-loop with tuple/list target: `for a, b in xs:` —
// tuple-unpacking semantics applies to the for-target.
exists(Py::For f, Py::Expr lhs |
f.getTarget() = lhs and
(lhs instanceof Py::Tuple or lhs instanceof Py::List) and
lhs.getASubExpression+() = n.getNode()
)
)
}
override Cfg::ControlFlowNode getDefiningNode() {
// Default: the underlying `Name` CFG node (where the SSA def lives).
not exists(Cfg::StarredNode s |
s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode()
) and
result = super.getDefiningNode()
or
// Exception: for `*rest`, expose the enclosing `Starred` CFG node
// so that `IterableUnpacking::iterableUnpackingStarredElementStoreStep`
// can attach the rest-list to it.
exists(Cfg::StarredNode s |
s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode()
|
result = s
)
}
}
/**
* An implicit entry definition for a non-local / captured / global /
* builtin variable read in a scope but not defined there.
*
* Inherits from `EssaNodeDefinition` and exposes the scope's entry node
* as its defining node (matching legacy ESSA semantics).
*/
class ScopeEntryDefinition extends EssaNodeDefinition {
ScopeEntryDefinition() {
exists(CfgImpl::BasicBlock bb |
this.definesAt(_, bb, -1) and
bb instanceof CfgImpl::Cfg::EntryBasicBlock
)
}
/** Gets the enclosing scope (the scope whose entry block this def is in). */
override Py::Scope getScope() {
exists(CfgImpl::BasicBlock bb |
this.definesAt(_, bb, -1) and
result = bb.getNode(0).(Cfg::ControlFlowNode).getScope()
)
}
}
/** A phi node (alias matching legacy naming). */
class PhiFunction extends PhiNode {
/**
* Gets an input to this phi function (a definition that flows into
* the phi from one of its predecessor blocks). Mirrors legacy
* ESSA's `PhiFunction.getAnInput()`.
*/
Ssa::Definition getAnInput() { Ssa::phiHasInputFromBlock(this, result, _) }
}
/** Base class for all ESSA definitions (legacy-shaped). */
class EssaDefinition = Ssa::Definition;
/**
* An adapter representing a single SSA-defined "variable" — wrapping
* one `Ssa::Definition`. Mirrors legacy `EssaVariable` API.
*/
class EssaVariable extends Ssa::Definition {
/** Gets the underlying SSA definition (legacy name). */
Ssa::Definition getDefinition() { result = this }
/**
* Gets a CFG node where this definition is used. Includes regular
* `Name` reads as well as the synthetic scope-exit "use" registered
* via `SsaImplInput::variableRead` — mirrors legacy ESSA's
* `EssaVariable.getAUse()` which inherited the synthetic exit-use
* from `SsaSourceVariable`.
*/
Cfg::ControlFlowNode getAUse() {
exists(CfgImpl::BasicBlock bb, int i |
Ssa::ssaDefReachesRead(this.getSourceVariable(), this, bb, i) and
bb.getNode(i) = result
)
}
/** Gets the (textual) name of the underlying variable. */
string getName() { result = this.getSourceVariable().getVariable().getId() }
/** Gets the scope in which this variable lives. */
Py::Scope getScope() { result = this.getSourceVariable().getVariable().getScope() }
/** Gets an ultimate non-phi ancestor of this definition. */
EssaVariable getAnUltimateDefinition() {
if this instanceof PhiNode
then
exists(Ssa::Definition input |
Ssa::phiHasInputFromBlock(this, input, _) and
result = input.(EssaVariable).getAnUltimateDefinition()
)
else result = this
}
}
/**
* Adjacent use-use and def-use relations exposed by the shared SSA
* library. Provides the same interface as legacy
* `semmle.python.essa.SsaCompute::AdjacentUses`.
*/
module AdjacentUses {
/** Holds if `nodeFrom` and `nodeTo` are adjacent uses of the same SSA variable. */
predicate adjacentUseUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) {
exists(SsaSourceVariable v, CfgImpl::BasicBlock bb1, int i1, CfgImpl::BasicBlock bb2, int i2 |
Ssa::adjacentUseUse(bb1, i1, bb2, i2, v, _) and
nodeFrom = bb1.getNode(i1) and
nodeTo = bb2.getNode(i2)
)
}
/** Holds if `use` is a first use of definition `def`. */
predicate firstUse(Ssa::Definition def, Cfg::NameNode use) {
exists(CfgImpl::BasicBlock bb, int i |
Ssa::firstUse(def, bb, i, _) and
use = bb.getNode(i)
)
}
/**
* Holds if `use` is any reachable use of definition `def`. Combines
* `firstUse` with transitive use-use adjacency.
*/
predicate useOfDef(Ssa::Definition def, Cfg::NameNode use) {
firstUse(def, use)
or
exists(Cfg::NameNode mid | useOfDef(def, mid) and adjacentUseUse(mid, use))
}
}

View File

@@ -1,4 +1,6 @@
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
private import FlowSummaryImpl as FlowSummaryImpl
@@ -97,7 +99,7 @@ import Cached
* and isn't a big problem in practice.
*/
predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
exists(BinaryExprNode add | add = nodeTo.getNode() |
exists(Cfg::BinaryExprNode add | add = nodeTo.getNode() |
add.getOp() instanceof Add and add.getAnOperand() = nodeFrom.getNode()
)
}
@@ -106,7 +108,7 @@ predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to subscripting.
*/
predicate subscriptStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
nodeTo.getNode().(SubscriptNode).getObject() = nodeFrom.getNode()
nodeTo.getNode().(Cfg::SubscriptNode).getObject() = nodeFrom.getNode()
}
/**
@@ -122,15 +124,15 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
(
call = API::builtin(["str", "bytes", "unicode"]).getACall()
or
call.getFunction().asCfgNode().(NameNode).getId() in ["str", "bytes", "unicode"]
call.getFunction().asCfgNode().(Cfg::NameNode).getId() in ["str", "bytes", "unicode"]
) and
nodeFrom in [call.getArg(0), call.getArgByName("object")]
)
or
// String methods. Note that this doesn't recognize `meth = "foo".upper; meth()`
exists(CallNode call, string method_name, ControlFlowNode object |
exists(Cfg::CallNode call, string method_name, Cfg::ControlFlowNode object |
call = nodeTo.getNode() and
object = call.getFunction().(AttrNode).getObject(method_name)
object = call.getFunction().(Cfg::AttrNode).getObject(method_name)
|
nodeFrom.getNode() = object and
method_name in [
@@ -156,7 +158,7 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
)
or
// % formatting
exists(BinaryExprNode fmt | fmt = nodeTo.getNode() |
exists(Cfg::BinaryExprNode fmt | fmt = nodeTo.getNode() |
fmt.getOp() instanceof Mod and
(
fmt.getLeft() = nodeFrom.getNode()
@@ -166,7 +168,7 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
)
or
// string multiplication -- `"foo" * 10`
exists(BinaryExprNode mult | mult = nodeTo.getNode() |
exists(Cfg::BinaryExprNode mult | mult = nodeTo.getNode() |
mult.getOp() instanceof Mult and
mult.getLeft() = nodeFrom.getNode()
)
@@ -213,8 +215,8 @@ predicate awaitStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
* the variable `f` is tainted if the result of `open("foo")` is tainted.
*/
predicate asyncWithStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(With with, ControlFlowNode contextManager, ControlFlowNode var |
var = any(WithDefinition wd).getDefiningNode()
exists(With with, Cfg::ControlFlowNode contextManager, Cfg::ControlFlowNode var |
var = any(SsaImpl::WithDefinition wd).getDefiningNode()
|
nodeFrom.(DataFlow::CfgNode).getNode() = contextManager and
nodeTo.(DataFlow::CfgNode).getNode() = var and

View File

@@ -2,6 +2,8 @@ import codeql.util.Unit
import codeql.typetracking.TypeTracking as Shared
import codeql.typetracking.internal.TypeTrackingImpl as SharedImpl
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import semmle.python.internal.CachedStages
private import semmle.python.dataflow.new.internal.DataFlowPublic as DataFlowPublic
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
@@ -162,7 +164,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
// ignore the flow steps from the synthetic sequence node to the real sequence node,
// since we only support one level of content in type-trackers, and the nested
// structure requires two levels at least to be useful.
not exists(SequenceNode outer |
not exists(Cfg::SequenceNode outer |
outer.getAnElement() = nodeTo.asCfgNode() and
IterableUnpacking::iterableUnpackingTupleFlowStep(nodeFrom, nodeTo)
)
@@ -267,7 +269,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
// Since we only support one level of content in type-trackers we don't actually
// support `(aa, ab), (ba, bb) = ...`. Therefore we exclude the read-step from `(aa,
// ab)` to `aa` (since it is not needed).
not exists(SequenceNode outer |
not exists(Cfg::SequenceNode outer |
outer.getAnElement() = nodeFrom.asCfgNode() and
IterableUnpacking::iterableUnpackingTupleFlowStep(_, nodeFrom)
) and
@@ -277,7 +279,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
IterableUnpacking::iterableUnpackingForReadStep(_, _, seq) and
IterableUnpacking::iterableUnpackingConvertingReadStep(seq, _, elem) and
IterableUnpacking::iterableUnpackingConvertingStoreStep(elem, _, nodeFrom) and
nodeFrom.asCfgNode() instanceof SequenceNode
nodeFrom.asCfgNode() instanceof Cfg::SequenceNode
)
or
TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, DataFlowPublic::singleton(content))
@@ -315,13 +317,15 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
//
// nodeFrom is `expr`
// nodeTo is entry node for `f`
exists(ScopeEntryDefinition e, SsaSourceVariable var, DefinitionNode def |
exists(
SsaImpl::ScopeEntryDefinition e, SsaImpl::SsaSourceVariable var, Cfg::DefinitionNode def
|
e.getSourceVariable() = var and
var.hasDefiningNode(def)
def.getNode() = var.getVariable().getAStore()
|
nodeTo.(DataFlowPublic::ScopeEntryDefinitionNode).getDefinition() = e and
nodeFrom.asCfgNode() = def and
var.getScope().getScope*() = nodeFrom.getScope()
var.getVariable().getScope().getScope*() = nodeFrom.getScope()
)
}

View File

@@ -3,6 +3,9 @@ overlay[local]
module;
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import DataFlowPublic
private import semmle.python.dataflow.new.internal.DataFlowPrivate
private import codeql.dataflow.VariableCapture as Shared
@@ -14,10 +17,10 @@ private import codeql.dataflow.VariableCapture as Shared
// The first is the main implementation, the second is a performance motivated restriction.
// The restriction is to clear any `CapturedVariableContent` before writing a new one
// to avoid long access paths (see the link for a nice explanation).
private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBlock> {
private module CaptureInput implements Shared::InputSig<Location, CfgImpl::BasicBlock> {
private import python as PY
additional class ExprCfgNode extends ControlFlowNode {
additional class ExprCfgNode extends Cfg::ControlFlowNode {
ExprCfgNode() { isExpressionNode(this) }
}
@@ -25,7 +28,9 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
predicate isConstructor() { none() }
}
Callable basicBlockGetEnclosingCallable(Cfg::BasicBlock bb) { result = bb.getScope() }
Callable basicBlockGetEnclosingCallable(CfgImpl::BasicBlock bb) {
result = bb.getEnclosingCallable().asScope()
}
class CapturedVariable extends LocalVariable {
Function f;
@@ -51,21 +56,23 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
class CapturedParameter extends CapturedVariable {
CapturedParameter() { this.isParameter() }
ControlFlowNode getCfgNode() { result.getNode().(Parameter) = this.getAnAccess() }
Cfg::ControlFlowNode getCfgNode() { result.getNode().(Parameter) = this.getAnAccess() }
}
class Expr extends ExprCfgNode {
predicate hasCfgNode(Cfg::BasicBlock bb, int i) { this = bb.getNode(i) }
predicate hasCfgNode(CfgImpl::BasicBlock bb, int i) { this = bb.getNode(i) }
}
class VariableWrite extends ControlFlowNode {
class VariableWrite extends Cfg::ControlFlowNode {
CapturedVariable v;
VariableWrite() { exists(DefinitionNode d | d.getNode() = v.getAStore() | this = d.getValue()) }
VariableWrite() {
exists(Cfg::DefinitionNode d | d.getNode() = v.getAStore() | this = d.getValue())
}
CapturedVariable getVariable() { result = v }
predicate hasCfgNode(Cfg::BasicBlock bb, int i) { this = bb.getNode(i) }
predicate hasCfgNode(CfgImpl::BasicBlock bb, int i) { this = bb.getNode(i) }
}
class VariableRead extends Expr {
@@ -80,9 +87,14 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
// TODO: Other languages have an extra case here looking like
// simpleAstFlowStep(nodeFrom, nodeTo)
// we should investigate the potential benefit of adding that.
exists(SsaVariable def |
exists(SsaImpl::EssaVariable def |
def.getAUse() = nodeTo and
def.getAnUltimateDefinition().getDefinition().(DefinitionNode).getValue() = nodeFrom
def.getAnUltimateDefinition()
.getDefinition()
.(SsaImpl::EssaNodeDefinition)
.getDefiningNode()
.(Cfg::DefinitionNode)
.getValue() = nodeFrom
)
}
@@ -107,7 +119,7 @@ class CapturedVariable = CaptureInput::CapturedVariable;
class ClosureExpr = CaptureInput::ClosureExpr;
module Flow = Shared::Flow<Location, Cfg, CaptureInput>;
module Flow = Shared::Flow<Location, Cfg::CfgSigImpl, CaptureInput>;
private Flow::ClosureNode asClosureNode(Node n) {
result = n.(SynthCaptureNode).getSynthesizedCaptureNode()

View File

@@ -4,6 +4,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.dataflow.new.RemoteFlowSources
@@ -157,9 +158,9 @@ module Bottle {
DataFlow::Node value;
HeaderWriteSubscript() {
exists(SubscriptNode subscript |
exists(Cfg::SubscriptNode subscript |
this.asCfgNode() = subscript and
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
name.asCfgNode() = subscript.getIndex() and
subscript.getObject() = headers().asSource().asCfgNode()
)

View File

@@ -4,6 +4,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.TaintTracking
@@ -1305,7 +1306,7 @@ module PrivateDjango {
dict.(DataFlow::MethodCallNode).calls(files, "dict")
)
|
this.asCfgNode().(SubscriptNode).getObject() = dict.asCfgNode()
this.asCfgNode().(Cfg::SubscriptNode).getObject() = dict.asCfgNode()
or
this.(DataFlow::MethodCallNode).calls(dict, "get")
)
@@ -1314,7 +1315,7 @@ module PrivateDjango {
exists(DataFlow::AttrRead files, DataFlow::MethodCallNode getlistCall |
files.accesses(instance(), "FILES") and
getlistCall.calls(files, "getlist") and
this.asCfgNode().(SubscriptNode).getObject() = getlistCall.asCfgNode()
this.asCfgNode().(Cfg::SubscriptNode).getObject() = getlistCall.asCfgNode()
)
}
}
@@ -2216,7 +2217,7 @@ module PrivateDjango {
DataFlow::Node value;
DjangoResponseCookieSubscriptWrite() {
exists(SubscriptNode subscript, DataFlow::AttrRead cookieLookup |
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead cookieLookup |
// To give `this` a value, we need to choose between either LHS or RHS,
// and just go with the LHS
this.asCfgNode() = subscript
@@ -2228,7 +2229,7 @@ module PrivateDjango {
|
cookieLookup.flowsTo(subscriptObj)
) and
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
index.asCfgNode() = subscript.getIndex()
)
}
@@ -2249,7 +2250,7 @@ module PrivateDjango {
DataFlow::Node value;
DjangoResponseHeaderSubscriptWrite() {
exists(SubscriptNode subscript, DataFlow::AttrRead headerLookup |
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead headerLookup |
// To give `this` a value, we need to choose between either LHS or RHS,
// and just go with the LHS
this.asCfgNode() = subscript
@@ -2261,7 +2262,7 @@ module PrivateDjango {
|
headerLookup.flowsTo(subscriptObj)
) and
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
index.asCfgNode() = subscript.getIndex()
)
}
@@ -2284,14 +2285,14 @@ module PrivateDjango {
DataFlow::Node value;
DjangoResponseSubscriptWrite() {
exists(SubscriptNode subscript |
exists(Cfg::SubscriptNode subscript |
// To give `this` a value, we need to choose between either LHS or RHS,
// and just go with the LHS
this.asCfgNode() = subscript
|
subscript.getObject() =
DjangoImpl::DjangoHttp::Response::HttpResponse::instance().asCfgNode() and
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
index.asCfgNode() = subscript.getIndex()
)
}
@@ -2426,7 +2427,7 @@ module PrivateDjango {
/** Gets a reference to the result of calling the `as_view` classmethod of this class. */
private DataFlow::TypeTrackingNode asViewResult(DataFlow::TypeTracker t) {
t.start() and
result.asCfgNode().(CallNode).getFunction() = this.asViewRef().asCfgNode()
result.asCfgNode().(Cfg::CallNode).getFunction() = this.asViewRef().asCfgNode()
or
exists(DataFlow::TypeTracker t2 | result = this.asViewResult(t2).track(t2, t))
}

View File

@@ -4,6 +4,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.TaintTracking
@@ -441,7 +442,7 @@ module FastApi {
DataFlow::Node value;
HeaderSubscriptWrite() {
exists(SubscriptNode subscript, DataFlow::AttrRead headerLookup |
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead headerLookup |
// To give `this` a value, we need to choose between either LHS or RHS,
// and just go with the LHS
this.asCfgNode() = subscript
@@ -450,7 +451,7 @@ module FastApi {
exists(DataFlow::Node subscriptObj | subscriptObj.asCfgNode() = subscript.getObject() |
headerLookup.flowsTo(subscriptObj)
) and
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
index.asCfgNode() = subscript.getIndex()
)
}

View File

@@ -4,6 +4,7 @@
*/
import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.TaintTracking
import semmle.python.ApiGraphs
@@ -51,9 +52,9 @@ module Gradio {
// limit only to lists of parameters given to `inputs`.
(
(
call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode
call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode
or
call.getParameter(1).asSink().asCfgNode() instanceof ListNode
call.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode
) and
(
this = call.getKeywordParameter("inputs").getASubscript().getAValueReachingSink()
@@ -75,8 +76,8 @@ module Gradio {
exists(GradioInput call |
this = call.getParameter(0, "fn").getParameter(_).asSource() and
// exclude lists of parameters given to `inputs`
not call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode and
not call.getParameter(1).asSink().asCfgNode() instanceof ListNode
not call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode and
not call.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode
)
}
@@ -105,16 +106,16 @@ module Gradio {
// handle cases where there are multiple arguments passed as a list to `inputs`
(
(
node.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode
node.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode
or
node.getParameter(1).asSink().asCfgNode() instanceof ListNode
node.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode
) and
exists(int i | nodeTo = node.getParameter(0, "fn").getParameter(i).asSource() |
nodeFrom.asCfgNode() =
node.getKeywordParameter("inputs").asSink().asCfgNode().(ListNode).getElement(i)
node.getKeywordParameter("inputs").asSink().asCfgNode().(Cfg::ListNode).getElement(i)
or
nodeFrom.asCfgNode() =
node.getParameter(1).asSink().asCfgNode().(ListNode).getElement(i)
node.getParameter(1).asSink().asCfgNode().(Cfg::ListNode).getElement(i)
)
)
)

View File

@@ -4,6 +4,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
@@ -46,7 +47,7 @@ module MarkupSafeModel {
/** A direct instantiation of `markupsafe.Markup`. */
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
override CallNode node;
override Cfg::CallNode node;
ClassInstantiation() { this = classRef().getACall() }
}
@@ -64,7 +65,7 @@ module MarkupSafeModel {
/** A string concatenation with a `markupsafe.Markup` involved. */
class StringConcat extends Markup::InstanceSource, DataFlow::CfgNode {
override BinaryExprNode node;
override Cfg::BinaryExprNode node;
StringConcat() {
node.getOp() instanceof Add and
@@ -79,7 +80,7 @@ module MarkupSafeModel {
/** A %-style string format with `markupsafe.Markup` as the format string. */
class PercentStringFormat extends Markup::InstanceSource, DataFlow::CfgNode {
override BinaryExprNode node;
override Cfg::BinaryExprNode node;
PercentStringFormat() {
node.getOp() instanceof Mod and

View File

@@ -7,6 +7,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.data.ModelsAsData
@@ -56,7 +57,7 @@ module Pycurl {
{
OutgoingRequestCall() {
this = setopt().getACall() and
this.getArg(0).asCfgNode().(AttrNode).getName() = "URL"
this.getArg(0).asCfgNode().(Cfg::AttrNode).getName() = "URL"
}
override DataFlow::Node getAUrlPart() {
@@ -81,7 +82,7 @@ module Pycurl {
private class CurlSslCall extends Http::Client::Request::Range instanceof DataFlow::CallCfgNode {
CurlSslCall() {
this = setopt().getACall() and
this.getArg(0).asCfgNode().(AttrNode).getName() = ["SSL_VERIFYPEER", "SSL_VERIFYHOST"]
this.getArg(0).asCfgNode().(Cfg::AttrNode).getName() = ["SSL_VERIFYPEER", "SSL_VERIFYHOST"]
}
override DataFlow::Node getAUrlPart() { none() }

View File

@@ -7,6 +7,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
@@ -93,7 +94,7 @@ module Pydantic {
// be a Pydantic model. So `model[0]` will be an overapproximation, but should not
// really cause problems (since we don't expect real code to contain such accesses)
nodeFrom = instance() and
nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
nodeTo.asCfgNode().(Cfg::SubscriptNode).getObject() = nodeFrom.asCfgNode()
}
/**

View File

@@ -6,6 +6,7 @@ overlay[local?]
module;
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.dataflow.new.RemoteFlowSources
@@ -1246,7 +1247,7 @@ module StdlibPrivate {
/** An additional taint step for calls to `os.path.join` */
private class OsPathJoinCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(CallNode call |
exists(Cfg::CallNode call |
nodeTo.asCfgNode() = call and
call = OS::OsPath::join().getACall().asCfgNode() and
call.getAnArg() = nodeFrom.asCfgNode()
@@ -1317,13 +1318,13 @@ module StdlibPrivate {
// run, so if we're able to, we only mark the first element as the command
// (and not the arguments to the command).
//
result.asCfgNode() = arg_args.asCfgNode().(SequenceNode).getElement(0)
result.asCfgNode() = arg_args.asCfgNode().(Cfg::SequenceNode).getElement(0)
or
// Either the "args" argument is not a sequence (which is valid) or we where
// just not able to figure it out. Simply mark the "args" argument as the
// command.
//
not arg_args.asCfgNode() instanceof SequenceNode and
not arg_args.asCfgNode() instanceof Cfg::SequenceNode and
result = arg_args
)
)
@@ -1542,7 +1543,7 @@ module StdlibPrivate {
* See https://docs.python.org/3/library/functions.html#eval
*/
private class BuiltinsEvalCall extends CodeExecution::Range, DataFlow::CallCfgNode {
override CallNode node;
override Cfg::CallNode node;
BuiltinsEvalCall() { this = API::builtin("eval").getACall() }
@@ -1923,7 +1924,7 @@ module StdlibPrivate {
nodeFrom = instance().getAValueReachableFromSource() and
nodeTo = [getvalueRef(), getfirstRef(), getlistRef()].getAValueReachableFromSource()
or
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(CallNode).getFunction() and
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::CallNode).getFunction() and
(
nodeFrom = getvalueRef().getAValueReachableFromSource() and
nodeTo = getvalueResult().asSource()
@@ -1939,7 +1940,7 @@ module StdlibPrivate {
nodeFrom in [
instance().getAValueReachableFromSource(), fieldList().getAValueReachableFromSource()
] and
nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
nodeTo.asCfgNode().(Cfg::SubscriptNode).getObject() = nodeFrom.asCfgNode()
or
// Attributes on Field
nodeFrom = field().getAValueReachableFromSource() and
@@ -2254,8 +2255,8 @@ module StdlibPrivate {
DataFlow::CfgNode
{
WsgirefSimpleServerApplicationReturn() {
exists(WsgirefSimpleServerApplication requestHandler, Return ret |
ret.getScope() = requestHandler and
exists(Return ret |
ret.getScope() = any(WsgirefSimpleServerApplication requestHandler) and
node.getNode() = ret.getValue()
)
}
@@ -2338,9 +2339,9 @@ module StdlibPrivate {
DataFlow::Node value;
HeaderWriteSubscript() {
exists(SubscriptNode subscript |
exists(Cfg::SubscriptNode subscript |
this.asCfgNode() = subscript and
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
name.asCfgNode() = subscript.getIndex() and
subscript.getObject() = instance().asCfgNode()
)
@@ -2682,7 +2683,7 @@ module StdlibPrivate {
or
// Data injection
// Special handling of the `/` operator
exists(BinaryExprNode slash, DataFlow::Node pathOperand, DataFlow::TypeTracker t2 |
exists(Cfg::BinaryExprNode slash, DataFlow::Node pathOperand, DataFlow::TypeTracker t2 |
slash.getOp() instanceof Div and
pathOperand.asCfgNode() = slash.getAnOperand() and
pathlibPath(t2).flowsTo(pathOperand) and
@@ -2807,7 +2808,7 @@ module StdlibPrivate {
pathlibPath().flowsTo(nodeTo) and
(
// Special handling of the `/` operator
exists(BinaryExprNode slash, DataFlow::Node pathOperand |
exists(Cfg::BinaryExprNode slash, DataFlow::Node pathOperand |
slash.getOp() instanceof Div and
pathOperand.asCfgNode() = slash.getAnOperand() and
pathlibPath().flowsTo(pathOperand)
@@ -4605,9 +4606,9 @@ module StdlibPrivate {
}
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
exists(CallNode c, string name, ControlFlowNode n, DataFlow::AttributeContent ac |
c.getFunction().(NameNode).getId() = "replace" or
c.getFunction().(AttrNode).getName() = "replace"
exists(Cfg::CallNode c, string name, Cfg::ControlFlowNode n, DataFlow::AttributeContent ac |
c.getFunction().(Cfg::NameNode).getId() = "replace" or
c.getFunction().(Cfg::AttrNode).getName() = "replace"
|
n = c.getArgByName(name) and
ac.getAttribute() = name and
@@ -5171,10 +5172,10 @@ module StdlibPrivate {
* See https://docs.python.org/3.9/library/stdtypes.html#str.startswith
*/
private class StartswithCall extends Path::SafeAccessCheck::Range {
StartswithCall() { this.(CallNode).getFunction().(AttrNode).getName() = "startswith" }
StartswithCall() { this.(Cfg::CallNode).getFunction().(Cfg::AttrNode).getName() = "startswith" }
override predicate checks(ControlFlowNode node, boolean branch) {
node = this.(CallNode).getFunction().(AttrNode).getObject() and
override predicate checks(Cfg::ControlFlowNode node, boolean branch) {
node = this.(Cfg::CallNode).getFunction().(Cfg::AttrNode).getObject() and
branch = true
}
}

View File

@@ -8,6 +8,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.security.dataflow.UrlRedirectCustomizations
@@ -91,7 +92,7 @@ private module Urllib {
* A read of the `netloc` attribute of a parsed URL as returned by `urllib.parse.urlparse`,
* which is being checked in a way that is relevant for URL redirection vulnerabilities.
*/
private predicate netlocCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
private predicate netlocCheck(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) {
exists(DataFlow::CallCfgNode urlParseCall, DataFlow::AttrRead netlocRead |
urlParseCall = getUrlParseCall() and
netlocRead = urlParseCall.getAnAttributeRead("netloc") and

View File

@@ -4,6 +4,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.TaintTracking
@@ -72,9 +73,9 @@ module Tornado {
DataFlow::Node value;
TornadoHeaderSubscriptWrite() {
exists(SubscriptNode subscript |
exists(Cfg::SubscriptNode subscript |
subscript.getObject() = instance().asCfgNode() and
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
index.asCfgNode() = subscript.getIndex() and
this.asCfgNode() = subscript
)
@@ -422,7 +423,7 @@ module Tornado {
// be able to do something more structured for providing modeling of the members
// of a container-object.
exists(DataFlow::AttrRead files | files.accesses(instance(), "cookies") |
this.asCfgNode().(SubscriptNode).getObject() = files.asCfgNode()
this.asCfgNode().(Cfg::SubscriptNode).getObject() = files.asCfgNode()
or
this.(DataFlow::MethodCallNode).calls(files, "get")
)
@@ -479,20 +480,20 @@ module Tornado {
// routing
// ---------------------------------------------------------------------------
/** Gets a sequence that defines a number of route rules */
SequenceNode routeSetupRuleList() {
exists(CallNode call |
Cfg::SequenceNode routeSetupRuleList() {
exists(Cfg::CallNode call |
call = any(TornadoModule::Web::Application::ClassInstantiation c).asCfgNode()
|
result in [call.getArg(0), call.getArgByName("handlers")]
)
or
exists(CallNode call |
exists(Cfg::CallNode call |
call.getFunction() = TornadoModule::Web::Application::add_handlers().asCfgNode()
|
result in [call.getArg(1), call.getArgByName("host_handlers")]
)
or
result = routeSetupRuleList().getElement(_).(TupleNode).getElement(1)
result = routeSetupRuleList().getElement(_).(Cfg::TupleNode).getElement(1)
}
/** A tornado route setup. */
@@ -515,12 +516,12 @@ module Tornado {
/** A route setup using a tuple. */
private class TornadoTupleRouteSetup extends TornadoRouteSetup, DataFlow::CfgNode {
override TupleNode node;
override Cfg::TupleNode node;
TornadoTupleRouteSetup() {
node = routeSetupRuleList().getElement(_) and
count(node.getElement(_)) = 2 and
not node.getElement(1) instanceof SequenceNode
not node.getElement(1) instanceof Cfg::SequenceNode
}
override DataFlow::Node getUrlPatternArg() { result.asCfgNode() = node.getElement(0) }

View File

@@ -6,6 +6,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.ApiGraphs
@@ -221,9 +222,9 @@ module Werkzeug {
DataFlow::Node value;
HeaderWriteSubscript() {
exists(SubscriptNode subscript |
exists(Cfg::SubscriptNode subscript |
this.asCfgNode() = subscript and
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and
name.asCfgNode() = subscript.getIndex() and
subscript.getObject() = instance().asCfgNode()
)

View File

@@ -8,6 +8,7 @@
*/
private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
@@ -28,7 +29,7 @@ private module Yaml {
* See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down).
*/
private class YamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
override CallNode node;
override Cfg::CallNode node;
string func_name;
YamlLoadCall() {

Some files were not shown because too many files have changed in this diff Show More