1use crate::{PreprocessorError, Span};
7use pyo3::prelude::*;
8use scarf_parser::{PreprocessorCache, report::Sources};
9use std::fs;
10use yansi::Color;
11
12const NOTE_COLOR: Color = Color::Fixed(81);
13const NOTE_KIND: scarf_parser::report::ReportKind<'static> =
14 scarf_parser::report::ReportKind::Custom("note", NOTE_COLOR);
15
16#[pyclass(eq, from_py_object, module = "scarf_python")]
18#[derive(Clone, PartialEq, Eq, Hash)]
19pub enum ReportKind {
20 Error(),
21 Warning(),
22 Advice(),
23 Note(),
24}
25
26impl From<ReportKind> for scarf_parser::report::ReportKind<'static> {
27 fn from(value: ReportKind) -> Self {
28 match value {
29 ReportKind::Error() => scarf_parser::report::ReportKind::Error,
30 ReportKind::Warning() => scarf_parser::report::ReportKind::Warning,
31 ReportKind::Advice() => scarf_parser::report::ReportKind::Advice,
32 ReportKind::Note() => NOTE_KIND,
33 }
34 }
35}
36
37#[derive(Clone, PartialEq, Eq)]
39struct ReportBase {
40 kind: ReportKind,
41 span: Span,
42 code: String,
43 msg: String,
44}
45
46#[derive(Clone, PartialEq, Eq)]
48struct ReportLabel {
49 span: Span,
50 kind: ReportKind,
51 msg: String,
52}
53
54#[pyclass(eq, from_py_object, module = "scarf_python")]
60#[derive(Clone, PartialEq, Eq)]
61pub struct Report {
62 base: ReportBase,
64 labels: Vec<ReportLabel>,
66 additional_sources: Vec<(String, String)>,
68}
69
70impl Report {
71 fn report(&self) -> scarf_parser::report::Report {
73 let cache = PreprocessorCache::new();
74 let mut report = scarf_parser::report::Report::new(
75 self.base.kind.clone().into(),
76 self.base.span.to_span_ref(&cache),
77 &self.base.code,
78 &self.base.msg,
79 );
80 for label in &self.labels {
81 report = report.with_label(
82 label.span.to_span_ref(&cache),
83 label.kind.clone().into(),
84 &label.msg,
85 )
86 }
87 report
88 }
89
90 fn sources(&self) -> PyResult<impl scarf_parser::report::Cache<String>> {
92 let mut name_content_pairs = self.additional_sources.clone();
93 if !name_content_pairs
95 .iter()
96 .any(|(file_name, _)| *file_name == self.base.span.file)
97 {
98 let base_content = fs::read_to_string(&self.base.span.file)
99 .map_err(|err| {
100 pyo3::exceptions::PyIOError::new_err(err.to_string())
101 })?;
102 name_content_pairs
103 .push((self.base.span.file.clone(), base_content));
104 }
105 for label in &self.labels {
107 if !name_content_pairs
108 .iter()
109 .any(|(file_name, _)| *file_name == label.span.file)
110 {
111 let label_content = fs::read_to_string(&label.span.file)
112 .map_err(|err| {
113 pyo3::exceptions::PyIOError::new_err(err.to_string())
114 })?;
115 name_content_pairs
116 .push((label.span.file.clone(), label_content));
117 }
118 }
119 Ok(name_content_pairs.sources())
120 }
121}
122
123#[pymethods]
124impl Report {
125 #[new]
129 pub fn new(
130 kind: ReportKind,
131 span: Span,
132 code: String,
133 msg: String,
134 ) -> Self {
135 Self {
136 base: ReportBase {
137 kind: kind.clone(),
138 span,
139 code,
140 msg,
141 },
142 labels: vec![],
143 additional_sources: vec![],
144 }
145 }
146
147 pub fn include(&mut self, file_name: String, file_content: String) {
149 self.additional_sources.push((file_name, file_content))
150 }
151
152 pub fn label(&mut self, span: Span, kind: ReportKind, msg: String) {
157 self.labels.push(ReportLabel { span, kind, msg })
158 }
159
160 pub fn print(&self) -> PyResult<()> {
162 let report = self.report();
163 report.print(&mut self.sources()?).map_err(|err| {
164 pyo3::exceptions::PyIOError::new_err(err.to_string())
165 })
166 }
167
168 pub fn eprint(&self) -> PyResult<()> {
170 let report = self.report();
171 report.eprint(&mut self.sources()?).map_err(|err| {
172 pyo3::exceptions::PyIOError::new_err(err.to_string())
173 })
174 }
175}
176
177impl crate::VerboseError {
182 pub(crate) fn report_with_code(&self, code: String) -> PyResult<Report> {
184 let error_span = if self.found.is_none() {
185 let file_len = std::fs::metadata(&self.span.file)
186 .map_err(|err| {
187 pyo3::exceptions::PyIOError::new_err(err.to_string())
188 })?
189 .len();
190 let byte_span = std::ops::Range {
191 start: file_len as usize,
192 end: file_len as usize,
193 };
194 Span {
195 file: self.span.file.clone(),
196 bytes: byte_span.into(),
197 expanded_from: self.span.expanded_from.clone(),
198 included_from: self.span.included_from.clone(),
199 }
200 } else {
201 self.span.clone()
202 };
203 let mut report = Report::new(
204 ReportKind::Error(),
205 error_span.clone(),
206 code,
207 self.to_string(),
208 );
209 report.label(
210 error_span,
211 ReportKind::Error(),
212 match &self.found {
213 Some(tok) => format!("Didn't expect {}", tok),
214 None => "Didn't expect end of input".to_owned(),
215 },
216 );
217 Ok(report)
218 }
219}
220
221#[pymethods]
222impl crate::VerboseError {
223 fn report(&self) -> PyResult<Report> {
225 self.report_with_code("P1".to_owned())
226 }
227}
228
229#[pymethods]
234impl PreprocessorError {
235 pub fn report(&self) -> PyResult<Report> {
236 let report = match self {
237 PreprocessorError::Endif { endif_span } => {
238 let mut report = Report::new(
239 ReportKind::Error(),
240 endif_span.clone(),
241 "PP1".to_owned(),
242 "Unexpected `endif".to_owned(),
243 );
244 report.label(
245 endif_span.clone(),
246 ReportKind::Error(),
247 "Unexpected `endif".to_owned(),
248 );
249 report
250 }
251 PreprocessorError::NoEndif {
252 cond_token,
253 cond_token_span,
254 } => {
255 let mut report = Report::new(
256 ReportKind::Error(),
257 cond_token_span.clone(),
258 "PP2".to_owned(),
259 format!("No matching `endif for {cond_token}"),
260 );
261 report.label(
262 cond_token_span.clone(),
263 ReportKind::Error(),
264 "No matching `endif".to_owned(),
265 );
266 report
267 }
268 PreprocessorError::Elsif { elsif_span } => {
269 let mut report = Report::new(
270 ReportKind::Error(),
271 elsif_span.clone(),
272 "PP3".to_owned(),
273 "Unexpected `elsif".to_owned(),
274 );
275 report.label(
276 elsif_span.clone(),
277 ReportKind::Error(),
278 "Unexpected `elsif".to_owned(),
279 );
280 report
281 }
282 PreprocessorError::Else { else_span } => {
283 let mut report = Report::new(
284 ReportKind::Error(),
285 else_span.clone(),
286 "PP4".to_owned(),
287 "Unexpected `else".to_owned(),
288 );
289 report.label(
290 else_span.clone(),
291 ReportKind::Error(),
292 "Unexpected `else".to_owned(),
293 );
294 report
295 }
296 PreprocessorError::EndKeywords { end_keywords_span } => {
297 let mut report = Report::new(
298 ReportKind::Error(),
299 end_keywords_span.clone(),
300 "PP5".to_owned(),
301 "`end_keywords with no previous `begin_keywords".to_owned(),
302 );
303 report.label(
304 end_keywords_span.clone(),
305 ReportKind::Error(),
306 "No matching `begin_keywords".to_owned(),
307 );
308 report
309 }
310 PreprocessorError::NoEndKeywords {
311 begin_keywords_span,
312 } => {
313 let mut report = Report::new(
314 ReportKind::Error(),
315 begin_keywords_span.clone(),
316 "PP6".to_owned(),
317 "`begin_keywords with no matching `end_keywords".to_owned(),
318 );
319 report.label(
320 begin_keywords_span.clone(),
321 ReportKind::Error(),
322 "No matching `end_keywords".to_owned(),
323 );
324 report
325 }
326 PreprocessorError::InvalidDefineParameter {
327 other_token,
328 other_span,
329 } => {
330 let mut report = Report::new(
331 ReportKind::Error(),
332 other_span.clone(),
333 "PP7".to_owned(),
334 format!(
335 concat!(
336 "Found {}, expected a preprocessor ",
337 "macro parameter/identifier"
338 ),
339 other_token
340 ),
341 );
342 report.label(
343 other_span.clone(),
344 ReportKind::Error(),
345 format!("Unexpected {}", other_token),
346 );
347 report
348 }
349 PreprocessorError::InvalidDefineArgument {
350 other_token,
351 other_span,
352 } => {
353 let mut report = Report::new(
354 ReportKind::Error(),
355 other_span.clone(),
356 "PP7".to_owned(),
357 format!(
358 concat!(
359 "Found {}, expected a comma, ), ",
360 "or a preprocessor macro argument"
361 ),
362 other_token
363 ),
364 );
365 report.label(
366 other_span.clone(),
367 ReportKind::Error(),
368 format!("Unexpected {}", other_token),
369 );
370 report
371 }
372 PreprocessorError::InvalidVersionSpecifier {
373 invalid_version,
374 invalid_version_span,
375 } => {
376 let mut report = Report::new(
377 ReportKind::Error(),
378 invalid_version_span.clone(),
379 "PP8".to_owned(),
380 match invalid_version {
381 crate::Token::StringLiteral { text } => {
382 format!("{} is not a valid version specifier", text)
383 }
384 _ => {
385 format!(
386 "{} isn't a version specifier",
387 invalid_version
388 )
389 }
390 },
391 );
392 report.label(
393 invalid_version_span.clone(),
394 ReportKind::Error(),
395 "Invalid version specifier".to_owned(),
396 );
397 report
398 }
399 PreprocessorError::IncompleteDirective { directive_span } => {
400 let mut report = Report::new(
401 ReportKind::Error(),
402 directive_span.clone(),
403 "PP9".to_owned(),
404 "Incomplete directive".to_owned(),
405 );
406 report.label(
407 directive_span.clone(),
408 ReportKind::Error(),
409 "Expected a complete directive".to_owned(),
410 );
411 report
412 }
413 PreprocessorError::IncompleteDefine {
414 other_token,
415 other_span,
416 } => {
417 let mut report = Report::new(
418 ReportKind::Error(),
419 other_span.clone(),
420 "PP10".to_owned(),
421 format!(
422 "Found {}, expected more in the preprocessor definition",
423 other_token
424 ),
425 );
426 report.label(
427 other_span.clone(),
428 ReportKind::Error(),
429 "Expected more after".to_owned(),
430 );
431 report
432 }
433 PreprocessorError::UndefinedMacro {
434 undefined_name,
435 undefined_span,
436 } => {
437 let mut report = Report::new(
438 ReportKind::Error(),
439 undefined_span.clone(),
440 "PP11".to_owned(),
441 format!("{undefined_name} has not been previously defined"),
442 );
443 report.label(
444 undefined_span.clone(),
445 ReportKind::Error(),
446 "Not previously defined".to_owned(),
447 );
448 report
449 }
450 PreprocessorError::RedefinedMacro {
451 macro_name,
452 redef_span,
453 prev_def_span,
454 } => {
455 let mut report = Report::new(
456 ReportKind::Warning(),
457 redef_span.clone(),
458 "PP12".to_owned(),
459 format!("Redefining {macro_name}"),
460 );
461 report.label(
462 prev_def_span.clone(),
463 ReportKind::Note(),
464 "Previously defined here".to_owned(),
465 );
466 report.label(
467 redef_span.clone(),
468 ReportKind::Warning(),
469 "Redefined here".to_owned(),
470 );
471 report
472 }
473 PreprocessorError::NotPreviouslyDefinedMacro {
474 macro_name,
475 macro_span,
476 } => {
477 let mut report = Report::new(
478 ReportKind::Warning(),
479 macro_span.clone(),
480 "PP13".to_owned(),
481 format!(
482 "Undefining {}, which has not been previously defined",
483 macro_name
484 ),
485 );
486 report.label(
487 macro_span.clone(),
488 ReportKind::Warning(),
489 "Not previously defined".to_owned(),
490 );
491 report
492 }
493 PreprocessorError::DuplicateMacroParameter {
494 define_name,
495 param_name,
496 dup_span,
497 prev_span,
498 } => {
499 let mut report = Report::new(
500 ReportKind::Error(),
501 dup_span.clone(),
502 "PP14".to_owned(),
503 format!(
504 "'{}' was already declared as a macro parameter for {}",
505 param_name, define_name
506 ),
507 );
508 report.label(
509 prev_span.clone(),
510 ReportKind::Note(),
511 "Previously declared here".to_owned(),
512 );
513 report.label(
514 dup_span.clone(),
515 ReportKind::Error(),
516 "Duplicate parameter declaration".to_owned(),
517 );
518 report
519 }
520 PreprocessorError::NoDefaultAfterDefault {
521 default_param,
522 default_param_span,
523 non_default_param,
524 non_default_param_span,
525 } => {
526 let mut report = Report::new(
527 ReportKind::Error(),
528 non_default_param_span.clone(),
529 "PP15".to_owned(),
530 "No default specified for argument after one with a default".to_owned(),
531 );
532 report.label(
533 default_param_span.clone(),
534 ReportKind::Note(),
535 format!("{} had a default specified", default_param),
536 );
537 report.label(
538 non_default_param_span.clone(),
539 ReportKind::Error(),
540 format!("No default specified for {}", non_default_param),
541 );
542 report
543 }
544 PreprocessorError::NoMacroArguments {
545 macro_name,
546 define_span,
547 use_span,
548 } => {
549 let mut report = Report::new(
550 ReportKind::Error(),
551 use_span.clone(),
552 "PP16".to_owned(),
553 format!("Expected arguments when using {macro_name}"),
554 );
555 report.label(
556 define_span.clone(),
557 ReportKind::Note(),
558 "Macro defined here".to_owned(),
559 );
560 report.label(
561 use_span.clone(),
562 ReportKind::Error(),
563 "Expected arguments not present".to_owned(),
564 );
565 report
566 }
567 PreprocessorError::TooManyMacroArguments {
568 macro_name,
569 define_span,
570 use_span,
571 expected,
572 found,
573 } => {
574 let mut report = Report::new(
575 ReportKind::Error(),
576 use_span.clone(),
577 "PP17".to_owned(),
578 format!(
579 "{} expected {} arguments, but {} were provided",
580 macro_name, expected, found
581 ),
582 );
583 report.label(
584 define_span.clone(),
585 ReportKind::Note(),
586 format!("Macro definition expects {expected} arguments"),
587 );
588 report.label(
589 use_span.clone(),
590 ReportKind::Error(),
591 format!("{found} arguments provided"),
592 );
593 report
594 }
595 PreprocessorError::MissingMacroArgument {
596 define_span,
597 use_span,
598 param_name,
599 } => {
600 let mut report = Report::new(
601 ReportKind::Error(),
602 use_span.clone(),
603 "PP18".to_owned(),
604 format!(
605 "'{param_name}' wasn't specified and has no default"
606 ),
607 );
608 report.label(
609 define_span.clone(),
610 ReportKind::Note(),
611 "Macro defined here".to_owned(),
612 );
613 report.label(
614 use_span.clone(),
615 ReportKind::Error(),
616 "Missing argument".to_owned(),
617 );
618 report
619 }
620 PreprocessorError::InvalidIdentifierFormation {
621 param_name,
622 arg_span,
623 } => {
624 let mut report = Report::new(
625 ReportKind::Error(),
626 arg_span.clone(),
627 "PP19".to_owned(),
628 format!(
629 concat!(
630 "The argument for '{}' cannot be ",
631 "concatenated into an identifier"
632 ),
633 param_name
634 ),
635 );
636 report.label(
637 arg_span.clone(),
638 ReportKind::Error(),
639 "No valid conversion to identifier".to_owned(),
640 );
641 report
642 }
643 PreprocessorError::InvalidRelativeTimescales { timescale_span } => {
644 let mut report = Report::new(
645 ReportKind::Error(),
646 timescale_span.clone(),
647 "PP20".to_owned(),
648 "Time precision is larger than the time unit".to_owned(),
649 );
650 report.label(
651 timescale_span.clone(),
652 ReportKind::Error(),
653 "Cannot have delay unit be smaller than precision"
654 .to_owned(),
655 );
656 report
657 }
658 PreprocessorError::IncompleteMacroWithToken {
659 error_token,
660 error_span,
661 } => {
662 let mut report = Report::new(
663 ReportKind::Error(),
664 error_span.clone(),
665 "PP21".to_owned(),
666 format!(
667 "Usage of {} resulted in an incomplete macro",
668 error_token
669 ),
670 );
671 report.label(
672 error_span.clone(),
673 ReportKind::Error(),
674 concat!(
675 "Expected a complete macro argument or ",
676 "escaped newline after"
677 )
678 .to_owned(),
679 );
680 report
681 }
682 PreprocessorError::Include {
683 include_path,
684 include_path_span,
685 read_err,
686 } => {
687 let mut report = Report::new(
688 ReportKind::Error(),
689 include_path_span.clone(),
690 "PP22".to_owned(),
691 format!("Error when reading {}", include_path),
692 );
693 report.label(
694 include_path_span.clone(),
695 ReportKind::Error(),
696 read_err.to_string(),
697 );
698 report
699 }
700 PreprocessorError::IncludeDepth { include_span } => {
701 let mut report = Report::new(
702 ReportKind::Error(),
703 include_span.clone(),
704 "PP23".to_owned(),
705 "Max include depth reached".to_owned(),
706 );
707 report.label(
708 include_span.clone(),
709 ReportKind::Error(),
710 "Check for an `include loop".to_owned(),
711 );
712 report
713 }
714 PreprocessorError::VerboseError { err } => {
715 return err.report_with_code("PP24".to_owned());
716 }
717 };
718 Ok(report)
719 }
720}