From 2b45938f77395d63c5f223e9592a09b68b0d4e96 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Sun, 5 Jul 2026 13:47:35 +0000
Subject: [PATCH 01/15] docs: update install.sh stable version to 1.2.0
---
docs/public/install.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/public/install.sh b/docs/public/install.sh
index 0a432fa43..87af8cafa 100755
--- a/docs/public/install.sh
+++ b/docs/public/install.sh
@@ -346,7 +346,7 @@ download_and_install() {
}
# Version
-LATEST_STABLE_VERSION="1.1.7"
+LATEST_STABLE_VERSION="1.2.0"
if [ -n "${__SPECIFIED_VERSION}" ]; then
VERSION="${__SPECIFIED_VERSION}"
print_step "Using specified version: ${VERSION}"
From aa5ed086f7c1f0e2797c03204e49500427fddd6f Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Sun, 5 Jul 2026 22:58:26 +0900
Subject: [PATCH 02/15] Stabilize diagnostic collection names (#2000)
---
crates/tombi-lsp/src/handler/initialize.rs | 1 +
editors/vscode/src/options/client-options.ts | 1 +
2 files changed, 2 insertions(+)
diff --git a/crates/tombi-lsp/src/handler/initialize.rs b/crates/tombi-lsp/src/handler/initialize.rs
index 5d11c99ce..e28416b99 100644
--- a/crates/tombi-lsp/src/handler/initialize.rs
+++ b/crates/tombi-lsp/src/handler/initialize.rs
@@ -171,6 +171,7 @@ pub fn server_capabilities(
),
diagnostic_provider: if backend_capabilities.diagnostic_mode == DiagnosticMode::Pull {
Some(DiagnosticServerCapabilities::Options(DiagnosticOptions {
+ identifier: Some("tombi".to_string()),
inter_file_dependencies: false,
workspace_diagnostics: true,
..Default::default()
diff --git a/editors/vscode/src/options/client-options.ts b/editors/vscode/src/options/client-options.ts
index 4201e9c70..9a936fa05 100644
--- a/editors/vscode/src/options/client-options.ts
+++ b/editors/vscode/src/options/client-options.ts
@@ -9,6 +9,7 @@ export function clientOptions(
workspaceFolder?: vscode.WorkspaceFolder,
): languageclient.LanguageClientOptions {
const options = {
+ diagnosticCollectionName: "tombi",
documentSelector: SUPPORT_TOML_LANGUAGES.flatMap((language) => [
{ scheme: "file", language },
{ scheme: "untitled", language },
From aaa0da854bfb33180c188678d1727936bfe6bfb3 Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Sun, 5 Jul 2026 23:34:58 +0900
Subject: [PATCH 03/15] fix(lint): avoid false unused deprecated directive
(#2001)
---
.../tests/integration/tombi_schema.rs | 19 +++++++++++++++++++
crates/tombi-validator/src/validate/table.rs | 2 +-
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/crates/tombi-linter/tests/integration/tombi_schema.rs b/crates/tombi-linter/tests/integration/tombi_schema.rs
index 0a0ca34e6..2e191bc83 100644
--- a/crates/tombi-linter/tests/integration/tombi_schema.rs
+++ b/crates/tombi-linter/tests/integration/tombi_schema.rs
@@ -182,6 +182,25 @@ test_lint! {
) -> Ok(_)
}
+test_lint! {
+ #[test]
+ fn test_tombi_schema_deprecated_key_disabled_does_not_report_unused_noqa_on_value(
+ r#"
+ [extensions]
+ "tombi-toml/tombi" = {
+ lsp = {
+ document-link = {
+ # tombi: lint.rules.deprecated.disabled = true
+ path = { enabled = true }
+ },
+ goto-definition.path.enabled = true,
+ }
+ }
+ "#,
+ SchemaPath(tombi_schema_path()),
+ ) -> Ok(_)
+}
+
test_lint! {
#[test]
fn test_tombi_schema_lint_rules_deprecated(
diff --git a/crates/tombi-validator/src/validate/table.rs b/crates/tombi-validator/src/validate/table.rs
index fe69ae846..17369fa7f 100644
--- a/crates/tombi-validator/src/validate/table.rs
+++ b/crates/tombi-validator/src/validate/table.rs
@@ -748,7 +748,7 @@ async fn validate_table(
}
}
- if total_diagnostics.is_empty() {
+ if total_diagnostics.is_empty() && table_schema.deprecation.is_some() {
handle_deprecated(
&mut total_diagnostics,
table_schema.deprecation.as_ref(),
From 758aac98b462112f1734ba7a11887266fbda9968 Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Mon, 6 Jul 2026 09:54:57 +0900
Subject: [PATCH 04/15] fix(vscode): make executable settings machine-scoped
(#2002)
---
editors/vscode/package.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/editors/vscode/package.json b/editors/vscode/package.json
index 8e1c01143..5bf616f7b 100644
--- a/editors/vscode/package.json
+++ b/editors/vscode/package.json
@@ -219,14 +219,14 @@
"string",
"null"
],
- "scope": "resource",
+ "scope": "machine",
"default": null,
"pattern": "^.*tombi(\\.exe)?$"
},
"tombi.args": {
"description": "Arguments to `tombi` command.",
"type": "array",
- "scope": "resource",
+ "scope": "machine",
"default": null,
"items": {
"type": "string",
@@ -236,7 +236,7 @@
"tombi.env": {
"description": "Environment variables to pass to `tombi` command.",
"type": "object",
- "scope": "resource",
+ "scope": "machine",
"default": null,
"patternProperties": {
"^.+$": {
From b0da7bc8c246d2c1a0ab308b0b8c19b422f16422 Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Tue, 7 Jul 2026 10:51:43 +0900
Subject: [PATCH 05/15] docs: document deprecationMessage as non-standard
support (#2003)
---
docs/src/routes/docs/json-schema.mdx | 33 +++++++++++++++++++++-------
1 file changed, 25 insertions(+), 8 deletions(-)
diff --git a/docs/src/routes/docs/json-schema.mdx b/docs/src/routes/docs/json-schema.mdx
index 1604540a7..5860e1bec 100644
--- a/docs/src/routes/docs/json-schema.mdx
+++ b/docs/src/routes/docs/json-schema.mdx
@@ -346,6 +346,31 @@ It removes **all file caches** and fetches the latest schema over the network fo
VSCode supports associating a TOML schema with a file match pattern.
See [VSCode Extension](/docs/editors/vscode-extension#json-schema-association) for details.
+## Non-standard Supported Features
+
+Tombi supports the following features for compatibility with existing schema tooling, even though
+they are not part of the JSON Schema specification.
+
+### deprecationMessage
+
+`deprecationMessage` is a non-standard keyword originating from the VS Code JSON language service.
+Tombi accepts it for compatibility, but only honors it alongside `deprecated: true`.
+
+When both are present, the `deprecationMessage` string is used as the deprecation diagnostic message
+instead of the generic one. A `deprecationMessage` without `deprecated: true` is ignored.
+
+```json
+{
+ "properties": {
+ "old-key": {
+ "type": "string",
+ "deprecated": true,
+ "deprecationMessage": "Use `new-key` instead."
+ }
+ }
+}
+```
+
## Compliance Status
The table below summarizes keyword membership in each JSON Schema dialect and whether Tombi
@@ -432,14 +457,6 @@ accepted even before draft-2020-12.
Some are used as annotations for validation behavior, hover, completion, or diagnostics.
-
-`deprecationMessage` is a non-standard keyword originating from the VS Code JSON language service
-(it is not part of any JSON Schema dialect), and Tombi accepts it for compatibility. It is only
-honored alongside `deprecated: true`: when both are present, the `deprecationMessage` string is
-used as the deprecation diagnostic message (instead of the generic one). A `deprecationMessage`
-without `deprecated: true` is ignored.
-
-
Tombi defaults to a non-standard `strict` behavior: when `additionalProperties` is omitted, objects are treated as closed unless you disable `schema.strict`.
From ed489678210ab5d3455f731d5115a613708b2693 Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Wed, 8 Jul 2026 08:03:36 +0900
Subject: [PATCH 06/15] chore: resolve OSV code scanning alerts (#2004)
* chore: resolve OSV code scanning alerts
* ci: run OSV scanner on main events
---
.github/workflows/osv-scanner.yml | 22 ++++++++++++++++++++++
Cargo.lock | 12 ++++++------
Cargo.toml | 2 +-
3 files changed, 29 insertions(+), 7 deletions(-)
create mode 100644 .github/workflows/osv-scanner.yml
diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml
new file mode 100644
index 000000000..da1bd1d28
--- /dev/null
+++ b/.github/workflows/osv-scanner.yml
@@ -0,0 +1,22 @@
+name: OSV-Scanner
+
+on:
+ schedule:
+ - cron: "25 4 * * 2"
+ push:
+ branches: ["main"]
+ workflow_dispatch:
+
+permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+jobs:
+ scan-scheduled:
+ uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@b56b5191101d5f27d4787d5583d8d01e9518a7af" # v2.4.0
+ with:
+ scan-args: |-
+ --include-git-root
+ -r
+ ./
diff --git a/Cargo.lock b/Cargo.lock
index d09c981c2..ce4214d52 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -134,9 +134,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.98"
+version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
[[package]]
name = "arbitrary"
@@ -507,9 +507,9 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
-version = "0.9.18"
+version = "0.9.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
dependencies = [
"crossbeam-utils",
]
@@ -1753,9 +1753,9 @@ dependencies = [
[[package]]
name = "quinn-proto"
-version = "0.11.14"
+version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
+checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e"
dependencies = [
"bytes",
"getrandom 0.3.3",
diff --git a/Cargo.toml b/Cargo.toml
index 84d52d004..e8762b809 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,7 +29,7 @@ license = "MIT"
[workspace.dependencies]
addr = "0.15.6"
-anyhow = "1.0.98"
+anyhow = "1.0.103"
bytes = "1.11.1"
chrono = { version = "0.4.41", features = ["serde"] }
clap = { version = "4.5.37", features = ["derive", "env", "string"] }
From 536bb36974d9217756c7f9561363e73f5951700a Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Wed, 8 Jul 2026 08:18:30 +0900
Subject: [PATCH 07/15] ci: pin OSV scanner workflow to existing action ref
(#2005)
---
.github/workflows/osv-scanner.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml
index da1bd1d28..1e230208a 100644
--- a/.github/workflows/osv-scanner.yml
+++ b/.github/workflows/osv-scanner.yml
@@ -14,7 +14,7 @@ permissions:
jobs:
scan-scheduled:
- uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@b56b5191101d5f27d4787d5583d8d01e9518a7af" # v2.4.0
+ uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@9a498708959aeaef5ef730655706c5a1df1edbc2" # v2.3.8
with:
scan-args: |-
--include-git-root
From 9e9360badd9ba54c4b889b88c7a21849527f5337 Mon Sep 17 00:00:00 2001
From: Taku Kodama <79110363+risu729@users.noreply.github.com>
Date: Thu, 16 Jul 2026 13:02:42 +1000
Subject: [PATCH 08/15] fix(validator): report not schema resolution errors
(#2007)
* test(validator): cover unresolved not schema refs
* fix(validator): report not schema resolution errors
* fix(validator): address not schema review findings
* test(lsp): update schema path completion
---------
Co-authored-by: ya7010
---
crates/tombi-linter/tests/integration.rs | 2 +
.../tests/integration/not_schema.rs | 54 +++++++++++++++++++
.../tombi-lsp/tests/test_completion_labels.rs | 1 +
.../src/validate/not_schema.rs | 44 +++++++++------
schemas/not-schema-test.schema.json | 24 +++++++++
5 files changed, 109 insertions(+), 16 deletions(-)
create mode 100644 crates/tombi-linter/tests/integration/not_schema.rs
create mode 100644 schemas/not-schema-test.schema.json
diff --git a/crates/tombi-linter/tests/integration.rs b/crates/tombi-linter/tests/integration.rs
index 6c38b6401..edd486973 100644
--- a/crates/tombi-linter/tests/integration.rs
+++ b/crates/tombi-linter/tests/integration.rs
@@ -28,6 +28,8 @@ mod json_schema_test_suite;
mod min_max_contains_test_schema;
#[path = "integration/non_schema.rs"]
mod non_schema;
+#[path = "integration/not_schema.rs"]
+mod not_schema;
#[path = "integration/other_schema.rs"]
mod other_schema;
#[path = "integration/prefix_items_test_schema.rs"]
diff --git a/crates/tombi-linter/tests/integration/not_schema.rs b/crates/tombi-linter/tests/integration/not_schema.rs
new file mode 100644
index 000000000..91ab7fe57
--- /dev/null
+++ b/crates/tombi-linter/tests/integration/not_schema.rs
@@ -0,0 +1,54 @@
+use tombi_diagnostic::Level;
+use tombi_linter::test_lint;
+
+fn schema_path() -> std::path::PathBuf {
+ tombi_test_lib::project_root_path()
+ .join("schemas")
+ .join("not-schema-test.schema.json")
+}
+
+test_lint! {
+ #[test]
+ fn test_invalid_ref_reports_error_diagnostic(
+ r#"
+ value = "foo"
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_inline_not_rejects_matching_value(
+ r#"
+ inline = "foo"
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Err([
+ tombi_validator::DiagnosticKind::NotSchemaMatch
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_inline_not_accepts_non_matching_value(
+ r#"
+ inline = "bar"
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Ok(_)
+}
+
+test_lint! {
+ #[test]
+ fn test_inline_not_treats_warning_only_result_as_match(
+ r#"
+ warning = "foo"
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Err([
+ tombi_validator::DiagnosticKind::NotSchemaMatch
+ ])
+}
diff --git a/crates/tombi-lsp/tests/test_completion_labels.rs b/crates/tombi-lsp/tests/test_completion_labels.rs
index 358eb40cd..a10438fb8 100644
--- a/crates/tombi-lsp/tests/test_completion_labels.rs
+++ b/crates/tombi-lsp/tests/test_completion_labels.rs
@@ -740,6 +740,7 @@ mod completion_labels {
"issue-1895-rustfmt-like.schema.json",
"lsp-consistency-test.schema.json",
"min-max-contains-test.schema.json",
+ "not-schema-test.schema.json",
"one-of-hover-discriminator-test.schema.json",
"partial-taskipy.schema.json",
"prefix-items-test.schema.json",
diff --git a/crates/tombi-validator/src/validate/not_schema.rs b/crates/tombi-validator/src/validate/not_schema.rs
index 4f29363d5..f5f8de4ca 100644
--- a/crates/tombi-validator/src/validate/not_schema.rs
+++ b/crates/tombi-validator/src/validate/not_schema.rs
@@ -3,7 +3,10 @@ use tombi_document_tree::ValueImpl;
use tombi_schema_store::{CurrentSchema, SchemaContext};
use tombi_severity_level::SeverityLevelDefaultError;
-use crate::{Validate, validate::handle_unused_noqa};
+use crate::{
+ Validate,
+ validate::{handle_unused_noqa, is_assertion_success},
+};
pub async fn validate_not<'a, T>(
value: &T,
@@ -17,22 +20,31 @@ pub async fn validate_not<'a, T>(
where
T: Validate + ValueImpl + Sync + Send,
{
- if let Ok(Some(current_schema)) = not_schema
- .schema
- .write()
- .await
- .resolve(
- current_schema.schema_uri.clone(),
- current_schema.definitions.clone(),
- schema_context.store,
- )
- .await
- .inspect_err(|err| log::warn!("{err}"))
- && value
- .validate(accessors, Some(¤t_schema), schema_context)
- .await
- .is_ok()
+ let matches_not_schema = match tombi_schema_store::resolve_schema_item(
+ ¬_schema.schema,
+ current_schema.schema_uri.clone(),
+ current_schema.definitions.clone(),
+ schema_context.store,
+ )
+ .await
{
+ Ok(Some(current_schema)) => is_assertion_success(
+ &value
+ .validate(accessors, Some(¤t_schema), schema_context)
+ .await,
+ ),
+ Ok(None) => false,
+ Err(err) => {
+ return Err(vec![tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ value.range(),
+ )]
+ .into());
+ }
+ };
+
+ if matches_not_schema {
let mut diagnostics = Vec::with_capacity(1);
crate::Diagnostic {
kind: Box::new(crate::DiagnosticKind::NotSchemaMatch),
diff --git a/schemas/not-schema-test.schema.json b/schemas/not-schema-test.schema.json
new file mode 100644
index 000000000..13831d506
--- /dev/null
+++ b/schemas/not-schema-test.schema.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "type": "object",
+ "properties": {
+ "value": {
+ "type": "string",
+ "not": {
+ "$ref": "#/$defs/missing"
+ }
+ },
+ "inline": {
+ "type": "string",
+ "not": {
+ "pattern": "^foo$"
+ }
+ },
+ "warning": {
+ "type": "string",
+ "not": {
+ "deprecated": true
+ }
+ }
+ }
+}
From 1ea3ac2dee54f4d36acdc7a6f0afac91f9ef5371 Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Thu, 16 Jul 2026 13:09:28 +0900
Subject: [PATCH 09/15] test(lsp): remove brittle schema path completion test
(#2009)
---
.../tombi-lsp/tests/test_completion_labels.rs | 59 -------------------
1 file changed, 59 deletions(-)
diff --git a/crates/tombi-lsp/tests/test_completion_labels.rs b/crates/tombi-lsp/tests/test_completion_labels.rs
index a10438fb8..de9821b63 100644
--- a/crates/tombi-lsp/tests/test_completion_labels.rs
+++ b/crates/tombi-lsp/tests/test_completion_labels.rs
@@ -708,65 +708,6 @@ mod completion_labels {
]);
}
- test_completion_labels! {
- #[tokio::test]
- async fn tombi_schemars_path_file_completion(
- r#"
- [[schemas]]
- path = "█"
- "#,
- SourcePath(project_root_path().join("schemas").join("tombi.toml")),
- SchemaPath(tombi_schema_path()),
- ) -> Ok([
- "additional-properties-branch-keys-test.schema.json",
- "adjacent-applicators-test.schema.json",
- "adjacent-one-of-additional-properties-test.schema.json",
- "adjacent-one-of-hover-test.schema.json",
- "anchor-dynamic-ref-test.schema.json",
- "anchor-table-test.schema.json",
- "array-const-enum-test.schema.json",
- "contains-test.schema.json",
- "dependencies-strict-mode-test.schema.json",
- "dependencies-test.schema.json",
- "dependent-required-test.schema.json",
- "dependent-schemas-test.schema.json",
- "deprecated-test.schema.json",
- "exact-index-hover-test.schema.json",
- "exact-index-override-test.schema.json",
- "exact-index-string-test.schema.json",
- "format-annotation-test.schema.json",
- "format-assertion-vocab-test.schema.json",
- "if-then-else-test.schema.json",
- "issue-1895-rustfmt-like.schema.json",
- "lsp-consistency-test.schema.json",
- "min-max-contains-test.schema.json",
- "not-schema-test.schema.json",
- "one-of-hover-discriminator-test.schema.json",
- "partial-taskipy.schema.json",
- "prefix-items-test.schema.json",
- "recursive-anchor-ref-test.schema.json",
- "recursive-defs-any-of-test.schema.json",
- "recursive-schema.schema.json",
- "ref-sibling-annotations-test.schema.json",
- "root-ref-test.schema.json",
- "string-format-test.schema.json",
- "subschema-singleton-label-test.schema.json",
- "table-const-enum-test.schema.json",
- "tuple-items-test.schema.json",
- "type-test.schema.json",
- "unevaluated-items-test.schema.json",
- "unevaluated-properties-branch-additional-test.schema.json",
- "unevaluated-properties-test.schema.json",
- "untagged-union.schema.json",
- "x-tombi-table-keys-order.schema.json",
- "\"https://www.schemastore.org/cargo.json\"",
- "\"https://www.schemastore.org/pyproject.json\"",
- "\"tombi://www.schemastore.org/cargo.json\"",
- "\"tombi://www.schemastore.org/pyproject.json\"",
- "\"tombi://www.schemastore.org/tombi.json\"",
- ]);
- }
-
test_completion_labels! {
#[tokio::test]
async fn tombi_schemars_path_file_completion_from_dot_config_project_root(
From 9d92045c02e18fefa485cef20a87935aef5526cd Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Thu, 16 Jul 2026 17:25:13 +0900
Subject: [PATCH 10/15] fix(validator): report schema resolution errors (#2010)
* fix(validator): report schema resolution errors
* fix: satisfy Rust 1.97 clippy
---
crates/tombi-extension/src/completion.rs | 2 +-
crates/tombi-linter/tests/integration.rs | 2 +
.../integration/schema_resolution_error.rs | 241 +++++++++++++
.../tombi-lsp/src/completion/value/table.rs | 4 +-
crates/tombi-schema-store/src/schema.rs | 4 +-
.../src/schema/referable_schema.rs | 42 ++-
crates/tombi-validator/src/validate/all_of.rs | 23 +-
crates/tombi-validator/src/validate/any_of.rs | 23 +-
crates/tombi-validator/src/validate/array.rs | 160 ++++++---
.../src/validate/if_then_else.rs | 77 +++--
crates/tombi-validator/src/validate/one_of.rs | 40 ++-
crates/tombi-validator/src/validate/table.rs | 325 +++++++++++-------
rust/serde_tombi/src/config.rs | 12 +-
rust/tombi-cli/src/app/command/format.rs | 2 +-
.../schema-resolution-error-test.schema.json | 144 ++++++++
15 files changed, 842 insertions(+), 259 deletions(-)
create mode 100644 crates/tombi-linter/tests/integration/schema_resolution_error.rs
create mode 100644 schemas/schema-resolution-error-test.schema.json
diff --git a/crates/tombi-extension/src/completion.rs b/crates/tombi-extension/src/completion.rs
index 993536034..9d037cd85 100644
--- a/crates/tombi-extension/src/completion.rs
+++ b/crates/tombi-extension/src/completion.rs
@@ -512,7 +512,7 @@ impl FromLsp for tower_lsp::lsp_types::CompletionItem {
) -> tower_lsp::lsp_types::CompletionItem {
const SECTION_SEPARATOR: &str = "-----";
- let sorted_text = format!("{}_{}", source.priority.as_prefix(), &source.label);
+ let sorted_text = format!("{}_{}", source.priority.as_prefix(), source.label);
let mut schema_text = None;
if let Some(schema_uri) = &source.schema_uri {
diff --git a/crates/tombi-linter/tests/integration.rs b/crates/tombi-linter/tests/integration.rs
index edd486973..40de5ee0c 100644
--- a/crates/tombi-linter/tests/integration.rs
+++ b/crates/tombi-linter/tests/integration.rs
@@ -38,6 +38,8 @@ mod prefix_items_test_schema;
mod recursive_anchor_ref_test_schema;
#[path = "integration/recursive_defs_any_of_test_schema.rs"]
mod recursive_defs_any_of_test_schema;
+#[path = "integration/schema_resolution_error.rs"]
+mod schema_resolution_error;
#[path = "integration/string_format_test_schema.rs"]
mod string_format_test_schema;
#[path = "integration/table_const_enum_test_schema.rs"]
diff --git a/crates/tombi-linter/tests/integration/schema_resolution_error.rs b/crates/tombi-linter/tests/integration/schema_resolution_error.rs
new file mode 100644
index 000000000..52136af04
--- /dev/null
+++ b/crates/tombi-linter/tests/integration/schema_resolution_error.rs
@@ -0,0 +1,241 @@
+use tombi_diagnostic::Level;
+use tombi_linter::test_lint;
+
+fn schema_path() -> std::path::PathBuf {
+ tombi_test_lib::project_root_path()
+ .join("schemas")
+ .join("schema-resolution-error-test.schema.json")
+}
+
+test_lint! {
+ #[test]
+ fn test_property_schema_resolution_error(
+ r#"
+ direct = 1
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_all_of_schema_resolution_error(
+ r#"
+ all-of-error = 1
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_any_of_schema_resolution_error(
+ r#"
+ any-of-error = 1
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_one_of_schema_resolution_error(
+ r#"
+ one-of-error = 1
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_if_schema_resolution_error(
+ r#"
+ if-error = {}
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_then_schema_resolution_error(
+ r#"
+ then-error = {}
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_else_schema_resolution_error(
+ r#"
+ else-error = {}
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_items_schema_resolution_error(
+ r#"
+ items-error = [1]
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_prefix_items_schema_resolution_error(
+ r#"
+ prefix-items-error = [1]
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_overflow_items_schema_resolution_error(
+ r#"
+ overflow-items-error = [1, 2]
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_overflow_items_schema_not_applied(
+ r#"
+ overflow-items-error = [1]
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Ok(_)
+}
+
+test_lint! {
+ #[test]
+ fn test_contains_schema_resolution_error(
+ r#"
+ contains-error = [1]
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_unevaluated_items_schema_resolution_error(
+ r#"
+ unevaluated-items-error = [1]
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_pattern_property_schema_resolution_error(
+ r#"
+ [pattern-properties-error]
+ pat-key = 1
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_additional_property_schema_resolution_error(
+ r#"
+ [additional-properties-error]
+ extra = 1
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_unevaluated_property_schema_resolution_error(
+ r#"
+ [unevaluated-properties-error]
+ extra = 1
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR },
+ { code: "table-strict-additional-keys", level: Level::WARNING }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_property_names_schema_resolution_error(
+ r#"
+ [property-names-error]
+ key = 1
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_dependency_schema_resolution_error(
+ r#"
+ [dependencies-error]
+ trigger = "x"
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
+
+test_lint! {
+ #[test]
+ fn test_dependent_schema_resolution_error(
+ r#"
+ [dependent-schemas-error]
+ trigger = "x"
+ "#,
+ SchemaPath(schema_path()),
+ ) -> Diagnostics([
+ { code: "invalid-json-pointer", level: Level::ERROR }
+ ])
+}
diff --git a/crates/tombi-lsp/src/completion/value/table.rs b/crates/tombi-lsp/src/completion/value/table.rs
index a3f0adc3e..292833c22 100644
--- a/crates/tombi-lsp/src/completion/value/table.rs
+++ b/crates/tombi-lsp/src/completion/value/table.rs
@@ -248,7 +248,7 @@ impl FindCompletionContents for tombi_document_tree::Table {
{
log::trace!(
"property_schema = {:?}",
- ¤t_schema.value_schema
+ current_schema.value_schema
);
let Some(mut contents) =
@@ -311,7 +311,7 @@ impl FindCompletionContents for tombi_document_tree::Table {
if pattern.is_match(accessor_str) {
log::trace!(
"pattern_property_schema = {:?}",
- ¤t_schema.value_schema
+ current_schema.value_schema
);
if let Ok(Some(current_schema)) = table_schema
.resolve_pattern_property_schema(
diff --git a/crates/tombi-schema-store/src/schema.rs b/crates/tombi-schema-store/src/schema.rs
index c5579846e..4bfb84155 100644
--- a/crates/tombi-schema-store/src/schema.rs
+++ b/crates/tombi-schema-store/src/schema.rs
@@ -42,8 +42,8 @@ pub use not_schema::NotSchema;
pub use offset_date_time_schema::OffsetDateTimeSchema;
pub use one_of_schema::OneOfSchema;
pub use referable_schema::{
- CurrentSchema, Referable, is_online_url, resolve_and_collect_schemas, resolve_json_pointer,
- resolve_schema_item,
+ CurrentSchema, Referable, is_online_url, resolve_and_collect_schemas,
+ resolve_and_collect_schemas_with_errors, resolve_json_pointer, resolve_schema_item,
};
pub use schema_context::{ResolvedFormatOrder, SchemaContext};
pub use schema_cycle_guard::{SchemaCycleGuard, SchemaVisits};
diff --git a/crates/tombi-schema-store/src/schema/referable_schema.rs b/crates/tombi-schema-store/src/schema/referable_schema.rs
index f696279c8..c5fb9fe3e 100644
--- a/crates/tombi-schema-store/src/schema/referable_schema.rs
+++ b/crates/tombi-schema-store/src/schema/referable_schema.rs
@@ -692,19 +692,45 @@ fn is_plain_name_fragment(fragment: &str) -> bool {
!fragment.is_empty() && !fragment.contains('/')
}
+pub async fn resolve_and_collect_schemas(
+ schemas: &super::ReferableValueSchemas,
+ schema_uri: Cow<'_, SchemaUri>,
+ definitions: Cow<'_, SchemaDefinitions>,
+ schema_store: &crate::SchemaStore,
+ schema_visits: &crate::SchemaVisits,
+ accessors: &[crate::Accessor],
+) -> Option>> {
+ let (collected, errors) = resolve_and_collect_schemas_with_errors(
+ schemas,
+ schema_uri,
+ definitions,
+ schema_store,
+ schema_visits,
+ accessors,
+ )
+ .await?;
+
+ for err in errors {
+ log::warn!("{err}");
+ }
+
+ Some(collected)
+}
+
/// Two-path schema collection: tries a read lock first for already-resolved schemas,
/// resolves refs on cloned entries, and writes back only newly-resolved entries.
///
+/// Returns the successfully resolved schemas together with any resolution errors.
/// Returns `None` when schema traversal is re-entrant (cycle guard) or when
/// an initial read lock cannot be acquired due to concurrent mutation.
-pub async fn resolve_and_collect_schemas(
+pub async fn resolve_and_collect_schemas_with_errors(
schemas: &super::ReferableValueSchemas,
schema_uri: Cow<'_, SchemaUri>,
definitions: Cow<'_, SchemaDefinitions>,
schema_store: &crate::SchemaStore,
schema_visits: &crate::SchemaVisits,
accessors: &[crate::Accessor],
-) -> Option>> {
+) -> Option<(Vec>, Vec)> {
let Some(_cycle_guard) = schema_visits.get_cycle_guard(schemas) else {
log::debug!(
"detected composite schema cycle while collecting schemas: schema_uri={schema_uri} accessors={accessors} reason=reentrant_schema_traversal",
@@ -749,6 +775,7 @@ pub async fn resolve_and_collect_schemas(
// Build output from read result and avoid cloning the whole referable vector.
if let Some(resolved_schemas) = resolved_schemas {
let mut collected = Vec::with_capacity(resolved_schemas.len());
+ let mut errors = Vec::new();
let default_schema_uri = schema_uri.as_ref().clone();
let default_definitions = definitions.clone().into_owned();
@@ -765,7 +792,7 @@ pub async fn resolve_and_collect_schemas(
),
Ok(None) => (default_schema_uri.clone(), default_definitions.clone()),
Err(err) => {
- log::warn!("{err}");
+ errors.push(err);
continue;
}
}
@@ -780,11 +807,12 @@ pub async fn resolve_and_collect_schemas(
});
}
- return Some(collected);
+ return Some((collected, errors));
}
// Slow path: unresolved refs exist. Resolve on cloned entries and cache back.
let mut collected = Vec::with_capacity(schema_entries.len());
+ let mut errors = Vec::new();
let mut resolved_indices = Vec::new();
for (index, referable_schema) in schema_entries.iter_mut().enumerate() {
let was_ref = referable_schema.is_ref();
@@ -795,7 +823,7 @@ pub async fn resolve_and_collect_schemas(
Ok(Some(current_schema)) => collected.push(current_schema.into_owned()),
Ok(None) => {}
Err(err) => {
- log::warn!("{err}");
+ errors.push(err);
}
}
@@ -812,7 +840,7 @@ pub async fn resolve_and_collect_schemas(
schema_uri = schema_uri.as_ref(),
accessors = crate::Accessors::from(accessors.to_vec())
);
- return Some(collected);
+ return Some((collected, errors));
};
for index in resolved_indices {
@@ -826,7 +854,7 @@ pub async fn resolve_and_collect_schemas(
}
}
- Some(collected)
+ Some((collected, errors))
}
/// Resolve a schema item without holding its write lock across await points.
diff --git a/crates/tombi-validator/src/validate/all_of.rs b/crates/tombi-validator/src/validate/all_of.rs
index ba21e6ef3..35291be5d 100644
--- a/crates/tombi-validator/src/validate/all_of.rs
+++ b/crates/tombi-validator/src/validate/all_of.rs
@@ -33,19 +33,24 @@ where
let mut total_score = 0;
let mut evaluated_locations = crate::EvaluatedLocations::new();
- let Some(resolved_schemas) = tombi_schema_store::resolve_and_collect_schemas(
- &all_of_schema.schemas,
- current_schema.schema_uri.clone(),
- current_schema.definitions.clone(),
- schema_context.store,
- &schema_context.schema_visits,
- accessors,
- )
- .await
+ let Some((resolved_schemas, resolution_errors)) =
+ tombi_schema_store::resolve_and_collect_schemas_with_errors(
+ &all_of_schema.schemas,
+ current_schema.schema_uri.clone(),
+ current_schema.definitions.clone(),
+ schema_context.store,
+ &schema_context.schema_visits,
+ accessors,
+ )
+ .await
else {
return Ok(crate::EvaluatedLocations::new());
};
+ total_diagnostics.extend(resolution_errors.into_iter().map(|err| {
+ tombi_diagnostic::Diagnostic::new_error(err.to_string(), err.code(), value.range())
+ }));
+
for resolved_schema in &resolved_schemas {
match value
.validate(accessors, Some(resolved_schema), schema_context)
diff --git a/crates/tombi-validator/src/validate/any_of.rs b/crates/tombi-validator/src/validate/any_of.rs
index acbcf6304..f05ac0bf9 100644
--- a/crates/tombi-validator/src/validate/any_of.rs
+++ b/crates/tombi-validator/src/validate/any_of.rs
@@ -66,15 +66,16 @@ where
}
}
- let Some(resolved_schemas) = tombi_schema_store::resolve_and_collect_schemas(
- &any_of_schema.schemas,
- current_schema.schema_uri.clone(),
- current_schema.definitions.clone(),
- schema_context.store,
- &schema_context.schema_visits,
- accessors,
- )
- .await
+ let Some((resolved_schemas, resolution_errors)) =
+ tombi_schema_store::resolve_and_collect_schemas_with_errors(
+ &any_of_schema.schemas,
+ current_schema.schema_uri.clone(),
+ current_schema.definitions.clone(),
+ schema_context.store,
+ &schema_context.schema_visits,
+ accessors,
+ )
+ .await
else {
if total_diagnostics.is_empty() {
return Ok(base_evaluated_locations);
@@ -87,6 +88,10 @@ where
}
};
+ total_diagnostics.extend(resolution_errors.into_iter().map(|err| {
+ tombi_diagnostic::Diagnostic::new_error(err.to_string(), err.code(), value.range())
+ }));
+
let mut total_error = crate::Error::new();
let mut matched = false;
let mut matched_diagnostics = Vec::new();
diff --git a/crates/tombi-validator/src/validate/array.rs b/crates/tombi-validator/src/validate/array.rs
index f98bb6398..60d76a20d 100644
--- a/crates/tombi-validator/src/validate/array.rs
+++ b/crates/tombi-validator/src/validate/array.rs
@@ -150,34 +150,39 @@ async fn validate_array(
}
if let Some(prefix_items) = &array_schema.prefix_items {
- // Resolve the overflow schema once before the loop
- let overflow_schema =
- if let Some(additional_items_schema) = &array_schema.additional_items_schema {
- tombi_schema_store::resolve_schema_item(
- additional_items_schema,
- current_schema.schema_uri.clone(),
- current_schema.definitions.clone(),
- schema_context.store,
- )
- .await
- .inspect_err(|err| log::warn!("{err}"))
- .ok()
- .flatten()
- } else if let Some(items) = &array_schema.items {
- // 2020-12: items acts as additionalItems when prefixItems is present
- tombi_schema_store::resolve_schema_item(
- items,
- current_schema.schema_uri.clone(),
- current_schema.definitions.clone(),
- schema_context.store,
- )
- .await
- .inspect_err(|err| log::warn!("{err}"))
- .ok()
- .flatten()
- } else {
- None
- };
+ // Resolve the overflow schema once before the loop.
+ // 2020-12: items acts as additionalItems when prefixItems is present.
+ let overflow_schema = if array_value.values().len() > prefix_items.len() {
+ match array_schema
+ .additional_items_schema
+ .as_ref()
+ .or(array_schema.items.as_ref())
+ {
+ Some(overflow_item) => {
+ match tombi_schema_store::resolve_schema_item(
+ overflow_item,
+ current_schema.schema_uri.clone(),
+ current_schema.definitions.clone(),
+ schema_context.store,
+ )
+ .await
+ {
+ Ok(overflow_schema) => overflow_schema,
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ array_value.range(),
+ ));
+ None
+ }
+ }
+ }
+ None => None,
+ }
+ } else {
+ None
+ };
// Tuple validation: validate each element against its positional schema
for (index, value) in array_value.values().iter().enumerate() {
@@ -189,19 +194,30 @@ async fn validate_array(
if index < prefix_items.len() {
evaluated[index] = true;
- if let Ok(Some(item_schema)) = tombi_schema_store::resolve_schema_item(
+ match tombi_schema_store::resolve_schema_item(
&prefix_items[index],
current_schema.schema_uri.clone(),
current_schema.definitions.clone(),
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
- && let Err(crate::Error { diagnostics, .. }) = value
- .validate(&new_accessors, Some(&item_schema), schema_context)
- .await
{
- total_diagnostics.extend(diagnostics);
+ Ok(Some(item_schema)) => {
+ if let Err(crate::Error { diagnostics, .. }) = value
+ .validate(&new_accessors, Some(&item_schema), schema_context)
+ .await
+ {
+ total_diagnostics.extend(diagnostics);
+ }
+ }
+ Ok(None) => {}
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ value.range(),
+ ));
+ }
}
} else if let Some(overflow) = &overflow_schema {
evaluated[index] = true;
@@ -229,43 +245,65 @@ async fn validate_array(
}
} else if let Some(items) = &array_schema.items {
// Single schema for all items
- if let Ok(Some(current_schema)) = tombi_schema_store::resolve_schema_item(
+ match tombi_schema_store::resolve_schema_item(
items,
current_schema.schema_uri.clone(),
current_schema.definitions.clone(),
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
{
- for (index, value) in array_value.values().iter().enumerate() {
- evaluated[index] = true;
- let new_accessors = accessors
- .iter()
- .cloned()
- .chain(std::iter::once(tombi_schema_store::Accessor::Index(index)))
- .collect_vec();
-
- if let Err(crate::Error { diagnostics, .. }) = value
- .validate(&new_accessors, Some(¤t_schema), schema_context)
- .await
- {
- total_diagnostics.extend(diagnostics);
+ Ok(Some(current_schema)) => {
+ for (index, value) in array_value.values().iter().enumerate() {
+ evaluated[index] = true;
+ let new_accessors = accessors
+ .iter()
+ .cloned()
+ .chain(std::iter::once(tombi_schema_store::Accessor::Index(index)))
+ .collect_vec();
+
+ if let Err(crate::Error { diagnostics, .. }) = value
+ .validate(&new_accessors, Some(¤t_schema), schema_context)
+ .await
+ {
+ total_diagnostics.extend(diagnostics);
+ }
}
}
+ Ok(None) => {}
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ array_value.range(),
+ ));
+ }
}
}
- if let Some(contains) = &array_schema.contains
- && let Ok(Some(contains_schema)) = tombi_schema_store::resolve_schema_item(
+ let contains_schema = match &array_schema.contains {
+ Some(contains) => match tombi_schema_store::resolve_schema_item(
contains,
current_schema.schema_uri.clone(),
current_schema.definitions.clone(),
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
- {
+ {
+ Ok(contains_schema) => contains_schema,
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ array_value.range(),
+ ));
+ None
+ }
+ },
+ None => None,
+ };
+
+ if let Some(contains_schema) = contains_schema {
let min_contains = array_schema.min_contains.unwrap_or(1);
let max_contains = array_schema.max_contains;
let needs_full_count = max_contains.is_some() || has_unevaluated_items;
@@ -342,16 +380,24 @@ async fn validate_array(
// Run unevaluatedItems after all applicators that can mark items as evaluated.
if has_unevaluated_items {
let unevaluated_schema = if let Some(schema_item) = &array_schema.unevaluated_items_schema {
- tombi_schema_store::resolve_schema_item(
+ match tombi_schema_store::resolve_schema_item(
schema_item,
current_schema.schema_uri.clone(),
current_schema.definitions.clone(),
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
- .ok()
- .flatten()
+ {
+ Ok(unevaluated_schema) => unevaluated_schema,
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ array_value.range(),
+ ));
+ None
+ }
+ }
} else {
None
};
diff --git a/crates/tombi-validator/src/validate/if_then_else.rs b/crates/tombi-validator/src/validate/if_then_else.rs
index 5505156ef..4f7876079 100644
--- a/crates/tombi-validator/src/validate/if_then_else.rs
+++ b/crates/tombi-validator/src/validate/if_then_else.rs
@@ -32,57 +32,90 @@ where
};
// Resolve and validate the `if` schema
- let if_result = if let Ok(Some(if_current_schema)) = tombi_schema_store::resolve_schema_item(
+ let if_result = match tombi_schema_store::resolve_schema_item(
&if_then_else_schema.if_schema,
current_schema.schema_uri.clone(),
current_schema.definitions.clone(),
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
{
- value
- .validate(accessors, Some(&if_current_schema), schema_context)
- .await
- } else {
- return Ok(crate::EvaluatedLocations::new());
+ Ok(Some(if_current_schema)) => {
+ value
+ .validate(accessors, Some(&if_current_schema), schema_context)
+ .await
+ }
+ Ok(None) => return Ok(crate::EvaluatedLocations::new()),
+ Err(err) => {
+ return Err(vec![tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ value.range(),
+ )]
+ .into());
+ }
};
// Per JSON Schema spec: branching is based on assertion result.
if is_assertion_success(&if_result) {
// `if` matched → apply `then` schema if present
- if let Some(then_schema) = &if_then_else_schema.then_schema
- && let Ok(Some(then_current_schema)) = tombi_schema_store::resolve_schema_item(
+ if let Some(then_schema) = &if_then_else_schema.then_schema {
+ match tombi_schema_store::resolve_schema_item(
then_schema,
current_schema.schema_uri.clone(),
current_schema.definitions.clone(),
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
- {
- let branch_result = value
- .validate(accessors, Some(&then_current_schema), schema_context)
- .await;
- return merge_if_result(branch_result, if_result);
+ {
+ Ok(Some(then_current_schema)) => {
+ let branch_result = value
+ .validate(accessors, Some(&then_current_schema), schema_context)
+ .await;
+ return merge_if_result(branch_result, if_result);
+ }
+ Ok(None) => {}
+ Err(err) => {
+ return merge_if_result(
+ Err(vec![tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ value.range(),
+ )]
+ .into()),
+ if_result,
+ );
+ }
+ }
}
return merge_if_result(Ok(crate::EvaluatedLocations::new()), if_result);
} else {
// `if` did not match → apply `else` schema if present
- if let Some(else_schema) = &if_then_else_schema.else_schema
- && let Ok(Some(else_current_schema)) = tombi_schema_store::resolve_schema_item(
+ if let Some(else_schema) = &if_then_else_schema.else_schema {
+ match tombi_schema_store::resolve_schema_item(
else_schema,
Cow::Borrowed(current_schema.schema_uri.as_ref()),
Cow::Borrowed(current_schema.definitions.as_ref()),
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
- {
- return value
- .validate(accessors, Some(&else_current_schema), schema_context)
- .await;
+ {
+ Ok(Some(else_current_schema)) => {
+ return value
+ .validate(accessors, Some(&else_current_schema), schema_context)
+ .await;
+ }
+ Ok(None) => {}
+ Err(err) => {
+ return Err(vec![tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ value.range(),
+ )]
+ .into());
+ }
+ }
}
}
diff --git a/crates/tombi-validator/src/validate/one_of.rs b/crates/tombi-validator/src/validate/one_of.rs
index 718a7f907..a939340d3 100644
--- a/crates/tombi-validator/src/validate/one_of.rs
+++ b/crates/tombi-validator/src/validate/one_of.rs
@@ -66,15 +66,16 @@ where
let mut valid_count = 0;
- let Some(resolved_schemas) = tombi_schema_store::resolve_and_collect_schemas(
- &one_of_schema.schemas,
- current_schema.schema_uri.clone(),
- current_schema.definitions.clone(),
- schema_context.store,
- &schema_context.schema_visits,
- accessors,
- )
- .await
+ let Some((resolved_schemas, resolution_errors)) =
+ tombi_schema_store::resolve_and_collect_schemas_with_errors(
+ &one_of_schema.schemas,
+ current_schema.schema_uri.clone(),
+ current_schema.definitions.clone(),
+ schema_context.store,
+ &schema_context.schema_visits,
+ accessors,
+ )
+ .await
else {
if total_diagnostics.is_empty() {
return Ok(base_evaluated_locations);
@@ -86,16 +87,23 @@ where
});
}
};
+ let has_resolution_errors = !resolution_errors.is_empty();
+ total_diagnostics.extend(resolution_errors.into_iter().map(|err| {
+ tombi_diagnostic::Diagnostic::new_error(err.to_string(), err.code(), value.range())
+ }));
+
let total_count = resolved_schemas.len();
if total_count == 0 {
- crate::Diagnostic {
- kind: Box::new(crate::DiagnosticKind::OneOfNoMatch { total_count }),
- range: value.range(),
+ if !has_resolution_errors {
+ crate::Diagnostic {
+ kind: Box::new(crate::DiagnosticKind::OneOfNoMatch { total_count }),
+ range: value.range(),
+ }
+ .push_diagnostic_with_level(
+ SeverityLevelDefaultError::default(),
+ &mut total_diagnostics,
+ );
}
- .push_diagnostic_with_level(
- SeverityLevelDefaultError::default(),
- &mut total_diagnostics,
- );
return Err(crate::Error {
score: crate::error::TYPE_MATCHED_SCORE,
diagnostics: total_diagnostics,
diff --git a/crates/tombi-validator/src/validate/table.rs b/crates/tombi-validator/src/validate/table.rs
index 17369fa7f..5d0fcbd46 100644
--- a/crates/tombi-validator/src/validate/table.rs
+++ b/crates/tombi-validator/src/validate/table.rs
@@ -178,7 +178,7 @@ async fn validate_table(
{
matched_key = true;
- if let Ok(Some(current_schema)) = table_schema
+ match table_schema
.resolve_property_schema(
&schema_accessor,
current_schema.schema_uri.clone(),
@@ -186,17 +186,33 @@ async fn validate_table(
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
- && let Err(crate::Error {
- mut diagnostics, ..
- }) = value
- .validate(&new_accessors, Some(¤t_schema), schema_context)
- .await
{
- convert_deprecated_diagnostics_range(¤t_schema, value, key, &mut diagnostics)
- .await;
+ Ok(Some(current_schema)) => {
+ if let Err(crate::Error {
+ mut diagnostics, ..
+ }) = value
+ .validate(&new_accessors, Some(¤t_schema), schema_context)
+ .await
+ {
+ convert_deprecated_diagnostics_range(
+ ¤t_schema,
+ value,
+ key,
+ &mut diagnostics,
+ )
+ .await;
- total_diagnostics.extend(diagnostics);
+ total_diagnostics.extend(diagnostics);
+ }
+ }
+ Ok(None) => {}
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ key.range() + value.range(),
+ ));
+ }
}
}
@@ -214,7 +230,7 @@ async fn validate_table(
};
if pattern.is_match(accessor_raw_text) {
matched_key = true;
- if let Ok(Some(current_schema)) = table_schema
+ match table_schema
.resolve_pattern_property_schema(
&pattern_key,
current_schema.schema_uri.clone(),
@@ -222,22 +238,33 @@ async fn validate_table(
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
- && let Err(crate::Error {
- mut diagnostics, ..
- }) = value
- .validate(&new_accessors, Some(¤t_schema), schema_context)
- .await
{
- convert_deprecated_diagnostics_range(
- ¤t_schema,
- value,
- key,
- &mut diagnostics,
- )
- .await;
-
- total_diagnostics.extend(diagnostics);
+ Ok(Some(current_schema)) => {
+ if let Err(crate::Error {
+ mut diagnostics, ..
+ }) = value
+ .validate(&new_accessors, Some(¤t_schema), schema_context)
+ .await
+ {
+ convert_deprecated_diagnostics_range(
+ ¤t_schema,
+ value,
+ key,
+ &mut diagnostics,
+ )
+ .await;
+
+ total_diagnostics.extend(diagnostics);
+ }
+ }
+ Ok(None) => {}
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ key.range() + value.range(),
+ ));
+ }
}
}
}
@@ -284,34 +311,45 @@ async fn validate_table(
let mut validated_by_additional_schema = false;
if let Some((_, referable_additional_property_schema)) =
&table_schema.additional_property_schema
- && let Ok(Some(current_schema)) = tombi_schema_store::resolve_schema_item(
+ {
+ match tombi_schema_store::resolve_schema_item(
referable_additional_property_schema,
current_schema.schema_uri.clone(),
current_schema.definitions.clone(),
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
- {
- let deprecation = current_schema.value_schema.deprecation().await;
- handle_deprecated_value(
- &mut total_diagnostics,
- deprecation.as_ref(),
- &new_accessors,
- value,
- Some(¤t_schema),
- schema_context,
- table_value.comment_directives(),
- table_rules.as_ref().map(|rules| &rules.common),
- );
-
- if let Err(crate::Error { diagnostics, .. }) = value
- .validate(&new_accessors, Some(¤t_schema), schema_context)
- .await
{
- total_diagnostics.extend(diagnostics);
+ Ok(Some(current_schema)) => {
+ let deprecation = current_schema.value_schema.deprecation().await;
+ handle_deprecated_value(
+ &mut total_diagnostics,
+ deprecation.as_ref(),
+ &new_accessors,
+ value,
+ Some(¤t_schema),
+ schema_context,
+ table_value.comment_directives(),
+ table_rules.as_ref().map(|rules| &rules.common),
+ );
+
+ if let Err(crate::Error { diagnostics, .. }) = value
+ .validate(&new_accessors, Some(¤t_schema), schema_context)
+ .await
+ {
+ total_diagnostics.extend(diagnostics);
+ }
+ validated_by_additional_schema = true;
+ }
+ Ok(None) => {}
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ key.range() + value.range(),
+ ));
+ }
}
- validated_by_additional_schema = true;
}
// `additionalProperties` contributes to evaluated properties only when the keyword exists.
@@ -322,23 +360,33 @@ async fn validate_table(
&& !validated_by_additional_schema
&& !evaluated_by_additional_default
{
- if let Some(schema_item) = &table_schema.unevaluated_property_schema
- && let Ok(Some(unevaluated_schema)) = tombi_schema_store::resolve_schema_item(
+ if let Some(schema_item) = &table_schema.unevaluated_property_schema {
+ match tombi_schema_store::resolve_schema_item(
schema_item,
current_schema.schema_uri.clone(),
current_schema.definitions.clone(),
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
- {
- if let Err(crate::Error { diagnostics, .. }) = value
- .validate(&new_accessors, Some(&unevaluated_schema), schema_context)
- .await
{
- total_diagnostics.extend(diagnostics);
+ Ok(Some(unevaluated_schema)) => {
+ if let Err(crate::Error { diagnostics, .. }) = value
+ .validate(&new_accessors, Some(&unevaluated_schema), schema_context)
+ .await
+ {
+ total_diagnostics.extend(diagnostics);
+ }
+ continue;
+ }
+ Ok(None) => {}
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ key.range() + value.range(),
+ ));
+ }
}
- continue;
}
if table_schema.unevaluated_properties == Some(false) {
@@ -545,37 +593,46 @@ async fn validate_table(
}
}
tombi_schema_store::Dependency::Schema(schema_item) => {
- if let Ok(Some(dep_schema)) = tombi_schema_store::resolve_schema_item(
+ match tombi_schema_store::resolve_schema_item(
schema_item,
current_schema.schema_uri.clone(),
current_schema.definitions.clone(),
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
{
- // A dependency schema is an additional constraint layered on top of
- // the parent table schema. Running strict mode here against the
- // partial dependency schema causes false-positive additional key
- // diagnostics for valid keys defined by the parent schema.
- let dependency_schema_context = tombi_schema_store::SchemaContext {
- toml_version: schema_context.toml_version,
- root_schema: schema_context.root_schema,
- sub_schema_uri_map: schema_context.sub_schema_uri_map,
- deprecated_lint_level: schema_context.deprecated_lint_level,
- schema_format_rules: schema_context.schema_format_rules,
- schema_lint_rules: schema_context.schema_lint_rules,
- schema_overrides: schema_context.schema_overrides,
- schema_visits: schema_context.schema_visits.clone(),
- store: schema_context.store,
- strict: Some(false),
- };
-
- if let Err(crate::Error { diagnostics, .. }) = table_value
- .validate(accessors, Some(&dep_schema), &dependency_schema_context)
- .await
- {
- total_diagnostics.extend(diagnostics);
+ Ok(Some(dep_schema)) => {
+ // A dependency schema is an additional constraint layered on top of
+ // the parent table schema. Running strict mode here against the
+ // partial dependency schema causes false-positive additional key
+ // diagnostics for valid keys defined by the parent schema.
+ let dependency_schema_context = tombi_schema_store::SchemaContext {
+ toml_version: schema_context.toml_version,
+ root_schema: schema_context.root_schema,
+ sub_schema_uri_map: schema_context.sub_schema_uri_map,
+ deprecated_lint_level: schema_context.deprecated_lint_level,
+ schema_format_rules: schema_context.schema_format_rules,
+ schema_lint_rules: schema_context.schema_lint_rules,
+ schema_overrides: schema_context.schema_overrides,
+ schema_visits: schema_context.schema_visits.clone(),
+ store: schema_context.store,
+ strict: Some(false),
+ };
+
+ if let Err(crate::Error { diagnostics, .. }) = table_value
+ .validate(accessors, Some(&dep_schema), &dependency_schema_context)
+ .await
+ {
+ total_diagnostics.extend(diagnostics);
+ }
+ }
+ Ok(None) => {}
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ table_value.range(),
+ ));
}
}
}
@@ -613,34 +670,43 @@ async fn validate_table(
continue;
}
- if let Ok(Some(dep_schema)) = tombi_schema_store::resolve_schema_item(
+ match tombi_schema_store::resolve_schema_item(
schema_item,
current_schema.schema_uri.clone(),
current_schema.definitions.clone(),
schema_context.store,
)
.await
- .inspect_err(|err| log::warn!("{err}"))
{
- // See the rationale in the `Dependency::Schema` branch above.
- let dependency_schema_context = tombi_schema_store::SchemaContext {
- toml_version: schema_context.toml_version,
- root_schema: schema_context.root_schema,
- sub_schema_uri_map: schema_context.sub_schema_uri_map,
- deprecated_lint_level: schema_context.deprecated_lint_level,
- schema_format_rules: schema_context.schema_format_rules,
- schema_lint_rules: schema_context.schema_lint_rules,
- schema_overrides: schema_context.schema_overrides,
- schema_visits: schema_context.schema_visits.clone(),
- store: schema_context.store,
- strict: Some(false),
- };
-
- if let Err(crate::Error { diagnostics, .. }) = table_value
- .validate(accessors, Some(&dep_schema), &dependency_schema_context)
- .await
- {
- total_diagnostics.extend(diagnostics);
+ Ok(Some(dep_schema)) => {
+ // See the rationale in the `Dependency::Schema` branch above.
+ let dependency_schema_context = tombi_schema_store::SchemaContext {
+ toml_version: schema_context.toml_version,
+ root_schema: schema_context.root_schema,
+ sub_schema_uri_map: schema_context.sub_schema_uri_map,
+ deprecated_lint_level: schema_context.deprecated_lint_level,
+ schema_format_rules: schema_context.schema_format_rules,
+ schema_lint_rules: schema_context.schema_lint_rules,
+ schema_overrides: schema_context.schema_overrides,
+ schema_visits: schema_context.schema_visits.clone(),
+ store: schema_context.store,
+ strict: Some(false),
+ };
+
+ if let Err(crate::Error { diagnostics, .. }) = table_value
+ .validate(accessors, Some(&dep_schema), &dependency_schema_context)
+ .await
+ {
+ total_diagnostics.extend(diagnostics);
+ }
+ }
+ Ok(None) => {}
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ table_value.range(),
+ ));
}
}
}
@@ -716,35 +782,40 @@ async fn validate_table(
}
}
- if let Some(property_name_schema) = &table_schema.property_names
- && let Ok(Some(property_name_current_schema)) = tombi_schema_store::resolve_schema_item(
- property_name_schema,
- current_schema.schema_uri.clone(),
- current_schema.definitions.clone(),
- schema_context.store,
- )
- .await
- .inspect_err(|err| log::warn!("{err}"))
- {
- for key in table_value.keys() {
- if let Err(crate::Error { diagnostics, .. }) = key
- .validate(
- accessors,
- Some(&property_name_current_schema),
- schema_context,
- )
- .await
- {
- total_diagnostics.extend(diagnostics);
- }
- }
- } else {
- for key in table_value.keys() {
- if let Err(crate::Error { diagnostics, .. }) =
- key.validate(accessors, None, schema_context).await
+ let property_name_current_schema =
+ if let Some(property_name_schema) = &table_schema.property_names {
+ match tombi_schema_store::resolve_schema_item(
+ property_name_schema,
+ current_schema.schema_uri.clone(),
+ current_schema.definitions.clone(),
+ schema_context.store,
+ )
+ .await
{
- total_diagnostics.extend(diagnostics);
+ Ok(property_name_current_schema) => property_name_current_schema,
+ Err(err) => {
+ total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
+ err.to_string(),
+ err.code(),
+ table_value.range(),
+ ));
+ None
+ }
}
+ } else {
+ None
+ };
+
+ for key in table_value.keys() {
+ if let Err(crate::Error { diagnostics, .. }) = key
+ .validate(
+ accessors,
+ property_name_current_schema.as_ref(),
+ schema_context,
+ )
+ .await
+ {
+ total_diagnostics.extend(diagnostics);
}
}
diff --git a/rust/serde_tombi/src/config.rs b/rust/serde_tombi/src/config.rs
index 3bd033eb3..f323e4f36 100644
--- a/rust/serde_tombi/src/config.rs
+++ b/rust/serde_tombi/src/config.rs
@@ -115,9 +115,9 @@ pub fn load_with_path_and_level(
current_dir.join(TOMBI_TOML_FILENAME),
current_dir.join(".config").join(TOMBI_TOML_FILENAME),
] {
- log::trace!("Checking config file at {:?}", &config_path);
+ log::trace!("Checking config file at {:?}", config_path);
if config_path.is_file() {
- log::debug!("Project config found at {:?}", &config_path);
+ log::debug!("Project config found at {:?}", config_path);
let Some(config) = try_from_path(&config_path)? else {
unreachable!(
@@ -131,7 +131,7 @@ pub fn load_with_path_and_level(
}
let pyproject_toml_path = current_dir.join(PYPROJECT_TOML_FILENAME);
- log::trace!("Checking pyproject.toml file at {:?}", &pyproject_toml_path);
+ log::trace!("Checking pyproject.toml file at {:?}", pyproject_toml_path);
if pyproject_toml_path.exists() {
log::debug!(
"\"{}\" found at {:?}",
@@ -144,12 +144,12 @@ pub fn load_with_path_and_level(
return Ok((config, Some(pyproject_toml_path), ConfigLevel::Project));
}
Ok(None) => {
- log::debug!("No [tool.tombi] found in {:?}", &pyproject_toml_path);
+ log::debug!("No [tool.tombi] found in {:?}", pyproject_toml_path);
}
Err(error) => {
log::debug!(
"Failed to parse pyproject.toml file for config at {:?}: {}",
- &pyproject_toml_path,
+ pyproject_toml_path,
error
);
}
@@ -164,7 +164,7 @@ pub fn load_with_path_and_level(
if let Some((user_config_path, config_level)) = get_user_or_system_tombi_config_path_and_level()
{
- log::debug!("{CONFIG_TOML_FILENAME} found at {:?}", &user_config_path);
+ log::debug!("{CONFIG_TOML_FILENAME} found at {:?}", user_config_path);
let Some(config) = try_from_path(&user_config_path)? else {
unreachable!("{CONFIG_TOML_FILENAME} should always be parsed successfully.");
};
diff --git a/rust/tombi-cli/src/app/command/format.rs b/rust/tombi-cli/src/app/command/format.rs
index 928b13465..e742d3ee7 100644
--- a/rust/tombi-cli/src/app/command/format.rs
+++ b/rust/tombi-cli/src/app/command/format.rs
@@ -190,7 +190,7 @@ where
for file in files {
match file {
FileSearchEntry::Found(source_path) => {
- log::debug!("Formatting... {:?}", &source_path);
+ log::debug!("Formatting... {:?}", source_path);
// Get format options with override support
let Some(format_options) = tombi_glob::get_format_options(
diff --git a/schemas/schema-resolution-error-test.schema.json b/schemas/schema-resolution-error-test.schema.json
new file mode 100644
index 000000000..bacf84b50
--- /dev/null
+++ b/schemas/schema-resolution-error-test.schema.json
@@ -0,0 +1,144 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "type": "object",
+ "properties": {
+ "direct": {
+ "$ref": "#/$defs/missing"
+ },
+ "all-of-error": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/missing"
+ }
+ ]
+ },
+ "any-of-error": {
+ "anyOf": [
+ {
+ "$ref": "#/$defs/missing"
+ }
+ ]
+ },
+ "one-of-error": {
+ "oneOf": [
+ {
+ "$ref": "#/$defs/missing"
+ }
+ ]
+ },
+ "if-error": {
+ "type": "object",
+ "if": {
+ "$ref": "#/$defs/missing"
+ }
+ },
+ "then-error": {
+ "type": "object",
+ "if": {
+ "type": "object"
+ },
+ "then": {
+ "$ref": "#/$defs/missing"
+ }
+ },
+ "else-error": {
+ "type": "object",
+ "if": {
+ "type": "string"
+ },
+ "else": {
+ "$ref": "#/$defs/missing"
+ }
+ },
+ "items-error": {
+ "type": "array",
+ "items": {
+ "$ref": "#/$defs/missing"
+ }
+ },
+ "prefix-items-error": {
+ "type": "array",
+ "prefixItems": [
+ {
+ "$ref": "#/$defs/missing"
+ }
+ ]
+ },
+ "overflow-items-error": {
+ "type": "array",
+ "prefixItems": [
+ {
+ "type": "integer"
+ }
+ ],
+ "items": {
+ "$ref": "#/$defs/missing"
+ }
+ },
+ "contains-error": {
+ "type": "array",
+ "contains": {
+ "$ref": "#/$defs/missing"
+ }
+ },
+ "unevaluated-items-error": {
+ "type": "array",
+ "unevaluatedItems": {
+ "$ref": "#/$defs/missing"
+ }
+ },
+ "pattern-properties-error": {
+ "type": "object",
+ "patternProperties": {
+ "^pat": {
+ "$ref": "#/$defs/missing"
+ }
+ }
+ },
+ "additional-properties-error": {
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/$defs/missing"
+ }
+ },
+ "unevaluated-properties-error": {
+ "type": "object",
+ "unevaluatedProperties": {
+ "$ref": "#/$defs/missing"
+ }
+ },
+ "property-names-error": {
+ "type": "object",
+ "additionalProperties": true,
+ "propertyNames": {
+ "$ref": "#/$defs/missing"
+ }
+ },
+ "dependencies-error": {
+ "type": "object",
+ "properties": {
+ "trigger": {
+ "type": "string"
+ }
+ },
+ "dependencies": {
+ "trigger": {
+ "$ref": "#/$defs/missing"
+ }
+ }
+ },
+ "dependent-schemas-error": {
+ "type": "object",
+ "properties": {
+ "trigger": {
+ "type": "string"
+ }
+ },
+ "dependentSchemas": {
+ "trigger": {
+ "$ref": "#/$defs/missing"
+ }
+ }
+ }
+ }
+}
From f5a06c75965d4d16c81f3be7edf2d108d410ebe1 Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Fri, 17 Jul 2026 00:20:40 +0900
Subject: [PATCH 11/15] fix(glob): remove unused profile import (#2011)
---
crates/tombi-glob/src/bin/profile.rs | 1 -
1 file changed, 1 deletion(-)
diff --git a/crates/tombi-glob/src/bin/profile.rs b/crates/tombi-glob/src/bin/profile.rs
index fb6126b52..d1fc53c84 100644
--- a/crates/tombi-glob/src/bin/profile.rs
+++ b/crates/tombi-glob/src/bin/profile.rs
@@ -1,6 +1,5 @@
use std::{env, path::PathBuf, time::Instant};
-use tombi_config::FilesOptions;
use tombi_glob::{FileSearchEntry, search_pattern_matched_paths};
#[tokio::main]
From 866201f89d56159fdc821763c36c2345005e2c70 Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Fri, 17 Jul 2026 00:39:48 +0900
Subject: [PATCH 12/15] test(lsp): silence deprecated fixture warnings (#2012)
---
.../dot-config-project-root/.config/tombi.toml | 9 +++++++--
.../cargo-document-link-all-enabled/tombi.toml | 12 ++++++++----
.../tombi.toml | 6 ++++--
.../tombi.toml | 12 ++++++++----
.../tombi.toml | 6 ++++--
.../cargo-document-link-git-disabled/tombi.toml | 6 ++++--
.../cargo-document-link-path-disabled/tombi.toml | 7 ++++++-
.../tombi.toml | 9 ++++++---
.../tombi.toml | 9 ++++++---
.../pyproject-document-link-pypi-disabled/tombi.toml | 3 ++-
.../tombi.toml | 7 ++++++-
.../fixtures/extensions/tombi-disabled/tombi.toml | 9 +++++++--
.../tombi-document-link-all-enabled/tombi.toml | 7 ++++++-
13 files changed, 74 insertions(+), 28 deletions(-)
diff --git a/crates/tombi-lsp/tests/fixtures/dot-config-project-root/.config/tombi.toml b/crates/tombi-lsp/tests/fixtures/dot-config-project-root/.config/tombi.toml
index 2fbc533eb..96c59ff13 100644
--- a/crates/tombi-lsp/tests/fixtures/dot-config-project-root/.config/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/dot-config-project-root/.config/tombi.toml
@@ -9,6 +9,11 @@ include = ["product.toml"]
[extensions]
"tombi-toml/tombi" = {
- lsp.document-link.path.enabled = true,
- lsp.goto-definition.path.enabled = true,
+ lsp = {
+ document-link = {
+ # tombi: lint.rules.deprecated.disabled = true
+ path = { enabled = true },
+ },
+ goto-definition.path.enabled = true,
+ },
}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-all-enabled/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-all-enabled/tombi.toml
index 192741f66..58df84538 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-all-enabled/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-all-enabled/tombi.toml
@@ -1,10 +1,14 @@
[extensions]
"tombi-toml/cargo" = {
lsp.document-link = {
- cargo-toml.enabled = true,
+ # tombi: lint.rules.deprecated.disabled = true
+ cargo-toml = { enabled = true },
crates-io.enabled = true,
- git.enabled = true,
- path.enabled = true,
- workspace.enabled = true,
+ # tombi: lint.rules.deprecated.disabled = true
+ git = { enabled = true },
+ # tombi: lint.rules.deprecated.disabled = true
+ path = { enabled = true },
+ # tombi: lint.rules.deprecated.disabled = true
+ workspace = { enabled = true },
},
}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-cargo-toml-disabled-bin/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-cargo-toml-disabled-bin/tombi.toml
index ec9f0dfac..26c0b1911 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-cargo-toml-disabled-bin/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-cargo-toml-disabled-bin/tombi.toml
@@ -2,8 +2,10 @@
"tombi-toml/cargo" = {
lsp = {
document-link = {
- cargo-toml.enabled = false,
- path.enabled = true,
+ # tombi: lint.rules.deprecated.disabled = true
+ cargo-toml = { enabled = false },
+ # tombi: lint.rules.deprecated.disabled = true
+ path = { enabled = true },
},
},
}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-cargo-toml-disabled/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-cargo-toml-disabled/tombi.toml
index 6e9e2626d..26ee7ed41 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-cargo-toml-disabled/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-cargo-toml-disabled/tombi.toml
@@ -2,10 +2,14 @@
"tombi-toml/cargo" = {
lsp = {
document-link = {
- cargo-toml.enabled = false,
- git.enabled = true,
- path.enabled = true,
- workspace.enabled = true,
+ # tombi: lint.rules.deprecated.disabled = true
+ cargo-toml = { enabled = false },
+ # tombi: lint.rules.deprecated.disabled = true
+ git = { enabled = true },
+ # tombi: lint.rules.deprecated.disabled = true
+ path = { enabled = true },
+ # tombi: lint.rules.deprecated.disabled = true
+ workspace = { enabled = true },
},
},
}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-crates-io-disabled/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-crates-io-disabled/tombi.toml
index 94d947a7d..5a3c84943 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-crates-io-disabled/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-crates-io-disabled/tombi.toml
@@ -2,9 +2,11 @@
"tombi-toml/cargo" = {
lsp = {
document-link = {
- cargo-toml.enabled = true,
+ # tombi: lint.rules.deprecated.disabled = true
+ cargo-toml = { enabled = true },
crates-io.enabled = false,
- path.enabled = true,
+ # tombi: lint.rules.deprecated.disabled = true
+ path = { enabled = true },
},
},
}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-git-disabled/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-git-disabled/tombi.toml
index e3a0c8f31..69febe0cf 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-git-disabled/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-git-disabled/tombi.toml
@@ -2,8 +2,10 @@
"tombi-toml/cargo" = {
lsp = {
document-link = {
- cargo-toml.enabled = true,
- git.enabled = false,
+ # tombi: lint.rules.deprecated.disabled = true
+ cargo-toml = { enabled = true },
+ # tombi: lint.rules.deprecated.disabled = true
+ git = { enabled = false },
},
},
}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-path-disabled/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-path-disabled/tombi.toml
index 48b2c5f07..6121819c2 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-path-disabled/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-path-disabled/tombi.toml
@@ -1,2 +1,7 @@
[extensions]
-"tombi-toml/cargo" = { lsp.document-link.path.enabled = false }
+"tombi-toml/cargo" = {
+ lsp.document-link = {
+ # tombi: lint.rules.deprecated.disabled = true
+ path = { enabled = false },
+ },
+}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-workspace-disabled/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-workspace-disabled/tombi.toml
index 42fe7cf67..c442f61b1 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-workspace-disabled/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-workspace-disabled/tombi.toml
@@ -2,9 +2,12 @@
"tombi-toml/cargo" = {
lsp = {
document-link = {
- cargo-toml.enabled = true,
- path.enabled = false,
- workspace.enabled = false,
+ # tombi: lint.rules.deprecated.disabled = true
+ cargo-toml = { enabled = true },
+ # tombi: lint.rules.deprecated.disabled = true
+ path = { enabled = false },
+ # tombi: lint.rules.deprecated.disabled = true
+ workspace = { enabled = false },
},
},
}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-workspace-local-cargo-toml-disabled/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-workspace-local-cargo-toml-disabled/tombi.toml
index 964e56115..56b662ecf 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-workspace-local-cargo-toml-disabled/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/cargo-document-link-workspace-local-cargo-toml-disabled/tombi.toml
@@ -2,9 +2,12 @@
"tombi-toml/cargo" = {
lsp = {
document-link = {
- cargo-toml.enabled = false,
- path.enabled = false,
- workspace.enabled = true,
+ # tombi: lint.rules.deprecated.disabled = true
+ cargo-toml = { enabled = false },
+ # tombi: lint.rules.deprecated.disabled = true
+ path = { enabled = false },
+ # tombi: lint.rules.deprecated.disabled = true
+ workspace = { enabled = true },
},
},
}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/pyproject-document-link-pypi-disabled/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/pyproject-document-link-pypi-disabled/tombi.toml
index a7ed0a4cb..f449f95aa 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/pyproject-document-link-pypi-disabled/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/pyproject-document-link-pypi-disabled/tombi.toml
@@ -3,7 +3,8 @@
lsp = {
document-link = {
pypi-org.enabled = false,
- pyproject-toml.enabled = true,
+ # tombi: lint.rules.deprecated.disabled = true
+ pyproject-toml = { enabled = true },
},
},
}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/pyproject-document-link-pyproject-disabled/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/pyproject-document-link-pyproject-disabled/tombi.toml
index 9abedf402..586aed0ae 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/pyproject-document-link-pyproject-disabled/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/pyproject-document-link-pyproject-disabled/tombi.toml
@@ -1,2 +1,7 @@
[extensions]
-"tombi-toml/pyproject" = { lsp.document-link.pyproject-toml.enabled = false }
+"tombi-toml/pyproject" = {
+ lsp.document-link = {
+ # tombi: lint.rules.deprecated.disabled = true
+ pyproject-toml = { enabled = false },
+ },
+}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/tombi-disabled/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/tombi-disabled/tombi.toml
index 814230ea6..01cbcd359 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/tombi-disabled/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/tombi-disabled/tombi.toml
@@ -1,5 +1,10 @@
[extensions]
"tombi-toml/tombi" = {
- lsp.document-link.path.enabled = false,
- lsp.goto-definition.path.enabled = false,
+ lsp = {
+ document-link = {
+ # tombi: lint.rules.deprecated.disabled = true
+ path = { enabled = false },
+ },
+ goto-definition.path.enabled = false,
+ },
}
diff --git a/crates/tombi-lsp/tests/fixtures/extensions/tombi-document-link-all-enabled/tombi.toml b/crates/tombi-lsp/tests/fixtures/extensions/tombi-document-link-all-enabled/tombi.toml
index 4fae39c1e..583f3ba40 100644
--- a/crates/tombi-lsp/tests/fixtures/extensions/tombi-document-link-all-enabled/tombi.toml
+++ b/crates/tombi-lsp/tests/fixtures/extensions/tombi-document-link-all-enabled/tombi.toml
@@ -1,2 +1,7 @@
[extensions]
-"tombi-toml/tombi" = { lsp.document-link.path.enabled = true }
+"tombi-toml/tombi" = {
+ lsp.document-link = {
+ # tombi: lint.rules.deprecated.disabled = true
+ path = { enabled = true },
+ },
+}
From ab31aec58d2d01b60f0305fb3ef099b720a5121d Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Fri, 17 Jul 2026 17:23:13 +0900
Subject: [PATCH 13/15] feat(diagnostic): integrate tombi-diagnostic for error
reporting in validator (#2015)
* feat(diagnostic): integrate tombi-diagnostic for error reporting in validator
* refactor(error): add inline attribute to to_diagnostic method for performance optimization
---
Cargo.lock | 1 +
crates/tombi-schema-store/Cargo.toml | 1 +
crates/tombi-schema-store/src/error.rs | 5 +++
crates/tombi-validator/src/validate/array.rs | 30 +++-----------
crates/tombi-validator/src/validate/table.rs | 42 ++++----------------
5 files changed, 19 insertions(+), 60 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index ce4214d52..09b9f2942 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3186,6 +3186,7 @@ dependencies = [
"tombi-ast",
"tombi-cache",
"tombi-config",
+ "tombi-diagnostic",
"tombi-document-tree",
"tombi-future",
"tombi-hashmap",
diff --git a/crates/tombi-schema-store/Cargo.toml b/crates/tombi-schema-store/Cargo.toml
index 5e23b8824..c5fba68c6 100644
--- a/crates/tombi-schema-store/Cargo.toml
+++ b/crates/tombi-schema-store/Cargo.toml
@@ -22,6 +22,7 @@ tombi-accessor.workspace = true
tombi-ast.workspace = true
tombi-cache.workspace = true
tombi-config.workspace = true
+tombi-diagnostic.workspace = true
tombi-document-tree = { workspace = true, optional = true }
tombi-future.workspace = true
tombi-hashmap = { workspace = true, features = ["serde"] }
diff --git a/crates/tombi-schema-store/src/error.rs b/crates/tombi-schema-store/src/error.rs
index 05c4c01e9..9408f4d1c 100644
--- a/crates/tombi-schema-store/src/error.rs
+++ b/crates/tombi-schema-store/src/error.rs
@@ -140,4 +140,9 @@ impl Error {
Self::CacheError(error) => error.code(),
}
}
+
+ #[inline]
+ pub fn to_diagnostic(&self, range: tombi_text::Range) -> tombi_diagnostic::Diagnostic {
+ tombi_diagnostic::Diagnostic::new_error(self.to_string(), self.code(), range)
+ }
}
diff --git a/crates/tombi-validator/src/validate/array.rs b/crates/tombi-validator/src/validate/array.rs
index 60d76a20d..ff0988ba4 100644
--- a/crates/tombi-validator/src/validate/array.rs
+++ b/crates/tombi-validator/src/validate/array.rs
@@ -169,11 +169,7 @@ async fn validate_array(
{
Ok(overflow_schema) => overflow_schema,
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- array_value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(array_value.range()));
None
}
}
@@ -212,11 +208,7 @@ async fn validate_array(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(value.range()));
}
}
} else if let Some(overflow) = &overflow_schema {
@@ -272,11 +264,7 @@ async fn validate_array(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- array_value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(array_value.range()));
}
}
}
@@ -292,11 +280,7 @@ async fn validate_array(
{
Ok(contains_schema) => contains_schema,
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- array_value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(array_value.range()));
None
}
},
@@ -390,11 +374,7 @@ async fn validate_array(
{
Ok(unevaluated_schema) => unevaluated_schema,
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- array_value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(array_value.range()));
None
}
}
diff --git a/crates/tombi-validator/src/validate/table.rs b/crates/tombi-validator/src/validate/table.rs
index 5d0fcbd46..397e3b399 100644
--- a/crates/tombi-validator/src/validate/table.rs
+++ b/crates/tombi-validator/src/validate/table.rs
@@ -207,11 +207,7 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- key.range() + value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(key.range() + value.range()));
}
}
}
@@ -259,11 +255,7 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- key.range() + value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(key.range() + value.range()));
}
}
}
@@ -343,11 +335,7 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- key.range() + value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(key.range() + value.range()));
}
}
}
@@ -380,11 +368,7 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- key.range() + value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(key.range() + value.range()));
}
}
}
@@ -628,11 +612,7 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- table_value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(table_value.range()));
}
}
}
@@ -702,11 +682,7 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- table_value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(table_value.range()));
}
}
}
@@ -794,11 +770,7 @@ async fn validate_table(
{
Ok(property_name_current_schema) => property_name_current_schema,
Err(err) => {
- total_diagnostics.push(tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- table_value.range(),
- ));
+ total_diagnostics.push(err.to_diagnostic(table_value.range()));
None
}
}
From bad59db0ac94047d48c1bd47ccdad65569dd3f6a Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Fri, 17 Jul 2026 17:36:57 +0900
Subject: [PATCH 14/15] Update error handling (#2016)
* feat(diagnostic): integrate tombi-diagnostic for error reporting in validator
* refactor(error): add inline attribute to to_diagnostic method for performance optimization
* refactor(diagnostic): streamline error handling by using to_diagnostic method across validation modules
---
crates/tombi-validator/src/validate/all_of.rs | 8 ++++---
crates/tombi-validator/src/validate/any_of.rs | 8 ++++---
.../src/validate/if_then_else.rs | 21 +++----------------
.../src/validate/not_schema.rs | 7 +------
crates/tombi-validator/src/validate/one_of.rs | 8 ++++---
5 files changed, 19 insertions(+), 33 deletions(-)
diff --git a/crates/tombi-validator/src/validate/all_of.rs b/crates/tombi-validator/src/validate/all_of.rs
index 35291be5d..a1d33e7c9 100644
--- a/crates/tombi-validator/src/validate/all_of.rs
+++ b/crates/tombi-validator/src/validate/all_of.rs
@@ -47,9 +47,11 @@ where
return Ok(crate::EvaluatedLocations::new());
};
- total_diagnostics.extend(resolution_errors.into_iter().map(|err| {
- tombi_diagnostic::Diagnostic::new_error(err.to_string(), err.code(), value.range())
- }));
+ total_diagnostics.extend(
+ resolution_errors
+ .into_iter()
+ .map(|err| err.to_diagnostic(value.range())),
+ );
for resolved_schema in &resolved_schemas {
match value
diff --git a/crates/tombi-validator/src/validate/any_of.rs b/crates/tombi-validator/src/validate/any_of.rs
index f05ac0bf9..a0444d039 100644
--- a/crates/tombi-validator/src/validate/any_of.rs
+++ b/crates/tombi-validator/src/validate/any_of.rs
@@ -88,9 +88,11 @@ where
}
};
- total_diagnostics.extend(resolution_errors.into_iter().map(|err| {
- tombi_diagnostic::Diagnostic::new_error(err.to_string(), err.code(), value.range())
- }));
+ total_diagnostics.extend(
+ resolution_errors
+ .into_iter()
+ .map(|err| err.to_diagnostic(value.range())),
+ );
let mut total_error = crate::Error::new();
let mut matched = false;
diff --git a/crates/tombi-validator/src/validate/if_then_else.rs b/crates/tombi-validator/src/validate/if_then_else.rs
index 4f7876079..e883aee6a 100644
--- a/crates/tombi-validator/src/validate/if_then_else.rs
+++ b/crates/tombi-validator/src/validate/if_then_else.rs
@@ -47,12 +47,7 @@ where
}
Ok(None) => return Ok(crate::EvaluatedLocations::new()),
Err(err) => {
- return Err(vec![tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- value.range(),
- )]
- .into());
+ return Err(vec![err.to_diagnostic(value.range())].into());
}
};
@@ -77,12 +72,7 @@ where
Ok(None) => {}
Err(err) => {
return merge_if_result(
- Err(vec![tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- value.range(),
- )]
- .into()),
+ Err(vec![err.to_diagnostic(value.range())].into()),
if_result,
);
}
@@ -108,12 +98,7 @@ where
}
Ok(None) => {}
Err(err) => {
- return Err(vec![tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- value.range(),
- )]
- .into());
+ return Err(vec![err.to_diagnostic(value.range())].into());
}
}
}
diff --git a/crates/tombi-validator/src/validate/not_schema.rs b/crates/tombi-validator/src/validate/not_schema.rs
index f5f8de4ca..b8d8d48b2 100644
--- a/crates/tombi-validator/src/validate/not_schema.rs
+++ b/crates/tombi-validator/src/validate/not_schema.rs
@@ -35,12 +35,7 @@ where
),
Ok(None) => false,
Err(err) => {
- return Err(vec![tombi_diagnostic::Diagnostic::new_error(
- err.to_string(),
- err.code(),
- value.range(),
- )]
- .into());
+ return Err(vec![err.to_diagnostic(value.range())].into());
}
};
diff --git a/crates/tombi-validator/src/validate/one_of.rs b/crates/tombi-validator/src/validate/one_of.rs
index a939340d3..7c38644d8 100644
--- a/crates/tombi-validator/src/validate/one_of.rs
+++ b/crates/tombi-validator/src/validate/one_of.rs
@@ -88,9 +88,11 @@ where
}
};
let has_resolution_errors = !resolution_errors.is_empty();
- total_diagnostics.extend(resolution_errors.into_iter().map(|err| {
- tombi_diagnostic::Diagnostic::new_error(err.to_string(), err.code(), value.range())
- }));
+ total_diagnostics.extend(
+ resolution_errors
+ .into_iter()
+ .map(|err| err.to_diagnostic(value.range())),
+ );
let total_count = resolved_schemas.len();
if total_count == 0 {
From bc1c04219ae30d6a77ac3f25889d208ad36285c3 Mon Sep 17 00:00:00 2001
From: ya7010 <47286750+ya7010@users.noreply.github.com>
Date: Sat, 18 Jul 2026 00:52:31 +0900
Subject: [PATCH 15/15] chore: change schemastore diagnostics level (#2017)
* feat(diagnostic): integrate tombi-diagnostic for error reporting in validator
* refactor(error): add inline attribute to to_diagnostic method for performance optimization
* refactor(diagnostic): streamline error handling by using to_diagnostic method across validation modules
* refactor(diagnostic): rename to_diagnostic to to_warning_diagnostic for clarity in error reporting across validation modules
* test: expect warning schema resolution diagnostics
---
.../tests/integration/not_schema.rs | 4 +--
.../integration/schema_resolution_error.rs | 36 +++++++++----------
crates/tombi-schema-store/src/error.rs | 4 +--
crates/tombi-validator/src/validate/all_of.rs | 2 +-
crates/tombi-validator/src/validate/any_of.rs | 2 +-
crates/tombi-validator/src/validate/array.rs | 10 +++---
.../src/validate/if_then_else.rs | 6 ++--
.../src/validate/not_schema.rs | 2 +-
crates/tombi-validator/src/validate/one_of.rs | 2 +-
crates/tombi-validator/src/validate/table.rs | 17 +++++----
10 files changed, 44 insertions(+), 41 deletions(-)
diff --git a/crates/tombi-linter/tests/integration/not_schema.rs b/crates/tombi-linter/tests/integration/not_schema.rs
index 91ab7fe57..5eb2efa1a 100644
--- a/crates/tombi-linter/tests/integration/not_schema.rs
+++ b/crates/tombi-linter/tests/integration/not_schema.rs
@@ -9,13 +9,13 @@ fn schema_path() -> std::path::PathBuf {
test_lint! {
#[test]
- fn test_invalid_ref_reports_error_diagnostic(
+ fn test_invalid_ref_reports_warning_diagnostic(
r#"
value = "foo"
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
diff --git a/crates/tombi-linter/tests/integration/schema_resolution_error.rs b/crates/tombi-linter/tests/integration/schema_resolution_error.rs
index 52136af04..db4991988 100644
--- a/crates/tombi-linter/tests/integration/schema_resolution_error.rs
+++ b/crates/tombi-linter/tests/integration/schema_resolution_error.rs
@@ -15,7 +15,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -27,7 +27,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -39,7 +39,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -51,7 +51,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -63,7 +63,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -75,7 +75,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -87,7 +87,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -99,7 +99,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -111,7 +111,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -123,7 +123,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -145,7 +145,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -157,7 +157,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -170,7 +170,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -183,7 +183,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -196,7 +196,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR },
+ { code: "invalid-json-pointer", level: Level::WARNING },
{ code: "table-strict-additional-keys", level: Level::WARNING }
])
}
@@ -210,7 +210,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -223,7 +223,7 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
@@ -236,6 +236,6 @@ test_lint! {
"#,
SchemaPath(schema_path()),
) -> Diagnostics([
- { code: "invalid-json-pointer", level: Level::ERROR }
+ { code: "invalid-json-pointer", level: Level::WARNING }
])
}
diff --git a/crates/tombi-schema-store/src/error.rs b/crates/tombi-schema-store/src/error.rs
index 9408f4d1c..5f129cc9d 100644
--- a/crates/tombi-schema-store/src/error.rs
+++ b/crates/tombi-schema-store/src/error.rs
@@ -142,7 +142,7 @@ impl Error {
}
#[inline]
- pub fn to_diagnostic(&self, range: tombi_text::Range) -> tombi_diagnostic::Diagnostic {
- tombi_diagnostic::Diagnostic::new_error(self.to_string(), self.code(), range)
+ pub fn to_warning_diagnostic(&self, range: tombi_text::Range) -> tombi_diagnostic::Diagnostic {
+ tombi_diagnostic::Diagnostic::new_warning(self.to_string(), self.code(), range)
}
}
diff --git a/crates/tombi-validator/src/validate/all_of.rs b/crates/tombi-validator/src/validate/all_of.rs
index a1d33e7c9..b26bb35f5 100644
--- a/crates/tombi-validator/src/validate/all_of.rs
+++ b/crates/tombi-validator/src/validate/all_of.rs
@@ -50,7 +50,7 @@ where
total_diagnostics.extend(
resolution_errors
.into_iter()
- .map(|err| err.to_diagnostic(value.range())),
+ .map(|err| err.to_warning_diagnostic(value.range())),
);
for resolved_schema in &resolved_schemas {
diff --git a/crates/tombi-validator/src/validate/any_of.rs b/crates/tombi-validator/src/validate/any_of.rs
index a0444d039..fc0bdcb5f 100644
--- a/crates/tombi-validator/src/validate/any_of.rs
+++ b/crates/tombi-validator/src/validate/any_of.rs
@@ -91,7 +91,7 @@ where
total_diagnostics.extend(
resolution_errors
.into_iter()
- .map(|err| err.to_diagnostic(value.range())),
+ .map(|err| err.to_warning_diagnostic(value.range())),
);
let mut total_error = crate::Error::new();
diff --git a/crates/tombi-validator/src/validate/array.rs b/crates/tombi-validator/src/validate/array.rs
index ff0988ba4..da14d7d22 100644
--- a/crates/tombi-validator/src/validate/array.rs
+++ b/crates/tombi-validator/src/validate/array.rs
@@ -169,7 +169,7 @@ async fn validate_array(
{
Ok(overflow_schema) => overflow_schema,
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(array_value.range()));
+ total_diagnostics.push(err.to_warning_diagnostic(array_value.range()));
None
}
}
@@ -208,7 +208,7 @@ async fn validate_array(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(value.range()));
+ total_diagnostics.push(err.to_warning_diagnostic(value.range()));
}
}
} else if let Some(overflow) = &overflow_schema {
@@ -264,7 +264,7 @@ async fn validate_array(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(array_value.range()));
+ total_diagnostics.push(err.to_warning_diagnostic(array_value.range()));
}
}
}
@@ -280,7 +280,7 @@ async fn validate_array(
{
Ok(contains_schema) => contains_schema,
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(array_value.range()));
+ total_diagnostics.push(err.to_warning_diagnostic(array_value.range()));
None
}
},
@@ -374,7 +374,7 @@ async fn validate_array(
{
Ok(unevaluated_schema) => unevaluated_schema,
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(array_value.range()));
+ total_diagnostics.push(err.to_warning_diagnostic(array_value.range()));
None
}
}
diff --git a/crates/tombi-validator/src/validate/if_then_else.rs b/crates/tombi-validator/src/validate/if_then_else.rs
index e883aee6a..2c0c827c2 100644
--- a/crates/tombi-validator/src/validate/if_then_else.rs
+++ b/crates/tombi-validator/src/validate/if_then_else.rs
@@ -47,7 +47,7 @@ where
}
Ok(None) => return Ok(crate::EvaluatedLocations::new()),
Err(err) => {
- return Err(vec![err.to_diagnostic(value.range())].into());
+ return Err(vec![err.to_warning_diagnostic(value.range())].into());
}
};
@@ -72,7 +72,7 @@ where
Ok(None) => {}
Err(err) => {
return merge_if_result(
- Err(vec![err.to_diagnostic(value.range())].into()),
+ Err(vec![err.to_warning_diagnostic(value.range())].into()),
if_result,
);
}
@@ -98,7 +98,7 @@ where
}
Ok(None) => {}
Err(err) => {
- return Err(vec![err.to_diagnostic(value.range())].into());
+ return Err(vec![err.to_warning_diagnostic(value.range())].into());
}
}
}
diff --git a/crates/tombi-validator/src/validate/not_schema.rs b/crates/tombi-validator/src/validate/not_schema.rs
index b8d8d48b2..d058d8c5d 100644
--- a/crates/tombi-validator/src/validate/not_schema.rs
+++ b/crates/tombi-validator/src/validate/not_schema.rs
@@ -35,7 +35,7 @@ where
),
Ok(None) => false,
Err(err) => {
- return Err(vec![err.to_diagnostic(value.range())].into());
+ return Err(vec![err.to_warning_diagnostic(value.range())].into());
}
};
diff --git a/crates/tombi-validator/src/validate/one_of.rs b/crates/tombi-validator/src/validate/one_of.rs
index 7c38644d8..7ca5fe8bd 100644
--- a/crates/tombi-validator/src/validate/one_of.rs
+++ b/crates/tombi-validator/src/validate/one_of.rs
@@ -91,7 +91,7 @@ where
total_diagnostics.extend(
resolution_errors
.into_iter()
- .map(|err| err.to_diagnostic(value.range())),
+ .map(|err| err.to_warning_diagnostic(value.range())),
);
let total_count = resolved_schemas.len();
diff --git a/crates/tombi-validator/src/validate/table.rs b/crates/tombi-validator/src/validate/table.rs
index 397e3b399..5c0b161e6 100644
--- a/crates/tombi-validator/src/validate/table.rs
+++ b/crates/tombi-validator/src/validate/table.rs
@@ -207,7 +207,7 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(key.range() + value.range()));
+ total_diagnostics.push(err.to_warning_diagnostic(key.range() + value.range()));
}
}
}
@@ -255,7 +255,8 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(key.range() + value.range()));
+ total_diagnostics
+ .push(err.to_warning_diagnostic(key.range() + value.range()));
}
}
}
@@ -335,7 +336,8 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(key.range() + value.range()));
+ total_diagnostics
+ .push(err.to_warning_diagnostic(key.range() + value.range()));
}
}
}
@@ -368,7 +370,8 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(key.range() + value.range()));
+ total_diagnostics
+ .push(err.to_warning_diagnostic(key.range() + value.range()));
}
}
}
@@ -612,7 +615,7 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(table_value.range()));
+ total_diagnostics.push(err.to_warning_diagnostic(table_value.range()));
}
}
}
@@ -682,7 +685,7 @@ async fn validate_table(
}
Ok(None) => {}
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(table_value.range()));
+ total_diagnostics.push(err.to_warning_diagnostic(table_value.range()));
}
}
}
@@ -770,7 +773,7 @@ async fn validate_table(
{
Ok(property_name_current_schema) => property_name_current_schema,
Err(err) => {
- total_diagnostics.push(err.to_diagnostic(table_value.range()));
+ total_diagnostics.push(err.to_warning_diagnostic(table_value.range()));
None
}
}