diff options
Diffstat (limited to 'crates/ra_hir/src/semantics.rs')
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 138 |
1 files changed, 124 insertions, 14 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 307b336f2..36b688ccb 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -8,7 +8,7 @@ use hir_def::{ | |||
8 | resolver::{self, HasResolver, Resolver}, | 8 | resolver::{self, HasResolver, Resolver}, |
9 | AsMacroCall, FunctionId, TraitId, VariantId, | 9 | AsMacroCall, FunctionId, TraitId, VariantId, |
10 | }; | 10 | }; |
11 | use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, ExpansionInfo}; | 11 | use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo}; |
12 | use hir_ty::associated_type_shorthand_candidates; | 12 | use hir_ty::associated_type_shorthand_candidates; |
13 | use itertools::Itertools; | 13 | use itertools::Itertools; |
14 | use ra_db::{FileId, FileRange}; | 14 | use ra_db::{FileId, FileRange}; |
@@ -24,8 +24,9 @@ use crate::{ | |||
24 | diagnostics::Diagnostic, | 24 | diagnostics::Diagnostic, |
25 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, | 25 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, |
26 | source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer}, | 26 | source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer}, |
27 | AssocItem, Callable, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, | 27 | AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, |
28 | ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef, | 28 | Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, TypeRef, |
29 | VariantDef, | ||
29 | }; | 30 | }; |
30 | use resolver::TypeNs; | 31 | use resolver::TypeNs; |
31 | 32 | ||
@@ -109,13 +110,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
109 | self.imp.parse(file_id) | 110 | self.imp.parse(file_id) |
110 | } | 111 | } |
111 | 112 | ||
112 | pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST { | ||
113 | let file_id = d.source().file_id; | ||
114 | let root = self.db.parse_or_expand(file_id).unwrap(); | ||
115 | self.imp.cache(root, file_id); | ||
116 | d.ast(self.db.upcast()) | ||
117 | } | ||
118 | |||
119 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { | 113 | pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { |
120 | self.imp.expand(macro_call) | 114 | self.imp.expand(macro_call) |
121 | } | 115 | } |
@@ -145,8 +139,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
145 | self.imp.original_range(node) | 139 | self.imp.original_range(node) |
146 | } | 140 | } |
147 | 141 | ||
148 | pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { | 142 | pub fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { |
149 | self.imp.diagnostics_range(diagnostics) | 143 | self.imp.diagnostics_display_range(diagnostics) |
150 | } | 144 | } |
151 | 145 | ||
152 | pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { | 146 | pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { |
@@ -228,6 +222,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
228 | self.imp.resolve_path(path) | 222 | self.imp.resolve_path(path) |
229 | } | 223 | } |
230 | 224 | ||
225 | pub fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> { | ||
226 | self.imp.resolve_extern_crate(extern_crate) | ||
227 | } | ||
228 | |||
231 | pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> { | 229 | pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> { |
232 | self.imp.resolve_variant(record_lit).map(VariantDef::from) | 230 | self.imp.resolve_variant(record_lit).map(VariantDef::from) |
233 | } | 231 | } |
@@ -275,6 +273,18 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
275 | pub fn assert_contains_node(&self, node: &SyntaxNode) { | 273 | pub fn assert_contains_node(&self, node: &SyntaxNode) { |
276 | self.imp.assert_contains_node(node) | 274 | self.imp.assert_contains_node(node) |
277 | } | 275 | } |
276 | |||
277 | pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool { | ||
278 | self.imp.is_unsafe_method_call(method_call_expr) | ||
279 | } | ||
280 | |||
281 | pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { | ||
282 | self.imp.is_unsafe_ref_expr(ref_expr) | ||
283 | } | ||
284 | |||
285 | pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { | ||
286 | self.imp.is_unsafe_ident_pat(ident_pat) | ||
287 | } | ||
278 | } | 288 | } |
279 | 289 | ||
280 | impl<'db> SemanticsImpl<'db> { | 290 | impl<'db> SemanticsImpl<'db> { |
@@ -372,10 +382,11 @@ impl<'db> SemanticsImpl<'db> { | |||
372 | original_range(self.db, node.as_ref()) | 382 | original_range(self.db, node.as_ref()) |
373 | } | 383 | } |
374 | 384 | ||
375 | fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { | 385 | fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { |
376 | let src = diagnostics.source(); | 386 | let src = diagnostics.display_source(); |
377 | let root = self.db.parse_or_expand(src.file_id).unwrap(); | 387 | let root = self.db.parse_or_expand(src.file_id).unwrap(); |
378 | let node = src.value.to_node(&root); | 388 | let node = src.value.to_node(&root); |
389 | self.cache(root, src.file_id); | ||
379 | original_range(self.db, src.with_value(&node)) | 390 | original_range(self.db, src.with_value(&node)) |
380 | } | 391 | } |
381 | 392 | ||
@@ -443,6 +454,17 @@ impl<'db> SemanticsImpl<'db> { | |||
443 | self.analyze(path.syntax()).resolve_path(self.db, path) | 454 | self.analyze(path.syntax()).resolve_path(self.db, path) |
444 | } | 455 | } |
445 | 456 | ||
457 | fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> { | ||
458 | let krate = self.scope(extern_crate.syntax()).krate()?; | ||
459 | krate.dependencies(self.db).into_iter().find_map(|dep| { | ||
460 | if dep.name == extern_crate.name_ref()?.as_name() { | ||
461 | Some(dep.krate) | ||
462 | } else { | ||
463 | None | ||
464 | } | ||
465 | }) | ||
466 | } | ||
467 | |||
446 | fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> { | 468 | fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> { |
447 | self.analyze(record_lit.syntax()).resolve_variant(self.db, record_lit) | 469 | self.analyze(record_lit.syntax()).resolve_variant(self.db, record_lit) |
448 | } | 470 | } |
@@ -559,6 +581,90 @@ impl<'db> SemanticsImpl<'db> { | |||
559 | }); | 581 | }); |
560 | InFile::new(file_id, node) | 582 | InFile::new(file_id, node) |
561 | } | 583 | } |
584 | |||
585 | pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool { | ||
586 | method_call_expr | ||
587 | .expr() | ||
588 | .and_then(|expr| { | ||
589 | let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr { | ||
590 | field_expr | ||
591 | } else { | ||
592 | return None; | ||
593 | }; | ||
594 | let ty = self.type_of_expr(&field_expr.expr()?)?; | ||
595 | if !ty.is_packed(self.db) { | ||
596 | return None; | ||
597 | } | ||
598 | |||
599 | let func = self.resolve_method_call(&method_call_expr).map(Function::from)?; | ||
600 | let is_unsafe = func.has_self_param(self.db) | ||
601 | && matches!(func.params(self.db).first(), Some(TypeRef::Reference(..))); | ||
602 | Some(is_unsafe) | ||
603 | }) | ||
604 | .unwrap_or(false) | ||
605 | } | ||
606 | |||
607 | pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { | ||
608 | ref_expr | ||
609 | .expr() | ||
610 | .and_then(|expr| { | ||
611 | let field_expr = match expr { | ||
612 | ast::Expr::FieldExpr(field_expr) => field_expr, | ||
613 | _ => return None, | ||
614 | }; | ||
615 | let expr = field_expr.expr()?; | ||
616 | self.type_of_expr(&expr) | ||
617 | }) | ||
618 | // Binding a reference to a packed type is possibly unsafe. | ||
619 | .map(|ty| ty.is_packed(self.db)) | ||
620 | .unwrap_or(false) | ||
621 | |||
622 | // FIXME This needs layout computation to be correct. It will highlight | ||
623 | // more than it should with the current implementation. | ||
624 | } | ||
625 | |||
626 | pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { | ||
627 | if !ident_pat.ref_token().is_some() { | ||
628 | return false; | ||
629 | } | ||
630 | |||
631 | ident_pat | ||
632 | .syntax() | ||
633 | .parent() | ||
634 | .and_then(|parent| { | ||
635 | // `IdentPat` can live under `RecordPat` directly under `RecordPatField` or | ||
636 | // `RecordPatFieldList`. `RecordPatField` also lives under `RecordPatFieldList`, | ||
637 | // so this tries to lookup the `IdentPat` anywhere along that structure to the | ||
638 | // `RecordPat` so we can get the containing type. | ||
639 | let record_pat = ast::RecordPatField::cast(parent.clone()) | ||
640 | .and_then(|record_pat| record_pat.syntax().parent()) | ||
641 | .or_else(|| Some(parent.clone())) | ||
642 | .and_then(|parent| { | ||
643 | ast::RecordPatFieldList::cast(parent)? | ||
644 | .syntax() | ||
645 | .parent() | ||
646 | .and_then(ast::RecordPat::cast) | ||
647 | }); | ||
648 | |||
649 | // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if | ||
650 | // this is initialized from a `FieldExpr`. | ||
651 | if let Some(record_pat) = record_pat { | ||
652 | self.type_of_pat(&ast::Pat::RecordPat(record_pat)) | ||
653 | } else if let Some(let_stmt) = ast::LetStmt::cast(parent) { | ||
654 | let field_expr = match let_stmt.initializer()? { | ||
655 | ast::Expr::FieldExpr(field_expr) => field_expr, | ||
656 | _ => return None, | ||
657 | }; | ||
658 | |||
659 | self.type_of_expr(&field_expr.expr()?) | ||
660 | } else { | ||
661 | None | ||
662 | } | ||
663 | }) | ||
664 | // Binding a reference to a packed type is possibly unsafe. | ||
665 | .map(|ty| ty.is_packed(self.db)) | ||
666 | .unwrap_or(false) | ||
667 | } | ||
562 | } | 668 | } |
563 | 669 | ||
564 | pub trait ToDef: AstNode + Clone { | 670 | pub trait ToDef: AstNode + Clone { |
@@ -612,6 +718,10 @@ impl<'a> SemanticsScope<'a> { | |||
612 | Some(Module { id: self.resolver.module()? }) | 718 | Some(Module { id: self.resolver.module()? }) |
613 | } | 719 | } |
614 | 720 | ||
721 | pub fn krate(&self) -> Option<Crate> { | ||
722 | Some(Crate { id: self.resolver.krate()? }) | ||
723 | } | ||
724 | |||
615 | /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type | 725 | /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type |
616 | // FIXME: rename to visible_traits to not repeat scope? | 726 | // FIXME: rename to visible_traits to not repeat scope? |
617 | pub fn traits_in_scope(&self) -> FxHashSet<TraitId> { | 727 | pub fn traits_in_scope(&self) -> FxHashSet<TraitId> { |