Daniel García
2 months ago
10 changed files with 1862 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||
[workspace.metadata.dylint] |
|||
libraries = [{ path = "dylints/*" }] |
@ -0,0 +1,7 @@ |
|||
# How to run Lints |
|||
|
|||
```sh |
|||
cargo install cargo-dylint dylint-link |
|||
|
|||
RUSTFLAGS="-Aunreachable_patterns" cargo dylint --all -- --features sqlite |
|||
``` |
@ -0,0 +1,2 @@ |
|||
[target.'cfg(all())'] |
|||
linker = "dylint-link" |
@ -0,0 +1 @@ |
|||
/target |
File diff suppressed because it is too large
@ -0,0 +1,20 @@ |
|||
[package] |
|||
name = "non_authenticated_routes" |
|||
version = "0.1.0" |
|||
authors = ["authors go here"] |
|||
description = "description goes here" |
|||
edition = "2021" |
|||
publish = false |
|||
|
|||
[lib] |
|||
crate-type = ["cdylib"] |
|||
|
|||
[dependencies] |
|||
clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "4f0e46b74dbc8441daf084b6f141a7fe414672a2" } |
|||
dylint_linting = "3.2.1" |
|||
|
|||
[dev-dependencies] |
|||
dylint_testing = "3.2.1" |
|||
|
|||
[package.metadata.rust-analyzer] |
|||
rustc_private = true |
@ -0,0 +1,3 @@ |
|||
[toolchain] |
|||
channel = "nightly-2024-11-09" |
|||
components = ["llvm-tools-preview", "rustc-dev"] |
@ -0,0 +1,167 @@ |
|||
#![feature(rustc_private)] |
|||
#![feature(let_chains)] |
|||
|
|||
extern crate rustc_arena; |
|||
extern crate rustc_ast; |
|||
extern crate rustc_ast_pretty; |
|||
extern crate rustc_attr; |
|||
extern crate rustc_data_structures; |
|||
extern crate rustc_errors; |
|||
extern crate rustc_hir; |
|||
extern crate rustc_hir_pretty; |
|||
extern crate rustc_index; |
|||
extern crate rustc_infer; |
|||
extern crate rustc_lexer; |
|||
extern crate rustc_middle; |
|||
extern crate rustc_mir_dataflow; |
|||
extern crate rustc_parse; |
|||
extern crate rustc_span; |
|||
extern crate rustc_target; |
|||
extern crate rustc_trait_selection; |
|||
|
|||
use clippy_utils::diagnostics::span_lint; |
|||
use rustc_hir::{def_id::DefId, Item, ItemKind, QPath, TyKind}; |
|||
use rustc_lint::{LateContext, LateLintPass}; |
|||
use rustc_span::{symbol::Ident, Span, Symbol}; |
|||
|
|||
dylint_linting::impl_late_lint! { |
|||
/// ### What it does
|
|||
///
|
|||
/// ### Why is this bad?
|
|||
///
|
|||
/// ### Known problems
|
|||
/// Remove if none.
|
|||
///
|
|||
/// ### Example
|
|||
/// ```rust
|
|||
/// // example code where a warning is issued
|
|||
/// ```
|
|||
/// Use instead:
|
|||
/// ```rust
|
|||
/// // example code that does not raise a warning
|
|||
/// ```
|
|||
pub NON_AUTHENTICATED_ROUTES, |
|||
Warn, |
|||
"description goes here", |
|||
NonAuthenticatedRoutes::default() |
|||
} |
|||
|
|||
#[derive(Default)] |
|||
pub struct NonAuthenticatedRoutes { |
|||
last_function_item: Option<(Ident, Span, bool)>, |
|||
} |
|||
|
|||
// Collect all the attribute macros that are applied to the given span
|
|||
fn attr_def_ids(mut span: rustc_span::Span) -> Vec<(DefId, Symbol, Option<DefId>)> { |
|||
use rustc_span::hygiene::{walk_chain, ExpnKind, MacroKind}; |
|||
use rustc_span::{ExpnData, SyntaxContext}; |
|||
|
|||
let mut def_ids = Vec::new(); |
|||
while span.ctxt() != SyntaxContext::root() { |
|||
if let ExpnData { |
|||
kind: ExpnKind::Macro(MacroKind::Attr, macro_symbol), |
|||
macro_def_id: Some(def_id), |
|||
parent_module, |
|||
.. |
|||
} = span.ctxt().outer_expn_data() |
|||
{ |
|||
def_ids.push((def_id, macro_symbol, parent_module)); |
|||
} |
|||
span = walk_chain(span, SyntaxContext::root()); |
|||
} |
|||
def_ids |
|||
} |
|||
|
|||
const ROCKET_MACRO_EXCEPTIONS: [(&str, &str); 1] = [("rocket::catch", "catch")]; |
|||
|
|||
const VALID_AUTH_HEADERS: [&str; 6] = [ |
|||
"auth::Headers", |
|||
"auth::OrgHeaders", |
|||
"auth::AdminHeaders", |
|||
"auth::ManagerHeaders", |
|||
"auth::ManagerHeadersLoose", |
|||
"auth::OwnerHeaders", |
|||
]; |
|||
|
|||
impl<'tcx> LateLintPass<'tcx> for NonAuthenticatedRoutes { |
|||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item) { |
|||
if let ItemKind::Fn(sig, ..) = item.kind { |
|||
let mut has_auth_headers = false; |
|||
|
|||
for input in sig.decl.inputs { |
|||
let TyKind::Path(QPath::Resolved(_, path)) = input.kind else { |
|||
continue; |
|||
}; |
|||
|
|||
for seg in path.segments { |
|||
if let Some(def_id) = seg.res.opt_def_id() { |
|||
let def = cx.tcx.def_path_str(def_id); |
|||
if VALID_AUTH_HEADERS.contains(&def.as_str()) { |
|||
has_auth_headers = true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
self.last_function_item = Some((item.ident, sig.span, has_auth_headers)); |
|||
return; |
|||
} |
|||
|
|||
let ItemKind::Struct(_data, _generics) = item.kind else { |
|||
return; |
|||
}; |
|||
|
|||
let def_ids = attr_def_ids(item.span); |
|||
|
|||
let mut is_rocket_route = false; |
|||
|
|||
for (def_id, sym, parent) in &def_ids { |
|||
let def_id = cx.tcx.def_path_str(*def_id); |
|||
let sym = sym.as_str(); |
|||
let parent = parent.map(|parent| cx.tcx.def_path_str(parent)); |
|||
|
|||
if ROCKET_MACRO_EXCEPTIONS.contains(&(&def_id, sym)) { |
|||
is_rocket_route = false; |
|||
break; |
|||
} |
|||
|
|||
if def_id.starts_with("rocket::") || parent.as_deref() == Some("rocket_codegen") { |
|||
is_rocket_route = true; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if !is_rocket_route { |
|||
return; |
|||
} |
|||
|
|||
let Some((func_ident, func_span, has_auth_headers)) = self.last_function_item.take() else { |
|||
span_lint(cx, NON_AUTHENTICATED_ROUTES, item.span, "No function found before the expanded route"); |
|||
return; |
|||
}; |
|||
|
|||
if func_ident != item.ident { |
|||
span_lint( |
|||
cx, |
|||
NON_AUTHENTICATED_ROUTES, |
|||
item.span, |
|||
"The function before the expanded route does not match the route", |
|||
); |
|||
return; |
|||
} |
|||
|
|||
if !has_auth_headers { |
|||
span_lint( |
|||
cx, |
|||
NON_AUTHENTICATED_ROUTES, |
|||
func_span, |
|||
"This Rocket route does not have any authentication headers", |
|||
); |
|||
} |
|||
} |
|||
} |
|||
|
|||
#[test] |
|||
fn ui() { |
|||
dylint_testing::ui_test(env!("CARGO_PKG_NAME"), "ui"); |
|||
} |
@ -0,0 +1 @@ |
|||
fn main() {} |
Loading…
Reference in new issue