aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r--crates/ra_hir/src/semantics.rs110
1 files changed, 75 insertions, 35 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 1072b3971..f706a186e 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -25,7 +25,8 @@ use crate::{
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, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, 27 AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef,
28 Module, 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};
30use resolver::TypeNs; 31use resolver::TypeNs;
31 32
@@ -280,45 +281,84 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
280 self.imp.assert_contains_node(node) 281 self.imp.assert_contains_node(node)
281 } 282 }
282 283
283 pub fn is_unsafe_pat(&self, pat: &ast::Pat) -> bool { 284 pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> Option<()> {
284 let ty = (|| { 285 let expr = method_call_expr.expr()?;
285 let parent = match pat { 286 let field_expr =
286 ast::Pat::BindPat(bind_pat) => bind_pat.syntax().parent()?, 287 if let ast::Expr::FieldExpr(field_expr) = expr { field_expr } else { return None };
287 _ => return None, 288 let ty = self.type_of_expr(&field_expr.expr()?)?;
288 }; 289 if !ty.is_packed(self.db) {
289 290 return None;
290 // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or 291 }
291 // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
292 // so this tries to lookup the `BindPat` anywhere along that structure to the
293 // `RecordPat` so we can get the containing type.
294 let record_pat = ast::RecordFieldPat::cast(parent.clone())
295 .and_then(|record_pat| record_pat.syntax().parent())
296 .or_else(|| Some(parent.clone()))
297 .and_then(|parent| {
298 ast::RecordFieldPatList::cast(parent)?
299 .syntax()
300 .parent()
301 .and_then(ast::RecordPat::cast)
302 });
303
304 // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
305 // this is initialized from a `FieldExpr`.
306 if let Some(record_pat) = record_pat {
307 self.type_of_pat(&ast::Pat::RecordPat(record_pat))
308 } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
309 let field_expr = match let_stmt.initializer()? {
310 ast::Expr::FieldExpr(field_expr) => field_expr,
311 _ => return None,
312 };
313 292
314 self.type_of_expr(&field_expr.expr()?) 293 let func = self.resolve_method_call(&method_call_expr)?;
294 if func.has_self_param(self.db) {
295 let params = func.params(self.db);
296 if matches!(params.into_iter().next(), Some(TypeRef::Reference(..))) {
297 Some(())
315 } else { 298 } else {
316 None 299 None
317 } 300 }
318 })(); 301 } else {
302 None
303 }
304 }
319 305
320 // Binding a reference to a packed type is possibly unsafe. 306 pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
321 ty.map(|ty| ty.is_packed(self.db)).unwrap_or(false) 307 ref_expr
308 .expr()
309 .and_then(|expr| {
310 let field_expr = match expr {
311 ast::Expr::FieldExpr(field_expr) => field_expr,
312 _ => return None,
313 };
314 let expr = field_expr.expr()?;
315 self.type_of_expr(&expr)
316 })
317 // Binding a reference to a packed type is possibly unsafe.
318 .map(|ty| ty.is_packed(self.db))
319 .unwrap_or(false)
320
321 // FIXME This needs layout computation to be correct. It will highlight
322 // more than it should with the current implementation.
323 }
324
325 pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool {
326 bind_pat
327 .syntax()
328 .parent()
329 .and_then(|parent| {
330 // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
331 // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
332 // so this tries to lookup the `BindPat` anywhere along that structure to the
333 // `RecordPat` so we can get the containing type.
334 let record_pat = ast::RecordFieldPat::cast(parent.clone())
335 .and_then(|record_pat| record_pat.syntax().parent())
336 .or_else(|| Some(parent.clone()))
337 .and_then(|parent| {
338 ast::RecordFieldPatList::cast(parent)?
339 .syntax()
340 .parent()
341 .and_then(ast::RecordPat::cast)
342 });
343
344 // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
345 // this is initialized from a `FieldExpr`.
346 if let Some(record_pat) = record_pat {
347 self.type_of_pat(&ast::Pat::RecordPat(record_pat))
348 } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
349 let field_expr = match let_stmt.initializer()? {
350 ast::Expr::FieldExpr(field_expr) => field_expr,
351 _ => return None,
352 };
353
354 self.type_of_expr(&field_expr.expr()?)
355 } else {
356 None
357 }
358 })
359 // Binding a reference to a packed type is possibly unsafe.
360 .map(|ty| ty.is_packed(self.db))
361 .unwrap_or(false)
322 } 362 }
323} 363}
324 364