{"record":{"id":"c06f28769980583a","repo":"fish-shell/fish-shell","slug":"function-has-no-source-range","errorCode":null,"errorMessage":"Function has no source range","messagePattern":"Function has no source range","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/function.rs","lineNumber":395,"sourceCode":"        self.definition_file.as_ref().map(|f| f.as_utfstr())\n    }\n\n    /// Return a reference to the vars that this function has inherited from its definition scope.\n    pub fn inherit_vars(&self) -> &[(WString, Vec<WString>)] {\n        &self.inherit_vars\n    }\n\n    /// If this function is a copy, return a reference to the original definition file, or None if it was defined interactively or copied.\n    pub fn copy_definition_file(&self) -> Option<&wstr> {\n        self.copy_definition_file.as_ref().map(|f| f.as_utfstr())\n    }\n\n    /// Return the 1-based line number of the function's definition.\n    pub fn definition_lineno(&self) -> i32 {\n        // Return one plus the number of newlines at offsets less than the start of our function's\n        // statement (which includes the header).\n        let Some(source_range) = self.func_node.try_source_range() else {\n            panic!(\"Function has no source range\");\n        };\n        let func_start = source_range.start as usize;\n        let source = &self.func_node.parsed_source().src;\n        assert!(\n            func_start <= source.char_count(),\n            \"function start out of bounds\"\n        );\n        1 + source\n            .slice_to(func_start)\n            .chars()\n            .filter(|&c| c == '\\n')\n            .count() as i32\n    }\n\n    /// If this function is a copy, return the original 1-based line number. Otherwise, return 0.\n    pub fn copy_definition_lineno(&self) -> u32 {\n        self.copy_definition_lineno.map_or(0, |val| val.get())\n    }","sourceCodeStart":377,"sourceCodeEnd":413,"githubUrl":"https://github.com/fish-shell/fish-shell/blob/edb719d76c8f76633f09dbbccedbda1823a7d9d5/src/function.rs#L377-L413","documentation":"`FunctionDefinition::definition_lineno()` returns the 1-based line of a function's definition by consulting the source range of the function's parse node. If `func_node.try_source_range()` returns None — the AST node has no attached source span — the code panics with \"Function has no source range\". This is an internal invariant violation, meaning a function definition node reached line-number computation without valid source text (e.g. a synthesized or detached node).","triggerScenarios":"Calling `definition_lineno()` on a FunctionDefinition whose `func_node` was not produced from a real parse of source text, or whose node lost its source range (synthesized node, node from a reused/trimmed parse tree).","commonSituations":"Programmatically constructing function definitions in tests or tooling; parsing pathological/edge-case input where source ranges are elided; using a FunctionDefinition obtained from a modified parse tree after edits.","solutions":["Ensure the function definition comes from a real `parse()` of non-empty source so its node has a source range","Do not call `definition_lineno()` on synthesized/constructed FunctionDefinition values; track line numbers separately","Check `func_node.try_source_range()` yourself and fall back to 0 or an Option instead of panicking","Update/re-parse the source after any tree mutation before asking for line numbers"],"exampleFix":"// before\nlet lineno = func.definition_lineno();\n// after\nlet lineno = func.func_node.try_source_range().map(|r| (r.start as usize) as i32 + 1).unwrap_or(0);","handlingStrategy":"type-guard","validationCode":"if func.func_node.try_source_range().is_none() { /* skip or fallback */ }","typeGuard":"fn has_source_range(f: &FunctionDefinition) -> bool { f.func_node.try_source_range().is_some() }","tryCatchPattern":"std::panic::catch_unwind(|| func.definition_lineno()).unwrap_or(0)","preventionTips":["Only query line numbers for nodes parsed from real source text","Never hand-construct parse nodes in production code","Re-parse after tree mutations before reading source ranges"],"tags":["rust","panic","parser","invariant-violation"],"backgroundTag":"missing-source-range","analyzedSha":"edb719d76c8f76633f09dbbccedbda1823a7d9d5","analyzedAt":"2026-09-03T01:23:54.744Z","contentChangedAt":"2026-09-03T01:23:54.744Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}