Integrate diagnostic decorations and current-source Text input boundaries
This commit is contained in:
@@ -40,6 +40,7 @@ pub enum DiskExpectation {
|
||||
|
||||
/// `bundle` retains detached and invalid buffers. Compile `reachable_bundle()`;
|
||||
/// use `diagnostics` to explain files that discovery could not read or parse.
|
||||
/// Diagnostic spans describe `bundle` bytes, including before UUID stabilization.
|
||||
/// Expectations always describe disk bytes before UUID stabilization.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DiskProject {
|
||||
@@ -526,10 +527,11 @@ pub fn discover_missing(
|
||||
result.expectations.keys().chain(result.bundle.files.keys()),
|
||||
)?;
|
||||
if result.diagnostics.is_empty() {
|
||||
match compile_bundle(&result.reachable_bundle(), limits) {
|
||||
Ok(compiled) => result.diagnostics = compiled.diagnostics,
|
||||
Err(diagnostics) => result.diagnostics = diagnostics,
|
||||
}
|
||||
// Discovery retains raw editor bytes. Compilation may insert UUIDs or
|
||||
// rewrite older syntax, so its returned locations belong to a different
|
||||
// bundle. Lint still performs semantic migration and model validation;
|
||||
// Open/Apply separately compile and install the rewritten source.
|
||||
result.diagnostics = lint_bundle(&result.reachable_bundle(), limits);
|
||||
}
|
||||
let pending = journal_path(&result.root, &result.bundle.entry);
|
||||
if fs::symlink_metadata(&pending).is_ok() {
|
||||
|
||||
@@ -383,3 +383,87 @@ fn atomic_save_preserves_existing_unix_permissions() {
|
||||
0o640
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn discovery_diagnostics_keep_raw_no_uuid_spans() {
|
||||
let directory = setup();
|
||||
let path = directory.path().join("parts/a.arcadia");
|
||||
let raw =
|
||||
"// Unicode: 航海\nsa_function first \"First\" {}\nsa_function second \"Second\" {}\n";
|
||||
fs::write(&path, raw).unwrap();
|
||||
let disk = load_project(
|
||||
&directory.path().join("project.arcadia"),
|
||||
ProjectLimits::default(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(disk.bundle.files["parts/a.arcadia"], raw);
|
||||
let issue = disk
|
||||
.diagnostics
|
||||
.iter()
|
||||
.find(|issue| {
|
||||
issue.diagnostic.code == "unallocated_leaf"
|
||||
&& issue.diagnostic.message.contains("Second")
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(issue.path, "parts/a.arcadia");
|
||||
assert_eq!(issue.diagnostic.line, 3);
|
||||
assert_eq!(issue.diagnostic.column, 1);
|
||||
let span = issue.diagnostic.span;
|
||||
assert_eq!(span.start, raw.find("sa_function second").unwrap());
|
||||
assert_eq!(
|
||||
&raw[span.start..span.end],
|
||||
"sa_function second \"Second\" {}"
|
||||
);
|
||||
let compiled = compile_bundle(&disk.reachable_bundle(), ProjectLimits::default()).unwrap();
|
||||
assert_ne!(compiled.bundle, disk.bundle);
|
||||
assert!(compiled.diagnostics.iter().any(|later| {
|
||||
later.diagnostic.code == issue.diagnostic.code
|
||||
&& later.diagnostic.message == issue.diagnostic.message
|
||||
&& later.diagnostic.span.start > span.start
|
||||
}));
|
||||
assert_eq!(fs::read_to_string(path).unwrap(), raw);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn discovery_diagnoses_legacy_semantics_before_source_rewrite() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let path = directory.path().join("project.arcadia");
|
||||
let raw = include_str!("fixtures/v3-data-contract.arcadia");
|
||||
fs::write(&path, raw).unwrap();
|
||||
let disk = load_project(&path, ProjectLimits::default()).unwrap();
|
||||
assert_eq!(disk.bundle.files["project.arcadia"], raw);
|
||||
assert!(!disk.diagnostics.is_empty());
|
||||
assert!(
|
||||
disk.diagnostics
|
||||
.iter()
|
||||
.all(|issue| { issue.diagnostic.severity == oa_core::validation::Severity::Warning })
|
||||
);
|
||||
assert_eq!(
|
||||
disk.diagnostics,
|
||||
lint_bundle(&disk.reachable_bundle(), ProjectLimits::default())
|
||||
);
|
||||
let compiled = compile_bundle(&disk.reachable_bundle(), ProjectLimits::default()).unwrap();
|
||||
assert_eq!(compiled.decoded.migration.as_ref().unwrap().from_schema, 3);
|
||||
assert_ne!(compiled.bundle, disk.bundle);
|
||||
for issue in &disk.diagnostics {
|
||||
assert!(compiled.diagnostics.iter().any(|later| {
|
||||
later.diagnostic.code == issue.diagnostic.code
|
||||
&& later.diagnostic.object_id == issue.diagnostic.object_id
|
||||
&& later.diagnostic.message == issue.diagnostic.message
|
||||
}));
|
||||
let span = issue.diagnostic.span;
|
||||
assert!(raw.get(span.start..span.end).is_some());
|
||||
}
|
||||
// Invalid legacy references still fail during semantic resolution on the
|
||||
// authored bytes; deferring the source rewrite does not hide those errors.
|
||||
let invalid = raw.replace("@coordinate", "@missing");
|
||||
fs::write(&path, &invalid).unwrap();
|
||||
let broken = load_project(&path, ProjectLimits::default()).unwrap();
|
||||
assert!(broken.diagnostics.iter().any(|issue| {
|
||||
issue.diagnostic.severity == oa_core::validation::Severity::Error
|
||||
&& issue.diagnostic.message.contains("@missing")
|
||||
}));
|
||||
assert_eq!(broken.bundle.files["project.arcadia"], invalid);
|
||||
assert!(compile_bundle(&broken.reachable_bundle(), ProjectLimits::default()).is_err());
|
||||
assert_eq!(fs::read_to_string(path).unwrap(), invalid);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user