iter 22b.2.5: overriding-non-existent-method diagnostic
This commit is contained in:
@@ -1029,6 +1029,23 @@ fn workspace_error_to_diagnostic(
|
||||
"method": method,
|
||||
})),
|
||||
),
|
||||
W::OverridingNonExistentMethod {
|
||||
class,
|
||||
type_repr,
|
||||
method,
|
||||
} => Some(
|
||||
ailang_check::Diagnostic::error(
|
||||
"overriding-non-existent-method",
|
||||
format!(
|
||||
"instance `{class} {type_repr}` provides body for method `{method}`, but class `{class}` does not declare it"
|
||||
),
|
||||
)
|
||||
.with_ctx(serde_json::json!({
|
||||
"class": class,
|
||||
"type": type_repr,
|
||||
"method": method,
|
||||
})),
|
||||
),
|
||||
W::KindMismatch {
|
||||
class,
|
||||
param,
|
||||
|
||||
@@ -200,6 +200,18 @@ pub enum WorkspaceLoadError {
|
||||
method: String,
|
||||
},
|
||||
|
||||
/// Iter 22b.2: an instance specifies a body for a method name
|
||||
/// that the corresponding class does not declare. Symmetric to
|
||||
/// `MissingMethod` but in the opposite direction.
|
||||
#[error(
|
||||
"instance `{class} {type_repr}` provides body for method `{method}`, but class `{class}` does not declare it"
|
||||
)]
|
||||
OverridingNonExistentMethod {
|
||||
class: String,
|
||||
type_repr: String,
|
||||
method: String,
|
||||
},
|
||||
|
||||
/// Iter 22b.2: class-schema validation. The class parameter
|
||||
/// appears in applied position (e.g. as the head of a
|
||||
/// `Type::Con { name == param, args.len() > 0 }`) inside a method
|
||||
@@ -420,6 +432,23 @@ fn build_registry(
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Symmetric to MissingMethod: an instance must
|
||||
// not specify a body for a method name the class
|
||||
// never declared. Decision 11 forbids ad-hoc
|
||||
// additions to a class's method set at the
|
||||
// instance site.
|
||||
let declared: BTreeSet<&str> =
|
||||
class_def.methods.iter().map(|m| m.name.as_str()).collect();
|
||||
for inst_method in &inst.methods {
|
||||
if !declared.contains(inst_method.name.as_str()) {
|
||||
return Err(WorkspaceLoadError::OverridingNonExistentMethod {
|
||||
class: inst.class.clone(),
|
||||
type_repr: type_repr.clone(),
|
||||
method: inst_method.name.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entries.insert(
|
||||
@@ -890,6 +919,33 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iter 22b.2: an instance that specifies a body for a method
|
||||
/// name the class never declared must fire
|
||||
/// `OverridingNonExistentMethod`. Symmetric counterpart to
|
||||
/// `MissingMethod`: the latter fires when the class declares a
|
||||
/// non-default method that the instance omits; this one fires
|
||||
/// when the instance provides a body the class did not ask for.
|
||||
/// Decision 11 forbids ad-hoc additions to a class's method set
|
||||
/// at the instance site.
|
||||
#[test]
|
||||
fn instance_overriding_nonexistent_method_fires() {
|
||||
let entry = std::path::PathBuf::from(
|
||||
"../../examples/test_22b2_overriding_nonexistent.ail.json",
|
||||
);
|
||||
let err = load_workspace(&entry)
|
||||
.expect_err("must fire overriding-non-existent-method");
|
||||
match err {
|
||||
WorkspaceLoadError::OverridingNonExistentMethod {
|
||||
class, type_repr, method,
|
||||
} => {
|
||||
assert_eq!(class, "Eq");
|
||||
assert_eq!(type_repr, "Int");
|
||||
assert_eq!(method, "ne");
|
||||
}
|
||||
other => panic!("expected OverridingNonExistentMethod, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Iter 22b.2: a class method's `Type::Forall` whose `constraints`
|
||||
/// reference a type variable that is neither bound by `Forall.vars`
|
||||
/// nor equal to the class's `param` must fire
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"schema": "ailang/v0",
|
||||
"name": "test_22b2_overriding_nonexistent",
|
||||
"imports": [],
|
||||
"defs": [
|
||||
{
|
||||
"kind": "class",
|
||||
"name": "Eq",
|
||||
"param": "a",
|
||||
"methods": [
|
||||
{
|
||||
"name": "eq",
|
||||
"type": {
|
||||
"k": "fn",
|
||||
"params": [{ "k": "var", "name": "a" }, { "k": "var", "name": "a" }],
|
||||
"ret": { "k": "con", "name": "Bool" },
|
||||
"effects": []
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Eq",
|
||||
"type": { "k": "con", "name": "Int" },
|
||||
"methods": [
|
||||
{
|
||||
"name": "eq",
|
||||
"body": { "t": "lit", "lit": { "kind": "bool", "value": true } }
|
||||
},
|
||||
{
|
||||
"name": "ne",
|
||||
"body": { "t": "lit", "lit": { "kind": "bool", "value": false } }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user