Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@
"callsite",
"catthehacker",
"ctxt",
"desugared",
"dylint",
"krate",
"qpath",
"rustsec",
"rustup",
"typeck",
"usize"
]
}
2 changes: 1 addition & 1 deletion rules/indexing_usage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ mod tests {
#[test]
fn ui() {
Test::src_base(env!("CARGO_PKG_NAME"), "ui")
.rustc_flags(["-Z", "ui-testing"])
.rustc_flags(["--edition=2024", "-Z", "ui-testing"])
.run();
}
}
24 changes: 23 additions & 1 deletion rules/missing_type/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ impl<'tcx> LateLintPass<'tcx> for MissingType {
if matches!(local.pat.kind, PatKind::Wild) {
return;
}
// Skip if the let statement is from a macro expansion, as it may not
// be possible to determine the type annotation in that case.
// Ignore anything coming from macro expansion (async_trait, derives,
// etc.)
if local.span.from_expansion() {
return;
}

// Ignore desugared constructs (async lowering, ?, for loops, etc.)
if local.span.desugaring_kind().is_some() {
return;
}

// Check if the let statement has an explicit type annotation. If not,
// emit a warning.
Expand Down Expand Up @@ -84,12 +96,22 @@ impl<'tcx> LateLintPass<'tcx> for MissingType {
context: &LateContext<'tcx>,
expression: &'tcx Expr<'tcx>,
) {
if expression.span.from_expansion() {
return;
}

// Only check closure expressions.
let ExprKind::Closure(closure): &ExprKind<'tcx> = &expression.kind
else {
return;
};

// Skip if the expression is from a macro expansion, as it may not be
// possible to determine the type annotation in that case.
Comment on lines +109 to +110
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "Skip if the expression is from a macro expansion" is inaccurate here. The check from_expansion() was already performed at lines 99-101. This check is actually filtering out coroutines (async blocks, generators), not macro expansions. The comment should be updated to say something like "Skip coroutines (async blocks, generators) as they have compiler-generated state machines with implicit type annotations."

Suggested change
// Skip if the expression is from a macro expansion, as it may not be
// possible to determine the type annotation in that case.
// Skip coroutines (async blocks, generators) as they have
// compiler-generated state machines with implicit type annotations.

Copilot uses AI. Check for mistakes.
if matches!(closure.kind, rustc_hir::ClosureKind::Coroutine(_)) {
return;
}

// Get the body of the closure to access its parameters.
let body: &Body<'_> = context.tcx.hir_body(closure.body);

Expand Down Expand Up @@ -154,7 +176,7 @@ mod tests {
#[test]
fn ui() {
Test::src_base(env!("CARGO_PKG_NAME"), "ui")
.rustc_flags(["-Z", "ui-testing"])
.rustc_flags(["--edition=2024", "-Z", "ui-testing"])
.run();
}
}
16 changes: 16 additions & 0 deletions rules/missing_type/ui/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,19 @@ fn main() {
// Closure with `_` pattern (should not trigger).
let ignore: fn(i32) -> i32 = |_| 0;
}

/// Asynchronous function example to demonstrate that the `missing_type` lint
/// does not trigger for async functions, as they may have implicit return
/// types and parameters that are not explicitly annotated. This function takes
/// an `i32` parameter and returns a future that resolves to an `i32`. The lint
/// should not emit a warning for this function, as it is common for async
/// functions to have implicit return types and parameters without explicit
/// type annotations, especially when using async/await syntax.
async fn async_example(x: i32) -> i32 {
x + 1
}

async fn async_with_let() -> i32 {
let value: i32 = 10;
value
}
58 changes: 24 additions & 34 deletions rules/panic_usage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext, LintStore};
use rustc_middle::ty::TyCtxt;
use rustc_session::{Session, declare_lint, declare_lint_pass};
use rustc_span::sym;

declare_lint! {
pub SECURITY_PANIC_USAGE,
Expand All @@ -21,26 +22,6 @@ declare_lint! {

declare_lint_pass!(SecurityPanicUsage => [SECURITY_PANIC_USAGE]);

/// Enum representing the different kinds of panic-related constructs that can
/// be detected by the `SECURITY_PANIC_USAGE` lint, such as calls to `unwrap`
/// and `expect` methods, as well as calls to panic-related functions in the
/// standard library.
#[derive(Debug, Clone, Copy)]
enum PanicKind {
Unwrap,
Expect,
}

impl PanicKind {
fn from_method(name: &str) -> Option<Self> {
match name {
"unwrap" => Some(Self::Unwrap),
"expect" => Some(Self::Expect),
_ => None,
}
}
}

/// Enum representing the different panic backends that can be detected by the
/// `SECURITY_PANIC_USAGE` lint, such as the `panicking` module, the
/// `panic_fmt` function, the `panic_display` function, the `assert_failed`
Expand Down Expand Up @@ -87,20 +68,29 @@ impl<'tcx> LateLintPass<'tcx> for SecurityPanicUsage {
expression: &'tcx Expr<'tcx>,
) {
// Detect direct calls to `unwrap` and `expect` methods.
if let ExprKind::MethodCall(segment, _, _, _) = &expression.kind
&& let Some(kind) =
PanicKind::from_method(segment.ident.name.as_str())
// This checks for method calls where the method name is `unwrap` or
// `expect`, and the method is defined in the local crate (to avoid
// false positives from external crates).
Comment on lines +71 to +73
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states "the method is defined in the local crate (to avoid false positives from external crates)" but the code actually checks if the method matches standard library diagnostic items (unwrap, option_unwrap, etc.), which are from external crates. The comment should be updated to accurately reflect that this code detects calls to standard library unwrap/expect methods using diagnostic items.

Copilot uses AI. Check for mistakes.
if let ExprKind::MethodCall(_, _, _, _) = &expression.kind
&& let Some(def_id) = context
.typeck_results()
.type_dependent_def_id(expression.hir_id)
{
context.span_lint(
SECURITY_PANIC_USAGE,
expression.span,
|diagnostic: &mut Diag<'_, ()>| {
diagnostic.primary_message(format!(
"Call to panic backend `{kind:?}` detected."
));
},
);
return;
if context.tcx.is_diagnostic_item(sym::unwrap, def_id)
|| context.tcx.is_diagnostic_item(sym::option_unwrap, def_id)
|| context.tcx.is_diagnostic_item(sym::except, def_id)
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in diagnostic item symbol: sym::except should be sym::expect. This typo prevents the lint from correctly detecting calls to the expect method on Result types. The correct diagnostic item name for the expect method is expect, not except.

Suggested change
|| context.tcx.is_diagnostic_item(sym::except, def_id)
|| context.tcx.is_diagnostic_item(sym::expect, def_id)

Copilot uses AI. Check for mistakes.
|| context.tcx.is_diagnostic_item(sym::option_expect, def_id)
Comment on lines +81 to +82
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code only checks for sym::unwrap and sym::option_unwrap but may be missing Result::unwrap. The standard library provides diagnostic items for both Option and Result versions of these methods. Consider adding checks for sym::result_unwrap and sym::result_expect (if they exist) to ensure complete coverage of panic-inducing methods on both Option and Result types.

Suggested change
|| context.tcx.is_diagnostic_item(sym::except, def_id)
|| context.tcx.is_diagnostic_item(sym::option_expect, def_id)
|| context.tcx.is_diagnostic_item(sym::expect, def_id)
|| context.tcx.is_diagnostic_item(sym::option_expect, def_id)
|| context.tcx.is_diagnostic_item(sym::result_unwrap, def_id)
|| context.tcx.is_diagnostic_item(sym::result_expect, def_id)

Copilot uses AI. Check for mistakes.
{
context.span_lint(
SECURITY_PANIC_USAGE,
expression.span,
|diagnostic: &mut Diag<'_, ()>| {
diagnostic.primary_message(
"Call to panic backend `unwrap/expect` detected.",
);
},
);
}
}

// Detect calls to panic-related functions in the standard library.
Expand Down Expand Up @@ -162,7 +152,7 @@ mod tests {
#[test]
fn ui() {
Test::src_base(env!("CARGO_PKG_NAME"), "ui")
.rustc_flags(["-Z", "ui-testing"])
.rustc_flags(["--edition=2024", "-Z", "ui-testing"])
.run();
}
}
6 changes: 3 additions & 3 deletions rules/panic_usage/ui/main.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
warning: Call to panic backend `Unwrap` detected.
warning: Call to panic backend `unwrap/expect` detected.
--> $DIR/main.rs:10:5
|
LL | x.unwrap(); // Should trigger.
Expand All @@ -10,13 +10,13 @@ note: the lint level is defined here
LL | #![warn(security_panic_usage)]
| ^^^^^^^^^^^^^^^^^^^^

warning: Call to panic backend `Expect` detected.
warning: Call to panic backend `unwrap/expect` detected.
--> $DIR/main.rs:11:5
|
LL | x.expect(""); // should trigger.
| ^^^^^^^^^^^^

warning: Call to panic backend `BeginPanic` detected.
warning: Call to panic backend `PanicFmt` detected.
--> $DIR/main.rs:13:5
|
LL | panic!(""); // Should trigger.
Expand Down
2 changes: 1 addition & 1 deletion rules/unsafe_usage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ mod tests {
#[test]
fn ui() {
Test::src_base(env!("CARGO_PKG_NAME"), "ui")
.rustc_flags(["-Z", "ui-testing"])
.rustc_flags(["--edition=2024", "-Z", "ui-testing"])
.run();
}
}