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
63 changes: 33 additions & 30 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,44 @@ on:
branches: [master]
pull_request:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

env:
CARGO_TERM_COLOR: always

jobs:
ci:
name: Continuous Integration
name: Dylint (nightly)
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
pkg-config \
libssl-dev \
libgit2-dev \
libcurl4-openssl-dev \
build-essential

- uses: dtolnay/rust-toolchain@nightly
- uses: dtolnay/rust-toolchain@v1
with:
components: rustc-dev,rust-src,llvm-tools-preview
toolchain: nightly
components: rustfmt, clippy, rustc-dev, rust-src, llvm-tools-preview

- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
~/.dylint_drivers
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-

- name: Install Dylint
run: cargo install cargo-dylint dylint-link --locked

- name: Run Dylint
run: cargo dylint --workspace --all
- name: Ensure cargo in PATH
run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH

- uses: Swatinem/rust-cache@v2

- uses: cargo-bins/cargo-binstall@main

- name: Install dependencies
run: |
cargo binstall -y \
cargo-make \
cargo-deny \
cargo-audit \
cargo-dylint \
dylint-link

- name: Run CI
run: cargo make ci
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# OS generated files
.DS_Store
Thumbs.db

# Generated by Cargo
# will have compiled files and executables
debug
Expand Down
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"dylint",
"krate",
"qpath",
"rustflags",
"rustsec",
"rustup",
"typeck",
Expand Down
1 change: 1 addition & 0 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,6 @@ args = [
"ubuntu-latest=ghcr.io/catthehacker/ubuntu:full-latest",
"--container-architecture",
"linux/amd64",
"--rm",
]
workspace = false
5 changes: 0 additions & 5 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ confidence-threshold = 0.9
allow = [
"MIT",
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"Zlib",
"Unicode-3.0",
"CDLA-Permissive-2.0",
]

[bans]
Expand Down
106 changes: 50 additions & 56 deletions rules/indexing_usage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,51 +33,47 @@ impl<'tcx> LateLintPass<'tcx> for SecurityIndexingUsage {
context: &LateContext<'tcx>,
expression: &'tcx Expr<'tcx>,
) {
match &expression.kind {
ExprKind::Index(_, index_expr, _) => {
match &index_expr.kind {
// Literal indexing: array[0].
ExprKind::Lit(_) => {
context.span_lint(
SECURITY_INDEXING_USAGE,
expression.span,
|diagnostic: &mut Diag<'_, ()>| {
diagnostic.primary_message(
"Usage of indexing operation detected.",
);
},
);
},
if let ExprKind::Index(_, index_expr, _) = &expression.kind {
match &index_expr.kind {
// Literal indexing: array[0].
ExprKind::Lit(_) => {
context.span_lint(
SECURITY_INDEXING_USAGE,
expression.span,
|diagnostic: &mut Diag<'_, ()>| {
diagnostic.primary_message(
"Usage of indexing operation detected.",
);
},
);
},

// Range slicing: array[1..], array[..], array[a..b].
ExprKind::Struct(_, _, _) => {
context.span_lint(
SECURITY_INDEXING_USAGE,
expression.span,
|diagnostic: &mut Diag<'_, ()>| {
diagnostic.primary_message(
"Usage of slicing operation detected.",
);
},
);
},
// Range slicing: array[1..], array[..], array[a..b].
ExprKind::Struct(_, _, _) => {
context.span_lint(
SECURITY_INDEXING_USAGE,
expression.span,
|diagnostic: &mut Diag<'_, ()>| {
diagnostic.primary_message(
"Usage of slicing operation detected.",
);
},
);
},

// Any other dynamic indexing: array[i].
_ => {
context.span_lint(
SECURITY_INDEXING_USAGE,
expression.span,
|diagnostic: &mut Diag<'_, ()>| {
diagnostic.primary_message(
"Usage of indexing operation detected.",
);
},
);
},
}
},

_ => {},
// Any other dynamic indexing: array[i].
_ => {
context.span_lint(
SECURITY_INDEXING_USAGE,
expression.span,
|diagnostic: &mut Diag<'_, ()>| {
diagnostic.primary_message(
"Usage of indexing operation detected.",
);
},
);
},
}
}
}

Expand All @@ -97,20 +93,18 @@ impl<'tcx> LateLintPass<'tcx> for SecurityIndexingUsage {
if let ItemKind::Impl(implementation) = &item.kind
&& let Some(trait_ref) = implementation.of_trait
&& let Some(def_id) = trait_ref.trait_ref.path.res.opt_def_id()
&& (context.tcx.lang_items().index_trait() == Some(def_id)
|| context.tcx.lang_items().index_mut_trait() == Some(def_id))
{
if context.tcx.lang_items().index_trait() == Some(def_id)
|| context.tcx.lang_items().index_mut_trait() == Some(def_id)
{
context.span_lint(
SECURITY_INDEXING_USAGE,
item.span,
|diagnostic: &mut Diag<'_, ()>| {
diagnostic.primary_message(
"Implementation of Index/IndexMut trait detected.",
);
},
);
}
context.span_lint(
SECURITY_INDEXING_USAGE,
item.span,
|diagnostic: &mut Diag<'_, ()>| {
diagnostic.primary_message(
"Implementation of Index/IndexMut trait detected.",
);
},
);
}
}
}
Expand Down
14 changes: 13 additions & 1 deletion rules/missing_type/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ extern crate rustc_hir;
extern crate rustc_lint;
extern crate rustc_middle;
extern crate rustc_session;
extern crate rustc_span;

use rustc_errors::Diag;
use rustc_hir::{Body, Expr, ExprKind, LetStmt, PatKind};
use rustc_hir::{Body, BodyId, Expr, ExprKind, LetStmt, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext, LintStore};
use rustc_middle::ty::TyCtxt;
use rustc_session::{Session, declare_lint, declare_lint_pass};
Expand Down Expand Up @@ -55,6 +56,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingType {
if matches!(local.pat.kind, PatKind::Wild) {
return;
}

let Some(body_id): Option<BodyId> = context.enclosing_body else {
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.
Comment on lines +64 to +65
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The comment on lines 64-65 is nearly identical to the comment on lines 70-72, but they describe different checks. The first check (lines 60-68) verifies if the enclosing function body is from a macro expansion, while the second check (line 74) verifies if the let statement itself is from a macro expansion. The comment should be updated to clarify this distinction. Consider updating the comment to: "Skip if the enclosing function body is from a macro expansion, as the let statement may be generated by macro code."

Suggested change
// Skip if the let statement is from a macro expansion, as it may not
// be possible to determine the type annotation in that case.
// Skip if the enclosing function body is from a macro expansion, as the
// let statement may be generated by macro code.

Copilot uses AI. Check for mistakes.
if context.tcx.hir_body(body_id).value.span.from_expansion() {
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,
Expand Down
26 changes: 12 additions & 14 deletions rules/panic_usage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,20 @@ impl<'tcx> LateLintPass<'tcx> for SecurityPanicUsage {
&& let Some(def_id) = context
.typeck_results()
.type_dependent_def_id(expression.hir_id)
{
if context.tcx.is_diagnostic_item(sym::unwrap, def_id)
&& (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)
|| context.tcx.is_diagnostic_item(sym::option_expect, def_id)
{
context.span_lint(
SECURITY_PANIC_USAGE,
expression.span,
|diagnostic: &mut Diag<'_, ()>| {
diagnostic.primary_message(
"Call to panic backend `unwrap/expect` detected.",
);
},
);
}
|| context.tcx.is_diagnostic_item(sym::option_expect, def_id))
{
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