Skip to content

Commit 3ecfe60

Browse files
committed
semantic: implement GCC and Clang specific interpreters
1 parent a49dbdd commit 3ecfe60

File tree

10 files changed

+3531
-50
lines changed

10 files changed

+3531
-50
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ directories = "6.0"
3232
shell-words = "1.1"
3333
tempfile = { version = "3.19", default-features = false }
3434
signal-hook = { version = "0.3", default-features = false }
35+
regex = "1.0"
3536
libc = "0.2"
3637
which = "8.0"
3738
cc = "1.2"

bear/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ shell-words.workspace = true
3333
tempfile.workspace = true
3434
signal-hook.workspace = true
3535
crossbeam-channel.workspace = true
36+
regex.workspace = true
3637

3738
[dev-dependencies]
3839
tempfile.workspace = true
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
3+
//! Specialized Arguments implementations for different compiler argument types.
4+
//!
5+
//! This module provides concrete implementations of the [`Arguments`] trait
6+
//! for various types of compiler arguments, enabling more sophisticated
7+
//! argument parsing than the basic [`BasicArguments`] implementation.
8+
9+
#[cfg(test)]
10+
use crate::semantic::CompilerPass;
11+
use crate::semantic::{ArgumentKind, Arguments};
12+
use std::borrow::Cow;
13+
use std::path::{Path, PathBuf};
14+
15+
/// Represents a generic argument that holds an ArgumentKind and a vector of strings.
16+
///
17+
/// This handles all argument types except source and output files:
18+
/// - Simple flags: `-c`, `-Wall`
19+
/// - Flags with values: `-I /usr/include`, `-D MACRO=value`
20+
/// - Combined flags: `-Ipath`, `-DMACRO=value`
21+
/// - Compiler executable names
22+
/// - Response files: `@file`
23+
/// - Any other arguments
24+
#[derive(Debug, Clone, PartialEq)]
25+
pub struct OtherArguments {
26+
/// The argument strings (e.g., ["-I", "/usr/include"] or ["-Wall"])
27+
arguments: Vec<String>,
28+
/// The semantic meaning of this argument
29+
kind: ArgumentKind,
30+
}
31+
32+
impl OtherArguments {
33+
/// Creates a new argument with the given strings and kind.
34+
pub fn new(arguments: Vec<String>, kind: ArgumentKind) -> Self {
35+
Self { arguments, kind }
36+
}
37+
}
38+
39+
impl Arguments for OtherArguments {
40+
fn kind(&self) -> ArgumentKind {
41+
self.kind.clone()
42+
}
43+
44+
fn as_arguments(&self, _path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Vec<String> {
45+
self.arguments.clone()
46+
}
47+
48+
fn as_file(&self, _path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Option<PathBuf> {
49+
// Other arguments don't represent files directly
50+
None
51+
}
52+
}
53+
54+
/// Represents a source file argument.
55+
#[derive(Debug, Clone, PartialEq)]
56+
pub struct SourceArgument {
57+
/// Path to the source file
58+
path: String,
59+
}
60+
61+
impl SourceArgument {
62+
/// Creates a new source file argument.
63+
pub fn new(path: String) -> Self {
64+
Self { path }
65+
}
66+
}
67+
68+
impl Arguments for SourceArgument {
69+
fn kind(&self) -> ArgumentKind {
70+
ArgumentKind::Source
71+
}
72+
73+
fn as_arguments(&self, path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Vec<String> {
74+
let path = Path::new(&self.path);
75+
let updated_path = path_updater(path);
76+
vec![updated_path.to_string_lossy().to_string()]
77+
}
78+
79+
fn as_file(&self, path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Option<PathBuf> {
80+
let path = Path::new(&self.path);
81+
let updated_path = path_updater(path);
82+
Some(updated_path.to_path_buf())
83+
}
84+
}
85+
86+
/// Represents an output file argument.
87+
#[derive(Debug, Clone, PartialEq)]
88+
pub struct OutputArgument {
89+
/// The output flag (usually "-o")
90+
flag: String,
91+
/// Path to the output file
92+
path: String,
93+
}
94+
95+
impl OutputArgument {
96+
/// Creates a new output argument.
97+
pub fn new(flag: String, path: String) -> Self {
98+
Self { flag, path }
99+
}
100+
}
101+
102+
impl Arguments for OutputArgument {
103+
fn kind(&self) -> ArgumentKind {
104+
ArgumentKind::Output
105+
}
106+
107+
fn as_arguments(&self, path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Vec<String> {
108+
let path = Path::new(&self.path);
109+
let updated_path = path_updater(path);
110+
vec![
111+
self.flag.clone(),
112+
updated_path.to_string_lossy().to_string(),
113+
]
114+
}
115+
116+
fn as_file(&self, path_updater: &dyn Fn(&Path) -> Cow<Path>) -> Option<PathBuf> {
117+
let path = Path::new(&self.path);
118+
let updated_path = path_updater(path);
119+
Some(updated_path.to_path_buf())
120+
}
121+
}
122+
123+
#[cfg(test)]
124+
mod tests {
125+
use super::*;
126+
127+
#[test]
128+
fn test_other_arguments_new_with_simple_flag() {
129+
let arg = OtherArguments::new(
130+
vec!["-c".to_string()],
131+
ArgumentKind::Other(Some(CompilerPass::Compiling)),
132+
);
133+
134+
assert_eq!(
135+
arg.kind(),
136+
ArgumentKind::Other(Some(CompilerPass::Compiling))
137+
);
138+
assert_eq!(arg.as_arguments(&|p| Cow::Borrowed(p)), vec!["-c"]);
139+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
140+
}
141+
142+
#[test]
143+
fn test_other_arguments_new_with_flag_and_value() {
144+
let arg = OtherArguments::new(
145+
vec!["-I".to_string(), "/usr/include".to_string()],
146+
ArgumentKind::Other(Some(CompilerPass::Preprocessing)),
147+
);
148+
149+
assert_eq!(
150+
arg.kind(),
151+
ArgumentKind::Other(Some(CompilerPass::Preprocessing))
152+
);
153+
assert_eq!(
154+
arg.as_arguments(&|p| Cow::Borrowed(p)),
155+
vec!["-I", "/usr/include"]
156+
);
157+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
158+
}
159+
160+
#[test]
161+
fn test_other_arguments_new_with_combined_flag() {
162+
let arg = OtherArguments::new(
163+
vec!["-I/usr/include".to_string()],
164+
ArgumentKind::Other(Some(CompilerPass::Preprocessing)),
165+
);
166+
167+
assert_eq!(
168+
arg.kind(),
169+
ArgumentKind::Other(Some(CompilerPass::Preprocessing))
170+
);
171+
assert_eq!(
172+
arg.as_arguments(&|p| Cow::Borrowed(p)),
173+
vec!["-I/usr/include"]
174+
);
175+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
176+
}
177+
178+
#[test]
179+
fn test_other_arguments_new_with_compiler() {
180+
let arg = OtherArguments::new(vec!["gcc".to_string()], ArgumentKind::Compiler);
181+
182+
assert_eq!(arg.kind(), ArgumentKind::Compiler);
183+
assert_eq!(arg.as_arguments(&|p| Cow::Borrowed(p)), vec!["gcc"]);
184+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
185+
}
186+
187+
#[test]
188+
fn test_other_arguments_new_with_response_file() {
189+
let arg = OtherArguments::new(vec!["@response.txt".to_string()], ArgumentKind::Other(None));
190+
191+
assert_eq!(arg.kind(), ArgumentKind::Other(None));
192+
assert_eq!(
193+
arg.as_arguments(&|p| Cow::Borrowed(p)),
194+
vec!["@response.txt"]
195+
);
196+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
197+
}
198+
199+
#[test]
200+
fn test_source_argument() {
201+
let arg = SourceArgument::new("main.c".to_string());
202+
203+
assert_eq!(arg.kind(), ArgumentKind::Source);
204+
assert_eq!(arg.as_arguments(&|p| Cow::Borrowed(p)), vec!["main.c"]);
205+
assert_eq!(
206+
arg.as_file(&|p| Cow::Borrowed(p)),
207+
Some(PathBuf::from("main.c"))
208+
);
209+
}
210+
211+
#[test]
212+
fn test_output_argument() {
213+
let arg = OutputArgument::new("-o".to_string(), "main.o".to_string());
214+
215+
assert_eq!(arg.kind(), ArgumentKind::Output);
216+
assert_eq!(
217+
arg.as_arguments(&|p| Cow::Borrowed(p)),
218+
vec!["-o", "main.o"]
219+
);
220+
assert_eq!(
221+
arg.as_file(&|p| Cow::Borrowed(p)),
222+
Some(PathBuf::from("main.o"))
223+
);
224+
}
225+
226+
#[test]
227+
fn test_path_updater_functionality() {
228+
let arg = SourceArgument::new("src/main.c".to_string());
229+
230+
// Test with identity path updater (no change)
231+
assert_eq!(
232+
arg.as_arguments(&|p| std::borrow::Cow::Borrowed(p)),
233+
vec!["src/main.c"]
234+
);
235+
assert_eq!(
236+
arg.as_file(&|p| std::borrow::Cow::Borrowed(p)),
237+
Some(PathBuf::from("src/main.c"))
238+
);
239+
}
240+
241+
#[test]
242+
fn test_other_arguments_new() {
243+
let arg = OtherArguments::new(
244+
vec!["-D".to_string(), "MACRO=value".to_string()],
245+
ArgumentKind::Other(Some(CompilerPass::Preprocessing)),
246+
);
247+
248+
assert_eq!(
249+
arg.kind(),
250+
ArgumentKind::Other(Some(CompilerPass::Preprocessing))
251+
);
252+
assert_eq!(
253+
arg.as_arguments(&|p| Cow::Borrowed(p)),
254+
vec!["-D", "MACRO=value"]
255+
);
256+
assert_eq!(arg.as_file(&|p| Cow::Borrowed(p)), None);
257+
}
258+
}

0 commit comments

Comments
 (0)