Skip to content

Commit f9e67d6

Browse files
bors[bot]matklad
andauthored
9230: internal: move inference diagnostics to hir r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
2 parents c1c2c92 + 0413d51 commit f9e67d6

File tree

7 files changed

+362
-405
lines changed

7 files changed

+362
-405
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
1414
pub use hir_ty::{
1515
diagnostics::{
1616
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms,
17-
MissingOkOrSomeInTailExpr, NoSuchField, RemoveThisSemicolon,
18-
ReplaceFilterMapNextWithFindMap,
17+
MissingOkOrSomeInTailExpr, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap,
1918
},
2019
diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder},
2120
};
@@ -251,3 +250,78 @@ impl Diagnostic for UnimplementedBuiltinMacro {
251250
self
252251
}
253252
}
253+
254+
// Diagnostic: no-such-field
255+
//
256+
// This diagnostic is triggered if created structure does not have field provided in record.
257+
#[derive(Debug)]
258+
pub struct NoSuchField {
259+
pub file: HirFileId,
260+
pub field: AstPtr<ast::RecordExprField>,
261+
}
262+
263+
impl Diagnostic for NoSuchField {
264+
fn code(&self) -> DiagnosticCode {
265+
DiagnosticCode("no-such-field")
266+
}
267+
268+
fn message(&self) -> String {
269+
"no such field".to_string()
270+
}
271+
272+
fn display_source(&self) -> InFile<SyntaxNodePtr> {
273+
InFile::new(self.file, self.field.clone().into())
274+
}
275+
276+
fn as_any(&self) -> &(dyn Any + Send + 'static) {
277+
self
278+
}
279+
}
280+
281+
// Diagnostic: break-outside-of-loop
282+
//
283+
// This diagnostic is triggered if the `break` keyword is used outside of a loop.
284+
#[derive(Debug)]
285+
pub struct BreakOutsideOfLoop {
286+
pub file: HirFileId,
287+
pub expr: AstPtr<ast::Expr>,
288+
}
289+
290+
impl Diagnostic for BreakOutsideOfLoop {
291+
fn code(&self) -> DiagnosticCode {
292+
DiagnosticCode("break-outside-of-loop")
293+
}
294+
fn message(&self) -> String {
295+
"break outside of loop".to_string()
296+
}
297+
fn display_source(&self) -> InFile<SyntaxNodePtr> {
298+
InFile { file_id: self.file, value: self.expr.clone().into() }
299+
}
300+
fn as_any(&self) -> &(dyn Any + Send + 'static) {
301+
self
302+
}
303+
}
304+
305+
// Diagnostic: missing-unsafe
306+
//
307+
// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
308+
#[derive(Debug)]
309+
pub struct MissingUnsafe {
310+
pub file: HirFileId,
311+
pub expr: AstPtr<ast::Expr>,
312+
}
313+
314+
impl Diagnostic for MissingUnsafe {
315+
fn code(&self) -> DiagnosticCode {
316+
DiagnosticCode("missing-unsafe")
317+
}
318+
fn message(&self) -> String {
319+
format!("This operation is unsafe and requires an unsafe function or block")
320+
}
321+
fn display_source(&self) -> InFile<SyntaxNodePtr> {
322+
InFile { file_id: self.file, value: self.expr.clone().into() }
323+
}
324+
fn as_any(&self) -> &(dyn Any + Send + 'static) {
325+
self
326+
}
327+
}

crates/hir/src/lib.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ use std::{iter, sync::Arc};
3636
use arrayvec::ArrayVec;
3737
use base_db::{CrateDisplayName, CrateId, Edition, FileId};
3838
use diagnostics::{
39-
InactiveCode, MacroError, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport,
40-
UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro,
39+
BreakOutsideOfLoop, InactiveCode, MacroError, MissingUnsafe, NoSuchField,
40+
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
41+
UnresolvedModule, UnresolvedProcMacro,
4142
};
4243
use either::Either;
4344
use hir_def::{
4445
adt::{ReprKind, VariantData},
45-
body::BodyDiagnostic,
46+
body::{BodyDiagnostic, SyntheticSyntax},
4647
expr::{BindingAnnotation, LabelId, Pat, PatId},
4748
item_tree::ItemTreeNode,
4849
lang_item::LangItemTarget,
@@ -1042,6 +1043,35 @@ impl Function {
10421043
}
10431044
}
10441045

1046+
let infer = db.infer(self.id.into());
1047+
let (_, source_map) = db.body_with_source_map(self.id.into());
1048+
for d in &infer.diagnostics {
1049+
match d {
1050+
hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
1051+
let field = source_map.field_syntax(*expr);
1052+
sink.push(NoSuchField { file: field.file_id, field: field.value })
1053+
}
1054+
hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
1055+
let ptr = source_map
1056+
.expr_syntax(*expr)
1057+
.expect("break outside of loop in synthetic syntax");
1058+
sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value })
1059+
}
1060+
}
1061+
}
1062+
1063+
for expr in hir_ty::diagnostics::missing_unsafe(db, self.id.into()) {
1064+
match source_map.as_ref().expr_syntax(expr) {
1065+
Ok(in_file) => {
1066+
sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value })
1067+
}
1068+
Err(SyntheticSyntax) => {
1069+
// FIXME: The `expr` was desugared, report or assert that
1070+
// this dosen't happen.
1071+
}
1072+
}
1073+
}
1074+
10451075
hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
10461076
hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
10471077
}

crates/hir_ty/src/diagnostics.rs

Lines changed: 4 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ use crate::{
1717
diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
1818
};
1919

20-
pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields};
20+
pub use crate::diagnostics::{
21+
expr::{record_literal_missing_fields, record_pattern_missing_fields},
22+
unsafe_check::missing_unsafe,
23+
};
2124

2225
pub fn validate_module_item(
2326
db: &dyn HirDatabase,
@@ -33,38 +36,8 @@ pub fn validate_module_item(
3336
pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
3437
let _p = profile::span("validate_body");
3538
let infer = db.infer(owner);
36-
infer.add_diagnostics(db, owner, sink);
3739
let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
3840
validator.validate_body(db);
39-
let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink);
40-
validator.validate_body(db);
41-
}
42-
43-
// Diagnostic: no-such-field
44-
//
45-
// This diagnostic is triggered if created structure does not have field provided in record.
46-
#[derive(Debug)]
47-
pub struct NoSuchField {
48-
pub file: HirFileId,
49-
pub field: AstPtr<ast::RecordExprField>,
50-
}
51-
52-
impl Diagnostic for NoSuchField {
53-
fn code(&self) -> DiagnosticCode {
54-
DiagnosticCode("no-such-field")
55-
}
56-
57-
fn message(&self) -> String {
58-
"no such field".to_string()
59-
}
60-
61-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
62-
InFile::new(self.file, self.field.clone().into())
63-
}
64-
65-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
66-
self
67-
}
6841
}
6942

7043
// Diagnostic: missing-structure-fields
@@ -247,54 +220,6 @@ impl Diagnostic for RemoveThisSemicolon {
247220
}
248221
}
249222

250-
// Diagnostic: break-outside-of-loop
251-
//
252-
// This diagnostic is triggered if the `break` keyword is used outside of a loop.
253-
#[derive(Debug)]
254-
pub struct BreakOutsideOfLoop {
255-
pub file: HirFileId,
256-
pub expr: AstPtr<ast::Expr>,
257-
}
258-
259-
impl Diagnostic for BreakOutsideOfLoop {
260-
fn code(&self) -> DiagnosticCode {
261-
DiagnosticCode("break-outside-of-loop")
262-
}
263-
fn message(&self) -> String {
264-
"break outside of loop".to_string()
265-
}
266-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
267-
InFile { file_id: self.file, value: self.expr.clone().into() }
268-
}
269-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
270-
self
271-
}
272-
}
273-
274-
// Diagnostic: missing-unsafe
275-
//
276-
// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
277-
#[derive(Debug)]
278-
pub struct MissingUnsafe {
279-
pub file: HirFileId,
280-
pub expr: AstPtr<ast::Expr>,
281-
}
282-
283-
impl Diagnostic for MissingUnsafe {
284-
fn code(&self) -> DiagnosticCode {
285-
DiagnosticCode("missing-unsafe")
286-
}
287-
fn message(&self) -> String {
288-
format!("This operation is unsafe and requires an unsafe function or block")
289-
}
290-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
291-
InFile { file_id: self.file, value: self.expr.clone().into() }
292-
}
293-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
294-
self
295-
}
296-
}
297-
298223
// Diagnostic: mismatched-arg-count
299224
//
300225
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
@@ -530,129 +455,6 @@ mod tests {
530455
assert_eq!(annotations, actual);
531456
}
532457

533-
#[test]
534-
fn no_such_field_diagnostics() {
535-
check_diagnostics(
536-
r#"
537-
struct S { foo: i32, bar: () }
538-
impl S {
539-
fn new() -> S {
540-
S {
541-
//^ Missing structure fields:
542-
//| - bar
543-
foo: 92,
544-
baz: 62,
545-
//^^^^^^^ no such field
546-
}
547-
}
548-
}
549-
"#,
550-
);
551-
}
552-
#[test]
553-
fn no_such_field_with_feature_flag_diagnostics() {
554-
check_diagnostics(
555-
r#"
556-
//- /lib.rs crate:foo cfg:feature=foo
557-
struct MyStruct {
558-
my_val: usize,
559-
#[cfg(feature = "foo")]
560-
bar: bool,
561-
}
562-
563-
impl MyStruct {
564-
#[cfg(feature = "foo")]
565-
pub(crate) fn new(my_val: usize, bar: bool) -> Self {
566-
Self { my_val, bar }
567-
}
568-
#[cfg(not(feature = "foo"))]
569-
pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
570-
Self { my_val }
571-
}
572-
}
573-
"#,
574-
);
575-
}
576-
577-
#[test]
578-
fn no_such_field_enum_with_feature_flag_diagnostics() {
579-
check_diagnostics(
580-
r#"
581-
//- /lib.rs crate:foo cfg:feature=foo
582-
enum Foo {
583-
#[cfg(not(feature = "foo"))]
584-
Buz,
585-
#[cfg(feature = "foo")]
586-
Bar,
587-
Baz
588-
}
589-
590-
fn test_fn(f: Foo) {
591-
match f {
592-
Foo::Bar => {},
593-
Foo::Baz => {},
594-
}
595-
}
596-
"#,
597-
);
598-
}
599-
600-
#[test]
601-
fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
602-
check_diagnostics(
603-
r#"
604-
//- /lib.rs crate:foo cfg:feature=foo
605-
struct S {
606-
#[cfg(feature = "foo")]
607-
foo: u32,
608-
#[cfg(not(feature = "foo"))]
609-
bar: u32,
610-
}
611-
612-
impl S {
613-
#[cfg(feature = "foo")]
614-
fn new(foo: u32) -> Self {
615-
Self { foo }
616-
}
617-
#[cfg(not(feature = "foo"))]
618-
fn new(bar: u32) -> Self {
619-
Self { bar }
620-
}
621-
fn new2(bar: u32) -> Self {
622-
#[cfg(feature = "foo")]
623-
{ Self { foo: bar } }
624-
#[cfg(not(feature = "foo"))]
625-
{ Self { bar } }
626-
}
627-
fn new2(val: u32) -> Self {
628-
Self {
629-
#[cfg(feature = "foo")]
630-
foo: val,
631-
#[cfg(not(feature = "foo"))]
632-
bar: val,
633-
}
634-
}
635-
}
636-
"#,
637-
);
638-
}
639-
640-
#[test]
641-
fn no_such_field_with_type_macro() {
642-
check_diagnostics(
643-
r#"
644-
macro_rules! Type { () => { u32 }; }
645-
struct Foo { bar: Type![] }
646-
647-
impl Foo {
648-
fn new() -> Self {
649-
Foo { bar: 0 }
650-
}
651-
}
652-
"#,
653-
);
654-
}
655-
656458
#[test]
657459
fn missing_record_pat_field_diagnostic() {
658460
check_diagnostics(
@@ -734,16 +536,6 @@ pub struct Claims {
734536
);
735537
}
736538

737-
#[test]
738-
fn break_outside_of_loop() {
739-
check_diagnostics(
740-
r#"
741-
fn foo() { break; }
742-
//^^^^^ break outside of loop
743-
"#,
744-
);
745-
}
746-
747539
#[test]
748540
fn missing_semicolon() {
749541
check_diagnostics(

0 commit comments

Comments
 (0)