{"record":{"id":"12f3289ca910ac59","repo":"slint-ui/slint","slug":"there-is-always-one-line","errorCode":null,"errorMessage":"There is always one line","messagePattern":"There is always one line","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"tools/lsp/preview/drop_location.rs","lineNumber":1051,"sourceCode":"\n    let mut to_delete_ranges = property_ranges(element, remove_properties);\n    to_delete_ranges.sort_by(|a, b| u32::from(a.start()).cmp(&u32::from(b.start())));\n    let mut offset = start_offset;\n    for dr in to_delete_ranges {\n        let start = usize::from(dr.start()) - offset;\n        let end = usize::from(dr.end()) - offset;\n\n        offset += end - start;\n\n        text.drain(start..end);\n    }\n\n    // Trim leading WS to get \"raw\" lines\n    let lines = text.split('\\n').collect::<Vec<_>>();\n    let indent = util::find_element_indent(element).unwrap_or_else(|| {\n        lines\n            .last()\n            .expect(\"There is always one line\")\n            .chars()\n            .take_while(|c| c.is_whitespace())\n            .collect()\n    });\n\n    lines\n        .iter()\n        .map(|l| if l.starts_with(&indent) { l[indent.len()..].to_string() } else { l.to_string() })\n        .collect::<Vec<_>>()\n}\n\nfn node_removal_text_edit(\n    document_cache: &editor_preview::DocumentCache,\n    node: &SyntaxNode,\n    replace_with: String,\n) -> Option<editor_preview::editing::SingleTextEdit> {\n    let range = util::text_range_to_lsp_range(\n        &node.source_file.clone(),","sourceCodeStart":1033,"sourceCodeEnd":1069,"githubUrl":"https://github.com/slint-ui/slint/blob/bb937076de3f7919766c1f25e2e969367cf77e9a/tools/lsp/preview/drop_location.rs#L1033-L1069","documentation":"This is an internal invariant panic from `expect(\"There is always one line\")` in `extract_text_of_element` (tools/lsp/preview/drop_location.rs:1051). `String::split('\\n')` always yields at least one item, so `lines.last()` is expected to be `Some`. The panic would only fire if `lines` were empty, which the stdlib API makes impossible; it exists to justify unwrapping the last element when computing the element's indentation fallback.","triggerScenarios":"Only reachable if the invariant 'split always returns >=1 line' were broken — e.g. if the code were refactored to filter/trim `lines` before this point, or the `text` string were replaced by an empty collection. Not triggerable from any public API with the current source.","commonSituations":"Developers hit this only after refactoring `extract_text_of_element` so that the `lines` vec can become empty before line 1048 (e.g. filtering out blank lines), or when reimplementing the indentation logic and dropping the `split` that guarantees a non-empty vec.","solutions":["Do not filter or empty the `lines` vec between the `text.split('\\n').collect()` and the `lines.last()` call.","If restructuring, replace `.expect(\"There is always one line\")` with `.unwrap_or(\"\")` so an empty vec degrades to empty indent instead of panicking.","Keep a unit test that calls extract_text_of_element with a minimal element to pin the non-empty-lines invariant."],"exampleFix":"// before\nlet indent = util::find_element_indent(element).unwrap_or_else(|| {\n    lines.last().expect(\"There is always one line\").chars().take_while(|c| c.is_whitespace()).collect()\n});\n// after\nlet indent = util::find_element_indent(element).unwrap_or_else(|| {\n    lines.last().map(|l| l.chars().take_while(|c| c.is_whitespace()).collect()).unwrap_or_default()\n});","handlingStrategy":"fallback","validationCode":"let lines: Vec<&str> = text.split('\\n').collect();\nassert!(!lines.is_empty(), \"split always yields at least one line\");","typeGuard":"fn last_line(lines: &[&str]) -> Option<&str> { lines.last().copied() }","tryCatchPattern":"// Rust: no catch; replace expect with unwrap_or at the call site\nlines.last().map(|l| l.chars().take_while(|c| c.is_whitespace()).collect()).unwrap_or_default()","preventionTips":["Never filter the lines vec between split and last().","Prefer Option-combining (map/unwrap_or_default) over expect for derivable invariants.","Add regression tests for extract_text_of_element with empty and single-line elements."],"tags":["rust","panic","lsp","invariant","internal"],"backgroundTag":"internal-invariant-violation","analyzedSha":"bb937076de3f7919766c1f25e2e969367cf77e9a","analyzedAt":"2026-09-16T01:37:20.251Z","contentChangedAt":"2026-09-16T01:37:20.251Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}