Skip to content

Commit 539db62

Browse files
committed
Checker: cfg-gate: detect enclosing block scope
The cfg-gate checker walked backwards from `use` lines looking for `#[cfg(target_os = "macos")]`, but stopped at any code line that didn't end with `{`. This caused false positives for `use` statements inside cfg-gated functions when other code lines (e.g. another `use`) appeared between the opening brace and the flagged line. Split hasMacOSCfgAttribute into two phases: (1) check for direct attributes above the line, (2) find the enclosing block via brace depth tracking and recursively check if it's cfg-gated.
1 parent 60baeba commit 539db62

3 files changed

Lines changed: 50 additions & 19 deletions

File tree

apps/desktop/src-tauri/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ mod stubs;
109109

110110
use menu::{
111111
CLOSE_TAB_ID, CommandScope, EDIT_COPY_ID, EDIT_CUT_ID, EDIT_PASTE_ID, MenuState, SHOW_HIDDEN_FILES_ID,
112-
SORT_ASCENDING_ID, SORT_BY_CREATED_ID, SORT_BY_EXTENSION_ID, SORT_BY_MODIFIED_ID, SORT_BY_NAME_ID,
113-
SORT_BY_SIZE_ID, SORT_DESCENDING_ID, TAB_CLOSE_ID, TAB_CLOSE_OTHERS_ID, TAB_PIN_ID, VIEW_MODE_BRIEF_ID,
114-
VIEW_MODE_FULL_ID, VIEWER_WORD_WRAP_ID, ViewMode, menu_id_to_command,
112+
SORT_ASCENDING_ID, SORT_BY_CREATED_ID, SORT_BY_EXTENSION_ID, SORT_BY_MODIFIED_ID, SORT_BY_NAME_ID, SORT_BY_SIZE_ID,
113+
SORT_DESCENDING_ID, TAB_CLOSE_ID, TAB_CLOSE_OTHERS_ID, TAB_PIN_ID, VIEW_MODE_BRIEF_ID, VIEW_MODE_FULL_ID,
114+
VIEWER_WORD_WRAP_ID, ViewMode, menu_id_to_command,
115115
};
116116
use tauri::{Emitter, Manager};
117117

scripts/check/checks/desktop-rust-cfg-gate.go

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -265,43 +265,60 @@ var attrLinePattern = regexp.MustCompile(`^\s*#\[`)
265265
// to check if any preceding attribute contains target_os = "macos" (and not negated with not(...)).
266266
// Also handles `use` statements inside cfg-gated blocks (e.g., inside a #[cfg(target_os = "macos")] fn).
267267
func hasMacOSCfgAttribute(lines []string, lineIdx int) bool {
268+
// Phase 1: Check for cfg attributes directly above this line (no code lines in between).
269+
if hasDirectCfgAttribute(lines, lineIdx) {
270+
return true
271+
}
272+
273+
// Phase 2: Find the enclosing block via brace tracking and check if it's cfg-gated.
274+
return isInsideCfgGatedBlock(lines, lineIdx)
275+
}
276+
277+
// hasDirectCfgAttribute checks whether a macOS cfg attribute appears directly above lineIdx,
278+
// separated only by blank lines and other attributes.
279+
func hasDirectCfgAttribute(lines []string, lineIdx int) bool {
268280
for j := lineIdx - 1; j >= 0; j-- {
269281
trimmed := strings.TrimSpace(lines[j])
270282

271-
// Skip blank lines
272283
if trimmed == "" {
273284
continue
274285
}
275286

276-
// If this is an attribute line (starts with #[), check it
277287
if attrLinePattern.MatchString(lines[j]) {
278-
// This attribute might be multi-line. Collect the full attribute text.
279288
attrText := collectAttribute(lines, j)
280289
if isMacOSGateAttribute(attrText) {
281290
return true
282291
}
283-
// It's an attribute but not a macOS gate — keep walking (there could be stacked attributes)
284292
continue
285293
}
286294

287-
// If we hit a line that's part of a multi-line attribute (doesn't start with #[
288-
// but looks like attribute content — e.g., ends with )] or contains attribute-like content),
289-
// skip it. We handle multi-line attributes by collecting from the #[ opener.
290295
if isAttributeContinuation(trimmed) {
291296
continue
292297
}
293298

294-
// Hit a non-blank, non-attribute line. If it ends with '{', it could be a
295-
// function/block/impl opening that's itself cfg-gated (e.g., #[cfg(target_os = "macos")] fn foo() {).
296-
// Recursively check the attributes above this enclosing block.
297-
if strings.HasSuffix(trimmed, "{") {
298-
if hasMacOSCfgAttribute(lines, j) {
299-
return true
300-
}
299+
// Hit a code line — no direct cfg attribute
300+
break
301+
}
302+
return false
303+
}
304+
305+
// isInsideCfgGatedBlock walks backwards from lineIdx, tracking brace depth, to find the
306+
// enclosing block opener. If found, it recursively checks whether that block is cfg-gated.
307+
func isInsideCfgGatedBlock(lines []string, lineIdx int) bool {
308+
braceDepth := 0
309+
for j := lineIdx - 1; j >= 0; j-- {
310+
trimmed := strings.TrimSpace(lines[j])
311+
if trimmed == "" {
312+
continue
301313
}
302314

303-
// Stop walking — this is a regular code line
304-
break
315+
braceDepth += strings.Count(trimmed, "}") - strings.Count(trimmed, "{")
316+
317+
if braceDepth < 0 {
318+
// Found an unmatched '{' — this is the enclosing block.
319+
// Check if it (or its enclosing scope) has a macOS cfg gate.
320+
return hasMacOSCfgAttribute(lines, j)
321+
}
305322
}
306323
return false
307324
}

scripts/check/checks/desktop-rust-cfg-gate_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,20 @@ use core_foundation::base;`, "\n")
946946
}
947947
}
948948

949+
func TestHasMacOSCfgAttribute_CodeLinesBetweenBraceAndUse(t *testing.T) {
950+
lines := strings.Split(`#[cfg(target_os = "macos")]
951+
fn macos_only() {
952+
use objc2::sel;
953+
use core_foundation::base;
954+
}`, "\n")
955+
956+
// Line 3 is "use core_foundation::base;". There's another use statement (line 2) between
957+
// it and the opening brace (line 1). The checker should still find the enclosing cfg gate.
958+
if !hasMacOSCfgAttribute(lines, 3) {
959+
t.Error("expected true for use inside cfg-gated block with intervening code lines")
960+
}
961+
}
962+
949963
func TestHasMacOSCfgAttribute_NestedBlock(t *testing.T) {
950964
lines := strings.Split(`#[cfg(target_os = "macos")]
951965
impl Foo {

0 commit comments

Comments
 (0)