Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
0e288bd
db: record which preprocessor arm a definition sits under
rikvanriel Sep 30, 2026
fea14a9
treesitter: pin which arm the name-keyed collapse keeps today
rikvanriel Sep 30, 2026
7aa67f3
treesitter: keep an outer arm's disjunction whole when chaining guards
rikvanriel Sep 30, 2026
0085759
treesitter: keep one definition per preprocessor arm, not per name
rikvanriel Sep 30, 2026
45089fc
db: start functions and object_macros again when they predate guard
rikvanriel Sep 30, 2026
5631067
treesitter: give declarations and macro-opened functions their arm
rikvanriel Sep 30, 2026
599c450
treesitter: parenthesize every compound conjunct of a guard
rikvanriel Sep 30, 2026
403c34c
db: an object macro is an attribute if any of its arms makes it one
rikvanriel Sep 30, 2026
2ad65b1
treesitter: read an #if condition without its comments and splices
rikvanriel Sep 30, 2026
6c3d1b7
db: forget indexed branches with the functions table, clear object_ma…
rikvanriel Sep 30, 2026
3ae058f
db: hand back every arm of a name from git-aware lookups
rikvanriel Oct 1, 2026
b9bfa65
guard: write every guard as a flat list of terms, without include guards
rikvanriel Oct 1, 2026
2285fba
display: say which arm each definition sits under
rikvanriel Oct 1, 2026
3f97bae
callchain: walk every arm of a name, and stop where a path contradict…
rikvanriel Oct 1, 2026
b84fe49
guard: strip only whole-file include guards; read more Kconfig facts
rikvanriel Oct 1, 2026
d131fc0
resolution: rank one candidate per file, not every arm
rikvanriel Oct 1, 2026
2f325c6
callchain: list a callee's arms in the REPL and MCP chains alike
rikvanriel Oct 1, 2026
bd4d455
resolution: one choice for a pinned chain's name and its callees
rikvanriel Oct 1, 2026
3b0966b
callchain: prune only on facts every root arm and every build share
rikvanriel Oct 1, 2026
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
30 changes: 30 additions & 0 deletions src/bin/query_impl/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,18 @@ async fn show_callchain_with_limits(
println!("{} {}", "Ambiguous:".bold().yellow(), note);
}
let func = chosen.function;
// What every arm of the root asserts: the root's callees are the union
// over those arms, so only these facts hold on all of their paths.
let root_facts = semcode::guard::shared_facts(
db.get_callee_arms_in(
function_name,
git_sha,
semcode::domain::Context::In(semcode::domain::domain_of(&func.file_path)),
)
.await?
.iter()
.map(|arm| arm.guard.as_deref()),
);

println!("{}", "=== Function Information ===".bold().green());
println!(
Expand Down Expand Up @@ -403,6 +415,24 @@ async fn show_callchain_with_limits(
for (i, callee) in limited_callees.iter().enumerate() {
println!("{}. {}", (i + 1).to_string().yellow(), callee.cyan());

// A callee its file defines once per configuration: every arm,
// under its guard, with what that arm calls. Showing one arm
// here is how a chain reads as a dead end at `return 0;`.
let arms = db
.get_callee_arms_in(callee, git_sha, chain_context)
.await
.unwrap_or_default();
if arms.len() > 1 {
semcode::callchain::write_callee_arms(
&mut std::io::stdout(),
&arms,
&root_facts,
down_levels,
true,
)?;
continue;
}

// Show callee details if available
if let Ok(Some(chosen)) = db
.find_function_git_aware_reporting(callee, git_sha, chain_context)
Expand Down
54 changes: 48 additions & 6 deletions src/bin/semcode-mcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ use std::path::PathBuf;
use std::sync::Arc;

/// Truncate output at 3,000 lines with a warning message
/// "Under: <guard>" as its own line for a definition inside a conditional,
/// nothing at file scope.
fn under_line(guard: Option<&str>) -> String {
guard.map_or_else(String::new, |guard| format!("Under: {guard}\n"))
}

fn truncate_output(output: String) -> String {
const MAX_LINES: usize = 3000;

Expand Down Expand Up @@ -108,8 +114,8 @@ async fn mcp_query_function_or_macro(
.unwrap_or_default();

format!(
"Macro: {} (git SHA: {})\nFile: {}:{}\nParameters: ({})\nCalls: {} functions\nCalled by: {} functions\nDefinition:\n{}",
entity.name, git_sha, entity.file_path, entity.line_start, params_str, macro_calls.len(), macro_callers.len(), entity.body
"Macro: {} (git SHA: {})\nFile: {}:{}\n{}Parameters: ({})\nCalls: {} functions\nCalled by: {} functions\nDefinition:\n{}",
entity.name, git_sha, entity.file_path, entity.line_start, under_line(entity.guard.as_deref()), params_str, macro_calls.len(), macro_callers.len(), entity.body
)
} else {
// Found function
Expand All @@ -133,12 +139,13 @@ async fn mcp_query_function_or_macro(
.unwrap_or_default();

format!(
"Function: {} (git SHA: {})\nFile: {}:{}-{}\nReturn Type: {}\nParameters: ({})\nCalls: {} functions\nCalled by: {} functions\nBody:\n{}\n\n",
"Function: {} (git SHA: {})\nFile: {}:{}-{}\n{}Return Type: {}\nParameters: ({})\nCalls: {} functions\nCalled by: {} functions\nBody:\n{}\n\n",
func.name,
git_sha,
func.file_path,
func.line_start,
func.line_end,
under_line(func.guard.as_deref()),
func.return_type,
params_str,
calls.len(),
Expand Down Expand Up @@ -172,8 +179,13 @@ async fn mcp_query_function_or_macro(
.join(", ");

result.push_str(&format!(
"Macro: {}\nFile: {}:{}\nParameters: ({})\nDefinition:\n{}\n\n",
entity.name, entity.file_path, entity.line_start, params_str, entity.body
"Macro: {}\nFile: {}:{}\n{}Parameters: ({})\nDefinition:\n{}\n\n",
entity.name,
entity.file_path,
entity.line_start,
under_line(entity.guard.as_deref()),
params_str,
entity.body
));
} else {
// Function
Expand All @@ -185,11 +197,12 @@ async fn mcp_query_function_or_macro(
.join(", ");

result.push_str(&format!(
"Function: {}\nFile: {}:{}-{}\nReturn Type: {}\nParameters: ({})\nBody:\n{}\n\n",
"Function: {}\nFile: {}:{}-{}\n{}Return Type: {}\nParameters: ({})\nBody:\n{}\n\n",
entity.name,
entity.file_path,
entity.line_start,
entity.line_end,
under_line(entity.guard.as_deref()),
entity.return_type,
params_str,
entity.body
Expand Down Expand Up @@ -1980,6 +1993,18 @@ async fn mcp_show_callchain_with_limits(
writeln!(buffer, "Ambiguous: {note}")?;
}
let func = chosen.function;
// What every arm of the root asserts: the root's callees are the union
// over those arms, so only these facts hold on all of their paths.
let root_facts = semcode::guard::shared_facts(
db.get_callee_arms_in(
function_name,
git_sha,
semcode::domain::Context::In(semcode::domain::domain_of(&func.file_path)),
)
.await?
.iter()
.map(|arm| arm.guard.as_deref()),
);
// Every hop of this chain is about the definition the root resolved
// to, so it is answered within that definition's build. Sticky: a hop
// into generic code keeps the architecture, because generic code
Expand Down Expand Up @@ -2113,6 +2138,23 @@ async fn mcp_show_callchain_with_limits(
for (i, callee) in limited_callees.iter().enumerate() {
writeln!(buffer, "{}. {}", i + 1, callee)?;

// A callee its file defines once per configuration: every
// arm, under its guard, with what that arm calls.
let arms = db
.get_callee_arms_in(callee, git_sha, chain_context)
.await
.unwrap_or_default();
if arms.len() > 1 {
semcode::callchain::write_callee_arms(
&mut buffer,
&arms,
&root_facts,
down_levels,
false,
)?;
continue;
}

// Show callee details if available
if let Ok(Resolution::Chosen(callee_chosen)) = db
.find_function_git_aware_reporting(callee, git_sha, chain_context)
Expand Down
Loading
Loading