aboutsummaryrefslogtreecommitdiff
path: root/crates/hir
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir')
-rw-r--r--crates/hir/Cargo.toml1
-rw-r--r--crates/hir/src/diagnostics.rs372
-rw-r--r--crates/hir/src/diagnostics_sink.rs109
-rw-r--r--crates/hir/src/lib.rs236
4 files changed, 692 insertions, 26 deletions
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index 560b15238..7c148fd40 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -16,6 +16,7 @@ either = "1.5.3"
16arrayvec = "0.7" 16arrayvec = "0.7"
17itertools = "0.10.0" 17itertools = "0.10.0"
18smallvec = "1.4.0" 18smallvec = "1.4.0"
19once_cell = "1"
19 20
20stdx = { path = "../stdx", version = "0.0.0" } 21stdx = { path = "../stdx", version = "0.0.0" }
21syntax = { path = "../syntax", version = "0.0.0" } 22syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 2cdbd172a..8a7c3a4fd 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -7,17 +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 crate::diagnostics_sink::{
15 diagnostics::{ 15 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
16 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms,
17 MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon,
18 ReplaceFilterMapNextWithFindMap,
19 },
20 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder},
21}; 16};
22 17
23// Diagnostic: unresolved-module 18// Diagnostic: unresolved-module
@@ -251,3 +246,364 @@ impl Diagnostic for UnimplementedBuiltinMacro {
251 self 246 self
252 } 247 }
253} 248}
249
250// Diagnostic: no-such-field
251//
252// This diagnostic is triggered if created structure does not have field provided in record.
253#[derive(Debug)]
254pub struct NoSuchField {
255 pub file: HirFileId,
256 pub field: AstPtr<ast::RecordExprField>,
257}
258
259impl Diagnostic for NoSuchField {
260 fn code(&self) -> DiagnosticCode {
261 DiagnosticCode("no-such-field")
262 }
263
264 fn message(&self) -> String {
265 "no such field".to_string()
266 }
267
268 fn display_source(&self) -> InFile<SyntaxNodePtr> {
269 InFile::new(self.file, self.field.clone().into())
270 }
271
272 fn as_any(&self) -> &(dyn Any + Send + 'static) {
273 self
274 }
275}
276
277// Diagnostic: break-outside-of-loop
278//
279// This diagnostic is triggered if the `break` keyword is used outside of a loop.
280#[derive(Debug)]
281pub struct BreakOutsideOfLoop {
282 pub file: HirFileId,
283 pub expr: AstPtr<ast::Expr>,
284}
285
286impl Diagnostic for BreakOutsideOfLoop {
287 fn code(&self) -> DiagnosticCode {
288 DiagnosticCode("break-outside-of-loop")
289 }
290 fn message(&self) -> String {
291 "break outside of loop".to_string()
292 }
293 fn display_source(&self) -> InFile<SyntaxNodePtr> {
294 InFile { file_id: self.file, value: self.expr.clone().into() }
295 }
296 fn as_any(&self) -> &(dyn Any + Send + 'static) {
297 self
298 }
299}
300
301// Diagnostic: missing-unsafe
302//
303// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
304#[derive(Debug)]
305pub struct MissingUnsafe {
306 pub file: HirFileId,
307 pub expr: AstPtr<ast::Expr>,
308}
309
310impl Diagnostic for MissingUnsafe {
311 fn code(&self) -> DiagnosticCode {
312 DiagnosticCode("missing-unsafe")
313 }
314 fn message(&self) -> String {
315 format!("This operation is unsafe and requires an unsafe function or block")
316 }
317 fn display_source(&self) -> InFile<SyntaxNodePtr> {
318 InFile { file_id: self.file, value: self.expr.clone().into() }
319 }
320 fn as_any(&self) -> &(dyn Any + Send + 'static) {
321 self
322 }
323}
324
325// Diagnostic: missing-structure-fields
326//
327// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
328//
329// Example:
330//
331// ```rust
332// struct A { a: u8, b: u8 }
333//
334// let a = A { a: 10 };
335// ```
336#[derive(Debug)]
337pub struct MissingFields {
338 pub file: HirFileId,
339 pub field_list_parent: AstPtr<ast::RecordExpr>,
340 pub field_list_parent_path: Option<AstPtr<ast::Path>>,
341 pub missed_fields: Vec<Name>,
342}
343
344impl Diagnostic for MissingFields {
345 fn code(&self) -> DiagnosticCode {
346 DiagnosticCode("missing-structure-fields")
347 }
348 fn message(&self) -> String {
349 let mut buf = String::from("Missing structure fields:\n");
350 for field in &self.missed_fields {
351 format_to!(buf, "- {}\n", field);
352 }
353 buf
354 }
355
356 fn display_source(&self) -> InFile<SyntaxNodePtr> {
357 InFile {
358 file_id: self.file,
359 value: self
360 .field_list_parent_path
361 .clone()
362 .map(SyntaxNodePtr::from)
363 .unwrap_or_else(|| self.field_list_parent.clone().into()),
364 }
365 }
366
367 fn as_any(&self) -> &(dyn Any + Send + 'static) {
368 self
369 }
370}
371
372// Diagnostic: missing-pat-fields
373//
374// This diagnostic is triggered if pattern lacks some fields that exist in the corresponding structure.
375//
376// Example:
377//
378// ```rust
379// struct A { a: u8, b: u8 }
380//
381// let a = A { a: 10, b: 20 };
382//
383// if let A { a } = a {
384// // ...
385// }
386// ```
387#[derive(Debug)]
388pub struct MissingPatFields {
389 pub file: HirFileId,
390 pub field_list_parent: AstPtr<ast::RecordPat>,
391 pub field_list_parent_path: Option<AstPtr<ast::Path>>,
392 pub missed_fields: Vec<Name>,
393}
394
395impl Diagnostic for MissingPatFields {
396 fn code(&self) -> DiagnosticCode {
397 DiagnosticCode("missing-pat-fields")
398 }
399 fn message(&self) -> String {
400 let mut buf = String::from("Missing structure fields:\n");
401 for field in &self.missed_fields {
402 format_to!(buf, "- {}\n", field);
403 }
404 buf
405 }
406 fn display_source(&self) -> InFile<SyntaxNodePtr> {
407 InFile {
408 file_id: self.file,
409 value: self
410 .field_list_parent_path
411 .clone()
412 .map(SyntaxNodePtr::from)
413 .unwrap_or_else(|| self.field_list_parent.clone().into()),
414 }
415 }
416 fn as_any(&self) -> &(dyn Any + Send + 'static) {
417 self
418 }
419}
420
421// Diagnostic: replace-filter-map-next-with-find-map
422//
423// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
424#[derive(Debug)]
425pub struct ReplaceFilterMapNextWithFindMap {
426 pub file: HirFileId,
427 /// This expression is the whole method chain up to and including `.filter_map(..).next()`.
428 pub next_expr: AstPtr<ast::Expr>,
429}
430
431impl Diagnostic for ReplaceFilterMapNextWithFindMap {
432 fn code(&self) -> DiagnosticCode {
433 DiagnosticCode("replace-filter-map-next-with-find-map")
434 }
435 fn message(&self) -> String {
436 "replace filter_map(..).next() with find_map(..)".to_string()
437 }
438 fn display_source(&self) -> InFile<SyntaxNodePtr> {
439 InFile { file_id: self.file, value: self.next_expr.clone().into() }
440 }
441 fn as_any(&self) -> &(dyn Any + Send + 'static) {
442 self
443 }
444}
445
446// Diagnostic: mismatched-arg-count
447//
448// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
449#[derive(Debug)]
450pub struct MismatchedArgCount {
451 pub file: HirFileId,
452 pub call_expr: AstPtr<ast::Expr>,
453 pub expected: usize,
454 pub found: usize,
455}
456
457impl Diagnostic for MismatchedArgCount {
458 fn code(&self) -> DiagnosticCode {
459 DiagnosticCode("mismatched-arg-count")
460 }
461 fn message(&self) -> String {
462 let s = if self.expected == 1 { "" } else { "s" };
463 format!("Expected {} argument{}, found {}", self.expected, s, self.found)
464 }
465 fn display_source(&self) -> InFile<SyntaxNodePtr> {
466 InFile { file_id: self.file, value: self.call_expr.clone().into() }
467 }
468 fn as_any(&self) -> &(dyn Any + Send + 'static) {
469 self
470 }
471 fn is_experimental(&self) -> bool {
472 true
473 }
474}
475
476#[derive(Debug)]
477pub struct RemoveThisSemicolon {
478 pub file: HirFileId,
479 pub expr: AstPtr<ast::Expr>,
480}
481
482impl Diagnostic for RemoveThisSemicolon {
483 fn code(&self) -> DiagnosticCode {
484 DiagnosticCode("remove-this-semicolon")
485 }
486
487 fn message(&self) -> String {
488 "Remove this semicolon".to_string()
489 }
490
491 fn display_source(&self) -> InFile<SyntaxNodePtr> {
492 InFile { file_id: self.file, value: self.expr.clone().into() }
493 }
494
495 fn as_any(&self) -> &(dyn Any + Send + 'static) {
496 self
497 }
498}
499
500// Diagnostic: missing-ok-or-some-in-tail-expr
501//
502// This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
503// or if a block that should return `Option` returns a value not wrapped in `Some`.
504//
505// Example:
506//
507// ```rust
508// fn foo() -> Result<u8, ()> {
509// 10
510// }
511// ```
512#[derive(Debug)]
513pub struct MissingOkOrSomeInTailExpr {
514 pub file: HirFileId,
515 pub expr: AstPtr<ast::Expr>,
516 // `Some` or `Ok` depending on whether the return type is Result or Option
517 pub required: String,
518}
519
520impl Diagnostic for MissingOkOrSomeInTailExpr {
521 fn code(&self) -> DiagnosticCode {
522 DiagnosticCode("missing-ok-or-some-in-tail-expr")
523 }
524 fn message(&self) -> String {
525 format!("wrap return expression in {}", self.required)
526 }
527 fn display_source(&self) -> InFile<SyntaxNodePtr> {
528 InFile { file_id: self.file, value: self.expr.clone().into() }
529 }
530 fn as_any(&self) -> &(dyn Any + Send + 'static) {
531 self
532 }
533}
534
535// Diagnostic: missing-match-arm
536//
537// This diagnostic is triggered if `match` block is missing one or more match arms.
538#[derive(Debug)]
539pub struct MissingMatchArms {
540 pub file: HirFileId,
541 pub match_expr: AstPtr<ast::Expr>,
542 pub arms: AstPtr<ast::MatchArmList>,
543}
544
545impl Diagnostic for MissingMatchArms {
546 fn code(&self) -> DiagnosticCode {
547 DiagnosticCode("missing-match-arm")
548 }
549 fn message(&self) -> String {
550 String::from("Missing match arm")
551 }
552 fn display_source(&self) -> InFile<SyntaxNodePtr> {
553 InFile { file_id: self.file, value: self.match_expr.clone().into() }
554 }
555 fn as_any(&self) -> &(dyn Any + Send + 'static) {
556 self
557 }
558}
559
560#[derive(Debug)]
561pub struct InternalBailedOut {
562 pub file: HirFileId,
563 pub pat_syntax_ptr: SyntaxNodePtr,
564}
565
566impl Diagnostic for InternalBailedOut {
567 fn code(&self) -> DiagnosticCode {
568 DiagnosticCode("internal:match-check-bailed-out")
569 }
570 fn message(&self) -> String {
571 format!("Internal: match check bailed out")
572 }
573 fn display_source(&self) -> InFile<SyntaxNodePtr> {
574 InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() }
575 }
576 fn as_any(&self) -> &(dyn Any + Send + 'static) {
577 self
578 }
579}
580
581pub use hir_ty::diagnostics::IncorrectCase;
582
583impl Diagnostic for IncorrectCase {
584 fn code(&self) -> DiagnosticCode {
585 DiagnosticCode("incorrect-ident-case")
586 }
587
588 fn message(&self) -> String {
589 format!(
590 "{} `{}` should have {} name, e.g. `{}`",
591 self.ident_type,
592 self.ident_text,
593 self.expected_case.to_string(),
594 self.suggested_text
595 )
596 }
597
598 fn display_source(&self) -> InFile<SyntaxNodePtr> {
599 InFile::new(self.file, self.ident.clone().into())
600 }
601
602 fn as_any(&self) -> &(dyn Any + Send + 'static) {
603 self
604 }
605
606 fn is_experimental(&self) -> bool {
607 true
608 }
609}
diff --git a/crates/hir/src/diagnostics_sink.rs b/crates/hir/src/diagnostics_sink.rs
new file mode 100644
index 000000000..084fa8b06
--- /dev/null
+++ b/crates/hir/src/diagnostics_sink.rs
@@ -0,0 +1,109 @@
1//! Semantic errors and warnings.
2//!
3//! The `Diagnostic` trait defines a trait object which can represent any
4//! diagnostic.
5//!
6//! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating
7//! a `DiagnosticSink`, you supply a callback which can react to a `dyn
8//! Diagnostic` or to any concrete diagnostic (downcasting is used internally).
9//!
10//! Because diagnostics store file offsets, it's a bad idea to store them
11//! directly in salsa. For this reason, every hir subsytem defines it's own
12//! strongly-typed closed set of diagnostics which use hir ids internally, are
13//! stored in salsa and do *not* implement the `Diagnostic` trait. Instead, a
14//! subsystem provides a separate, non-query-based API which can walk all stored
15//! values and transform them into instances of `Diagnostic`.
16
17use std::{any::Any, fmt};
18
19use hir_expand::InFile;
20use syntax::SyntaxNodePtr;
21
22#[derive(Copy, Clone, Debug, PartialEq)]
23pub struct DiagnosticCode(pub &'static str);
24
25impl DiagnosticCode {
26 pub fn as_str(&self) -> &str {
27 self.0
28 }
29}
30
31pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
32 fn code(&self) -> DiagnosticCode;
33 fn message(&self) -> String;
34 /// Source element that triggered the diagnostics.
35 ///
36 /// Note that this should reflect "semantics", rather than specific span we
37 /// want to highlight. When rendering the diagnostics into an error message,
38 /// the IDE will fetch the `SyntaxNode` and will narrow the span
39 /// appropriately.
40 fn display_source(&self) -> InFile<SyntaxNodePtr>;
41 fn as_any(&self) -> &(dyn Any + Send + 'static);
42 fn is_experimental(&self) -> bool {
43 false
44 }
45}
46
47pub struct DiagnosticSink<'a> {
48 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
49 filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>,
50 default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>,
51}
52
53impl<'a> DiagnosticSink<'a> {
54 pub fn push(&mut self, d: impl Diagnostic) {
55 let d: &dyn Diagnostic = &d;
56 self._push(d);
57 }
58
59 fn _push(&mut self, d: &dyn Diagnostic) {
60 for filter in &mut self.filters {
61 if !filter(d) {
62 return;
63 }
64 }
65 for cb in &mut self.callbacks {
66 match cb(d) {
67 Ok(()) => return,
68 Err(()) => (),
69 }
70 }
71 (self.default_callback)(d)
72 }
73}
74
75pub struct DiagnosticSinkBuilder<'a> {
76 callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
77 filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>,
78}
79
80impl<'a> DiagnosticSinkBuilder<'a> {
81 pub fn new() -> Self {
82 Self { callbacks: Vec::new(), filters: Vec::new() }
83 }
84
85 pub fn filter<F: FnMut(&dyn Diagnostic) -> bool + 'a>(mut self, cb: F) -> Self {
86 self.filters.push(Box::new(cb));
87 self
88 }
89
90 pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> Self {
91 let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::<D>() {
92 Some(d) => {
93 cb(d);
94 Ok(())
95 }
96 None => Err(()),
97 };
98 self.callbacks.push(Box::new(cb));
99 self
100 }
101
102 pub fn build<F: FnMut(&dyn Diagnostic) + 'a>(self, default_callback: F) -> DiagnosticSink<'a> {
103 DiagnosticSink {
104 callbacks: self.callbacks,
105 filters: self.filters,
106 default_callback: Box::new(default_callback),
107 }
108 }
109}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b9c1dc44d..2468c0dc6 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -27,6 +27,7 @@ mod attrs;
27mod has_source; 27mod has_source;
28 28
29pub mod diagnostics; 29pub mod diagnostics;
30pub mod diagnostics_sink;
30pub mod db; 31pub mod db;
31 32
32mod display; 33mod display;
@@ -35,14 +36,10 @@ use std::{iter, sync::Arc};
35 36
36use arrayvec::ArrayVec; 37use arrayvec::ArrayVec;
37use base_db::{CrateDisplayName, CrateId, Edition, FileId}; 38use base_db::{CrateDisplayName, CrateId, Edition, FileId};
38use diagnostics::{
39 InactiveCode, MacroError, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport,
40 UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
41};
42use either::Either; 39use either::Either;
43use hir_def::{ 40use hir_def::{
44 adt::{ReprKind, VariantData}, 41 adt::{ReprKind, VariantData},
45 body::BodyDiagnostic, 42 body::{BodyDiagnostic, SyntheticSyntax},
46 expr::{BindingAnnotation, LabelId, Pat, PatId}, 43 expr::{BindingAnnotation, LabelId, Pat, PatId},
47 item_tree::ItemTreeNode, 44 item_tree::ItemTreeNode,
48 lang_item::LangItemTarget, 45 lang_item::LangItemTarget,
@@ -60,8 +57,8 @@ use hir_ty::{
60 autoderef, 57 autoderef,
61 consteval::ConstExt, 58 consteval::ConstExt,
62 could_unify, 59 could_unify,
63 diagnostics_sink::DiagnosticSink, 60 diagnostics::BodyValidationDiagnostic,
64 method_resolution::{self, def_crates, TyFingerprint}, 61 method_resolution::{self, TyFingerprint},
65 primitive::UintTy, 62 primitive::UintTy,
66 subst_prefix, 63 subst_prefix,
67 traits::FnTrait, 64 traits::FnTrait,
@@ -72,6 +69,7 @@ use hir_ty::{
72}; 69};
73use itertools::Itertools; 70use itertools::Itertools;
74use nameres::diagnostics::DefDiagnosticKind; 71use nameres::diagnostics::DefDiagnosticKind;
72use once_cell::unsync::Lazy;
75use rustc_hash::FxHashSet; 73use rustc_hash::FxHashSet;
76use stdx::{format_to, impl_from}; 74use stdx::{format_to, impl_from};
77use syntax::{ 75use syntax::{
@@ -80,7 +78,17 @@ use syntax::{
80}; 78};
81use tt::{Ident, Leaf, Literal, TokenTree}; 79use tt::{Ident, Leaf, Literal, TokenTree};
82 80
83use crate::db::{DefDatabase, HirDatabase}; 81use crate::{
82 db::{DefDatabase, HirDatabase},
83 diagnostics::{
84 BreakOutsideOfLoop, InactiveCode, InternalBailedOut, MacroError, MismatchedArgCount,
85 MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, MissingPatFields,
86 MissingUnsafe, NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
87 UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
88 UnresolvedModule, UnresolvedProcMacro,
89 },
90 diagnostics_sink::DiagnosticSink,
91};
84 92
85pub use crate::{ 93pub use crate::{
86 attrs::{HasAttrs, Namespace}, 94 attrs::{HasAttrs, Namespace},
@@ -353,7 +361,9 @@ impl ModuleDef {
353 None => return, 361 None => return,
354 }; 362 };
355 363
356 hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink) 364 for diag in hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id) {
365 sink.push(diag)
366 }
357 } 367 }
358} 368}
359 369
@@ -445,7 +455,12 @@ impl Module {
445 self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into()) 455 self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into())
446 } 456 }
447 457
448 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 458 pub fn diagnostics(
459 self,
460 db: &dyn HirDatabase,
461 sink: &mut DiagnosticSink,
462 internal_diagnostics: bool,
463 ) {
449 let _p = profile::span("Module::diagnostics").detail(|| { 464 let _p = profile::span("Module::diagnostics").detail(|| {
450 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) 465 format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
451 }); 466 });
@@ -591,11 +606,11 @@ impl Module {
591 } 606 }
592 for decl in self.declarations(db) { 607 for decl in self.declarations(db) {
593 match decl { 608 match decl {
594 crate::ModuleDef::Function(f) => f.diagnostics(db, sink), 609 crate::ModuleDef::Function(f) => f.diagnostics(db, sink, internal_diagnostics),
595 crate::ModuleDef::Module(m) => { 610 crate::ModuleDef::Module(m) => {
596 // Only add diagnostics from inline modules 611 // Only add diagnostics from inline modules
597 if def_map[m.id.local_id].origin.is_inline() { 612 if def_map[m.id.local_id].origin.is_inline() {
598 m.diagnostics(db, sink) 613 m.diagnostics(db, sink, internal_diagnostics)
599 } 614 }
600 } 615 }
601 _ => { 616 _ => {
@@ -607,7 +622,7 @@ impl Module {
607 for impl_def in self.impl_defs(db) { 622 for impl_def in self.impl_defs(db) {
608 for item in impl_def.items(db) { 623 for item in impl_def.items(db) {
609 if let AssocItem::Function(f) = item { 624 if let AssocItem::Function(f) = item {
610 f.diagnostics(db, sink); 625 f.diagnostics(db, sink, internal_diagnostics);
611 } 626 }
612 } 627 }
613 } 628 }
@@ -1009,7 +1024,12 @@ impl Function {
1009 db.function_data(self.id).is_async() 1024 db.function_data(self.id).is_async()
1010 } 1025 }
1011 1026
1012 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 1027 pub fn diagnostics(
1028 self,
1029 db: &dyn HirDatabase,
1030 sink: &mut DiagnosticSink,
1031 internal_diagnostics: bool,
1032 ) {
1013 let krate = self.module(db).id.krate(); 1033 let krate = self.module(db).id.krate();
1014 1034
1015 let source_map = db.body_with_source_map(self.id.into()).1; 1035 let source_map = db.body_with_source_map(self.id.into()).1;
@@ -1042,8 +1062,174 @@ impl Function {
1042 } 1062 }
1043 } 1063 }
1044 1064
1045 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); 1065 let infer = db.infer(self.id.into());
1046 hir_ty::diagnostics::validate_body(db, self.id.into(), sink); 1066 let source_map = Lazy::new(|| db.body_with_source_map(self.id.into()).1);
1067 for d in &infer.diagnostics {
1068 match d {
1069 hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
1070 let field = source_map.field_syntax(*expr);
1071 sink.push(NoSuchField { file: field.file_id, field: field.value })
1072 }
1073 hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
1074 let ptr = source_map
1075 .expr_syntax(*expr)
1076 .expect("break outside of loop in synthetic syntax");
1077 sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value })
1078 }
1079 }
1080 }
1081
1082 for expr in hir_ty::diagnostics::missing_unsafe(db, self.id.into()) {
1083 match source_map.expr_syntax(expr) {
1084 Ok(in_file) => {
1085 sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value })
1086 }
1087 Err(SyntheticSyntax) => {
1088 // FIXME: Here and eslwhere in this file, the `expr` was
1089 // desugared, report or assert that this doesn't happen.
1090 }
1091 }
1092 }
1093
1094 for diagnostic in
1095 BodyValidationDiagnostic::collect(db, self.id.into(), internal_diagnostics)
1096 {
1097 match diagnostic {
1098 BodyValidationDiagnostic::RecordLiteralMissingFields {
1099 record_expr,
1100 variant,
1101 missed_fields,
1102 } => match source_map.expr_syntax(record_expr) {
1103 Ok(source_ptr) => {
1104 let root = source_ptr.file_syntax(db.upcast());
1105 if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root)
1106 {
1107 if let Some(_) = record_expr.record_expr_field_list() {
1108 let variant_data = variant.variant_data(db.upcast());
1109 let missed_fields = missed_fields
1110 .into_iter()
1111 .map(|idx| variant_data.fields()[idx].name.clone())
1112 .collect();
1113 sink.push(MissingFields {
1114 file: source_ptr.file_id,
1115 field_list_parent: AstPtr::new(&record_expr),
1116 field_list_parent_path: record_expr
1117 .path()
1118 .map(|path| AstPtr::new(&path)),
1119 missed_fields,
1120 })
1121 }
1122 }
1123 }
1124 Err(SyntheticSyntax) => (),
1125 },
1126 BodyValidationDiagnostic::RecordPatMissingFields {
1127 record_pat,
1128 variant,
1129 missed_fields,
1130 } => match source_map.pat_syntax(record_pat) {
1131 Ok(source_ptr) => {
1132 if let Some(expr) = source_ptr.value.as_ref().left() {
1133 let root = source_ptr.file_syntax(db.upcast());
1134 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
1135 if let Some(_) = record_pat.record_pat_field_list() {
1136 let variant_data = variant.variant_data(db.upcast());
1137 let missed_fields = missed_fields
1138 .into_iter()
1139 .map(|idx| variant_data.fields()[idx].name.clone())
1140 .collect();
1141 sink.push(MissingPatFields {
1142 file: source_ptr.file_id,
1143 field_list_parent: AstPtr::new(&record_pat),
1144 field_list_parent_path: record_pat
1145 .path()
1146 .map(|path| AstPtr::new(&path)),
1147 missed_fields,
1148 })
1149 }
1150 }
1151 }
1152 }
1153 Err(SyntheticSyntax) => (),
1154 },
1155
1156 BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
1157 if let Ok(next_source_ptr) = source_map.expr_syntax(method_call_expr) {
1158 sink.push(ReplaceFilterMapNextWithFindMap {
1159 file: next_source_ptr.file_id,
1160 next_expr: next_source_ptr.value,
1161 });
1162 }
1163 }
1164 BodyValidationDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
1165 match source_map.expr_syntax(call_expr) {
1166 Ok(source_ptr) => sink.push(MismatchedArgCount {
1167 file: source_ptr.file_id,
1168 call_expr: source_ptr.value,
1169 expected,
1170 found,
1171 }),
1172 Err(SyntheticSyntax) => (),
1173 }
1174 }
1175 BodyValidationDiagnostic::RemoveThisSemicolon { expr } => {
1176 match source_map.expr_syntax(expr) {
1177 Ok(source_ptr) => sink.push(RemoveThisSemicolon {
1178 file: source_ptr.file_id,
1179 expr: source_ptr.value,
1180 }),
1181 Err(SyntheticSyntax) => (),
1182 }
1183 }
1184 BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr, required } => {
1185 match source_map.expr_syntax(expr) {
1186 Ok(source_ptr) => sink.push(MissingOkOrSomeInTailExpr {
1187 file: source_ptr.file_id,
1188 expr: source_ptr.value,
1189 required,
1190 }),
1191 Err(SyntheticSyntax) => (),
1192 }
1193 }
1194 BodyValidationDiagnostic::MissingMatchArms { match_expr } => {
1195 match source_map.expr_syntax(match_expr) {
1196 Ok(source_ptr) => {
1197 let root = source_ptr.file_syntax(db.upcast());
1198 if let ast::Expr::MatchExpr(match_expr) =
1199 &source_ptr.value.to_node(&root)
1200 {
1201 if let (Some(match_expr), Some(arms)) =
1202 (match_expr.expr(), match_expr.match_arm_list())
1203 {
1204 sink.push(MissingMatchArms {
1205 file: source_ptr.file_id,
1206 match_expr: AstPtr::new(&match_expr),
1207 arms: AstPtr::new(&arms),
1208 })
1209 }
1210 }
1211 }
1212 Err(SyntheticSyntax) => (),
1213 }
1214 }
1215 BodyValidationDiagnostic::InternalBailedOut { pat } => {
1216 match source_map.pat_syntax(pat) {
1217 Ok(source_ptr) => {
1218 let pat_syntax_ptr = source_ptr.value.either(Into::into, Into::into);
1219 sink.push(InternalBailedOut {
1220 file: source_ptr.file_id,
1221 pat_syntax_ptr,
1222 });
1223 }
1224 Err(SyntheticSyntax) => (),
1225 }
1226 }
1227 }
1228 }
1229
1230 for diag in hir_ty::diagnostics::validate_module_item(db, krate, self.id.into()) {
1231 sink.push(diag)
1232 }
1047 } 1233 }
1048 1234
1049 /// Whether this function declaration has a definition. 1235 /// Whether this function declaration has a definition.
@@ -1451,6 +1637,20 @@ impl AssocItem {
1451 _ => None, 1637 _ => None,
1452 } 1638 }
1453 } 1639 }
1640
1641 pub fn containing_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> {
1642 match self.container(db) {
1643 AssocItemContainer::Impl(i) => i.trait_(db),
1644 _ => None,
1645 }
1646 }
1647
1648 pub fn containing_trait_or_trait_impl(self, db: &dyn HirDatabase) -> Option<Trait> {
1649 match self.container(db) {
1650 AssocItemContainer::Trait(t) => Some(t),
1651 AssocItemContainer::Impl(i) => i.trait_(db),
1652 }
1653 }
1454} 1654}
1455 1655
1456impl HasVisibility for AssocItem { 1656impl HasVisibility for AssocItem {
@@ -1748,7 +1948,7 @@ impl Impl {
1748 } 1948 }
1749 1949
1750 pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> { 1950 pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> {
1751 let def_crates = match def_crates(db, &ty, krate) { 1951 let def_crates = match method_resolution::def_crates(db, &ty, krate) {
1752 Some(def_crates) => def_crates, 1952 Some(def_crates) => def_crates,
1753 None => return Vec::new(), 1953 None => return Vec::new(),
1754 }; 1954 };
@@ -2154,7 +2354,7 @@ impl Type {
2154 krate: Crate, 2354 krate: Crate,
2155 mut callback: impl FnMut(AssocItem) -> Option<T>, 2355 mut callback: impl FnMut(AssocItem) -> Option<T>,
2156 ) -> Option<T> { 2356 ) -> Option<T> {
2157 for krate in def_crates(db, &self.ty, krate.id)? { 2357 for krate in method_resolution::def_crates(db, &self.ty, krate.id)? {
2158 let impls = db.inherent_impls_in_crate(krate); 2358 let impls = db.inherent_impls_in_crate(krate);
2159 2359
2160 for impl_def in impls.for_self_ty(&self.ty) { 2360 for impl_def in impls.for_self_ty(&self.ty) {