aboutsummaryrefslogtreecommitdiff
path: root/crates/hir/src/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir/src/diagnostics.rs')
-rw-r--r--crates/hir/src/diagnostics.rs263
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
8use cfg::{CfgExpr, CfgOptions, DnfExpr}; 8use cfg::{CfgExpr, CfgOptions, DnfExpr};
9use hir_def::path::ModPath; 9use hir_def::path::ModPath;
10use hir_expand::{HirFileId, InFile}; 10use hir_expand::{name::Name, HirFileId, InFile};
11use stdx::format_to; 11use stdx::format_to;
12use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; 12use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
13 13
14pub use hir_ty::{ 14pub 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)]
338pub 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
345impl 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)]
389pub 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
396impl 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)]
426pub 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
432impl 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)]
451pub struct MismatchedArgCount {
452 pub file: HirFileId,
453 pub call_expr: AstPtr<ast::Expr>,
454 pub expected: usize,
455 pub found: usize,
456}
457
458impl 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)]
478pub struct RemoveThisSemicolon {
479 pub file: HirFileId,
480 pub expr: AstPtr<ast::Expr>,
481}
482
483impl 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)]
514pub 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
521impl 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)]
540pub struct MissingMatchArms {
541 pub file: HirFileId,
542 pub match_expr: AstPtr<ast::Expr>,
543 pub arms: AstPtr<ast::MatchArmList>,
544}
545
546impl 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)]
562pub struct InternalBailedOut {
563 pub file: HirFileId,
564 pub pat_syntax_ptr: SyntaxNodePtr,
565}
566
567impl 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}