mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-09 19:45:13 +02:00
[pitboss/grind] deferred session-0009 (20260520T233019Z-6958)
This commit is contained in:
parent
a6f34554db
commit
38cc0ce05f
60 changed files with 509 additions and 541 deletions
|
|
@ -13,6 +13,7 @@
|
|||
//! Path placeholder vocabulary:
|
||||
//! - gin / echo / chi use `:id` and (chi) `{id}` interchangeably.
|
||||
//! - fiber uses `:id` and `+` / `*` greedy wildcards.
|
||||
//!
|
||||
//! [`extract_go_path_placeholders`] supports both syntaxes.
|
||||
|
||||
use crate::dynamic::framework::{HttpMethod, ParamBinding, ParamSource};
|
||||
|
|
@ -134,11 +135,10 @@ pub fn go_formal_names(func: Node<'_>, bytes: &[u8]) -> Vec<String> {
|
|||
}
|
||||
let mut pc = p.walk();
|
||||
for c in p.named_children(&mut pc) {
|
||||
if c.kind() == "identifier" {
|
||||
if let Ok(text) = c.utf8_text(bytes) {
|
||||
if c.kind() == "identifier"
|
||||
&& let Ok(text) = c.utf8_text(bytes) {
|
||||
out.push(text.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
|
|
|
|||
|
|
@ -38,11 +38,10 @@ fn verb_for(name: &str) -> Option<HttpMethod> {
|
|||
fn class_path_prefix(class: Node<'_>, bytes: &[u8]) -> String {
|
||||
let mut prefix = String::new();
|
||||
iter_annotations(class, bytes, |ann, name| {
|
||||
if name == "Path" {
|
||||
if let Some(p) = annotation_string_arg(ann, bytes) {
|
||||
if name == "Path"
|
||||
&& let Some(p) = annotation_string_arg(ann, bytes) {
|
||||
prefix = p;
|
||||
}
|
||||
}
|
||||
});
|
||||
prefix
|
||||
}
|
||||
|
|
@ -57,11 +56,10 @@ fn method_verb_and_path(
|
|||
if let Some(v) = verb_for(name) {
|
||||
verb = Some(v);
|
||||
}
|
||||
if name == "Path" {
|
||||
if let Some(p) = annotation_string_arg(ann, bytes) {
|
||||
if name == "Path"
|
||||
&& let Some(p) = annotation_string_arg(ann, bytes) {
|
||||
path = p;
|
||||
}
|
||||
}
|
||||
});
|
||||
Some((verb?, path))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,8 +114,8 @@ fn walk<'a>(
|
|||
if out.is_some() {
|
||||
return;
|
||||
}
|
||||
if node.kind() == "class_declaration" {
|
||||
if let Some(body) = node
|
||||
if node.kind() == "class_declaration"
|
||||
&& let Some(body) = node
|
||||
.child_by_field_name("body")
|
||||
.or_else(|| named_child_of_kind(node, "class_body"))
|
||||
{
|
||||
|
|
@ -127,15 +127,12 @@ fn walk<'a>(
|
|||
if let Some(name) = member
|
||||
.child_by_field_name("name")
|
||||
.and_then(|n| n.utf8_text(bytes).ok())
|
||||
{
|
||||
if name == target {
|
||||
&& name == target {
|
||||
*out = Some((node, member));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut cur = node.walk();
|
||||
for child in node.children(&mut cur) {
|
||||
walk(child, bytes, target, out);
|
||||
|
|
@ -287,8 +284,8 @@ pub fn extract_path_placeholders(path: &str) -> Vec<String> {
|
|||
let bytes = path.as_bytes();
|
||||
let mut i = 0;
|
||||
while i < bytes.len() {
|
||||
if bytes[i] == b'{' {
|
||||
if let Some(end) = bytes[i + 1..].iter().position(|&b| b == b'}') {
|
||||
if bytes[i] == b'{'
|
||||
&& let Some(end) = bytes[i + 1..].iter().position(|&b| b == b'}') {
|
||||
let inner = &path[i + 1..i + 1 + end];
|
||||
let name = inner.split(':').next().unwrap_or(inner).trim();
|
||||
if !name.is_empty() && !out.iter().any(|n| n == name) {
|
||||
|
|
@ -297,7 +294,6 @@ pub fn extract_path_placeholders(path: &str) -> Vec<String> {
|
|||
i += end + 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
out
|
||||
|
|
|
|||
|
|
@ -48,11 +48,10 @@ fn class_is_controller(class: Node<'_>, bytes: &[u8]) -> bool {
|
|||
fn class_route_prefix(class: Node<'_>, bytes: &[u8]) -> String {
|
||||
let mut prefix = String::new();
|
||||
iter_annotations(class, bytes, |ann, name| {
|
||||
if name == "RequestMapping" {
|
||||
if let Some(p) = annotation_string_arg(ann, bytes) {
|
||||
if name == "RequestMapping"
|
||||
&& let Some(p) = annotation_string_arg(ann, bytes) {
|
||||
prefix = p;
|
||||
}
|
||||
}
|
||||
});
|
||||
prefix
|
||||
}
|
||||
|
|
|
|||
|
|
@ -455,14 +455,11 @@ fn walk_for_registration<'a>(
|
|||
if let Some(method) = http_verb_from_method(prop_text)
|
||||
&& receiver_accepts(last_segment(object_text))
|
||||
&& let Some(args) = node.child_by_field_name("arguments")
|
||||
{
|
||||
if call_args_reference_target(args, bytes, target) {
|
||||
if let Some(path) = first_string_arg(args, bytes) {
|
||||
&& call_args_reference_target(args, bytes, target)
|
||||
&& let Some(path) = first_string_arg(args, bytes) {
|
||||
*out = Some((method, path));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fastify options-object: `fastify.route({ method, url, handler })`.
|
||||
if prop_text == "route"
|
||||
&& receiver_accepts(last_segment(object_text))
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ fn extract_version(file_bytes: &[u8]) -> Option<String> {
|
|||
let needle = "# Generated by Django ";
|
||||
if let Some(idx) = text.find(needle) {
|
||||
let after = &text[idx + needle.len()..];
|
||||
if let Some(end) = after.find(|c: char| c == ' ' || c == '\n') {
|
||||
if let Some(end) = after.find([' ', '\n']) {
|
||||
return Some(after[..end].trim().to_owned());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,8 +258,8 @@ pub(super) fn arg_is_tainted_param(
|
|||
else {
|
||||
return false;
|
||||
};
|
||||
summary.tainted_sink_params.iter().any(|&i| i == idx)
|
||||
|| summary.propagating_params.iter().any(|&i| i == idx)
|
||||
summary.tainted_sink_params.contains(&idx)
|
||||
|| summary.propagating_params.contains(&idx)
|
||||
}
|
||||
|
||||
/// True when any descendant identifier in `node`'s subtree resolves to
|
||||
|
|
|
|||
|
|
@ -122,8 +122,7 @@ fn walk<'a>(
|
|||
&& let Some(name) = node
|
||||
.child_by_field_name("name")
|
||||
.and_then(|n| n.utf8_text(bytes).ok())
|
||||
{
|
||||
if name == target {
|
||||
&& name == target {
|
||||
let klass = if node.kind() == "method_declaration" {
|
||||
here_class
|
||||
} else {
|
||||
|
|
@ -132,7 +131,6 @@ fn walk<'a>(
|
|||
*out = Some((node, klass));
|
||||
return;
|
||||
}
|
||||
}
|
||||
let mut cur = node.walk();
|
||||
for child in node.children(&mut cur) {
|
||||
walk(child, bytes, target, here_class, out);
|
||||
|
|
|
|||
|
|
@ -90,20 +90,18 @@ fn walk_url_registrations(
|
|||
.and_then(|n| n.utf8_text(bytes).ok())
|
||||
{
|
||||
let last = callee.rsplit_once('.').map(|(_, s)| s).unwrap_or(callee);
|
||||
if matches!(last, "path" | "re_path" | "url") {
|
||||
if let Some(args) = node.child_by_field_name("arguments") {
|
||||
if matches!(last, "path" | "re_path" | "url")
|
||||
&& let Some(args) = node.child_by_field_name("arguments") {
|
||||
let positional = positional_args(args);
|
||||
if positional.len() >= 2 {
|
||||
let view_arg = positional[1];
|
||||
if view_arg_references(view_arg, bytes, target, class_target) {
|
||||
if let Some(template) = first_string_arg(args, bytes) {
|
||||
if view_arg_references(view_arg, bytes, target, class_target)
|
||||
&& let Some(template) = first_string_arg(args, bytes) {
|
||||
*out = Some(template);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut cur = node.walk();
|
||||
for child in node.children(&mut cur) {
|
||||
|
|
@ -138,13 +136,11 @@ fn view_arg_references(
|
|||
.strip_suffix(')')
|
||||
.and_then(|s| s.rfind('(').map(|i| &s[..i]))
|
||||
.and_then(|s| s.strip_suffix(".as_view"))
|
||||
{
|
||||
if let Some(ct) = class_target
|
||||
&& let Some(ct) = class_target
|
||||
&& class.rsplit_once('.').map(|(_, s)| s).unwrap_or(class) == ct
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
let stripped = trimmed.trim_end_matches("()");
|
||||
let last = stripped.rsplit_once('.').map(|(_, s)| s).unwrap_or(stripped);
|
||||
last == target || stripped == target
|
||||
|
|
|
|||
|
|
@ -91,17 +91,14 @@ pub fn find_python_function<'a>(
|
|||
}
|
||||
|
||||
fn walk<'a>(node: Node<'a>, bytes: &[u8], target: &str) -> Option<(Node<'a>, Option<Node<'a>>)> {
|
||||
if node.kind() == "function_definition" {
|
||||
if let Some(name) = node
|
||||
if node.kind() == "function_definition"
|
||||
&& let Some(name) = node
|
||||
.child_by_field_name("name")
|
||||
.and_then(|n| n.utf8_text(bytes).ok())
|
||||
{
|
||||
if name == target {
|
||||
&& name == target {
|
||||
let decorated = node.parent().filter(|p| p.kind() == "decorated_definition");
|
||||
return Some((node, decorated));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut cur = node.walk();
|
||||
for child in node.children(&mut cur) {
|
||||
if let Some(found) = walk(child, bytes, target) {
|
||||
|
|
|
|||
|
|
@ -48,17 +48,14 @@ fn walk_routes(node: Node<'_>, bytes: &[u8], target: &str, out: &mut Option<(Htt
|
|||
.and_then(|n| n.utf8_text(bytes).ok())
|
||||
{
|
||||
let last = callee.rsplit_once('.').map(|(_, s)| s).unwrap_or(callee);
|
||||
if matches!(last, "Route" | "WebSocketRoute") {
|
||||
if let Some(args) = node.child_by_field_name("arguments") {
|
||||
if let Some(path) = first_string_arg(args, bytes) {
|
||||
if endpoint_references(args, bytes, target) {
|
||||
if matches!(last, "Route" | "WebSocketRoute")
|
||||
&& let Some(args) = node.child_by_field_name("arguments")
|
||||
&& let Some(path) = first_string_arg(args, bytes)
|
||||
&& endpoint_references(args, bytes, target) {
|
||||
let method = methods_kwarg(args, bytes).unwrap_or(HttpMethod::GET);
|
||||
*out = Some((method, path));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut cur = node.walk();
|
||||
for child in node.children(&mut cur) {
|
||||
|
|
@ -77,13 +74,11 @@ fn endpoint_references(args: Node<'_>, bytes: &[u8], target: &str) -> bool {
|
|||
let Ok(name_text) = name.utf8_text(bytes) else {
|
||||
continue;
|
||||
};
|
||||
if name_text == "endpoint" {
|
||||
if let Some(value) = arg.child_by_field_name("value") {
|
||||
if identifier_matches(value, bytes, target) {
|
||||
if name_text == "endpoint"
|
||||
&& let Some(value) = arg.child_by_field_name("value")
|
||||
&& identifier_matches(value, bytes, target) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
seen_positional += 1;
|
||||
// Second positional argument is the endpoint when no
|
||||
|
|
|
|||
|
|
@ -64,12 +64,11 @@ fn visit_routes<'a>(
|
|||
if out.is_some() {
|
||||
return;
|
||||
}
|
||||
if node.kind() == "call" {
|
||||
if let Some(found) = try_route_mapping(node, bytes, controller, action) {
|
||||
if node.kind() == "call"
|
||||
&& let Some(found) = try_route_mapping(node, bytes, controller, action) {
|
||||
*out = Some(found);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let mut cur = node.walk();
|
||||
for child in node.children(&mut cur) {
|
||||
visit_routes(child, bytes, controller, action, out);
|
||||
|
|
@ -125,7 +124,7 @@ fn rails_controller_path(class_name: &str) -> String {
|
|||
// for module-namespaced controllers (`Api::Users` → `api/users`).
|
||||
let segments: Vec<String> = stripped
|
||||
.split("::")
|
||||
.map(|seg| snake_case(seg))
|
||||
.map(snake_case)
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
segments.join("/")
|
||||
|
|
|
|||
|
|
@ -95,12 +95,11 @@ fn walk_class<'a>(
|
|||
if out.is_some() {
|
||||
return;
|
||||
}
|
||||
if node.kind() == "class" {
|
||||
if let Some(method) = find_method_in_class(node, bytes, target) {
|
||||
if node.kind() == "class"
|
||||
&& let Some(method) = find_method_in_class(node, bytes, target) {
|
||||
*out = Some((node, method));
|
||||
return;
|
||||
}
|
||||
}
|
||||
let mut cur = node.walk();
|
||||
for child in node.children(&mut cur) {
|
||||
walk_class(child, bytes, target, out);
|
||||
|
|
@ -117,11 +116,10 @@ pub fn find_method_in_class<'a>(class: Node<'a>, bytes: &'a [u8], target: &str)
|
|||
if member.kind() != "method" {
|
||||
continue;
|
||||
}
|
||||
if let Some(name) = method_identifier(member, bytes) {
|
||||
if name == target {
|
||||
if let Some(name) = method_identifier(member, bytes)
|
||||
&& name == target {
|
||||
return Some(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,12 +40,11 @@ fn collect_routes(root: Node<'_>, bytes: &[u8]) -> Vec<SinatraRoute> {
|
|||
}
|
||||
|
||||
fn visit(node: Node<'_>, bytes: &[u8], out: &mut Vec<SinatraRoute>) {
|
||||
if node.kind() == "call" {
|
||||
if let Some(route) = try_route(node, bytes) {
|
||||
if node.kind() == "call"
|
||||
&& let Some(route) = try_route(node, bytes) {
|
||||
out.push(route);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Sinatra routes live at top level or directly under a `class App <
|
||||
// Sinatra::Base` body — never inside a helper method's body. Skip
|
||||
// descent through `method` / `singleton_method` so a stray `get '/x'
|
||||
|
|
@ -101,11 +100,10 @@ fn block_parameter_names(block: Node<'_>, bytes: &[u8]) -> Vec<String> {
|
|||
}
|
||||
let mut bc = child.walk();
|
||||
for p in child.named_children(&mut bc) {
|
||||
if p.kind() == "identifier" {
|
||||
if let Ok(t) = p.utf8_text(bytes) {
|
||||
if p.kind() == "identifier"
|
||||
&& let Ok(t) = p.utf8_text(bytes) {
|
||||
out.push(t.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
|
|
|
|||
|
|
@ -142,11 +142,10 @@ pub fn rust_formal_names(func: Node<'_>, bytes: &[u8]) -> Vec<String> {
|
|||
fn push_pattern_name(pat: Node<'_>, bytes: &[u8], out: &mut Vec<String>) {
|
||||
match pat.kind() {
|
||||
"identifier" => {
|
||||
if let Ok(text) = pat.utf8_text(bytes) {
|
||||
if text != "_" {
|
||||
if let Ok(text) = pat.utf8_text(bytes)
|
||||
&& text != "_" {
|
||||
out.push(text.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
"mut_pattern" | "ref_pattern" => {
|
||||
let mut cur = pat.walk();
|
||||
|
|
@ -316,11 +315,10 @@ pub fn find_method_attribute<'a>(
|
|||
// try those too.
|
||||
let mut cur = func.walk();
|
||||
for c in func.children(&mut cur) {
|
||||
if c.kind() == "attribute_item" {
|
||||
if let Some(hit) = read_route_attribute(c, bytes) {
|
||||
if c.kind() == "attribute_item"
|
||||
&& let Some(hit) = read_route_attribute(c, bytes) {
|
||||
return Some(hit);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -528,27 +526,23 @@ fn walk_warp<'a>(
|
|||
let mut verb = HttpMethod::GET;
|
||||
let mut hit_target = false;
|
||||
while let Some(p) = parent {
|
||||
match p.kind() {
|
||||
"call_expression" => {
|
||||
if let Some(func) = p.child_by_field_name("function")
|
||||
&& func.kind() == "field_expression"
|
||||
&& let Some(field) = func.child_by_field_name("field")
|
||||
&& let Ok(field_text) = field.utf8_text(bytes)
|
||||
&& matches!(field_text, "map" | "and_then" | "untuple_one")
|
||||
{
|
||||
let args = p.child_by_field_name("arguments");
|
||||
if let Some(args) = args {
|
||||
let mut cur = args.walk();
|
||||
for c in args.named_children(&mut cur) {
|
||||
if axum_callable_matches(c, bytes, target) {
|
||||
hit_target = true;
|
||||
}
|
||||
if p.kind() == "call_expression"
|
||||
&& let Some(func) = p.child_by_field_name("function")
|
||||
&& func.kind() == "field_expression"
|
||||
&& let Some(field) = func.child_by_field_name("field")
|
||||
&& let Ok(field_text) = field.utf8_text(bytes)
|
||||
&& matches!(field_text, "map" | "and_then" | "untuple_one")
|
||||
{
|
||||
let args = p.child_by_field_name("arguments");
|
||||
if let Some(args) = args {
|
||||
let mut cur = args.walk();
|
||||
for c in args.named_children(&mut cur) {
|
||||
if axum_callable_matches(c, bytes, target) {
|
||||
hit_target = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Detect verb-filter calls (`warp::get()`, `warp::post()`).
|
||||
let mut cur = p.walk();
|
||||
for child in p.children(&mut cur) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue