#[path = "substrate_support/declaration_scan.rs"]
mod declaration_scan;
#[path = "substrate_support/release_contract.rs"]
mod release_contract;
#[cfg(feature = "resolution-test-support")]
#[path = "substrate_support/path_normalization.rs"]
mod path_normalization;
#[path = "substrate_support/resolution/mod.rs"]
mod resolution;
mod substrate_behavior {
use pedant_core::capabilities::detect_capabilities;
use pedant_core::ir::extract;
use pedant_types::Capability;
const SOURCE: &str = "use std::fs;\n\nfn read_manifest(path: &str) -> String {\n fs::read_to_string(path).unwrap_or_default()\n}\n";
#[test]
fn substrate_entry_points_produce_facts_without_judgment() {
let syntax = syn::parse_file(SOURCE).expect("the fixture source should parse");
let ir = extract("substrate_fixture.rs", &syntax, None);
let functions: Vec<&str> = ir.functions.iter().map(|fact| &*fact.name).collect();
assert_eq!(
functions,
["read_manifest"],
"one free function is expected"
);
let use_paths: Vec<&str> = ir.use_paths.iter().map(|fact| &*fact.path).collect();
assert_eq!(
use_paths,
["std::fs", "fs::read_to_string"],
"the import and the call path are both flattened into use-path facts"
);
let profile = detect_capabilities(&ir, None);
let found: Vec<Capability> = profile.findings.iter().map(|it| it.capability).collect();
assert_eq!(
found,
[Capability::FileRead],
"`std::fs` should resolve to one file-read capability"
);
}
}
mod declared_surface {
use std::collections::BTreeSet;
use crate::declaration_scan::{
CHECKS_FEATURE, LibSurface, PathIdents, SUBSTRATE_ROOTS, file_name, has_checks_gate,
manifest_table, parse_rust_file, test_root_paths,
};
const CLOSED_DEPENDENCY_SET: &[&str] = &[
"pedant-types",
"proc-macro2",
"quote",
"semver",
"sha2",
"syn",
"thiserror",
"toml",
];
#[test]
fn judgment_test_roots_carry_the_checks_gate() {
let surface = LibSurface::classify();
assert!(
!surface.judgment_names.is_empty(),
"lib.rs should declare a judgment surface behind `{CHECKS_FEATURE}`"
);
let roots = test_root_paths();
let names: Box<[Box<str>]> = roots.iter().map(|path| file_name(path)).collect();
let mut missing_gate: Vec<Box<str>> = Vec::new();
let mut needless_gate: Vec<&str> = Vec::new();
for (path, name) in roots.iter().zip(&names) {
let file = parse_rust_file(path);
let gated = has_checks_gate(&file.attrs);
let references = surface.judgment_references(&PathIdents::scan(&file));
if !gated && !references.is_empty() {
missing_gate.push(format!("{name} references {references:?}").into_boxed_str());
}
if gated && SUBSTRATE_ROOTS.contains(&&**name) {
needless_gate.push(name);
}
}
assert!(
missing_gate.is_empty(),
"test roots reach the judgment surface without a file-level checks gate: {missing_gate:?}"
);
assert!(
needless_gate.is_empty(),
"substrate test roots must carry no checks gate: {needless_gate:?}"
);
let present: BTreeSet<&str> = names.iter().map(|name| &**name).collect();
for expected in SUBSTRATE_ROOTS {
assert!(
present.contains(*expected),
"{expected} is asserted ungated but is not a test root"
);
}
}
#[test]
fn checks_feature_declares_no_rust_analyzer_dependency() {
let manifest = manifest_table();
let features = manifest
.get("features")
.and_then(toml::Value::as_table)
.expect("[features] should be declared");
let entries = features
.get(CHECKS_FEATURE)
.and_then(toml::Value::as_array)
.expect("the checks feature should be declared as a list");
for entry in entries {
let name = entry.as_str().expect("a feature entry should be a string");
assert!(
!name.contains("ra_ap_") && !name.contains("line-index"),
"the checks feature must not enable rust-analyzer: {name}"
);
}
let default = features
.get("default")
.and_then(toml::Value::as_array)
.expect("the default feature should be declared as a list");
let names: Vec<&str> = default.iter().filter_map(toml::Value::as_str).collect();
assert_eq!(
names,
[CHECKS_FEATURE],
"default should select exactly the judgment surface"
);
}
#[test]
fn non_optional_dependencies_are_the_declared_closed_set() {
let manifest = manifest_table();
let dependencies = manifest
.get("dependencies")
.and_then(toml::Value::as_table)
.expect("[dependencies] should be declared");
let required: BTreeSet<&str> = dependencies
.iter()
.filter(|(_, spec)| !is_optional(spec))
.map(|(name, _)| name.as_str())
.collect();
assert_eq!(
required,
CLOSED_DEPENDENCY_SET
.iter()
.copied()
.collect::<BTreeSet<_>>(),
"the substrate's non-optional dependency closure changed"
);
}
fn is_optional(spec: &toml::Value) -> bool {
spec.as_table()
.and_then(|table| table.get("optional"))
.and_then(toml::Value::as_bool)
.unwrap_or(false)
}
}
mod parse_only {
use std::path::PathBuf;
use crate::declaration_scan::{
LibSurface, assert_semantic_exclusion_is_not_vacuous, crate_path, excluded_root,
module_files, process_evidence,
};
#[test]
fn scanned_substrate_source_set_names_no_process_api() {
let sources = scanned_sources();
let excluded = excluded_root();
assert!(
!sources.is_empty(),
"the scanned substrate source set should not be empty"
);
assert!(
sources
.iter()
.any(|path| path.ends_with("ir/extract/visitor/implementation.rs")),
"directory-module subtrees should be expanded: visitor implementation is missing"
);
assert!(
!sources.iter().any(|path| path.starts_with(&excluded)),
"the semantic adapter subtree should stay out of the scanned set"
);
assert_semantic_exclusion_is_not_vacuous();
let offenders: Box<[Box<str>]> = sources
.iter()
.filter_map(|path| process_evidence(path))
.collect();
assert!(
offenders.is_empty(),
"substrate sources name a process-capability API: {offenders:?}"
);
}
fn scanned_sources() -> Box<[PathBuf]> {
let surface = LibSurface::classify();
let excluded = excluded_root();
let mut files = vec![crate_path("src").join("lib.rs")];
for module in &surface.ungated_modules {
files.extend(module_files(module));
}
files.retain(|path| !path.starts_with(&excluded));
files.sort();
files.dedup();
files.into_boxed_slice()
}
}
#[cfg(feature = "semantic")]
mod semantic_loader {
use pedant_core::SemanticContext;
use crate::declaration_scan::crate_path;
#[test]
fn semantic_context_loads_without_the_checks_feature() {
let workspace = crate_path("tests/fixtures/semantic_workspace");
assert!(
SemanticContext::load(&workspace).is_some(),
"SemanticContext::load should succeed for the committed fixture workspace"
);
}
}