Warn about unknown fields when editing settings json (#33678)

Closes #30017

* While generating the settings JSON schema, defaults all schema
definitions to reject unknown fields via `additionalProperties: false`.

* Uses `unevaluatedProperties: false` at the top level to check fields
that remain after the settings field names + release stage override
field names.

* Changes json schema version from `draft07` to `draft_2019_09` to have
support for `unevaluatedProperties`.

Release Notes:

- Added warnings for unknown fields when editing `settings.json`.
This commit is contained in:
Michael Sloan 2025-06-30 17:34:25 -06:00 committed by GitHub
parent 7609ca7a8d
commit c74ecb4654
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 86 additions and 40 deletions

View file

@ -1,17 +1,19 @@
use anyhow::Result;
use gpui::App;
use schemars::{JsonSchema, Schema};
use schemars::{JsonSchema, Schema, transform::transform_subschemas};
use serde::{Serialize, de::DeserializeOwned};
use serde_json::Value;
use std::{ops::Range, sync::LazyLock};
use tree_sitter::{Query, StreamingIterator as _};
use util::RangeExt;
/// Parameters that are used when generating some JSON schemas at runtime.
pub struct SettingsJsonSchemaParams<'a> {
pub language_names: &'a [String],
pub font_names: &'a [String],
}
/// Value registered which specifies JSON schemas that are generated at runtime.
pub struct ParameterizedJsonSchema {
pub add_and_get_ref:
fn(&mut schemars::SchemaGenerator, &SettingsJsonSchemaParams, &App) -> schemars::Schema,
@ -19,24 +21,26 @@ pub struct ParameterizedJsonSchema {
inventory::collect!(ParameterizedJsonSchema);
const DEFS_PATH: &str = "#/$defs/";
/// Replaces the JSON schema definition for some type, and returns a reference to it.
pub fn replace_subschema<T: JsonSchema>(
generator: &mut schemars::SchemaGenerator,
schema: schemars::Schema,
) -> schemars::Schema {
const DEFINITIONS_PATH: &str = "#/definitions/";
// The key in definitions may not match T::schema_name() if multiple types have the same name.
// This is a workaround for there being no straightforward way to get the key used for a type -
// see https://github.com/GREsau/schemars/issues/449
let ref_schema = generator.subschema_for::<T>();
if let Some(serde_json::Value::String(definition_pointer)) = ref_schema.get("$ref") {
if let Some(definition_name) = definition_pointer.strip_prefix(DEFINITIONS_PATH) {
if let Some(definition_name) = definition_pointer.strip_prefix(DEFS_PATH) {
generator
.definitions_mut()
.insert(definition_name.to_string(), schema.to_value());
return ref_schema;
} else {
log::error!(
"bug: expected `$ref` field to start with {DEFINITIONS_PATH}, \
"bug: expected `$ref` field to start with {DEFS_PATH}, \
got {definition_pointer}"
);
}
@ -48,7 +52,39 @@ pub fn replace_subschema<T: JsonSchema>(
generator
.definitions_mut()
.insert(schema_name.to_string(), schema.to_value());
Schema::new_ref(format!("{DEFINITIONS_PATH}{schema_name}"))
Schema::new_ref(format!("{DEFS_PATH}{schema_name}"))
}
/// Adds a new JSON schema definition and returns a reference to it. **Panics** if the name is
/// already in use.
pub fn add_new_subschema(
generator: &mut schemars::SchemaGenerator,
name: &str,
schema: Value,
) -> Schema {
let old_definition = generator.definitions_mut().insert(name.to_string(), schema);
assert_eq!(old_definition, None);
schemars::Schema::new_ref(format!("{DEFS_PATH}{name}"))
}
/// Defaults `additionalProperties` to `true`, as if `#[schemars(deny_unknown_fields)]` was on every
/// struct. Skips structs that have `additionalProperties` set (such as if #[serde(flatten)] is used
/// on a map).
#[derive(Clone)]
pub struct DefaultDenyUnknownFields;
impl schemars::transform::Transform for DefaultDenyUnknownFields {
fn transform(&mut self, schema: &mut schemars::Schema) {
if let Some(object) = schema.as_object_mut() {
if object.contains_key("properties")
&& !object.contains_key("additionalProperties")
&& !object.contains_key("unevaluatedProperties")
{
object.insert("additionalProperties".to_string(), false.into());
}
}
transform_subschemas(self, schema);
}
}
pub fn update_value_in_json_text<'a>(