diff options
| author | Tomasz Miąsko <tomasz.miasko@gmail.com> | 2020-11-25 00:00:00 +0000 |
|---|---|---|
| committer | Tomasz Miąsko <tomasz.miasko@gmail.com> | 2020-11-25 00:00:00 +0000 |
| commit | 22d3431221df2e9136d303d1762ea7afdded1b5e (patch) | |
| tree | 7968a31ba31fa97d8e9c569a39268f7654f1e560 /compiler/rustc_passes | |
| parent | 773ddbada7183b57928a1d2f5e0722b7f53a3bf7 (diff) | |
| download | rust-22d3431221df2e9136d303d1762ea7afdded1b5e.tar.gz rust-22d3431221df2e9136d303d1762ea7afdded1b5e.zip | |
Validate use of parameters in naked functions
* Reject use of parameters inside naked function body. * Reject use of patterns inside function parameters, to emphasize role of parameters a signature declaration (mirroring existing behaviour for function declarations) and avoid generating code introducing specified bindings.
Diffstat (limited to 'compiler/rustc_passes')
| -rw-r--r-- | compiler/rustc_passes/src/lib.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/naked_functions.rs | 113 |
2 files changed, 116 insertions, 0 deletions
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index c32c9c8eaa6..9759a500e06 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -7,6 +7,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(const_fn)] #![feature(const_panic)] +#![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] #![feature(nll)] #![feature(or_patterns)] @@ -32,6 +33,7 @@ pub mod layout_test; mod lib_features; mod liveness; pub mod loops; +mod naked_functions; mod reachable; mod region; pub mod stability; @@ -46,6 +48,7 @@ pub fn provide(providers: &mut Providers) { lang_items::provide(providers); lib_features::provide(providers); loops::provide(providers); + naked_functions::provide(providers); liveness::provide(providers); intrinsicck::provide(providers); reachable::provide(providers); diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs new file mode 100644 index 00000000000..6ef45cdd391 --- /dev/null +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -0,0 +1,113 @@ +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{ErasedMap, FnKind, NestedVisitorMap, Visitor}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::sym; +use rustc_span::Span; + +fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { + tcx.hir().visit_item_likes_in_module( + module_def_id, + &mut CheckNakedFunctions { tcx }.as_deep_visitor(), + ); +} + +crate fn provide(providers: &mut Providers) { + *providers = Providers { check_mod_naked_functions, ..*providers }; +} + +struct CheckNakedFunctions<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> { + type Map = ErasedMap<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } + + fn visit_fn( + &mut self, + fk: FnKind<'v>, + _fd: &'tcx hir::FnDecl<'tcx>, + body_id: hir::BodyId, + _span: Span, + _hir_id: hir::HirId, + ) { + match fk { + // Rejected during attribute check. Do not validate further. + FnKind::Closure(..) => return, + FnKind::ItemFn(..) | FnKind::Method(..) => {} + } + + let naked = fk.attrs().iter().any(|attr| attr.has_name(sym::naked)); + if naked { + let body = self.tcx.hir().body(body_id); + check_params(self.tcx, body); + check_body(self.tcx, body); + } + } +} + +/// Checks that parameters don't use patterns. Mirrors the checks for function declarations. +fn check_params(tcx: TyCtxt<'_>, body: &hir::Body<'_>) { + for param in body.params { + match param.pat.kind { + hir::PatKind::Wild + | hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, _, None) => {} + _ => { + tcx.sess + .struct_span_err( + param.pat.span, + "patterns not allowed in naked function parameters", + ) + .emit(); + } + } + } +} + +/// Checks that function parameters aren't referenced in the function body. +fn check_body<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) { + let mut params = hir::HirIdSet::default(); + for param in body.params { + param.pat.each_binding(|_binding_mode, hir_id, _span, _ident| { + params.insert(hir_id); + }); + } + CheckBody { tcx, params }.visit_body(body); +} + +struct CheckBody<'tcx> { + tcx: TyCtxt<'tcx>, + params: hir::HirIdSet, +} + +impl<'tcx> Visitor<'tcx> for CheckBody<'tcx> { + type Map = ErasedMap<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::Path(hir::QPath::Resolved( + _, + hir::Path { res: hir::def::Res::Local(var_hir_id), .. }, + )) = expr.kind + { + if self.params.contains(var_hir_id) { + self.tcx + .sess + .struct_span_err( + expr.span, + "use of parameters not allowed inside naked functions", + ) + .emit(); + } + } + hir::intravisit::walk_expr(self, expr); + } +} |
