tldr_cli/commands/
reaching_defs.rs1use std::path::PathBuf;
9
10use anyhow::Result;
11use clap::Args;
12
13use tldr_core::dfg::{
14 build_reaching_defs_report, filter_reaching_defs_by_variable, format_reaching_defs_json,
15 format_reaching_defs_text_with_options, ReachingDefsFormatOptions, ReachingDefsReport,
16};
17use tldr_core::{get_cfg_context, get_dfg_context, Language};
18
19use crate::output::OutputFormat;
20
21#[derive(Debug, Args)]
23pub struct ReachingDefsArgs {
24 pub file: PathBuf,
26
27 pub function: String,
29
30 #[arg(long, short = 'l')]
32 pub lang: Option<Language>,
33
34 #[arg(long)]
36 pub var: Option<String>,
37
38 #[arg(long)]
40 pub line: Option<usize>,
41
42 #[arg(long, default_value = "true")]
44 pub show_chains: bool,
45
46 #[arg(long, default_value = "true")]
48 pub show_uninitialized: bool,
49
50 #[arg(long)]
52 pub show_in_out: bool,
53
54 #[arg(long)]
56 pub chains_only: bool,
57
58 #[arg(long)]
60 pub params: Option<String>,
61}
62
63impl ReachingDefsArgs {
64 pub fn run(&self, format: OutputFormat, quiet: bool) -> Result<()> {
66 use crate::output::OutputWriter;
67
68 let writer = OutputWriter::new(format, quiet);
69
70 let language = self
72 .lang
73 .unwrap_or_else(|| Language::from_path(&self.file).unwrap_or(Language::Python));
74
75 writer.progress(&format!(
76 "Analyzing reaching definitions for {} in {}...",
77 self.function,
78 self.file.display()
79 ));
80
81 if !self.file.exists() {
83 return Err(anyhow::anyhow!("File not found: {}", self.file.display()));
84 }
85
86 let cfg = get_cfg_context(
88 self.file.to_str().unwrap_or_default(),
89 &self.function,
90 language,
91 )?;
92
93 let dfg = get_dfg_context(
94 self.file.to_str().unwrap_or_default(),
95 &self.function,
96 language,
97 )?;
98
99 let mut report = build_reaching_defs_report(&cfg, &dfg.refs, self.file.clone());
101
102 if let Some(ref var) = self.var {
104 report = filter_reaching_defs_by_variable(&report, var);
105 }
106
107 if let Some(line) = self.line {
109 report = filter_report_by_line(&report, line as u32);
110 }
111
112 let format_options = if self.chains_only {
118 ReachingDefsFormatOptions::chains_only()
119 } else {
120 ReachingDefsFormatOptions {
121 show_blocks: self.show_in_out,
122 show_chains: self.show_chains,
123 show_uninitialized: self.show_uninitialized,
124 show_header: true,
125 show_stats: true,
126 }
127 };
128
129 match format {
131 OutputFormat::Text => {
132 let text = format_reaching_defs_text_with_options(&report, &format_options);
133 writer.write_text(&text)?;
134 }
135 OutputFormat::Json | OutputFormat::Compact => {
136 let json = format_reaching_defs_json(&report)
137 .map_err(|e| anyhow::anyhow!("JSON serialization failed: {}", e))?;
138 writer.write_text(&json)?;
139 }
140 OutputFormat::Dot => {
141 let json = format_reaching_defs_json(&report)
143 .map_err(|e| anyhow::anyhow!("JSON serialization failed: {}", e))?;
144 writer.write_text(&json)?;
145 }
146 OutputFormat::Sarif => {
147 let json = format_reaching_defs_json(&report)
149 .map_err(|e| anyhow::anyhow!("JSON serialization failed: {}", e))?;
150 writer.write_text(&json)?;
151 }
152 }
153
154 Ok(())
155 }
156}
157
158fn filter_report_by_line(report: &ReachingDefsReport, line: u32) -> ReachingDefsReport {
164 use std::collections::HashSet;
165
166 let relevant_use_def_chains: Vec<_> = report
168 .use_def_chains
169 .iter()
170 .filter(|c| c.use_site.line == line)
171 .cloned()
172 .collect();
173
174 let relevant_def_lines: HashSet<u32> = relevant_use_def_chains
176 .iter()
177 .flat_map(|c| c.reaching_defs.iter().map(|d| d.line))
178 .collect();
179
180 let relevant_def_use_chains: Vec<_> = report
182 .def_use_chains
183 .iter()
184 .filter(|c| relevant_def_lines.contains(&c.definition.line))
185 .cloned()
186 .collect();
187
188 let relevant_uninitialized: Vec<_> = report
190 .uninitialized
191 .iter()
192 .filter(|u| u.line == line)
193 .cloned()
194 .collect();
195
196 ReachingDefsReport {
197 function: report.function.clone(),
198 file: report.file.clone(),
199 blocks: report.blocks.clone(), def_use_chains: relevant_def_use_chains,
201 use_def_chains: relevant_use_def_chains,
202 uninitialized: relevant_uninitialized,
203 stats: report.stats.clone(),
204 uncertain_defs: report.uncertain_defs.clone(),
205 confidence: report.confidence,
206 }
207}