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: 0 additions & 3 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,5 @@ binary-dep-depinfo = true
[profile.dev]
split-debuginfo = "unpacked"

[env]
RPL_PATS = "docs/patterns-pest/"

# [profile.dev.package.lintcheck]
# rustflags = ["--remap-path-prefix", "=lintcheck"]
3 changes: 1 addition & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@ jobs:
# Unsetting this would make so that any malicious package could get our Github Token
persist-credentials: false
- run: cargo fmt --check
- run: cargo test --all
- run: cargo test --all
env:
RPL_PATS: docs/patterns-pest
- run: cargo clippy -- -D warnings
- run: cargo install --path .
- run: cargo rpl --workspace --all-targets
env:
RPL_PATS: docs/patterns-pest
# - uses: actions-rust-lang/audit@v1.2.4
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ This is the main source code repository of RPL. It contains the toolchain and do
RPL is a Rust linter which decouples the definition of rules from the detection logic.
In particular, RPL consists of two primary components:

- a Domain-Specific Language (DSL) that allows developers to model/define code patterns,
- a detection engine to detect instances of these patterns.
- a Domain-Specific Language (DSL) that allows developers to model/define code patterns,
- a detection engine to detect instances of these patterns.

The toolchain of RPL, which is a custom configuration of Rust compiler, enables accurate identification of code instances that demonstrate semantic equivalence to existing patterns.

Expand All @@ -28,8 +28,29 @@ The toolchain of RPL, which is a custom configuration of Rust compiler, enables

3. Run RPL analysis on your Rust project:

- `RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl` (using built-in RPL pattern definitions based on inline MIR)
- `RUSTFLAGS="-Zinline-mir=false" RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl` (using built-in RPL pattern definitions based on MIR)
- check using built-in RPL pattern definitions based on inline MIR:

```sh
RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl
```

- check using built-in RPL pattern definitions based on MIR:

```sh
RUSTFLAGS="-Zinline-mir=false" RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl
```

or

```sh
RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl -- -Zinline-mir=false
```

You can also store the environment variable `RPL_PATS` for convenience.

Without setting `RPL_PATS`, built-in RPL pattern definitions are used.

TIP: You can view all available lints with `cargo rpl -- -W help`.

## RPL Book

Expand Down
35 changes: 35 additions & 0 deletions crates/rpl_constraints/src/konst.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use derive_more::{Debug, Display};
use rustc_const_eval::interpret::Scalar;
use rustc_middle::mir;
use rustc_middle::ty::{self, ScalarInt, TyCtxt, TypingEnv};

#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Const<'tcx> {
#[debug("{_0:?}")]
#[display("{_0}")]
MIR(mir::Const<'tcx>),
#[debug("{_0:?}")]
#[display("{_0}")]
Param(ty::ParamConst),
}

impl<'tcx> Const<'tcx> {
pub fn try_eval_target_usize(self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> Option<u64> {
match self {
Self::MIR(konst) => Some(konst.eval_target_usize(tcx, typing_env)),
Self::Param(_) => None,
}
}
pub fn try_eval_scalar(self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> Option<Scalar> {
match self {
Self::MIR(konst) => konst.try_eval_scalar(tcx, typing_env),
Self::Param(_) => None,
}
}
pub fn try_eval_scalar_int(self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> Option<ScalarInt> {
match self {
Self::MIR(konst) => konst.try_eval_scalar_int(tcx, typing_env),
Self::Param(_) => None,
}
}
}
3 changes: 3 additions & 0 deletions crates/rpl_constraints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![feature(box_patterns)]

extern crate rustc_abi;
extern crate rustc_const_eval;
extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_errors;
Expand All @@ -26,13 +27,15 @@ extern crate either;
use std::ops::Deref;

use attributes::FnAttr;
pub use konst::Const;
use predicates::PredicateConjunction;
use rpl_parser::generics::Choice2;
use rpl_parser::pairs;

use crate::predicates::PredicateError;

pub mod attributes;
mod konst;
pub mod predicates;
pub mod tribool;

Expand Down
12 changes: 8 additions & 4 deletions crates/rpl_constraints/src/predicates/multiple_consts.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use rustc_middle::mir::{self};
use rustc_middle::ty::{self, TyCtxt};

use crate::Const;

// FIX: consider a more general way for error handling
pub type MultipleConstsPredsFnPtr = for<'tcx> fn(TyCtxt<'tcx>, ty::TypingEnv<'tcx>, Vec<mir::Const<'tcx>>) -> bool;
pub type MultipleConstsPredsFnPtr = for<'tcx> fn(TyCtxt<'tcx>, ty::TypingEnv<'tcx>, Vec<Const<'tcx>>) -> bool;

/// Check if those constants are in a strictly increasing order
#[instrument(level = "debug", skip(tcx), ret)]
pub fn usize_lt<'tcx>(tcx: TyCtxt<'tcx>, _: ty::TypingEnv<'tcx>, consts: Vec<mir::Const<'tcx>>) -> bool {
pub fn usize_lt<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, consts: Vec<Const<'tcx>>) -> bool {
consts.windows(2).all(|w| {
if let (Some(c1), Some(c2)) = (w[0].try_to_scalar_int(), w[1].try_to_scalar_int()) {
if let (Some(c1), Some(c2)) = (
w[0].try_eval_scalar_int(tcx, typing_env),
w[1].try_eval_scalar_int(tcx, typing_env),
) {
let c1 = c1.to_target_usize(tcx);
let c2 = c2.to_target_usize(tcx);
c1 < c2
Expand Down
3 changes: 2 additions & 1 deletion crates/rpl_constraints/src/predicates/single_const.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use rustc_middle::mir::Const;
use rustc_middle::ty::{self, TyCtxt};

use crate::Const;

pub type SingleConstPredsFnPtr = for<'tcx> fn(TyCtxt<'tcx>, ty::TypingEnv<'tcx>, Const<'tcx>) -> bool;

/// A predicate that checks if a type is a null pointer.
Expand Down
10 changes: 7 additions & 3 deletions crates/rpl_constraints/src/predicates/ty_const.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use rustc_middle::mir;
use rustc_middle::ty::{self, Ty, TyCtxt};

use crate::Const;

pub type TyConstPredsFnPtr =
for<'tcx> fn(TyCtxt<'tcx>, body: &mir::Body<'tcx>, ty::TypingEnv<'tcx>, Ty<'tcx>, mir::Const<'tcx>) -> bool;
for<'tcx> fn(TyCtxt<'tcx>, body: &mir::Body<'tcx>, ty::TypingEnv<'tcx>, Ty<'tcx>, Const<'tcx>) -> bool;

/// Check if `alignment` is enough for the given type `ty`.
#[instrument(level = "debug", skip(tcx), ret)]
Expand All @@ -11,7 +13,7 @@ pub fn maybe_misaligned<'tcx>(
body: &mir::Body<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
ty: Ty<'tcx>,
alignment: mir::Const<'tcx>,
alignment: Const<'tcx>,
) -> bool {
let typing_env = ty::TypingEnv::post_analysis(tcx, body.source.def_id());
match ty.kind() {
Expand All @@ -22,7 +24,9 @@ pub fn maybe_misaligned<'tcx>(
ty::TyKind::Foreign(_) => true,
_ => {
let layout = tcx.layout_of(typing_env.as_query_input(ty)).unwrap();
alignment.eval_target_usize(tcx, typing_env) < layout.align.abi.bytes()
alignment
.try_eval_target_usize(tcx, typing_env)
.is_none_or(|alignment| alignment < layout.align.abi.bytes())
},
}
}
18 changes: 6 additions & 12 deletions crates/rpl_context/src/pat/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
// )]

use derive_more::{Debug, Display};
use rpl_meta::collect_elems_separated_by_comma;
use rpl_meta::symbol_table::{DiagSymbolTable, MetaVariableType, NonLocalMetaSymTab, WithPath};
use rpl_meta::{DYNAMIC, collect_elems_separated_by_comma};
use rpl_parser::generics::Choice2;
use rpl_parser::pairs::diagMessageInner;
use rpl_parser::{SpanWrapper, pairs};
Expand Down Expand Up @@ -77,12 +77,6 @@ impl LintDiagnostic<'_, ()> for Box<DynamicError> {
}
}

const LINT: Lint = Lint {
name: "RPL::DYNAMIC",
desc: "dynamic RPL pattern",
..Lint::default_fields_for_macro()
};

impl DynamicError {
// const fn attr_error(span: Span) -> DynamicError {
// DynamicError {
Expand All @@ -102,7 +96,7 @@ impl DynamicError {
)],
helps: Vec::new(),
suggestions: Vec::new(),
lint: &LINT,
lint: DYNAMIC,
}
}
fn missing_primary_message_error(attr: &rustc_hir::Attribute) -> Self {
Expand All @@ -112,7 +106,7 @@ impl DynamicError {
notes: Vec::new(),
helps: Vec::new(),
suggestions: Vec::new(),
lint: &LINT,
lint: DYNAMIC,
}
}
fn item_to_value_str(item: &rustc_ast::MetaItemInner) -> Result<Symbol, Box<Self>> {
Expand All @@ -125,7 +119,7 @@ impl DynamicError {
notes: Vec::new(),
helps: Vec::new(),
suggestions: Vec::new(),
lint: &LINT,
lint: DYNAMIC,
}
.into()
})
Expand All @@ -137,7 +131,7 @@ impl DynamicError {
notes: Vec::new(),
helps: Vec::new(),
suggestions: Vec::new(),
lint: &LINT,
lint: DYNAMIC,
}
.into()
}
Expand Down Expand Up @@ -195,7 +189,7 @@ impl DynamicError {
notes,
helps,
suggestions: Vec::new(),
lint: &LINT,
lint: DYNAMIC,
}
.into())
}
Expand Down
3 changes: 2 additions & 1 deletion crates/rpl_context/src/pat/matched.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use core::fmt;
use std::collections::HashMap;

use rpl_constraints::Const;
use rpl_meta::collect_elems_separated_by_comma;
use rpl_parser::generics::{Choice2, Choice3};
use rpl_parser::pairs;
use rustc_errors::MultiSpan;
use rustc_hir::FnDecl;
use rustc_index::IndexVec;
use rustc_middle::mir::{Body, Const, PlaceRef};
use rustc_middle::mir::{Body, PlaceRef};
use rustc_middle::ty::Ty;
use rustc_span::{Span, Symbol};

Expand Down
25 changes: 17 additions & 8 deletions crates/rpl_interface/src/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rpl_context::PatternCtxt;
use rpl_driver::{ERROR_FOUND, ErrorFound};
#[cfg(feature = "timing")]
use rpl_driver::{TIMING, Timing};
use rpl_meta::cli::collect_file_from_string_args;
use rpl_meta::cli::{collect_default_patterns, collect_file_from_string_args};
// use rpl_middle::ty::RplConfig;
use rustc_interface::interface;
use rustc_middle::ty::TyCtxt;
Expand Down Expand Up @@ -66,11 +66,11 @@ impl rustc_driver::Callbacks for DefaultCallbacks {}

pub struct RplCallbacks {
rpl_args_var: Option<String>,
pattern_paths: Vec<String>,
pattern_paths: Option<Vec<String>>,
}

impl RplCallbacks {
pub fn new(rpl_args_var: Option<String>, pattern_paths: Vec<String>) -> Self {
pub fn new(rpl_args_var: Option<String>, pattern_paths: Option<Vec<String>>) -> Self {
Self {
rpl_args_var,
pattern_paths,
Expand Down Expand Up @@ -110,9 +110,13 @@ impl rustc_driver::Callbacks for RplCallbacks {

let mctx_arena = MCTX_ARENA.get_or_init(rpl_meta::arena::Arena::default);
let patterns_and_paths = PATTERNS.get_or_init(|| {
collect_file_from_string_args(&self.pattern_paths, || {
EarlyDiagCtxt::new(config.opts.error_format).early_fatal(ErrorFound)
})
self.pattern_paths
.as_ref()
.map_or_else(collect_default_patterns, |pattern_paths| {
collect_file_from_string_args(pattern_paths, || {
EarlyDiagCtxt::new(config.opts.error_format).early_fatal(ErrorFound)
})
})
});
// let dcx = compiler.sess.dcx();
let mut error_counter = 0;
Expand Down Expand Up @@ -184,8 +188,13 @@ impl rustc_driver::Callbacks for RplCallbacks {
let start = std::time::Instant::now();

let mctx_arena = MCTX_ARENA.get_or_init(rpl_meta::arena::Arena::default);
let patterns_and_paths = PATTERNS
.get_or_init(|| collect_file_from_string_args(&self.pattern_paths, || tcx.dcx().emit_fatal(ErrorFound)));
let patterns_and_paths = PATTERNS.get_or_init(|| {
self.pattern_paths
.as_ref()
.map_or_else(collect_default_patterns, |pattern_paths| {
collect_file_from_string_args(pattern_paths, || tcx.dcx().emit_fatal(ErrorFound))
})
});

// let dcx = compiler.sess.dcx();
let mut error_counter = 0;
Expand Down
4 changes: 2 additions & 2 deletions crates/rpl_match/src/fns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl<'a, 'pcx, 'tcx> MatchFnCtxt<'a, 'pcx, 'tcx> {
Self { ty, fn_pat }
}

#[instrument(level = "info", skip_all, fields(fn_pat = %self.fn_pat, fn_did = ?fn_did.into()), ret)]
#[instrument(level = "debug", skip_all, fields(fn_pat = %self.fn_pat, fn_did = ?fn_did.into()), ret)]
pub fn match_fn(&self, fn_did: impl Into<DefId> + Copy) -> bool {
let fn_did = fn_did.into();
let poly_fn_sig = match self.ty.tcx.type_of(fn_did).instantiate_identity().kind() {
Expand All @@ -33,7 +33,7 @@ impl<'a, 'pcx, 'tcx> MatchFnCtxt<'a, 'pcx, 'tcx> {
_ => unimplemented!(),
};
let fn_sig = self.ty.tcx.liberate_late_bound_regions(fn_did, poly_fn_sig);
info!(?fn_sig);
debug!(?fn_sig);
(self.fn_pat.params.len() <= fn_sig.inputs().len() || self.fn_pat.params.non_exhaustive)
&& zip(self.fn_pat.params.iter(), fn_sig.inputs())
.all(|(param_pat, &param_ty)| self.match_param(param_pat, param_ty))
Expand Down
3 changes: 2 additions & 1 deletion crates/rpl_match/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#![feature(iter_chain)]
#![feature(iterator_try_collect)]
#![feature(cell_update)]
#![warn(unused_qualifications)]

extern crate either;
extern crate rustc_abi;
Expand Down Expand Up @@ -46,4 +47,4 @@ pub use adt::{AdtMatch, Candidates, MatchAdtCtxt};
pub use counted::CountedMatch;
pub use fns::MatchFnCtxt;
pub use place::MatchPlaceCtxt;
pub use ty::MatchTyCtxt;
pub use ty::{MatchTyCtxt, TryCmpAs};
6 changes: 3 additions & 3 deletions crates/rpl_match/src/matches/artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use rpl_constraints::attributes::ExtraSpan;
use rpl_context::pat::{MatchedMap, Spanned};
use rustc_hir::FnDecl;
use rustc_index::IndexVec;
use rustc_middle::mir::{Body, Const, Local, PlaceRef};
use rustc_middle::mir::{Body, Local, PlaceRef};
use rustc_middle::ty::Ty;
use rustc_span::{Span, Symbol};

use super::{Matched, StatementMatch, pat};
use super::{Const, Matched, StatementMatch, pat};

/// A normalized version of [`Spanned`].
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -159,7 +159,7 @@ impl<'tcx> NormalizedMatched<'tcx> {
}

impl<'tcx> pat::Matched<'tcx> for NormalizedMatched<'tcx> {
fn span(&self, body: &rustc_middle::mir::Body<'_>, decl: &FnDecl<'tcx>, name: &str) -> Span {
fn span(&self, body: &Body<'_>, decl: &FnDecl<'tcx>, name: &str) -> Span {
let labels = &self.extra;
let i = labels
.binary_search_by_key(&Symbol::intern(name), |(label, _)| *label)
Expand Down
Loading