#![cfg(feature = "sysroot-abi")] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod common { pub(crate) mod utils; } use common::utils::{ create_empty_token_tree, proc_macro_test_dylib_path, request_bidirectional, with_server, }; use expect_test::expect; use proc_macro_api::{ ProtocolFormat::BidirectionalPostcardPrototype, bidirectional_protocol::{ msg::{ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response}, reject_subrequests, }, legacy_protocol::msg::{PanicMessage, ServerConfig, SpanDataIndexMap, SpanMode}, version::CURRENT_API_VERSION, }; #[test] fn test_bidi_version_check_bidirectional() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let response = request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); match response { Response::ApiVersionCheck(version) => { assert_eq!(version, CURRENT_API_VERSION); } other => panic!("unexpected response: {other:?}"), } }); } #[test] fn test_bidi_list_macros() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); let response = request_bidirectional( writer, reader, Request::ListMacros { dylib_path }, &reject_subrequests, ); let Response::ListMacros(Ok(macros)) = response else { panic!("expected successful ListMacros response"); }; let mut macro_list: Vec<_> = macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect(); macro_list.sort(); let macro_list_str = macro_list.join("\n"); expect![[r#" DeriveEmpty [CustomDerive] DeriveError [CustomDerive] DerivePanic [CustomDerive] DeriveReemit [CustomDerive] attr_error [Attr] attr_noop [Attr] attr_panic [Attr] fn_like_clone_tokens [Bang] fn_like_error [Bang] fn_like_mk_idents [Bang] fn_like_mk_literals [Bang] fn_like_noop [Bang] fn_like_panic [Bang] fn_like_span_join [Bang] fn_like_span_line_column [Bang] fn_like_span_ops [Bang]"#]] .assert_eq(¯o_list_str); }); } #[test] fn test_bidi_list_macros_invalid_path() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let response = request_bidirectional( writer, reader, Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, reject_subrequests, ); match response { Response::ListMacros(Err(e)) => assert!( e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"), "{e}" ), other => panic!("expected error response, got: {other:?}"), } }); } #[test] fn test_bidi_set_config() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let config = ServerConfig { span_mode: SpanMode::Id }; let response = request_bidirectional(writer, reader, Request::SetConfig(config), reject_subrequests); match response { Response::SetConfig(returned_config) => { assert_eq!(returned_config.span_mode, SpanMode::Id); } other => panic!("unexpected response: {other:?}"), } }); } #[test] fn test_bidi_set_config_rust_analyzer_mode() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let config = ServerConfig { span_mode: SpanMode::RustAnalyzer }; let response = request_bidirectional(writer, reader, Request::SetConfig(config), reject_subrequests); match response { Response::SetConfig(returned_config) => { assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer); } other => panic!("unexpected response: {other:?}"), } }); } #[test] fn test_bidi_expand_macro_panic() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); let mut span_data_table = SpanDataIndexMap::default(); let macro_body = common::utils::create_empty_token_tree(CURRENT_API_VERSION, &mut span_data_table); let request1 = Request::ExpandMacro(Box::new(ExpandMacro { lib: dylib_path, env: vec![], current_dir: None, data: ExpandMacroData { macro_body, macro_name: "fn_like_panic".to_owned(), attributes: None, has_global_spans: ExpnGlobals { def_site: 0, call_site: 0, mixed_site: 0 }, span_data_table: vec![], }, })); let response = request_bidirectional(writer, reader, request1, reject_subrequests); match response { Response::ExpandMacro(Err(PanicMessage(msg))) => { assert!(msg.contains("fn_like_panic"), "panic message should mention macro name"); } other => panic!("expected panic response, got: {other:?}"), } }); } #[test] fn test_bidi_basic_call_flow() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); let response1 = request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); assert!(matches!(response1, Response::ApiVersionCheck(_))); let response2 = request_bidirectional( writer, reader, Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }), reject_subrequests, ); assert!(matches!(response2, Response::SetConfig(_))); let response3 = request_bidirectional( writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }, reject_subrequests, ); assert!(matches!(response3, Response::ListMacros(Ok(_)))); }); } #[test] fn test_bidi_expand_nonexistent_macro() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); let version_response = request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); let Response::ApiVersionCheck(version) = version_response else { panic!("expected version check response"); }; let mut span_data_table = SpanDataIndexMap::default(); let macro_body = create_empty_token_tree(version, &mut span_data_table); let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { lib: dylib_path, env: vec![], current_dir: None, data: ExpandMacroData { macro_body, macro_name: "NonexistentMacro".to_owned(), attributes: None, has_global_spans: ExpnGlobals { def_site: 0, call_site: 0, mixed_site: 0 }, span_data_table: vec![], }, })); let response = request_bidirectional(writer, reader, expand_request, reject_subrequests); match response { Response::ExpandMacro(Err(PanicMessage(msg))) => { expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg) } other => panic!("expected error for nonexistent macro, got: {other:?}"), } }); }