Skip to content

Commit 27dc614

Browse files
authored
Merge pull request #19274 from Veykril/push-pouwrwwrlrlt
Highlight unsafe operations as unsafe, not definitions
2 parents b139e21 + 9fc0ffe commit 27dc614

37 files changed

+547
-537
lines changed

crates/hir-ty/src/diagnostics.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@ pub use crate::diagnostics::{
99
expr::{
1010
record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic,
1111
},
12-
unsafe_check::{missing_unsafe, unsafe_expressions, InsideUnsafeBlock, UnsafetyReason},
12+
unsafe_check::{
13+
missing_unsafe, unsafe_operations, unsafe_operations_for_body, InsideUnsafeBlock,
14+
UnsafetyReason,
15+
},
1316
};

crates/hir-ty/src/diagnostics/unsafe_check.rs

+34-8
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,26 @@ enum UnsafeDiagnostic {
9595
DeprecatedSafe2024 { node: ExprId, inside_unsafe_block: InsideUnsafeBlock },
9696
}
9797

98-
pub fn unsafe_expressions(
98+
pub fn unsafe_operations_for_body(
99+
db: &dyn HirDatabase,
100+
infer: &InferenceResult,
101+
def: DefWithBodyId,
102+
body: &Body,
103+
callback: &mut dyn FnMut(ExprOrPatId),
104+
) {
105+
let mut visitor_callback = |diag| {
106+
if let UnsafeDiagnostic::UnsafeOperation { node, .. } = diag {
107+
callback(node);
108+
}
109+
};
110+
let mut visitor = UnsafeVisitor::new(db, infer, body, def, &mut visitor_callback);
111+
visitor.walk_expr(body.body_expr);
112+
for &param in &body.params {
113+
visitor.walk_pat(param);
114+
}
115+
}
116+
117+
pub fn unsafe_operations(
99118
db: &dyn HirDatabase,
100119
infer: &InferenceResult,
101120
def: DefWithBodyId,
@@ -281,13 +300,6 @@ impl<'a> UnsafeVisitor<'a> {
281300
self.on_unsafe_op(current.into(), UnsafetyReason::RawPtrDeref);
282301
}
283302
}
284-
Expr::Unsafe { .. } => {
285-
let old_inside_unsafe_block =
286-
mem::replace(&mut self.inside_unsafe_block, InsideUnsafeBlock::Yes);
287-
self.body.walk_child_exprs_without_pats(current, |child| self.walk_expr(child));
288-
self.inside_unsafe_block = old_inside_unsafe_block;
289-
return;
290-
}
291303
&Expr::Assignment { target, value: _ } => {
292304
let old_inside_assignment = mem::replace(&mut self.inside_assignment, true);
293305
self.walk_pats_top(std::iter::once(target), current);
@@ -306,6 +318,20 @@ impl<'a> UnsafeVisitor<'a> {
306318
}
307319
}
308320
}
321+
Expr::Unsafe { statements, .. } => {
322+
let old_inside_unsafe_block =
323+
mem::replace(&mut self.inside_unsafe_block, InsideUnsafeBlock::Yes);
324+
self.walk_pats_top(
325+
statements.iter().filter_map(|statement| match statement {
326+
&Statement::Let { pat, .. } => Some(pat),
327+
_ => None,
328+
}),
329+
current,
330+
);
331+
self.body.walk_child_exprs_without_pats(current, |child| self.walk_expr(child));
332+
self.inside_unsafe_block = old_inside_unsafe_block;
333+
return;
334+
}
309335
Expr::Block { statements, .. } | Expr::Async { statements, .. } => {
310336
self.walk_pats_top(
311337
statements.iter().filter_map(|statement| match statement {

crates/hir/src/semantics.rs

+26-87
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::{
1212

1313
use either::Either;
1414
use hir_def::{
15+
expr_store::ExprOrPatSource,
1516
hir::{Expr, ExprOrPatId},
1617
lower::LowerCtx,
1718
nameres::{MacroSubNs, ModuleOrigin},
@@ -30,6 +31,7 @@ use hir_expand::{
3031
name::AsName,
3132
ExpandResult, FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
3233
};
34+
use hir_ty::diagnostics::unsafe_operations_for_body;
3335
use intern::{sym, Symbol};
3436
use itertools::Itertools;
3537
use rustc_hash::{FxHashMap, FxHashSet};
@@ -48,8 +50,8 @@ use crate::{
4850
db::HirDatabase,
4951
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
5052
source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer},
51-
Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
52-
ConstParam, Crate, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource,
53+
Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam,
54+
Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource,
5355
HirFileId, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro,
5456
Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait,
5557
TraitAlias, TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
@@ -1555,6 +1557,19 @@ impl<'db> SemanticsImpl<'db> {
15551557
.matched_arm
15561558
}
15571559

1560+
pub fn get_unsafe_ops(&self, def: DefWithBody) -> FxHashSet<ExprOrPatSource> {
1561+
let def = DefWithBodyId::from(def);
1562+
let (body, source_map) = self.db.body_with_source_map(def);
1563+
let infer = self.db.infer(def);
1564+
let mut res = FxHashSet::default();
1565+
unsafe_operations_for_body(self.db, &infer, def, &body, &mut |node| {
1566+
if let Ok(node) = source_map.expr_or_pat_syntax(node) {
1567+
res.insert(node);
1568+
}
1569+
});
1570+
res
1571+
}
1572+
15581573
pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
15591574
let Some(mac) = self.resolve_macro_call(macro_call) else { return false };
15601575
if mac.is_asm_or_global_asm(self.db) {
@@ -1682,6 +1697,15 @@ impl<'db> SemanticsImpl<'db> {
16821697
Some(res)
16831698
}
16841699

1700+
pub fn body_for(&self, node: InFile<&SyntaxNode>) -> Option<DefWithBody> {
1701+
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
1702+
1703+
match container {
1704+
ChildContainer::DefWithBodyId(def) => Some(def.into()),
1705+
_ => None,
1706+
}
1707+
}
1708+
16851709
/// Returns none if the file of the node is not part of a crate.
16861710
fn analyze(&self, node: &SyntaxNode) -> Option<SourceAnalyzer> {
16871711
let node = self.find_file(node);
@@ -1783,91 +1807,6 @@ impl<'db> SemanticsImpl<'db> {
17831807
InFile::new(file_id, node)
17841808
}
17851809

1786-
pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool {
1787-
method_call_expr
1788-
.receiver()
1789-
.and_then(|expr| {
1790-
let field_expr = match expr {
1791-
ast::Expr::FieldExpr(field_expr) => field_expr,
1792-
_ => return None,
1793-
};
1794-
let ty = self.type_of_expr(&field_expr.expr()?)?.original;
1795-
if !ty.is_packed(self.db) {
1796-
return None;
1797-
}
1798-
1799-
let func = self.resolve_method_call(method_call_expr)?;
1800-
let res = match func.self_param(self.db)?.access(self.db) {
1801-
Access::Shared | Access::Exclusive => true,
1802-
Access::Owned => false,
1803-
};
1804-
Some(res)
1805-
})
1806-
.unwrap_or(false)
1807-
}
1808-
1809-
pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
1810-
ref_expr
1811-
.expr()
1812-
.and_then(|expr| {
1813-
let field_expr = match expr {
1814-
ast::Expr::FieldExpr(field_expr) => field_expr,
1815-
_ => return None,
1816-
};
1817-
let expr = field_expr.expr()?;
1818-
self.type_of_expr(&expr)
1819-
})
1820-
// Binding a reference to a packed type is possibly unsafe.
1821-
.map(|ty| ty.original.is_packed(self.db))
1822-
.unwrap_or(false)
1823-
1824-
// FIXME This needs layout computation to be correct. It will highlight
1825-
// more than it should with the current implementation.
1826-
}
1827-
1828-
pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
1829-
if ident_pat.ref_token().is_none() {
1830-
return false;
1831-
}
1832-
1833-
ident_pat
1834-
.syntax()
1835-
.parent()
1836-
.and_then(|parent| {
1837-
// `IdentPat` can live under `RecordPat` directly under `RecordPatField` or
1838-
// `RecordPatFieldList`. `RecordPatField` also lives under `RecordPatFieldList`,
1839-
// so this tries to lookup the `IdentPat` anywhere along that structure to the
1840-
// `RecordPat` so we can get the containing type.
1841-
let record_pat = ast::RecordPatField::cast(parent.clone())
1842-
.and_then(|record_pat| record_pat.syntax().parent())
1843-
.or_else(|| Some(parent.clone()))
1844-
.and_then(|parent| {
1845-
ast::RecordPatFieldList::cast(parent)?
1846-
.syntax()
1847-
.parent()
1848-
.and_then(ast::RecordPat::cast)
1849-
});
1850-
1851-
// If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
1852-
// this is initialized from a `FieldExpr`.
1853-
if let Some(record_pat) = record_pat {
1854-
self.type_of_pat(&ast::Pat::RecordPat(record_pat))
1855-
} else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
1856-
let field_expr = match let_stmt.initializer()? {
1857-
ast::Expr::FieldExpr(field_expr) => field_expr,
1858-
_ => return None,
1859-
};
1860-
1861-
self.type_of_expr(&field_expr.expr()?)
1862-
} else {
1863-
None
1864-
}
1865-
})
1866-
// Binding a reference to a packed type is possibly unsafe.
1867-
.map(|ty| ty.original.is_packed(self.db))
1868-
.unwrap_or(false)
1869-
}
1870-
18711810
/// Returns `true` if the `node` is inside an `unsafe` context.
18721811
pub fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
18731812
let Some(enclosing_item) =

crates/hir/src/source_analyzer.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use hir_expand::{
3535
};
3636
use hir_ty::{
3737
diagnostics::{
38-
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
38+
record_literal_missing_fields, record_pattern_missing_fields, unsafe_operations,
3939
InsideUnsafeBlock,
4040
},
4141
from_assoc_type_id,
@@ -1160,7 +1160,7 @@ impl SourceAnalyzer {
11601160
if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) {
11611161
let mut is_unsafe = false;
11621162
let mut walk_expr = |expr_id| {
1163-
unsafe_expressions(db, infer, *def, body, expr_id, &mut |inside_unsafe_block| {
1163+
unsafe_operations(db, infer, *def, body, expr_id, &mut |inside_unsafe_block| {
11641164
is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No
11651165
})
11661166
};

crates/ide-assists/src/handlers/extract_function.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,10 @@ impl FunctionBody {
750750
ast::Stmt::Item(_) => (),
751751
ast::Stmt::LetStmt(stmt) => {
752752
if let Some(pat) = stmt.pat() {
753-
walk_pat(&pat, cb);
753+
walk_pat(&pat, &mut |pat| {
754+
cb(pat);
755+
std::ops::ControlFlow::<(), ()>::Continue(())
756+
});
754757
}
755758
if let Some(expr) = stmt.initializer() {
756759
walk_patterns_in_expr(&expr, cb);

crates/ide-db/src/syntax_helpers/node_ext.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
//! Various helper functions to work with SyntaxNodes.
2+
use std::ops::ControlFlow;
3+
24
use itertools::Itertools;
35
use parser::T;
46
use span::Edition;
@@ -119,7 +121,10 @@ pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
119121
match ast::Stmt::cast(node.clone()) {
120122
Some(ast::Stmt::LetStmt(l)) => {
121123
if let Some(pat) = l.pat() {
122-
walk_pat(&pat, cb);
124+
walk_pat(&pat, &mut |pat| {
125+
cb(pat);
126+
ControlFlow::<(), ()>::Continue(())
127+
});
123128
}
124129
if let Some(expr) = l.initializer() {
125130
walk_patterns_in_expr(&expr, cb);
@@ -154,15 +159,21 @@ pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
154159
}
155160
} else if let Some(pat) = ast::Pat::cast(node) {
156161
preorder.skip_subtree();
157-
walk_pat(&pat, cb);
162+
walk_pat(&pat, &mut |pat| {
163+
cb(pat);
164+
ControlFlow::<(), ()>::Continue(())
165+
});
158166
}
159167
}
160168
}
161169
}
162170
}
163171

164172
/// Preorder walk all the pattern's sub patterns.
165-
pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
173+
pub fn walk_pat<T>(
174+
pat: &ast::Pat,
175+
cb: &mut dyn FnMut(ast::Pat) -> ControlFlow<T>,
176+
) -> ControlFlow<T> {
166177
let mut preorder = pat.syntax().preorder();
167178
while let Some(event) = preorder.next() {
168179
let node = match event {
@@ -173,10 +184,10 @@ pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
173184
match ast::Pat::cast(node) {
174185
Some(pat @ ast::Pat::ConstBlockPat(_)) => {
175186
preorder.skip_subtree();
176-
cb(pat);
187+
cb(pat)?;
177188
}
178189
Some(pat) => {
179-
cb(pat);
190+
cb(pat)?;
180191
}
181192
// skip const args
182193
None if ast::GenericArg::can_cast(kind) => {
@@ -185,6 +196,7 @@ pub fn walk_pat(pat: &ast::Pat, cb: &mut dyn FnMut(ast::Pat)) {
185196
None => (),
186197
}
187198
}
199+
ControlFlow::Continue(())
188200
}
189201

190202
/// Preorder walk all the type's sub types.

0 commit comments

Comments
 (0)