diff options
Diffstat (limited to 'crates/hir/src/diagnostics.rs')
-rw-r--r-- | crates/hir/src/diagnostics.rs | 263 |
1 files changed, 258 insertions, 5 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index e888fc23b..26dbcd86a 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs | |||
@@ -7,15 +7,12 @@ use std::any::Any; | |||
7 | 7 | ||
8 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; | 8 | use cfg::{CfgExpr, CfgOptions, DnfExpr}; |
9 | use hir_def::path::ModPath; | 9 | use hir_def::path::ModPath; |
10 | use hir_expand::{HirFileId, InFile}; | 10 | use hir_expand::{name::Name, HirFileId, InFile}; |
11 | use stdx::format_to; | 11 | use stdx::format_to; |
12 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; | 12 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; |
13 | 13 | ||
14 | pub use hir_ty::{ | 14 | pub use hir_ty::{ |
15 | diagnostics::{ | 15 | diagnostics::IncorrectCase, |
16 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, | ||
17 | MissingOkOrSomeInTailExpr, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, | ||
18 | }, | ||
19 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder}, | 16 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder}, |
20 | }; | 17 | }; |
21 | 18 | ||
@@ -325,3 +322,259 @@ impl Diagnostic for MissingUnsafe { | |||
325 | self | 322 | self |
326 | } | 323 | } |
327 | } | 324 | } |
325 | |||
326 | // Diagnostic: missing-structure-fields | ||
327 | // | ||
328 | // This diagnostic is triggered if record lacks some fields that exist in the corresponding structure. | ||
329 | // | ||
330 | // Example: | ||
331 | // | ||
332 | // ```rust | ||
333 | // struct A { a: u8, b: u8 } | ||
334 | // | ||
335 | // let a = A { a: 10 }; | ||
336 | // ``` | ||
337 | #[derive(Debug)] | ||
338 | pub struct MissingFields { | ||
339 | pub file: HirFileId, | ||
340 | pub field_list_parent: AstPtr<ast::RecordExpr>, | ||
341 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, | ||
342 | pub missed_fields: Vec<Name>, | ||
343 | } | ||
344 | |||
345 | impl Diagnostic for MissingFields { | ||
346 | fn code(&self) -> DiagnosticCode { | ||
347 | DiagnosticCode("missing-structure-fields") | ||
348 | } | ||
349 | fn message(&self) -> String { | ||
350 | let mut buf = String::from("Missing structure fields:\n"); | ||
351 | for field in &self.missed_fields { | ||
352 | format_to!(buf, "- {}\n", field); | ||
353 | } | ||
354 | buf | ||
355 | } | ||
356 | |||
357 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
358 | InFile { | ||
359 | file_id: self.file, | ||
360 | value: self | ||
361 | .field_list_parent_path | ||
362 | .clone() | ||
363 | .map(SyntaxNodePtr::from) | ||
364 | .unwrap_or_else(|| self.field_list_parent.clone().into()), | ||
365 | } | ||
366 | } | ||
367 | |||
368 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
369 | self | ||
370 | } | ||
371 | } | ||
372 | |||
373 | // Diagnostic: missing-pat-fields | ||
374 | // | ||
375 | // This diagnostic is triggered if pattern lacks some fields that exist in the corresponding structure. | ||
376 | // | ||
377 | // Example: | ||
378 | // | ||
379 | // ```rust | ||
380 | // struct A { a: u8, b: u8 } | ||
381 | // | ||
382 | // let a = A { a: 10, b: 20 }; | ||
383 | // | ||
384 | // if let A { a } = a { | ||
385 | // // ... | ||
386 | // } | ||
387 | // ``` | ||
388 | #[derive(Debug)] | ||
389 | pub struct MissingPatFields { | ||
390 | pub file: HirFileId, | ||
391 | pub field_list_parent: AstPtr<ast::RecordPat>, | ||
392 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, | ||
393 | pub missed_fields: Vec<Name>, | ||
394 | } | ||
395 | |||
396 | impl Diagnostic for MissingPatFields { | ||
397 | fn code(&self) -> DiagnosticCode { | ||
398 | DiagnosticCode("missing-pat-fields") | ||
399 | } | ||
400 | fn message(&self) -> String { | ||
401 | let mut buf = String::from("Missing structure fields:\n"); | ||
402 | for field in &self.missed_fields { | ||
403 | format_to!(buf, "- {}\n", field); | ||
404 | } | ||
405 | buf | ||
406 | } | ||
407 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
408 | InFile { | ||
409 | file_id: self.file, | ||
410 | value: self | ||
411 | .field_list_parent_path | ||
412 | .clone() | ||
413 | .map(SyntaxNodePtr::from) | ||
414 | .unwrap_or_else(|| self.field_list_parent.clone().into()), | ||
415 | } | ||
416 | } | ||
417 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
418 | self | ||
419 | } | ||
420 | } | ||
421 | |||
422 | // Diagnostic: replace-filter-map-next-with-find-map | ||
423 | // | ||
424 | // This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`. | ||
425 | #[derive(Debug)] | ||
426 | pub struct ReplaceFilterMapNextWithFindMap { | ||
427 | pub file: HirFileId, | ||
428 | /// This expression is the whole method chain up to and including `.filter_map(..).next()`. | ||
429 | pub next_expr: AstPtr<ast::Expr>, | ||
430 | } | ||
431 | |||
432 | impl Diagnostic for ReplaceFilterMapNextWithFindMap { | ||
433 | fn code(&self) -> DiagnosticCode { | ||
434 | DiagnosticCode("replace-filter-map-next-with-find-map") | ||
435 | } | ||
436 | fn message(&self) -> String { | ||
437 | "replace filter_map(..).next() with find_map(..)".to_string() | ||
438 | } | ||
439 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
440 | InFile { file_id: self.file, value: self.next_expr.clone().into() } | ||
441 | } | ||
442 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
443 | self | ||
444 | } | ||
445 | } | ||
446 | |||
447 | // Diagnostic: mismatched-arg-count | ||
448 | // | ||
449 | // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. | ||
450 | #[derive(Debug)] | ||
451 | pub struct MismatchedArgCount { | ||
452 | pub file: HirFileId, | ||
453 | pub call_expr: AstPtr<ast::Expr>, | ||
454 | pub expected: usize, | ||
455 | pub found: usize, | ||
456 | } | ||
457 | |||
458 | impl Diagnostic for MismatchedArgCount { | ||
459 | fn code(&self) -> DiagnosticCode { | ||
460 | DiagnosticCode("mismatched-arg-count") | ||
461 | } | ||
462 | fn message(&self) -> String { | ||
463 | let s = if self.expected == 1 { "" } else { "s" }; | ||
464 | format!("Expected {} argument{}, found {}", self.expected, s, self.found) | ||
465 | } | ||
466 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
467 | InFile { file_id: self.file, value: self.call_expr.clone().into() } | ||
468 | } | ||
469 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
470 | self | ||
471 | } | ||
472 | fn is_experimental(&self) -> bool { | ||
473 | true | ||
474 | } | ||
475 | } | ||
476 | |||
477 | #[derive(Debug)] | ||
478 | pub struct RemoveThisSemicolon { | ||
479 | pub file: HirFileId, | ||
480 | pub expr: AstPtr<ast::Expr>, | ||
481 | } | ||
482 | |||
483 | impl Diagnostic for RemoveThisSemicolon { | ||
484 | fn code(&self) -> DiagnosticCode { | ||
485 | DiagnosticCode("remove-this-semicolon") | ||
486 | } | ||
487 | |||
488 | fn message(&self) -> String { | ||
489 | "Remove this semicolon".to_string() | ||
490 | } | ||
491 | |||
492 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
493 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
494 | } | ||
495 | |||
496 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
497 | self | ||
498 | } | ||
499 | } | ||
500 | |||
501 | // Diagnostic: missing-ok-or-some-in-tail-expr | ||
502 | // | ||
503 | // This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`, | ||
504 | // or if a block that should return `Option` returns a value not wrapped in `Some`. | ||
505 | // | ||
506 | // Example: | ||
507 | // | ||
508 | // ```rust | ||
509 | // fn foo() -> Result<u8, ()> { | ||
510 | // 10 | ||
511 | // } | ||
512 | // ``` | ||
513 | #[derive(Debug)] | ||
514 | pub struct MissingOkOrSomeInTailExpr { | ||
515 | pub file: HirFileId, | ||
516 | pub expr: AstPtr<ast::Expr>, | ||
517 | // `Some` or `Ok` depending on whether the return type is Result or Option | ||
518 | pub required: String, | ||
519 | } | ||
520 | |||
521 | impl Diagnostic for MissingOkOrSomeInTailExpr { | ||
522 | fn code(&self) -> DiagnosticCode { | ||
523 | DiagnosticCode("missing-ok-or-some-in-tail-expr") | ||
524 | } | ||
525 | fn message(&self) -> String { | ||
526 | format!("wrap return expression in {}", self.required) | ||
527 | } | ||
528 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
529 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
530 | } | ||
531 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
532 | self | ||
533 | } | ||
534 | } | ||
535 | |||
536 | // Diagnostic: missing-match-arm | ||
537 | // | ||
538 | // This diagnostic is triggered if `match` block is missing one or more match arms. | ||
539 | #[derive(Debug)] | ||
540 | pub struct MissingMatchArms { | ||
541 | pub file: HirFileId, | ||
542 | pub match_expr: AstPtr<ast::Expr>, | ||
543 | pub arms: AstPtr<ast::MatchArmList>, | ||
544 | } | ||
545 | |||
546 | impl Diagnostic for MissingMatchArms { | ||
547 | fn code(&self) -> DiagnosticCode { | ||
548 | DiagnosticCode("missing-match-arm") | ||
549 | } | ||
550 | fn message(&self) -> String { | ||
551 | String::from("Missing match arm") | ||
552 | } | ||
553 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
554 | InFile { file_id: self.file, value: self.match_expr.clone().into() } | ||
555 | } | ||
556 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
557 | self | ||
558 | } | ||
559 | } | ||
560 | |||
561 | #[derive(Debug)] | ||
562 | pub struct InternalBailedOut { | ||
563 | pub file: HirFileId, | ||
564 | pub pat_syntax_ptr: SyntaxNodePtr, | ||
565 | } | ||
566 | |||
567 | impl Diagnostic for InternalBailedOut { | ||
568 | fn code(&self) -> DiagnosticCode { | ||
569 | DiagnosticCode("internal:match-check-bailed-out") | ||
570 | } | ||
571 | fn message(&self) -> String { | ||
572 | format!("Internal: match check bailed out") | ||
573 | } | ||
574 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
575 | InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() } | ||
576 | } | ||
577 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
578 | self | ||
579 | } | ||
580 | } | ||