@@ -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
4240impl 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