Extract a function or method declaration.
(state: &mut ExtractionState, node: TsNode<'_>)
| 921 | |
| 922 | /// Extract a function or method declaration. |
| 923 | fn visit_function(state: &mut ExtractionState, node: TsNode<'_>) { |
| 924 | let name = find_direct_child_by_kind(node, "simple_identifier") |
| 925 | .map_or_else(|| "<anonymous>".to_string(), |n| state.node_text(n)); |
| 926 | |
| 927 | // Check if this is an extension function (has a receiver type before the dot and name). |
| 928 | let is_extension = Self::is_extension_function(node); |
| 929 | |
| 930 | let visibility = Self::extract_visibility(node, state); |
| 931 | let docstring = Self::extract_kdoc(state, node); |
| 932 | let signature = Some(Self::extract_declaration_signature(state, node)); |
| 933 | let start_line = node.start_position().row as u32; |
| 934 | let end_line = node.end_position().row as u32; |
| 935 | let start_column = node.start_position().column as u32; |
| 936 | let end_column = node.end_position().column as u32; |
| 937 | let qualified_name = format!("{}::{}", state.qualified_prefix(), name); |
| 938 | |
| 939 | // Check for suspend modifier. |
| 940 | let is_async = Self::has_modifier_keyword(node, state, "suspend"); |
| 941 | |
| 942 | // Determine if this is a function without a body (abstract) inside an interface. |
| 943 | let has_body = find_direct_child_by_kind(node, "function_body").is_some(); |
| 944 | |
| 945 | let kind = if state.inside_trait && !has_body { |
| 946 | NodeKind::AbstractMethod |
| 947 | } else if state.class_depth > 0 { |
| 948 | NodeKind::Method |
| 949 | } else { |
| 950 | NodeKind::Function |
| 951 | }; |
| 952 | |
| 953 | let id = generate_node_id(&state.file_path, &kind, &name, start_line); |
| 954 | let metrics = count_complexity(node, &KOTLIN_COMPLEXITY, &state.source); |
| 955 | |
| 956 | // For extension functions, build a richer signature including the receiver type. |
| 957 | let final_signature = if is_extension { |
| 958 | Some(Self::extract_extension_signature(state, node)) |
| 959 | } else { |
| 960 | signature |
| 961 | }; |
| 962 | |
| 963 | let graph_node = Node { |
| 964 | id: id.clone(), |
| 965 | kind, |
| 966 | name: name.clone(), |
| 967 | qualified_name, |
| 968 | file_path: state.file_path.clone(), |
| 969 | start_line, |
| 970 | attrs_start_line: start_line, |
| 971 | end_line, |
| 972 | start_column, |
| 973 | end_column, |
| 974 | signature: final_signature, |
| 975 | docstring, |
| 976 | visibility, |
| 977 | is_async, |
| 978 | branches: metrics.branches, |
| 979 | loops: metrics.loops, |
| 980 | returns: metrics.returns, |
nothing calls this directly
no test coverage detected