@@ -112,6 +112,7 @@ fn generate_crate_info(
112112 "--aspects={rules_rust}//rust:defs.bzl%rust_analyzer_aspect"
113113 ) )
114114 . arg ( "--output_groups=rust_analyzer_crate_spec,rust_generated_srcs,rust_analyzer_proc_macro_dylib,rust_analyzer_src" )
115+ . arg ( "--" )
115116 . args ( targets)
116117 . output ( ) ?;
117118
@@ -166,6 +167,53 @@ where
166167 serde_json:: from_str ( & content) . with_context ( || format ! ( "failed to deserialize file: {path}" ) )
167168}
168169
170+ /// Returns the bazel build args that `generate_crate_info` would pass, for testing.
171+ #[ cfg( test) ]
172+ fn build_crate_info_args ( rules_rust : & str , targets : & [ String ] ) -> Vec < String > {
173+ let mut args = vec ! [
174+ "build" . to_owned( ) ,
175+ "--norun_validations" . to_owned( ) ,
176+ "--remote_download_all" . to_owned( ) ,
177+ format!( "--aspects={rules_rust}//rust:defs.bzl%rust_analyzer_aspect" ) ,
178+ "--output_groups=rust_analyzer_crate_spec,rust_generated_srcs,rust_analyzer_proc_macro_dylib,rust_analyzer_src" . to_owned( ) ,
179+ "--" . to_owned( ) ,
180+ ] ;
181+ args. extend_from_slice ( targets) ;
182+ args
183+ }
184+
185+ #[ cfg( test) ]
186+ mod tests {
187+ use super :: * ;
188+
189+ /// Without `--` before targets, Bazel treats exclusion patterns like `-//foo:bar`
190+ /// as unknown flags and exits with "unrecognized option". This test replicates
191+ /// that failure mode and verifies the separator is in the correct position.
192+ #[ test]
193+ fn exclusion_targets_preceded_by_double_dash ( ) {
194+ let targets = vec ! [
195+ "//..." . to_owned( ) ,
196+ "-//some/excluded:target" . to_owned( ) ,
197+ ] ;
198+ let args = build_crate_info_args ( "rules_rust" , & targets) ;
199+
200+ let dash_pos = args. iter ( ) . position ( |a| a == "--" ) . expect ( "-- must be present" ) ;
201+ let targets_start = dash_pos + 1 ;
202+ assert_eq ! (
203+ & args[ targets_start..] ,
204+ targets. as_slice( ) ,
205+ "all targets must appear after --"
206+ ) ;
207+ // Exclusion pattern must not appear before --
208+ for arg in & args[ ..dash_pos] {
209+ assert ! (
210+ !arg. starts_with( '-' ) || arg. starts_with( "--" ) ,
211+ "target exclusion {arg:?} appeared before -- and would be parsed as a flag"
212+ ) ;
213+ }
214+ }
215+ }
216+
169217/// `rust-analyzer` associates workspaces with buildfiles. Therefore, when it passes in a
170218/// source file path, we use this function to identify the buildfile the file belongs to.
171219fn source_file_to_buildfile ( file : & Utf8Path ) -> anyhow:: Result < Utf8PathBuf > {
0 commit comments