Skip to content

Commit f8a9b0d

Browse files
authored
clean up added comments and revert cleanup edits to adapter.rs/swift.rs/SwiftSyntaxFFI.swift
1 parent 284dcb3 commit f8a9b0d

7 files changed

Lines changed: 141 additions & 127 deletions

File tree

‎.bazelrc‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ build --compilation_mode opt
1111
common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub
1212

1313
build --repo_env=CC=clang --repo_env=CXX=clang++
14-
1514
# Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues)
1615
build --repo_env=ANDROID_HOME=
1716

‎MODULE.bazel‎

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,9 @@ bazel_dep(name = "rules_rust", version = "0.69.0")
3434
bazel_dep(name = "rules_swift", version = "4.0.0-rc4")
3535
bazel_dep(name = "swift-syntax", version = "602.0.0.bcr.2")
3636

37-
# Direct dep on `apple_support` (transitively pulled in by `rules_swift`) so we
38-
# can `use_repo` `local_config_xcode` and `local_config_apple_cc_toolchains`
39-
# below. These repos are referenced by the per-target Xcode-config transition
40-
# in `unified/swift-syntax-rs/xcode_transition.bzl` (primary mechanism) and by
41-
# the opt-in `swift_macos` bazelrc config (manual escape hatch). See
42-
# `.bazelrc`.
37+
# Needed so we can `use_repo` `local_config_xcode` and
38+
# `local_config_apple_cc_toolchains` below (referenced by the per-target
39+
# Xcode-config transition in `unified/swift-syntax-rs/xcode_transition.bzl`).
4340
bazel_dep(name = "apple_support", version = "2.6.1")
4441
bazel_dep(name = "zstd", version = "1.5.7.bcr.1")
4542

@@ -230,11 +227,9 @@ use_repo(
230227
)
231228

232229
# Hermetic Swift toolchain (swift.org) for building `unified/swift-syntax-rs`.
233-
# Only registered as an `exec` toolchain on Linux; on macOS we rely on
234-
# `rules_swift`'s auto-registered `xcode_swift_toolchain`, which uses the
235-
# host Xcode and links against the OS-provided Swift runtime in `/usr/lib/swift`.
236-
# Version is read from `unified/swift-syntax-rs/.swift-version`, shared with
237-
# the local `cargo` build and `swift/Package.swift`.
230+
# Only registered as an exec toolchain on Linux; on macOS `rules_swift` auto-
231+
# registers `xcode_swift_toolchain` (host Xcode + OS-provided Swift runtime).
232+
# Version comes from `unified/swift-syntax-rs/.swift-version`.
238233
swift = use_extension("@rules_swift//swift:extensions.bzl", "swift")
239234
swift.toolchain(
240235
name = "swift_toolchain",
@@ -250,20 +245,16 @@ register_toolchains(
250245
"@swift_toolchain//:swift_toolchain_exec_ubuntu24.04",
251246
)
252247

253-
# `apple_support`'s xcode_config, selected via `--xcode_version_config` by
254-
# the per-target Xcode-config transition in
255-
# `unified/swift-syntax-rs/xcode_transition.bzl` (and by the opt-in
256-
# `swift_macos` bazelrc config; see `.bazelrc`). Needed because rules_swift's
257-
# `system_sdk` selects use Xcode version strings from `apple_support`'s
258-
# `xcode_locator.m`, which Bazel's built-in `host_xcodes` does not match.
248+
# `apple_support`'s xcode_config, selected via `--xcode_version_config` by the
249+
# per-target Xcode-config transition in `unified/swift-syntax-rs/xcode_transition.bzl`.
250+
# Needed because `rules_swift`'s `system_sdk` selects on version strings from
251+
# `apple_support`'s `xcode_locator.m`, which Bazel's built-in `host_xcodes` does not match.
259252
xcode_configure = use_extension("@apple_support//xcode:xcode_configure.bzl", "xcode_configure_extension")
260253
use_repo(xcode_configure, "local_config_xcode")
261254

262255
# `apple_support`'s CC toolchains, forced ahead of Bazel's built-in
263256
# `local_config_cc` via `--extra_toolchains` by the per-target Xcode-config
264-
# transition in `unified/swift-syntax-rs/xcode_transition.bzl` (and by the
265-
# opt-in `swift_macos` bazelrc config; see `.bazelrc`). Must be visible from
266-
# the root module for those references to resolve.
257+
# transition in `unified/swift-syntax-rs/xcode_transition.bzl`.
267258
apple_cc_configure = use_extension("@apple_support//crosstool:setup.bzl", "apple_cc_configure_extension")
268259
use_repo(apple_cc_configure, "local_config_apple_cc_toolchains")
269260

‎unified/extractor/src/languages/swift/adapter.rs‎

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
1-
//! Converts the swift-syntax JSON syntax tree into a [`yeast::Ast`].
1+
//! Converts the swift-syntax JSON syntax tree into a [`yeast::Ast`], the
2+
//! in-memory format the CodeQL desugaring rules operate on.
23
//!
3-
//! The JSON is produced out-of-process by `swift-syntax-rs`'s FFI shim, so
4-
//! this module is pure Rust (no Swift toolchain needed).
4+
//! The JSON tree is produced by the `swift-syntax-rs` crate's Swift FFI shim
5+
//! (`parse_to_json`). This module is pure Rust (only `yeast` + `serde_json`),
6+
//! so the extractor consumes swift-syntax output without pulling in the Swift
7+
//! toolchain (the JSON is produced out-of-process).
58
//!
6-
//! The mapping mirrors tree-sitter's node model:
9+
//! The mapping mirrors tree-sitter's node model, which is what yeast (and the
10+
//! extractor's rewrite rules) expect:
711
//!
8-
//! * **Layout nodes** (e.g. `functionDecl`) and **varying tokens**
9-
//! (identifiers, literals, operators — text not determined by kind) become
10-
//! **named** nodes, keyed by their kind name.
11-
//! * **Fixed tokens** (keywords, punctuation) become **anonymous** nodes,
12-
//! keyed by their text (like tree-sitter's anonymous tokens).
13-
//! * Collection nodes are already elided to JSON arrays upstream.
12+
//! * **Layout nodes** (e.g. `functionDecl`) and **varying tokens** (identifiers,
13+
//! literals, operators — the ones whose text is not determined by their kind)
14+
//! become **named** nodes, keyed by their kind name.
15+
//! * **Fixed tokens** (keywords and punctuation, whose text is fully determined
16+
//! by their kind) become **anonymous** nodes, keyed by their text — exactly
17+
//! how tree-sitter models anonymous tokens (e.g. `"func"`, `"->"`).
18+
//! * Collection nodes are already elided to JSON arrays upstream, so a
19+
//! list-valued field maps directly to that field holding several children.
1420
//!
15-
//! Kind/field names are preserved from swift-syntax; aligning them with the
16-
//! tree-sitter-swift schema is done incrementally in the rewrite rules.
21+
//! Note: this preserves swift-syntax's own kind/field names. Aligning those
22+
//! names with the tree-sitter-swift schema (so the rewrite rules in
23+
//! [`super::swift`] fire) is done incrementally in the rules.
1724
1825
use std::collections::BTreeMap;
1926

@@ -22,29 +29,32 @@ use yeast::schema::Schema;
2229
use yeast::{Ast, Id, NodeContent, Point, Range};
2330

2431
/// A comment (or `unexpectedText`) recovered from the syntax tree's trivia.
25-
/// Collected into a side channel rather than embedded in the [`yeast::Ast`],
26-
/// mirroring how the extractor treats tree-sitter `extra` nodes.
32+
///
33+
/// These are collected into a side channel rather than embedded in the
34+
/// [`yeast::Ast`], mirroring how the extractor treats tree-sitter `extra`
35+
/// nodes: they carry a location and text but are not attached to a parent.
2736
#[derive(Debug, Clone, PartialEq, Eq)]
2837
pub struct TriviaToken {
29-
/// The trivia kind (e.g. `lineComment`, `docLineComment`, `unexpectedText`).
38+
/// The trivia kind (e.g. `lineComment`, `blockComment`, `docLineComment`,
39+
/// `docBlockComment`, `unexpectedText`).
3040
pub kind: String,
31-
/// The verbatim source text (e.g. `// comment`).
41+
/// The verbatim source text of the piece (e.g. `// comment`).
3242
pub text: String,
3343
/// The source range the piece occupies.
3444
pub range: Range,
3545
}
3646

37-
/// The result of adapting a swift-syntax JSON tree: the [`yeast::Ast`] plus
38-
/// the comment/`unexpectedText` trivia harvested from it (in source order).
47+
/// The result of adapting a swift-syntax JSON tree: the [`yeast::Ast`] plus the
48+
/// comment/`unexpectedText` trivia harvested from it (in source order).
3949
pub struct AdaptedTree {
4050
pub ast: Ast,
4151
pub trivia: Vec<TriviaToken>,
4252
}
4353

4454
/// swift-syntax `TokenKind` cases whose text is *not* determined by the kind
45-
/// (i.e. `TokenKind.defaultText == nil`). Modelled as named leaf nodes; every
46-
/// other token is a fixed keyword/punctuation, modelled as an anonymous token
47-
/// keyed by its text.
55+
/// (i.e. `TokenKind.defaultText == nil`). These carry varying information and
56+
/// are modelled as named leaf nodes; every other token is a fixed
57+
/// keyword/punctuation token modelled as an anonymous token keyed by its text.
4858
const VARYING_TOKEN_KINDS: &[&str] = &[
4959
"identifier",
5060
"integerLiteral",
@@ -106,7 +116,7 @@ fn classify(node: &Value) -> Result<KindInfo, String> {
106116
.unwrap_or("")
107117
.to_string();
108118

109-
// Case name is the part before any `(payload)` in the debug rendering.
119+
// The case name is the part before any `(payload)` in the debug rendering.
110120
let case_name = token_kind.split('(').next().unwrap_or(token_kind);
111121

112122
if VARYING_TOKEN_KINDS.contains(&case_name) {
@@ -132,7 +142,8 @@ fn classify(node: &Value) -> Result<KindInfo, String> {
132142
}
133143
}
134144

135-
/// Iterate over a node's structural (field, value) pairs, skipping metadata.
145+
/// Iterate over a node object's structural (field, value) pairs in a stable
146+
/// order, skipping metadata keys.
136147
fn field_entries(node: &Value) -> Vec<(&str, &Value)> {
137148
node.as_object()
138149
.map(|map| {
@@ -144,8 +155,8 @@ fn field_entries(node: &Value) -> Vec<(&str, &Value)> {
144155
.unwrap_or_default()
145156
}
146157

147-
/// Child nodes held by a field value: either a single node or an elided
148-
/// collection (JSON array).
158+
/// The child node objects held by a field value, which is either a single node
159+
/// object or an array of them (an elided collection).
149160
fn children_of(value: &Value) -> Vec<&Value> {
150161
match value {
151162
Value::Array(items) => items.iter().collect(),
@@ -154,8 +165,12 @@ fn children_of(value: &Value) -> Vec<&Value> {
154165
}
155166

156167
/// Recursively build `node` (and its descendants) into `ast`, returning its id.
157-
/// Kinds and field names are registered in the schema on the fly. Comment /
158-
/// `unexpectedText` trivia is harvested into `trivia` in the same pass.
168+
///
169+
/// This is a single traversal: each node's kind and field names are registered
170+
/// in the schema on the fly, immediately before the node is created. Children
171+
/// are built first so a parent's field lists reference existing ids. Any
172+
/// comment/`unexpectedText` trivia carried by a token is harvested into
173+
/// `trivia` during the same pass rather than embedded in the tree.
159174
fn build(node: &Value, ast: &mut Ast, trivia: &mut Vec<TriviaToken>) -> Result<Id, String> {
160175
let info = classify(node)?;
161176
collect_trivia(node, trivia);
@@ -185,8 +200,9 @@ fn build(node: &Value, ast: &mut Ast, trivia: &mut Vec<TriviaToken>) -> Result<I
185200
))
186201
}
187202

188-
/// Harvest a token's `leadingTrivia`/`trailingTrivia` pieces (already filtered
189-
/// upstream) into `out`. No-op on non-token nodes.
203+
/// Harvest a token's `leadingTrivia`/`trailingTrivia` pieces (each already
204+
/// filtered to comments/`unexpectedText` upstream) into `out`. Non-token nodes
205+
/// have no trivia keys, so this is a no-op for them.
190206
fn collect_trivia(node: &Value, out: &mut Vec<TriviaToken>) {
191207
for key in ["leadingTrivia", "trailingTrivia"] {
192208
let Some(Value::Array(pieces)) = node.get(key) else {
@@ -215,9 +231,11 @@ fn collect_trivia(node: &Value, out: &mut Vec<TriviaToken>) {
215231

216232
/// Parse a node's `range` into a [`yeast::Range`].
217233
///
218-
/// JSON offsets are 0-based UTF-8 byte offsets; `line`/`column` are 1-based
219-
/// UTF-8. yeast uses byte offsets with 0-based rows/columns (exclusive end),
220-
/// so line/column are shifted down by one.
234+
/// The JSON carries, for `start` and `end`, a 0-based UTF-8 file byte `offset`,
235+
/// a 1-based `line`, and a 1-based UTF-8 byte `column`. yeast (like tree-sitter)
236+
/// uses byte offsets with 0-based rows/columns and an exclusive end, so the
237+
/// line/column are shifted down by one. swift-syntax's end position is already
238+
/// exclusive, so the byte offsets map across directly.
221239
fn parse_range(node: &Value) -> Option<Range> {
222240
let range = node.get("range")?;
223241
let point = |key: &str| -> Option<(usize, Point)> {
@@ -241,7 +259,8 @@ fn parse_range(node: &Value) -> Option<Range> {
241259
}
242260

243261
/// Convert a swift-syntax JSON tree (as produced by [`crate::parse_to_json`])
244-
/// into a [`yeast::Ast`] plus harvested trivia, in a single traversal.
262+
/// into a [`yeast::Ast`] plus the comment/`unexpectedText` trivia harvested
263+
/// from it. Both are produced in a single traversal.
245264
pub fn json_to_ast(json: &str) -> Result<AdaptedTree, String> {
246265
let root: Value = serde_json::from_str(json).map_err(|e| format!("invalid JSON: {e}"))?;
247266

‎unified/extractor/src/languages/swift/swift.rs‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,19 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
135135
// Declarations may be wrapped in local/global wrapper nodes.
136136
rule!((global_declaration _ @inner) => stmt { inner }),
137137
rule!((local_declaration _ @inner) => stmt { inner }),
138-
// ---- swift-syntax front-end ----
139-
// Rules targeting the swift-syntax AST (camelCase kind names, built
140-
// by the sibling `adapter` module). Kind namespaces don't overlap
141-
// with the tree-sitter (snake_case) rules, so these are inert on
142-
// the tree-sitter path. Unmatched nodes fall through to the
143-
// `unsupported_node` fallback below.
138+
// ---- swift-syntax front-end (minimal hook-up) ----
139+
// These rules target the swift-syntax AST (camelCase kind names),
140+
// produced by the sibling `adapter` module. They coexist with the
141+
// tree-sitter rules (snake_case names): rules are dispatched by exact
142+
// kind name, and the two name spaces never collide, so these are inert
143+
// on the tree-sitter path. Only the minimal top-level mapping lives here
144+
// to demonstrate the pipeline end-to-end; the full translation is added
145+
// separately. Unmatched swift-syntax nodes fall through to the
146+
// `unsupported_node` fallback at the end.
147+
//
148+
// `sourceFile` holds its top-level statements in an (elided)
149+
// `statements` collection; each element is a `codeBlockItem` wrapping
150+
// the real node.
144151
rule!(
145152
(sourceFile statements: _* @items)
146153
=>

‎unified/swift-syntax-rs/README.md‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,10 @@ Requirements:
150150
OSes, so on Windows Bazel skips them cleanly.
151151
- **macOS only:** the macOS toolchain is a `.pkg` archive that can only be
152152
fetched and extracted on a macOS host. On macOS, `rules_swift` also needs
153-
Xcode's CC toolchain and xcode_config; the targets above apply the
154-
necessary `--xcode_version_config` and `--extra_toolchains` flags via an
155-
incoming-edge Starlark transition
156-
([`xcode_transition.bzl`](xcode_transition.bzl)), so no manual `--config=…`
157-
is needed and other targets on macOS keep using Bazel's default CC
158-
toolchain unchanged.
153+
Xcode's CC toolchain and xcode_config; these are applied to the Swift
154+
target via an incoming-edge Starlark transition (see
155+
[`xcode_transition.bzl`](xcode_transition.bzl)), so other targets on macOS
156+
keep using Bazel's default CC toolchain.
159157

160158
The Swift compiler version is read from [`.swift-version`](.swift-version) by
161159
both the Bazel toolchain (`swift.toolchain(swift_version_file = …)`) and the

0 commit comments

Comments
 (0)