Repository navigation
Commit acdf563
Show signature mismatches as the actual and expected declaration, each under its location (#9140)
* Show signature mismatches as the actual and expected declaration, each under its location
Why:
A signature mismatch printed every enclosing module in full, then the two
declarations, then their locations as "file:line:" lines with a label on
the next line that read like headings with nothing under them, and the
reasons without a break. Which text was where had to be matched up by
hand, and declarations that read the same looked like a declaration not
including itself. The type checker's errors separate their parts with
blank lines and indent the content under a lead-in.
Includemod.report_error now leaves out a module or module type comparison
when an item inside it follows, so that only the items that differ are
printed, each after a line naming where it is: "In module P:", "In module
type P.T:", "In the result of functor P:", "In the parameter X of functor
P:", or "At position ..." otherwise, once for items at the same position.
Missing items are listed in the order of the expected signature, and in a
functor's parameter, where inclusion is reversed, the implementation's side
is still the actual declaration.
Diagnostic modified (type declaration in an interface):
Example:
// Foo.res
type t = {a: int, b: int}
// Foo.resi
type t = {x: int, y: int}
Before (diagnostic):
The implementation Foo.res
does not match the interface foo.cmi:
Type declarations do not match:
type t = {a: int, b: int}
is not included in
type t = {x: int, y: int}
Foo.resi:1:1-25:
Expected declaration
Foo.res:1:1-25:
Actual declaration
Fields number 1 have different names, a and x.
After (diagnostic):
The implementation Foo.res
does not match the interface foo.cmi:
Actual declaration (Foo.res:1:1-25):
type t = {a: int, b: int}
Expected declaration (Foo.resi:1:1-25):
type t = {x: int, y: int}
Fields number 1 have different names, a and x.
Diagnostic modified (value in a nested module):
Example:
// Foo.res
module Inner = {
let value = "not an int"
}
// Foo.resi
module Inner: {
let value: int
}
Before (diagnostic):
In module Inner:
Modules do not match:
{
let value: string
}
is not included in
{
let value: int
}
In module Inner:
Values do not match: let value: string is not included in let value: int
Foo.resi:2:3-16:
Expected declaration
Foo.res:2:7-11:
Actual declaration
After (diagnostic):
In module Inner:
Actual declaration (Foo.res:2:7-11):
let value: string
Expected declaration (Foo.resi:2:3-16):
let value: int
Diagnostic modified (functor result):
Example:
module type S = {
type t
}
module M: {
module F: (X: S) => S with type t = X.t
} = {
module F = (X: S) => {
type t = int
}
}
Before (diagnostic):
Signature mismatch:
Modules do not match:
…
In module F:
Modules do not match:
…
At position module F: (X) => <here>
Modules do not match:
…
At position module F: (X) => <here>
Type declarations do not match:
type t = int
is not included in
type t = X.t
functor_signature_mismatch.res:6:36:
Expected declaration
functor_signature_mismatch.res:9:5-16:
Actual declaration
After (diagnostic):
Signature mismatch:
In the result of functor F:
Actual declaration (functor_signature_mismatch.res:9:5-16):
type t = int
Expected declaration (functor_signature_mismatch.res:6:36):
type t = X.t
Diagnostic modified (functor parameter):
Example:
module M: {
module F: (X: {let a: int}) => {}
} = {
module F = (X: {let a: string}) => {}
}
Before (diagnostic):
Signature mismatch:
Modules do not match:
… the module, then the functor, each pair printed in full
At position module F: (X: <here>) => ...
Values do not match: let a: int is not included in let a: string
inclusion_layout_functor_parameter.res:12:7-19:
Expected declaration
inclusion_layout_functor_parameter.res:6:7-16:
Actual declaration
After (diagnostic):
In the parameter X of functor F:
Actual declaration (inclusion_layout_functor_parameter.res:12:7-19):
let a: string
Expected declaration (inclusion_layout_functor_parameter.res:6:7-16):
let a: int
Diagnostic modified (missing values):
Example:
module M: {
module N: {
let a: int
let b: string
}
} = {
module N = {}
}
Before (diagnostic):
Signature mismatch:
Modules do not match:
… the module, then module N, each pair printed in full
In module N:
The value `b` is required but not provided
inclusion_layout_missing_values.res:5:5-17:
Expected declaration
In module N:
The value `a` is required but not provided
inclusion_layout_missing_values.res:4:5-14:
Expected declaration
After (diagnostic):
In module N:
The value `a` is required but not provided.
Expected declaration (inclusion_layout_missing_values.res:4:5-14):
let a: int
The value `b` is required but not provided.
Expected declaration (inclusion_layout_missing_values.res:5:5-17):
let b: string
Diagnostic modified (runtime representation reason):
Before (diagnostic):
Their runtime representations differ:
the first declaration states values whose typeof is "object",
the second declaration states values whose typeof is "string".
After (diagnostic):
Their runtime representations differ:
The actual declaration states values whose typeof is "object".
The expected declaration states values whose typeof is "string".
Subst.extension_constructor dropped the location of an extension
constructor when writing a .cmi, unlike every other declaration. It keeps it
now, so the expected side of an extension mismatch has a position, and
reanalyze also records a reference to an exception declared in an interface.
Diagnostic modified (extension constructor declared in an interface):
Example:
// Foo.res
type t += Boom(string)
// Foo.resi
type t += Boom(int)
Before (diagnostic):
Foo.res:
Expected declaration
After (diagnostic):
Expected declaration (Foo.resi:4:3-13):
type t += Boom(int)
Review: Is anything dropped that was useful? The enclosing modules printed in full repeated the items below them; the path line names where the items are. A big part was printed as "..." for the same reason, so the size check is gone.
Review: Why "actual" and "expected" and not "implementation" and "interface"? The same errors are reported for module type constraints and functor arguments within one file, where there is no interface.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kbf3gLpf7RN1MiRcubPgRn
Signed-off-by: Christoph Knittel <ck@cca.io>
* Label the two sides when runtime representations differ
Why:
"The actual declaration states values whose typeof is "object"" reads
clumsily: a declaration doesn't state values. The two lines now use the
labels of the declaration blocks above them, followed by what each side
states.
Diagnostic modified (runtime representation reason):
Before (diagnostic):
Their runtime representations differ:
The actual declaration states values whose typeof is "object".
The expected declaration states values whose typeof is "string".
After (diagnostic):
Their runtime representations differ:
Actual declaration: typeof "object"
Expected declaration: typeof "string"
Diagnostic modified (@notundefined reason):
Before (diagnostic):
Their runtime representations differ:
The expected declaration is @notundefined,
but the actual declaration can hold undefined.
After (diagnostic):
Their runtime representations differ:
Actual declaration: can hold undefined
Expected declaration: @notundefined
Mapping modified (what a declaration states, Ast_untagged_variants.describe_declared_values):
Before (representation):
@instanceof("Dog") ↦ instances of Dog
@instanceof("Dog") of a subclass ↦ instances of Dog, a subclass of Animal
arrays ↦ arrays
@typeof("string") ↦ values whose typeof is "string"
nothing ↦ no runtime class or typeof
After (representation):
@instanceof("Dog") ↦ instances of Dog
@instanceof("Dog") of a subclass ↦ instances of Dog, a subclass of Animal
arrays ↦ arrays
@typeof("string") ↦ typeof "string"
nothing ↦ no stated class or typeof
Review: Where do the labels come from? From the side names the caller passes, without their article: "the actual" and "declaration" make "Actual declaration"; the type definition errors of Typedecl get "Original definition" and "This definition".
Review: Why no period? The lines are labels with a value, like the declaration labels, not sentences.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kbf3gLpf7RN1MiRcubPgRn
Signed-off-by: Christoph Knittel <ck@cca.io>
* Keep the module type comparison around a permutation of its fields
Why:
Codex review of #9140: when two module types have the same fields in
another order, the innermost symptom is the permutation, which shows no
declarations, and leaving out the module type comparison around it left
only "Illegal permutation of structure fields". A comparison is now left
out only when a later symptom shows the declarations that differ.
The parser gave a module type declaration in a signature no location,
unlike one in a structure, so the expected side printed without a
position. It now spans from the name to the end, as in a structure.
Diagnostic modified (fields of a module type in another order):
Example:
module M: {
module type T = {
let a: int
let b: int
}
} = {
module type T = {
let b: int
let a: int
}
}
Before (diagnostic):
Signature mismatch:
In module type T:
Illegal permutation of structure fields
After (diagnostic):
Signature mismatch:
Actual declaration (include_modtype_permutation.res:7:15-10:3):
module type T = {
let b: int
let a: int
}
Expected declaration (include_modtype_permutation.res:2:15-5:3):
module type T = {
let a: int
let b: int
}
In module type T:
Illegal permutation of structure fields
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kbf3gLpf7RN1MiRcubPgRn
Signed-off-by: Christoph Knittel <ck@cca.io>
* Report the actual side of a module type found by comparing the other way
Why:
Codex review of #9140: two module types must be equal, and
check_modtype_equiv also includes the expected one in the actual one. An
item only the actual module type has is found by that reverse check, but
the error called it required and its declaration the expected one. This
was mislabelled before too, and the comparison of the two module types
printed above it was the only hint, which the new layout leaves out.
The reverse check now adds a Reverse position to the path. Like a
functor parameter it swaps the sides, and it is not printed.
Diagnostic modified (an item only the actual module type has):
Example:
module M: {
module type T = {
let a: int
}
} = {
module type T = {
let a: int
let b: int
}
}
Before (diagnostic):
In module type T:
The value `b` is required but not provided.
Expected declaration (inclusion_layout_module_type_extra.res:10:5-14):
let b: int
After (diagnostic):
In module type T:
The value `b` is only present in the actual declaration.
Actual declaration (inclusion_layout_module_type_extra.res:10:5-14):
let b: int
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kbf3gLpf7RN1MiRcubPgRn
Signed-off-by: Christoph Knittel <ck@cca.io>
---------
Signed-off-by: Christoph Knittel <ck@cca.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>1 parent 25e3dfb commit acdf563
81 files changed
Lines changed: 741 additions & 801 deletions
File tree
- changes
- compiler
- ml
- syntax/src
- tests
- analysis_tests/tests-reanalyze/deadcode/expected
- build_tests
- super_errors_multi/expected
- super_errors
- expected
- fixtures
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
322 | 322 | | |
323 | 323 | | |
324 | 324 | | |
325 | | - | |
326 | | - | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
327 | 328 | | |
328 | 329 | | |
329 | 330 | | |
330 | 331 | | |
331 | 332 | | |
332 | | - | |
333 | | - | |
334 | | - | |
| 333 | + | |
| 334 | + | |
335 | 335 | | |
336 | 336 | | |
337 | 337 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
191 | 191 | | |
192 | 192 | | |
193 | 193 | | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
194 | 204 | | |
195 | 205 | | |
196 | 206 | | |
| |||
225 | 235 | | |
226 | 236 | | |
227 | 237 | | |
228 | | - | |
229 | | - | |
| 238 | + | |
| 239 | + | |
230 | 240 | | |
231 | | - | |
| 241 | + | |
232 | 242 | | |
233 | 243 | | |
234 | 244 | | |
235 | 245 | | |
236 | 246 | | |
237 | | - | |
238 | | - | |
239 | | - | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
240 | 251 | | |
241 | | - | |
242 | | - | |
243 | | - | |
| 252 | + | |
| 253 | + | |
244 | 254 | | |
245 | 255 | | |
246 | 256 | | |
247 | | - | |
248 | | - | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
249 | 263 | | |
250 | 264 | | |
251 | 265 | | |
| |||
390 | 404 | | |
391 | 405 | | |
392 | 406 | | |
393 | | - | |
394 | | - | |
| 407 | + | |
| 408 | + | |
395 | 409 | | |
396 | 410 | | |
397 | 411 | | |
| |||
0 commit comments