Skip to content

Commit 8aeaa75

Browse files
committed
semantic output converter method consider multiple source files
1 parent 06601a5 commit 8aeaa75

File tree

3 files changed

+89
-47
lines changed

3 files changed

+89
-47
lines changed

bear/src/semantic/clang/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,21 @@ impl Entry {
6161
arguments: Vec<String>,
6262
directory: impl Into<path::PathBuf>,
6363
output: Option<impl Into<path::PathBuf>>,
64-
as_command: bool,
64+
as_array: bool,
6565
) -> Self {
66-
if as_command {
66+
if as_array {
6767
Entry {
6868
file: file.into(),
69-
arguments: Vec::default(),
70-
command: shell_words::join(&arguments),
69+
arguments,
70+
command: String::default(),
7171
directory: directory.into(),
7272
output: output.map(|o| o.into()),
7373
}
7474
} else {
7575
Entry {
7676
file: file.into(),
77-
arguments,
78-
command: String::default(),
77+
arguments: Vec::default(),
78+
command: shell_words::join(&arguments),
7979
directory: directory.into(),
8080
output: output.map(|o| o.into()),
8181
}

bear/src/semantic/interpreters/filter.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl<T: Interpreter> Interpreter for FilteringInterpreter<T> {
4646
// Apply filtering to the compiler command
4747
match filter.filter_command(&compiler_cmd) {
4848
Ok(_) => Some(Command::Compiler(compiler_cmd)),
49-
Err(reason) => Some(Command::Filtered(reason)),
49+
Err(reason) => Some(Command::Ignored(reason)),
5050
}
5151
}
5252
// Pass through other command types unchanged
@@ -66,7 +66,7 @@ pub(super) struct Filter {
6666
}
6767

6868
impl Filter {
69-
fn filter_command(&self, cmd: &CompilerCommand) -> Result<(), String> {
69+
fn filter_command(&self, cmd: &CompilerCommand) -> Result<(), &'static str> {
7070
// Check if the compiler should be filtered
7171
if let Some(reason) = self.should_filter_compiler(&cmd.executable, &self.compiler_filters) {
7272
return Err(reason);
@@ -84,13 +84,10 @@ impl Filter {
8484
&self,
8585
compiler_path: &PathBuf,
8686
compiler_filters: &HashMap<PathBuf, config::IgnoreOrConsider>,
87-
) -> Option<String> {
87+
) -> Option<&'static str> {
8888
if let Some(ignore) = compiler_filters.get(compiler_path) {
8989
match ignore {
90-
config::IgnoreOrConsider::Always => Some(format!(
91-
"Compiler {} is configured to be ignored",
92-
compiler_path.display()
93-
)),
90+
config::IgnoreOrConsider::Always => Some("Compiler is configured to be ignored"),
9491
_ => None,
9592
}
9693
} else {
@@ -102,7 +99,7 @@ impl Filter {
10299
&self,
103100
cmd: &CompilerCommand,
104101
source_filters: &[config::DirectoryFilter],
105-
) -> Option<String> {
102+
) -> Option<&'static str> {
106103
// Get all source files from the command
107104
let source_files: Vec<&String> = cmd
108105
.arguments
@@ -124,11 +121,7 @@ impl Filter {
124121
for filter in source_filters {
125122
if source_path.starts_with(&filter.path) {
126123
return match filter.ignore {
127-
config::Ignore::Always => Some(format!(
128-
"Source file {} is in filtered directory {}",
129-
source_file,
130-
filter.path.display()
131-
)),
124+
config::Ignore::Always => Some("Source file is in filtered directory"),
132125
config::Ignore::Never => None,
133126
};
134127
}
@@ -355,7 +348,7 @@ mod tests {
355348
);
356349

357350
let result = sut.recognize(&execution);
358-
assert!(matches!(result, Some(Command::Filtered(_))));
351+
assert!(matches!(result, Some(Command::Ignored(_))));
359352
}
360353

361354
#[test]
@@ -393,7 +386,7 @@ mod tests {
393386
);
394387

395388
let result = sut.recognize(&execution);
396-
assert!(matches!(result, Some(Command::Filtered(_))));
389+
assert!(matches!(result, Some(Command::Ignored(_))));
397390
}
398391

399392
#[test]

bear/src/semantic/mod.rs

Lines changed: 75 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ pub enum Command {
3535
Compiler(CompilerCommand),
3636
/// A command that is intentionally ignored and not processed further.
3737
Ignored(&'static str),
38-
/// A command that is filtered out and not included in the output.
39-
Filtered(String),
4038
}
4139

4240
impl Command {
@@ -45,7 +43,6 @@ impl Command {
4543
match self {
4644
Command::Compiler(cmd) => cmd.to_entries(config),
4745
Command::Ignored(_) => vec![],
48-
Command::Filtered(_) => vec![],
4946
}
5047
}
5148
}
@@ -72,6 +69,16 @@ pub struct ArgumentGroup {
7269
pub kind: ArgumentKind,
7370
}
7471

72+
impl ArgumentGroup {
73+
fn as_file(&self) -> Option<String> {
74+
match self.kind {
75+
ArgumentKind::Source => self.args.first().cloned(),
76+
ArgumentKind::Output => self.args.get(1).cloned(),
77+
ArgumentKind::Compiler | ArgumentKind::Other(_) => None,
78+
}
79+
}
80+
}
81+
7582
/// Represents the meaning of the argument in the compiler call. Identifies
7683
/// the purpose of each argument in the command line.
7784
///
@@ -133,33 +140,19 @@ impl CompilerCommand {
133140
/// entries with the executable, arguments, working directory, and output file if present.
134141
pub(super) fn to_entries(&self, config: &config::EntryFormat) -> Vec<clang::Entry> {
135142
// Find all source files in the arguments
136-
let source_files: Vec<String> = self
137-
.arguments
138-
.iter()
139-
.filter(|arg| matches!(arg.kind, ArgumentKind::Source))
140-
.flat_map(|arg| &arg.args)
141-
.cloned()
142-
.collect();
143+
let source_files = self
144+
.find_arguments_by_kind(ArgumentKind::Source)
145+
.flat_map(ArgumentGroup::as_file)
146+
.collect::<Vec<String>>();
143147

144148
// If no source files found, return empty vector
145149
if source_files.is_empty() {
146150
return vec![];
147151
}
148152

149-
// Build the full command arguments by flattening all argument args
150-
let mut command_args = vec![self.executable.to_string_lossy().to_string()];
151-
for arg in &self.arguments {
152-
command_args.extend(arg.args.iter().cloned());
153-
}
154-
155153
// Find output file if present
156154
let output_file = if config.keep_output_field {
157-
self.arguments
158-
.iter()
159-
.filter(|arg| matches!(arg.kind, ArgumentKind::Output))
160-
.flat_map(|arg| &arg.args)
161-
.nth(1) // Skip the "-o" flag itself, take the output filename
162-
.map(PathBuf::from)
155+
self.compute_output_file()
163156
} else {
164157
None
165158
};
@@ -168,16 +161,72 @@ impl CompilerCommand {
168161
source_files
169162
.into_iter()
170163
.map(|source_file| {
164+
let command_args = self.build_command_args_for_source(&source_file);
165+
171166
clang::Entry::new(
172167
source_file,
173-
command_args.clone(),
168+
command_args,
174169
&self.working_dir,
175170
output_file.as_ref(),
176-
!config.command_field_as_array,
171+
config.command_field_as_array,
177172
)
178173
})
179174
.collect()
180175
}
176+
177+
/// Builds command arguments for a specific source file.
178+
///
179+
/// This method constructs the command arguments list that includes the executable,
180+
/// all non-source arguments, and the specific source file.
181+
/// It ensures that the source file is placed in the correct position relative to output arguments.
182+
fn build_command_args_for_source(&self, source_file: &str) -> Vec<String> {
183+
// Start with the executable
184+
let mut command_args = vec![self.executable.to_string_lossy().to_string()];
185+
186+
// Process arguments in the correct order for compilation database
187+
let mut source_added = false;
188+
189+
// Add all non-source arguments, while handling source file placement
190+
for arg in &self.arguments {
191+
if matches!(arg.kind, ArgumentKind::Source) {
192+
continue;
193+
}
194+
195+
// If we encounter output arguments and haven't added the source yet,
196+
// add the source first, then the output args
197+
if matches!(arg.kind, ArgumentKind::Output) && !source_added {
198+
command_args.push(source_file.to_string());
199+
source_added = true;
200+
}
201+
202+
command_args.extend(arg.args.iter().cloned());
203+
}
204+
205+
// If we haven't added the source yet, add it at the end
206+
if !source_added {
207+
command_args.push(source_file.to_string());
208+
}
209+
210+
command_args
211+
}
212+
213+
/// Returns arguments of a specific kind from the command.
214+
///
215+
/// This method filters arguments by their kind and returns their values as strings.
216+
fn find_arguments_by_kind(&self, kind: ArgumentKind) -> impl Iterator<Item = &ArgumentGroup> {
217+
self.arguments.iter().filter(move |arg| arg.kind == kind)
218+
}
219+
220+
/// Computes the output file path from the command arguments.
221+
///
222+
/// This method examines the output arguments (typically "-o filename")
223+
/// and returns the filename as a PathBuf.
224+
fn compute_output_file(&self) -> Option<String> {
225+
// Find output arguments and convert to a file path
226+
self.find_arguments_by_kind(ArgumentKind::Output)
227+
.nth(0)
228+
.and_then(|arg_group| arg_group.as_file())
229+
}
181230
}
182231

183232
#[cfg(test)]
@@ -234,13 +283,13 @@ mod test {
234283
let expected = vec![
235284
clang::Entry::from_arguments_str(
236285
"file1.cpp",
237-
vec!["/usr/bin/g++", "-c", "file1.cpp", "file2.cpp"],
286+
vec!["/usr/bin/g++", "-c", "file1.cpp"],
238287
"/home/user",
239288
None,
240289
),
241290
clang::Entry::from_arguments_str(
242291
"file2.cpp",
243-
vec!["/usr/bin/g++", "-c", "file1.cpp", "file2.cpp"],
292+
vec!["/usr/bin/g++", "-c", "file2.cpp"],
244293
"/home/user",
245294
None,
246295
),

0 commit comments

Comments
 (0)