Skip to content

Commit 454e1e5

Browse files
authored
fix: improve location of reported signature mismatch validations (#1420)
Changes some diagnostics from errors to notes, for this a new error has been added.
1 parent 096987e commit 454e1e5

File tree

5 files changed

+493
-293
lines changed

5 files changed

+493
-293
lines changed

compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ lazy_static! {
219219
E115, Error, include_str!("./error_codes/E115.md"), // Property in unsupported POU type
220220
E116, Error, include_str!("./error_codes/E116.md"), // Property defined in unsupported variable block
221221
E117, Error, include_str!("./error_codes/E117.md"), // Property with invalid number of GET and/or SET blocks
222+
E118, Info, include_str!("./error_codes/E118.md"), // Follow-up error to 112
222223
);
223224
}
224225

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Signature mismatch
2+
3+
This error is generally a follow-up error to "E112" to give more detailed information about why the error occured. Please use `plc explain E112` get more information about the general causes of these errors.

src/validation/pou.rs

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,13 @@ fn validate_implemented_methods<T: AnnotationMap>(
4444
.flat_map(|it| context.index.find_pou(it.get_qualified_name()));
4545
//XXX(ghha) should this not be combinations instead of tuple_windows?
4646
for (method1, method2) in methods.tuple_windows() {
47-
let diagnostics = validate_method_signature(context.index, method1, method2);
47+
let diagnostics = validate_method_signature(context.index, method1, method2, &pou.name_location);
4848
if !diagnostics.is_empty() {
4949
validator.push_diagnostic(
5050
Diagnostic::new(format!(
51-
"Method `{}` is defined with different signatures in interfaces `{}` and `{}`",
51+
"Method `{}` in `{}` is declared with conflicting signatures in `{}` and `{}`",
5252
method_name,
53+
pou.name,
5354
method1.get_parent_pou_name().unwrap(),
5455
method2.get_parent_pou_name().unwrap()
5556
))
@@ -76,10 +77,11 @@ fn validate_implemented_methods<T: AnnotationMap>(
7677
.filter(|it| it.is_concrete())
7778
.map(|it| context.index.find_pou(it.get_qualified_name()).unwrap())
7879
.next();
79-
// Validate that each concrete method that has an abstract counterpart has the same signature
80+
// Validate that each concrete method which has an abstract counterpart has the same signature
8081
if let Some(method_impl) = concrete {
8182
abstracts.for_each(|(_, method_ref)| {
82-
let diagnostics = validate_method_signature(context.index, method_ref, method_impl);
83+
let diagnostics =
84+
validate_method_signature(context.index, method_ref, method_impl, &pou.name_location);
8385
for diagnostic in diagnostics {
8486
validator.push_diagnostic(diagnostic);
8587
}
@@ -121,7 +123,8 @@ fn validate_method<T: AnnotationMap>(
121123
.flat_map(|it| context.index.find_pou(it.get_qualified_name()))
122124
.collect::<Vec<_>>();
123125
interface_methods.iter().for_each(|method_ref| {
124-
let diagnostics = validate_method_signature(context.index, method_ref, method_impl);
126+
let diagnostics =
127+
validate_method_signature(context.index, method_ref, method_impl, &pou.name_location);
125128
for diagnostic in diagnostics {
126129
validator.push_diagnostic(diagnostic);
127130
}
@@ -284,6 +287,7 @@ pub fn validate_action_container(validator: &mut Validator, implementation: &Imp
284287
pub(super) mod signature_validation {
285288
use itertools::Itertools;
286289
use plc_diagnostics::diagnostics::Diagnostic;
290+
use plc_source::source_location::SourceLocation;
287291

288292
use crate::{
289293
index::{Index, PouIndexEntry},
@@ -294,8 +298,9 @@ pub(super) mod signature_validation {
294298
index: &Index,
295299
method_ref: &PouIndexEntry,
296300
method_impl: &PouIndexEntry,
301+
primary_location: &SourceLocation,
297302
) -> Vec<Diagnostic> {
298-
let ctxt = Context::new(index, method_ref, method_impl);
303+
let ctxt = Context::new(index, method_ref, method_impl, primary_location);
299304
let mut validator = SignatureValidator::new(&ctxt);
300305
validator.validate();
301306
validator.diagnostics
@@ -305,15 +310,17 @@ pub(super) mod signature_validation {
305310
index: &'idx Index,
306311
method_ref: &'idx PouIndexEntry,
307312
method_impl: &'idx PouIndexEntry,
313+
primary_location: &'idx SourceLocation,
308314
}
309315

310316
impl<'idx> Context<'idx> {
311317
fn new(
312318
index: &'idx Index,
313319
method_ref: &'idx PouIndexEntry,
314320
method_impl: &'idx PouIndexEntry,
321+
primary_location: &'idx SourceLocation,
315322
) -> Self {
316-
Self { index, method_ref, method_impl }
323+
Self { index, method_ref, method_impl, primary_location }
317324
}
318325

319326
/// Returns a tuple of the return [DataType]s of the method reference and the method implementation
@@ -350,9 +357,12 @@ pub(super) mod signature_validation {
350357
let (return_type_ref, return_type_impl) = self.context.get_return_types();
351358
if let Some(sub_diagnostics) = self.validate_types(return_type_impl, return_type_ref) {
352359
self.diagnostics.push(
353-
Diagnostic::new("Return types do not match:")
354-
.with_error_code("E112")
355-
.with_sub_diagnostics(sub_diagnostics),
360+
Diagnostic::new(
361+
"Derived methods with conflicting signatures, return types do not match:",
362+
)
363+
.with_location(self.context.primary_location)
364+
.with_error_code("E112")
365+
.with_sub_diagnostics(sub_diagnostics),
356366
);
357367
}
358368
}
@@ -364,7 +374,7 @@ pub(super) mod signature_validation {
364374
let method_name = context.method_ref.get_flat_reference_name();
365375
let parameters_ref = context.index.get_declared_parameters(method_ref.get_name());
366376
let parameters_impl = context.index.get_declared_parameters(method_impl.get_name());
367-
377+
let mut diagnostics = vec![];
368378
// Conditionally skip the first parameter if the return type is aggregate.
369379
// Return types have already been validated and we don't want to show errors
370380
// for internally modified code.
@@ -379,14 +389,14 @@ pub(super) mod signature_validation {
379389
itertools::EitherOrBoth::Both(parameter_ref, parameter_impl) => {
380390
// Name
381391
if parameter_impl.get_name() != parameter_ref.get_name() {
382-
self.diagnostics.push(
392+
diagnostics.push(
383393
Diagnostic::new(format!(
384394
"Expected parameter `{}` but got `{}`",
385395
parameter_ref.get_name(),
386396
parameter_impl.get_name()
387397
))
388-
.with_error_code("E112")
389-
.with_location(&parameter_ref.source_location)
398+
.with_error_code("E118")
399+
.with_secondary_location(&parameter_ref.source_location)
390400
.with_secondary_location(&parameter_impl.source_location),
391401
);
392402
}
@@ -400,63 +410,71 @@ pub(super) mod signature_validation {
400410
.get_effective_type_or_void_by_name(parameter_ref.get_type_name());
401411

402412
if let Some(sub_diagnostics) = self.validate_types(impl_ty, ref_ty) {
403-
self.diagnostics.push(
413+
diagnostics.push(
404414
Diagnostic::new(format!(
405-
"Parameter `{}` has different types in declaration and implemenation:",
415+
"Parameter `{}` has conflicting type declarations:",
406416
parameter_ref.get_name(),
407417
))
408-
.with_error_code("E112")
418+
.with_error_code("E118")
409419
.with_sub_diagnostics(sub_diagnostics)
410-
.with_location(method_impl)
420+
.with_secondary_location(method_impl)
411421
.with_secondary_location(&parameter_ref.source_location)
412422
);
413423
}
414424

415425
// Declaration Type (VAR_INPUT, VAR_OUTPUT, VAR_IN_OUT)
416426
if parameter_impl.get_declaration_type() != parameter_ref.get_declaration_type() {
417-
self.diagnostics.push(
427+
diagnostics.push(
418428
Diagnostic::new(format!(
419429
"Expected parameter `{}` to have `{}` as its declaration type but got `{}`",
420430
parameter_impl.get_name(),
421431
parameter_ref.get_declaration_type().get_inner(),
422432
parameter_impl.get_declaration_type().get_inner(),
423433
))
424-
.with_error_code("E112")
425-
.with_location(method_impl)
434+
.with_error_code("E118")
435+
.with_secondary_location(method_impl)
426436
.with_secondary_location(&parameter_ref.source_location),
427437
);
428438
}
429439
}
430440
itertools::EitherOrBoth::Left(parameter_ref) => {
431-
self.diagnostics.push(
441+
diagnostics.push(
432442
Diagnostic::new(format!(
433443
"Parameter `{} : {}` missing in method `{}`",
434444
parameter_ref.get_name(),
435445
parameter_ref.get_type_name(),
436446
method_name,
437447
))
438-
.with_error_code("E112")
439-
.with_location(method_impl)
448+
.with_error_code("E118")
449+
.with_secondary_location(method_impl)
440450
.with_secondary_location(&parameter_ref.source_location),
441451
);
442452
}
443453
// Exceeding parameters in the POU, which we did not catch in the for loop above because we were only
444454
// iterating over the interface parameters; anyhow any exceeding parameter is considered an error because
445455
// the function signature no longer holds
446456
itertools::EitherOrBoth::Right(parameter_impl) => {
447-
self.diagnostics.push(
457+
diagnostics.push(
448458
Diagnostic::new(format!(
449459
"`{}` has more parameters than the method defined in `{}`",
450460
method_name,
451461
method_ref.get_parent_pou_name().unwrap(),
452462
))
453-
.with_error_code("E112")
454-
.with_location(&parameter_impl.source_location)
463+
.with_error_code("E118")
464+
.with_secondary_location(&parameter_impl.source_location)
455465
.with_secondary_location(method_ref),
456466
);
457467
}
458468
}
459-
})
469+
});
470+
if !diagnostics.is_empty() {
471+
self.diagnostics.push(
472+
Diagnostic::new("Derived methods with conflicting signatures, parameters do not match:")
473+
.with_error_code("E112")
474+
.with_location(self.context.primary_location)
475+
.with_sub_diagnostics(diagnostics),
476+
);
477+
}
460478
}
461479

462480
fn validate_types(&self, left: &DataType, right: &DataType) -> Option<Vec<Diagnostic>> {
@@ -516,12 +534,12 @@ pub(super) mod signature_validation {
516534

517535
fn create_diagnostic(&self, left: &str, right: &str) -> Diagnostic {
518536
Diagnostic::new(format!(
519-
"Type `{right}` declared in `{}` but `{}` implemented type `{left}`",
537+
"Type `{right}` declared in `{}` but `{}` declared type `{left}`",
520538
self.context.method_ref.get_name(),
521539
self.context.method_impl.get_name()
522540
))
523-
.with_error_code("E112")
524-
.with_location(self.context.method_impl)
541+
.with_error_code("E118")
542+
.with_secondary_location(self.context.method_impl)
525543
.with_secondary_location(self.context.method_ref)
526544
}
527545

@@ -552,8 +570,8 @@ pub(super) mod signature_validation {
552570
"Expected array of type `{}` but got `{}`",
553571
right_name, left_name
554572
))
555-
.with_error_code("E112")
556-
.with_location(method_impl)
573+
.with_error_code("E118")
574+
.with_secondary_location(method_impl)
557575
.with_secondary_location(method_ref),
558576
)
559577
};
@@ -580,7 +598,7 @@ pub(super) mod signature_validation {
580598
"Array range declared as `[{}..{}]` but implemented as `[{}..{}]`",
581599
r_range.start, r_range.end, l_range.start, l_range.end
582600
))
583-
.with_error_code("E112")
601+
.with_error_code("E118")
584602
.with_secondary_location(left.location.clone())
585603
.with_secondary_location(right.location.clone()),
586604
),
@@ -597,8 +615,8 @@ pub(super) mod signature_validation {
597615
if l_dims.len() == 1 { "" } else { "s" },
598616
r_dims.len()
599617
))
600-
.with_error_code("E112")
601-
.with_location(right.location.clone())
618+
.with_error_code("E118")
619+
.with_secondary_location(right.location.clone())
602620
.with_secondary_location(context.method_impl)
603621
.with_secondary_location(left.location.clone())
604622
.with_secondary_location(context.method_ref),
@@ -627,8 +645,8 @@ pub(super) mod signature_validation {
627645
"Expected string of length `{}` but got string of length `{}`",
628646
right_length, left_length
629647
))
630-
.with_error_code("E112")
631-
.with_location(method_impl)
648+
.with_error_code("E118")
649+
.with_secondary_location(method_impl)
632650
.with_secondary_location(method_ref),
633651
);
634652
});

0 commit comments

Comments
 (0)