diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index f24309fa95021..cd29caf304429 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -84,14 +84,14 @@ use self::LiveNodeKind::*; use self::VarKind::*; -use rustc_ast::InlineAsmOptions; +use rustc_ast::{InlineAsmOptions, LitKind, StrStyle}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::*; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet}; +use rustc_hir::{Expr, ExprKind, HirId, HirIdMap, HirIdSet, QPath}; use rustc_index::vec::IndexVec; use rustc_middle::hir::map::Map; use rustc_middle::ty::query::Providers; @@ -332,6 +332,48 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { } } + // Allow todo! macro + /* + Skips checking for unused variables when the trailing expression + of the body is a panic with a message that contains "not yet implemented". + + # Example + + fn foo(x: i32) { + // arbitrary code + todo!() + } + */ + if let ExprKind::Block(block, _) = &body.value.kind { + if let Some(expr) = block.expr { + if let ExprKind::Call(call, [arg]) = expr.kind { + if let ExprKind::Path(QPath::Resolved(_, path)) = call.kind { + if let Res::Def(DefKind::Fn, path_def_id) = &path.res { + let panic_fn = self.tcx.lang_items().panic_fn(); + // Note: there is no function for panic_fmt, so we have to extract it from the debug output :( + // builder doesn't like this being called without panic `self.tcx.def_path_str(*path_def_id);` + let path_str = format!("{:?}", path_def_id); + if Some(*path_def_id) == panic_fn + || ((path_str.contains("std[") || path_str.contains("core[")) + && path_str.contains("panicking::panic")) + { + if let ExprKind::Lit(spanned) = &arg.kind { + if let LitKind::Str(symbol, StrStyle::Cooked) = spanned.node { + if symbol.as_str().starts_with("not yet implemented") { + return; + } + } + } else if format!("{:?}", &arg.kind).contains("not yet implemented") + { + return; + } + } + } + } + } + } + } + if let Some(captures) = maps.tcx.typeck(local_def_id).closure_min_captures.get(&def_id) { for &var_hir_id in captures.keys() { let var_name = maps.tcx.hir().name(var_hir_id); diff --git a/src/test/ui/unused/allow-unused-variables-with-todo.rs b/src/test/ui/unused/allow-unused-variables-with-todo.rs new file mode 100644 index 0000000000000..6891292f5494c --- /dev/null +++ b/src/test/ui/unused/allow-unused-variables-with-todo.rs @@ -0,0 +1,30 @@ +// check-pass + +#[deny(unused_variables)] +fn plain(x: i32, y: i32) -> i32 { + todo!() +} + +#[deny(unused_variables)] +fn message(x: i32, y: i32) -> i32 { + todo!("message") +} + +#[deny(unused_variables)] +fn statement(x: i32, y: i32) -> i32 { + let z = x + y; + todo!() +} + +#[deny(unused_variables)] +fn statement_message(x: i32, y: i32) -> i32 { + let z = x + y; + todo!("message") +} + +fn main() { + plain(0, 1); + message(0, 1); + statement(0, 1); + statement_message(0, 1); +}