fallow-extract 2.34.0

Parsing and extraction engine for the fallow TypeScript/JavaScript codebase analyzer
Documentation
use crate::tests::parse_ts as parse_source;

// -- Unused import binding detection (oxc_semantic) --

#[test]
fn unused_import_binding_detected() {
    let info = parse_source("import { foo } from './utils';");
    assert!(
        info.unused_import_bindings.contains(&"foo".to_string()),
        "Import 'foo' is never used and should be in unused_import_bindings"
    );
}

#[test]
fn used_import_binding_not_in_unused() {
    let info = parse_source("import { foo } from './utils';\nconsole.log(foo);");
    assert!(
        !info.unused_import_bindings.contains(&"foo".to_string()),
        "Import 'foo' is used and should NOT be in unused_import_bindings"
    );
}

#[test]
fn unused_namespace_import_detected() {
    let info = parse_source("import * as utils from './utils';");
    assert!(
        info.unused_import_bindings.contains(&"utils".to_string()),
        "Namespace import 'utils' is never used and should be in unused_import_bindings"
    );
}

#[test]
fn used_namespace_import_not_in_unused() {
    let info = parse_source("import * as utils from './utils';\nutils.foo();");
    assert!(
        !info.unused_import_bindings.contains(&"utils".to_string()),
        "Namespace import 'utils' is used and should NOT be in unused_import_bindings"
    );
}

#[test]
fn reexported_import_not_in_unused() {
    let info = parse_source("import { foo } from './utils';\nexport { foo };");
    assert!(
        !info.unused_import_bindings.contains(&"foo".to_string()),
        "Import 'foo' is re-exported and should NOT be in unused_import_bindings"
    );
}

#[test]
fn type_only_import_used_as_type_not_in_unused() {
    let info = parse_source("import type { Foo } from './types';\nconst x: Foo = {} as any;");
    assert!(
        !info.unused_import_bindings.contains(&"Foo".to_string()),
        "Type import 'Foo' is used as a type annotation and should NOT be in unused_import_bindings"
    );
}

#[test]
fn value_import_used_only_as_type_not_in_unused() {
    // A value import (not `import type`) used only in a type annotation position
    // should NOT be in unused_import_bindings — oxc_semantic counts type-position
    // references as real references, which is correct since `import { Foo }` (without
    // the `type` keyword) may be needed at runtime depending on transpiler settings.
    let info = parse_source("import { Foo } from './types';\nconst x: Foo = {} as any;");
    assert!(
        !info.unused_import_bindings.contains(&"Foo".to_string()),
        "Value import 'Foo' used as type annotation should NOT be in unused_import_bindings"
    );
}

#[test]
fn side_effect_import_not_in_unused() {
    let info = parse_source("import './side-effect';");
    assert!(
        info.unused_import_bindings.is_empty(),
        "Side-effect imports have no binding and should not appear in unused_import_bindings"
    );
}

#[test]
fn mixed_used_and_unused_imports() {
    let info = parse_source("import { used, unused } from './utils';\nconsole.log(used);");
    assert!(
        !info.unused_import_bindings.contains(&"used".to_string()),
        "'used' is referenced"
    );
    assert!(
        info.unused_import_bindings.contains(&"unused".to_string()),
        "'unused' is not referenced"
    );
}

// ── Unused import bindings: additional coverage ──────────────

#[test]
fn unused_import_mixed_used_and_unused() {
    let info = parse_source("import { used, unused } from './mod';\nconsole.log(used);");
    assert!(
        info.unused_import_bindings.contains(&"unused".to_string()),
        "Unused import binding 'unused' should be detected"
    );
    assert!(
        !info.unused_import_bindings.contains(&"used".to_string()),
        "Used import binding 'used' should not be in unused list"
    );
}

#[test]
fn all_imports_used_empty_unused_list() {
    let info = parse_source("import { a, b } from './mod';\nconsole.log(a, b);");
    assert!(
        info.unused_import_bindings.is_empty(),
        "All imports used — no unused bindings expected"
    );
}

#[test]
fn side_effect_import_no_unused_bindings() {
    let info = parse_source("import './styles.css';");
    assert!(info.unused_import_bindings.is_empty());
}

#[test]
fn unused_default_import_in_unused_list() {
    let info = parse_source("import React from 'react';\nexport const x = 1;");
    assert!(
        info.unused_import_bindings.contains(&"React".to_string()),
        "Unused default import 'React' should be detected"
    );
}