Skip to content

Commit 329da8c

Browse files
committed
semantic: object files are represented as source kind
1 parent 6e717d7 commit 329da8c

File tree

10 files changed

+141
-193
lines changed

10 files changed

+141
-193
lines changed

bear/src/output/clang/converter.rs

Lines changed: 55 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
use super::Entry;
5151
use super::{ConfigurablePathFormatter, PathFormatter};
5252
use crate::config;
53-
use crate::semantic::interpreters::is_binary_file;
5453
use crate::semantic::{ArgumentKind, Arguments, Command, CompilerCommand, CompilerPass, PassEffect};
5554
use log::warn;
5655
use std::borrow::Cow;
@@ -101,8 +100,10 @@ impl CommandConverter {
101100
// Create output file if needed
102101
let output_file = self.create_output_file(&formatted_directory, &cmd.arguments);
103102

104-
// Create one entry per source argument
105-
Self::find_arguments_by_kind(cmd, ArgumentKind::Source)
103+
// Create one entry per source argument (only non-binary source files)
104+
cmd.arguments
105+
.iter()
106+
.filter(|arg| matches!(arg.kind(), ArgumentKind::Source { binary: false }))
106107
.filter_map(|source_arg| {
107108
// Get and format source file
108109
let path_updater: &dyn Fn(&Path) -> Cow<Path> = &|path: &Path| Cow::Borrowed(path);
@@ -202,7 +203,7 @@ impl CommandConverter {
202203
// Add all non-source arguments, while handling source file placement
203204
for arg in &cmd.arguments {
204205
// Skip this specific source argument (using pointer equality)
205-
if matches!(arg.kind(), ArgumentKind::Source) && !std::ptr::eq(arg.as_ref(), source_arg) {
206+
if matches!(arg.kind(), ArgumentKind::Source { .. }) && !std::ptr::eq(arg.as_ref(), source_arg) {
206207
continue;
207208
}
208209

@@ -221,7 +222,7 @@ impl CommandConverter {
221222

222223
// For file-type arguments, we need to format the paths
223224
match arg.kind() {
224-
ArgumentKind::Source | ArgumentKind::Output => {
225+
ArgumentKind::Source { .. } | ArgumentKind::Output => {
225226
// These might contain file paths that need formatting
226227
let formatted_args = original_args
227228
.into_iter()
@@ -264,11 +265,19 @@ impl CommandConverter {
264265
/// Returns arguments of a specific kind from the command.
265266
///
266267
/// This method filters arguments by their kind and returns their values as strings.
268+
/// For `ArgumentKind::Source`, this matches any source regardless of the `binary` flag.
267269
fn find_arguments_by_kind(
268270
cmd: &CompilerCommand,
269271
kind: ArgumentKind,
270272
) -> impl Iterator<Item = &Box<dyn Arguments>> {
271-
cmd.arguments.iter().filter(move |arg| arg.kind() == kind)
273+
cmd.arguments.iter().filter(move |arg| {
274+
match (arg.kind(), kind) {
275+
// For Source, match any source regardless of binary flag
276+
(ArgumentKind::Source { .. }, ArgumentKind::Source { .. }) => true,
277+
// For other kinds, use exact equality
278+
(a, b) => a == b,
279+
}
280+
})
272281
}
273282

274283
/// Determines if we should skip generating compilation database entries for a command.
@@ -289,9 +298,9 @@ impl CommandConverter {
289298
return true;
290299
}
291300

292-
// Find all source arguments
293-
let source_arguments =
294-
Self::find_arguments_by_kind(cmd, ArgumentKind::Source).collect::<Vec<&Box<dyn Arguments>>>();
301+
// Find all source arguments (using binary: false as a placeholder, find_arguments_by_kind matches any source)
302+
let source_arguments = Self::find_arguments_by_kind(cmd, ArgumentKind::Source { binary: false })
303+
.collect::<Vec<&Box<dyn Arguments>>>();
295304

296305
// If no source files found, skip entry generation
297306
if source_arguments.is_empty() {
@@ -333,9 +342,8 @@ impl CommandConverter {
333342
///
334343
/// This typically happens when linking pre-compiled object files or libraries.
335344
///
336-
/// Note: Arguments with `ArgumentKind::Source` have already been validated by
337-
/// `looks_like_a_source_file()` during semantic analysis, but we still need to
338-
/// exclude object files (.o, .a, etc.) which may be misclassified in tests.
345+
/// The `binary` flag on `ArgumentKind::Source` is set during semantic analysis
346+
/// by the interpreter, so we can simply check it here.
339347
fn is_linking_only(&self, cmd: &CompilerCommand) -> bool {
340348
// Check if the command has a flag that stops before linking (-c or -S)
341349
let stops_before_linking = cmd.arguments.iter().any(|arg| {
@@ -351,17 +359,10 @@ impl CommandConverter {
351359
return false;
352360
}
353361

354-
// Check if there are any compilable source files (not object/library files)
362+
// Check if there are any compilable source files (not binary files)
363+
// The binary flag is set by the interpreter during semantic analysis
355364
let has_compilable_sources =
356-
Self::find_arguments_by_kind(cmd, ArgumentKind::Source).any(|source_arg| {
357-
let path_updater: &dyn Fn(&Path) -> Cow<Path> = &|path: &Path| Cow::Borrowed(path);
358-
if let Some(file_path) = source_arg.as_file(path_updater) {
359-
// Check if this is a compilable source file (not a binary file)
360-
!is_binary_file(&file_path)
361-
} else {
362-
false
363-
}
364-
});
365+
cmd.arguments.iter().any(|arg| matches!(arg.kind(), ArgumentKind::Source { binary: false }));
365366

366367
// If no -c/-S flag and no compilable sources, it's linking-only
367368
!has_compilable_sources
@@ -385,7 +386,7 @@ mod tests {
385386
(ArgumentKind::Compiler, vec!["/usr/bin/gcc"]),
386387
(ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Compiling)), vec!["-c"]),
387388
(ArgumentKind::Other(PassEffect::None), vec!["-Wall"]),
388-
(ArgumentKind::Source, vec!["main.c"]),
389+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
389390
(ArgumentKind::Output, vec!["-o", "main.o"]),
390391
],
391392
));
@@ -411,8 +412,8 @@ mod tests {
411412
vec![
412413
(ArgumentKind::Compiler, vec!["/usr/bin/g++"]),
413414
(ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Compiling)), vec!["-c"]),
414-
(ArgumentKind::Source, vec!["file1.cpp"]),
415-
(ArgumentKind::Source, vec!["file2.cpp"]),
415+
(ArgumentKind::Source { binary: false }, vec!["file1.cpp"]),
416+
(ArgumentKind::Source { binary: false }, vec!["file2.cpp"]),
416417
],
417418
));
418419

@@ -451,7 +452,7 @@ mod tests {
451452
vec![
452453
(ArgumentKind::Compiler, vec!["/usr/bin/gcc"]),
453454
(ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Compiling)), vec!["-c"]),
454-
(ArgumentKind::Source, vec!["main.c"]),
455+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
455456
(ArgumentKind::Output, vec!["-o", "main.o"]),
456457
],
457458
));
@@ -475,7 +476,7 @@ mod tests {
475476
vec![
476477
(ArgumentKind::Compiler, vec!["/usr/bin/gcc"]),
477478
(ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Compiling)), vec!["-c"]),
478-
(ArgumentKind::Source, vec!["main.c"]),
479+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
479480
(ArgumentKind::Output, vec!["-o", "main.o"]),
480481
],
481482
));
@@ -509,7 +510,7 @@ mod tests {
509510
"/usr/bin/gcc",
510511
vec![
511512
(ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Compiling)), vec!["-c"]),
512-
(ArgumentKind::Source, vec!["test.c"]),
513+
(ArgumentKind::Source { binary: false }, vec!["test.c"]),
513514
],
514515
);
515516
let command = Command::Compiler(compiler_cmd);
@@ -539,7 +540,10 @@ mod tests {
539540
let compiler_cmd = CompilerCommand::from_strings(
540541
"/original/dir",
541542
"/usr/bin/gcc",
542-
vec![(ArgumentKind::Source, vec!["main.c"]), (ArgumentKind::Output, vec!["-o", "main.o"])],
543+
vec![
544+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
545+
(ArgumentKind::Output, vec!["-o", "main.o"]),
546+
],
543547
);
544548
let command = Command::Compiler(compiler_cmd);
545549

@@ -564,7 +568,7 @@ mod tests {
564568
let compiler_cmd = CompilerCommand::from_strings(
565569
"/nonexistent/dir",
566570
"/usr/bin/gcc",
567-
vec![(ArgumentKind::Source, vec!["main.c"])],
571+
vec![(ArgumentKind::Source { binary: false }, vec!["main.c"])],
568572
);
569573
let command = Command::Compiler(compiler_cmd);
570574

@@ -590,7 +594,7 @@ mod tests {
590594
let compiler_cmd = CompilerCommand::from_strings(
591595
"/home/user",
592596
"/usr/bin/gcc",
593-
vec![(ArgumentKind::Source, vec!["nonexistent.c"])],
597+
vec![(ArgumentKind::Source { binary: false }, vec!["nonexistent.c"])],
594598
);
595599
let command = Command::Compiler(compiler_cmd);
596600

@@ -633,7 +637,10 @@ mod tests {
633637
let compiler_cmd = CompilerCommand::from_strings(
634638
"/home/user",
635639
"/usr/bin/gcc",
636-
vec![(ArgumentKind::Source, vec!["main.c"]), (ArgumentKind::Output, vec!["-o", "main.o"])],
640+
vec![
641+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
642+
(ArgumentKind::Output, vec!["-o", "main.o"]),
643+
],
637644
);
638645
let command = Command::Compiler(compiler_cmd);
639646

@@ -655,7 +662,7 @@ mod tests {
655662
"gcc",
656663
vec![
657664
(ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Preprocessing)), vec!["-E"]),
658-
(ArgumentKind::Source, vec!["main.c"]),
665+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
659666
],
660667
);
661668
let command = Command::Compiler(compiler_cmd);
@@ -674,7 +681,8 @@ mod tests {
674681
"/home/user",
675682
"gcc",
676683
vec![
677-
(ArgumentKind::Source, vec!["main.o", "lib.o"]),
684+
(ArgumentKind::Source { binary: true }, vec!["main.o"]),
685+
(ArgumentKind::Source { binary: true }, vec!["lib.o"]),
678686
(ArgumentKind::Output, vec!["-o", "program"]),
679687
(ArgumentKind::Other(PassEffect::Configures(CompilerPass::Linking)), vec!["-L/usr/lib"]),
680688
],
@@ -695,7 +703,7 @@ mod tests {
695703
"gcc",
696704
vec![
697705
(ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Compiling)), vec!["-c"]),
698-
(ArgumentKind::Source, vec!["main.c"]),
706+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
699707
(ArgumentKind::Output, vec!["-o", "main.o"]),
700708
],
701709
);
@@ -718,7 +726,7 @@ mod tests {
718726
"/home/user",
719727
"gcc",
720728
vec![
721-
(ArgumentKind::Source, vec!["main.c"]),
729+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
722730
(ArgumentKind::Other(PassEffect::Configures(CompilerPass::Linking)), vec!["-L/usr/lib"]),
723731
(ArgumentKind::Other(PassEffect::Configures(CompilerPass::Linking)), vec!["-lmath"]),
724732
(ArgumentKind::Other(PassEffect::Configures(CompilerPass::Compiling)), vec!["-Wall"]),
@@ -765,8 +773,8 @@ mod tests {
765773
"/home/user",
766774
"gcc",
767775
vec![
768-
(ArgumentKind::Source, vec!["main.c"]), // Real source file
769-
(ArgumentKind::Source, vec!["utils.cpp"]), // Real source file
776+
(ArgumentKind::Source { binary: false }, vec!["main.c"]), // Real source file
777+
(ArgumentKind::Source { binary: false }, vec!["utils.cpp"]), // Real source file
770778
(ArgumentKind::Other(PassEffect::Configures(CompilerPass::Linking)), vec!["-lm"]),
771779
],
772780
);
@@ -780,8 +788,8 @@ mod tests {
780788
"/home/user",
781789
"gcc",
782790
vec![
783-
(ArgumentKind::Source, vec!["main.o"]), // Object file
784-
(ArgumentKind::Source, vec!["utils.a"]), // Static library
791+
(ArgumentKind::Source { binary: true }, vec!["main.o"]), // Object file
792+
(ArgumentKind::Source { binary: true }, vec!["utils.a"]), // Static library
785793
(ArgumentKind::Output, vec!["-o", "program"]),
786794
],
787795
);
@@ -809,7 +817,7 @@ mod tests {
809817
ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Preprocessing)),
810818
vec!["-E"], // Semantically classified as preprocessing
811819
),
812-
(ArgumentKind::Source, vec!["main.c"]),
820+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
813821
],
814822
);
815823
let command = Command::Compiler(compiler_cmd);
@@ -825,7 +833,7 @@ mod tests {
825833
ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Compiling)),
826834
vec!["-c"], // Semantically classified as compiling
827835
),
828-
(ArgumentKind::Source, vec!["main.c"]),
836+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
829837
],
830838
);
831839
let command = Command::Compiler(compiler_cmd);
@@ -850,7 +858,7 @@ mod tests {
850858
"/home/user",
851859
"gcc",
852860
vec![
853-
(ArgumentKind::Source, vec!["main.c"]),
861+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
854862
(
855863
ArgumentKind::Other(PassEffect::Configures(CompilerPass::Linking)),
856864
vec!["-lmath"], // Semantically classified as linking
@@ -886,7 +894,7 @@ mod tests {
886894
vec![
887895
(ArgumentKind::Compiler, vec!["gcc"]),
888896
(ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Compiling)), vec!["-c"]),
889-
(ArgumentKind::Source, vec!["main.c"]),
897+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
890898
(ArgumentKind::Output, vec!["-o", "main.o"]),
891899
],
892900
);
@@ -931,7 +939,7 @@ mod tests {
931939
vec!["-DWRAPPER_FLAG"],
932940
),
933941
(ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Compiling)), vec!["-c"]),
934-
(ArgumentKind::Source, vec!["test.c"]),
942+
(ArgumentKind::Source { binary: false }, vec!["test.c"]),
935943
],
936944
);
937945
let command = Command::Compiler(compiler_cmd);
@@ -968,7 +976,7 @@ mod tests {
968976
ArgumentKind::Other(PassEffect::Configures(CompilerPass::Preprocessing)),
969977
vec!["-DSOME_DEFINE"],
970978
),
971-
(ArgumentKind::Source, vec!["test.c"]),
979+
(ArgumentKind::Source { binary: false }, vec!["test.c"]),
972980
],
973981
);
974982
let command = Command::Compiler(compiler_cmd);
@@ -992,7 +1000,7 @@ mod tests {
9921000
(ArgumentKind::Compiler, vec!["gcc"]),
9931001
(ArgumentKind::Other(PassEffect::DriverOption), vec!["-pipe"]),
9941002
(ArgumentKind::Other(PassEffect::StopsAt(CompilerPass::Compiling)), vec!["-c"]),
995-
(ArgumentKind::Source, vec!["main.c"]),
1003+
(ArgumentKind::Source { binary: false }, vec!["main.c"]),
9961004
],
9971005
);
9981006
let command = Command::Compiler(compiler_cmd);

bear/src/semantic/interpreters/compilers/arguments.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//! for various types of compiler arguments, enabling more sophisticated
77
//! argument parsing than the basic [`BasicArguments`] implementation.
88
9+
use crate::semantic::interpreters::matchers::looks_like_a_source_file;
910
use crate::semantic::{ArgumentKind, Arguments};
1011
use std::borrow::Cow;
1112
use std::path::{Path, PathBuf};
@@ -54,18 +55,24 @@ impl Arguments for OtherArguments {
5455
pub struct SourceArgument {
5556
/// Path to the source file
5657
path: String,
58+
/// Whether this is a binary file (object file or library)
59+
binary: bool,
5760
}
5861

5962
impl SourceArgument {
6063
/// Creates a new source file argument.
64+
///
65+
/// Automatically determines if the file is a binary (object/library) file
66+
/// based on its extension.
6167
pub fn new(path: String) -> Self {
62-
Self { path }
68+
let source = looks_like_a_source_file(&path);
69+
Self { path, binary: !source }
6370
}
6471
}
6572

6673
impl Arguments for SourceArgument {
6774
fn kind(&self) -> ArgumentKind {
68-
ArgumentKind::Source
75+
ArgumentKind::Source { binary: self.binary }
6976
}
7077

7178
fn as_arguments(&self, path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Vec<String> {
@@ -179,11 +186,29 @@ mod tests {
179186
fn test_source_argument() {
180187
let arg = SourceArgument::new("main.c".to_string());
181188

182-
assert_eq!(arg.kind(), ArgumentKind::Source);
189+
assert_eq!(arg.kind(), ArgumentKind::Source { binary: false });
183190
assert_eq!(arg.as_arguments(&|p| Cow::Borrowed(p)), vec!["main.c"]);
184191
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), Some(PathBuf::from("main.c")));
185192
}
186193

194+
#[test]
195+
fn test_source_argument_binary_object_file() {
196+
let arg = SourceArgument::new("main.o".to_string());
197+
198+
assert_eq!(arg.kind(), ArgumentKind::Source { binary: true });
199+
assert_eq!(arg.as_arguments(&|p| Cow::Borrowed(p)), vec!["main.o"]);
200+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), Some(PathBuf::from("main.o")));
201+
}
202+
203+
#[test]
204+
fn test_source_argument_binary_library() {
205+
let arg = SourceArgument::new("libfoo.a".to_string());
206+
207+
assert_eq!(arg.kind(), ArgumentKind::Source { binary: true });
208+
assert_eq!(arg.as_arguments(&|p| Cow::Borrowed(p)), vec!["libfoo.a"]);
209+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), Some(PathBuf::from("libfoo.a")));
210+
}
211+
187212
#[test]
188213
fn test_output_argument() {
189214
let arg = OutputArgument::new("-o".to_string(), "main.o".to_string());

0 commit comments

Comments
 (0)