about summary refs log tree commit diff
path: root/compiler/rustc_passes
diff options
context:
space:
mode:
authorTomasz Miąsko <tomasz.miasko@gmail.com>2020-11-25 00:00:00 +0000
committerTomasz Miąsko <tomasz.miasko@gmail.com>2020-11-25 00:00:00 +0000
commit22d3431221df2e9136d303d1762ea7afdded1b5e (patch)
tree7968a31ba31fa97d8e9c569a39268f7654f1e560 /compiler/rustc_passes
parent773ddbada7183b57928a1d2f5e0722b7f53a3bf7 (diff)
downloadrust-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.rs3
-rw-r--r--compiler/rustc_passes/src/naked_functions.rs113
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);
+    }
+}