@@ -70,7 +70,9 @@ impl LanguageSpec for CppSpec {
7070 "type"
7171 }
7272 fn resolve_name ( & self , node : Node < ' _ > , source : & str ) -> Option < String > {
73- if let Some ( name) = recover_cpp_macro_defined_name ( node, source) {
73+ if let Some ( name) = recover_single_arg_macro_defined_name ( node, source)
74+ . or_else ( || recover_cpp_macro_defined_name ( node, source) )
75+ {
7476 return Some ( name) ;
7577 }
7678 let qid = declarator_qualified_id ( child_by_field ( node, "declarator" ) ?) ?;
@@ -921,6 +923,108 @@ fn mask_quoted_literal(bytes: &[u8], mask: &mut [bool], start: usize, quote: u8)
921923 i
922924}
923925
926+ /// Recover the function a single-argument macro defines —
927+ /// `#define NATIVE_FN(name) int name(void)` then `NATIVE_FN(get_version) { … }`
928+ /// (upstream #1373). Without its definition the shape is ambiguous, so a
929+ /// preceding local `#define` whose replacement uses its sole parameter as the
930+ /// function declarator is required; registration, token-pasting and `typedef`
931+ /// replacements, an `#undef` or redefinition in between, and any conditional
932+ /// block leave the name alone. C parses the macro as the return type with a
933+ /// `(name)` declarator; C++ as an implicit-return-type function.
934+ pub ( crate ) fn recover_single_arg_macro_defined_name (
935+ node : Node < ' _ > ,
936+ source : & str ,
937+ ) -> Option < String > {
938+ if node. kind ( ) != "function_definition" {
939+ return None ;
940+ }
941+ let declarator = child_by_field ( node, "declarator" ) ?;
942+ let ( macro_node, argument) = match declarator. kind ( ) {
943+ "parenthesized_declarator" if declarator. named_child_count ( ) == 1 => {
944+ let macro_node = child_by_field ( node, "type" ) ?;
945+ let argument = declarator. named_child ( 0 ) ?;
946+ if macro_node. kind ( ) != "type_identifier" || argument. kind ( ) != "identifier" {
947+ return None ;
948+ }
949+ ( macro_node, argument)
950+ }
951+ "function_declarator" if child_by_field ( node, "type" ) . is_none ( ) => {
952+ let macro_node = child_by_field ( declarator, "declarator" ) ?;
953+ let params = child_by_field ( declarator, "parameters" ) ?;
954+ let param = params. named_child ( 0 ) ?;
955+ if macro_node. kind ( ) != "identifier"
956+ || params. named_child_count ( ) != 1
957+ || param. kind ( ) != "parameter_declaration"
958+ || param. named_child_count ( ) != 1
959+ {
960+ return None ;
961+ }
962+ let argument = param. named_child ( 0 ) ?;
963+ if argument. kind ( ) != "type_identifier" {
964+ return None ;
965+ }
966+ ( macro_node, argument)
967+ }
968+ _ => return None ,
969+ } ;
970+ let macro_name = node_text ( macro_node, source) ;
971+ static REPLACEMENT : OnceLock < Regex > = OnceLock :: new ( ) ;
972+ let replacement_pattern = REPLACEMENT . get_or_init ( || {
973+ Regex :: new (
974+ r"^(?:[A-Za-z_][A-Za-z0-9_:]*\s+)+[*&\s]*([A-Za-z_][A-Za-z0-9_]*)\s*\([^(){};#]*\)\s*$" ,
975+ )
976+ . expect ( "single-argument macro replacement pattern" )
977+ } ) ;
978+ static TYPEDEF : OnceLock < Regex > = OnceLock :: new ( ) ;
979+ let typedef = TYPEDEF . get_or_init ( || Regex :: new ( r"(?-u:\b)typedef(?-u:\b)" ) . expect ( "typedef" ) ) ;
980+ let mut scope = Some ( node) ;
981+ while let Some ( current) = scope {
982+ if current. kind ( ) == "preproc_else" || current. kind ( ) . starts_with ( "preproc_elif" ) {
983+ return None ;
984+ }
985+ let mut previous = current. prev_named_sibling ( ) ;
986+ while let Some ( prev) = previous {
987+ previous = prev. prev_named_sibling ( ) ;
988+ if prev. kind ( ) . starts_with ( "preproc_if" ) {
989+ return None ;
990+ }
991+ if prev. kind ( ) == "preproc_call"
992+ && child_by_field ( prev, "directive" )
993+ . is_some_and ( |directive| node_text ( directive, source) == "#undef" )
994+ && child_by_field ( prev, "argument" )
995+ . is_some_and ( |argument| node_text ( argument, source) . trim ( ) == macro_name)
996+ {
997+ return None ;
998+ }
999+ if !matches ! ( prev. kind( ) , "preproc_function_def" | "preproc_def" )
1000+ || child_by_field ( prev, "name" ) . map ( |name| node_text ( name, source) )
1001+ != Some ( macro_name. clone ( ) )
1002+ {
1003+ continue ;
1004+ }
1005+ let params = child_by_field ( prev, "parameters" ) ?;
1006+ let param = params. named_child ( 0 ) ?;
1007+ let value = child_by_field ( prev, "value" ) ?;
1008+ if params. named_child_count ( ) != 1 || param. kind ( ) != "identifier" {
1009+ return None ;
1010+ }
1011+ let replacement = node_text ( value, source)
1012+ . replace ( "\\ \r \n " , " " )
1013+ . replace ( "\\ \n " , " " ) ;
1014+ let replacement = replacement. trim ( ) ;
1015+ let declared = replacement_pattern
1016+ . captures ( replacement)
1017+ . and_then ( |captures| captures. get ( 1 ) ) ?;
1018+ if typedef. is_match ( replacement) || declared. as_str ( ) != node_text ( param, source) {
1019+ return None ;
1020+ }
1021+ return Some ( node_text ( argument, source) ) ;
1022+ }
1023+ scope = current. parent ( ) ;
1024+ }
1025+ None
1026+ }
1027+
9241028/// Recover the real function name from the macro-definition idiom
9251029/// `MACRO_NAME(real_name, typed args…) { body }` (flash-attention's
9261030/// `DEFINE_FLASH_FORWARD_KERNEL(flash_fwd_kernel, bool Is_dropout, …) { … }`):
0 commit comments