Fix panic in split_imports assist

The fix is admittedly quit literally just papering over.

Long-term, I see two more principled approaches:

* we switch to a fully tree-based impl, without parse . to_string
  step; with this approach, there shouldn't be any panics. The results
  might be nonsensical, but so was the original input.

* we preserve the invariant that re-parsing constructed node is an
  identity, and make all the `make_xxx` method return an `Option`.

closes #4044
This commit is contained in:
Aleksey Kladov 2020-04-20 16:34:01 +02:00
parent 90f837829d
commit 8a04372fec
3 changed files with 17 additions and 4 deletions

View File

@ -37,7 +37,7 @@ pub(crate) fn split_import(ctx: AssistCtx) -> Option<Assist> {
#[cfg(test)]
mod tests {
use crate::helpers::{check_assist, check_assist_target};
use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
use super::*;
@ -63,4 +63,9 @@ mod tests {
fn split_import_target() {
check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::");
}
#[test]
fn issue4044() {
check_assist_not_applicable(split_import, "use crate::<|>:::self;")
}
}

View File

@ -10,8 +10,8 @@ use ra_text_edit::TextEditBuilder;
use rustc_hash::FxHashMap;
use crate::{
AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
TextRange, TextUnit,
AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr,
SyntaxToken, TextRange, TextUnit,
};
/// Returns ancestors of the node at the offset, sorted by length. This should
@ -90,6 +90,10 @@ pub fn neighbor<T: AstNode>(me: &T, direction: Direction) -> Option<T> {
me.syntax().siblings(direction).skip(1).find_map(T::cast)
}
pub fn has_errors(node: &SyntaxNode) -> bool {
node.children().any(|it| it.kind() == SyntaxKind::ERROR)
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum InsertPosition<T> {
First,

View File

@ -307,7 +307,11 @@ impl ast::UseTree {
fn split_path_prefix(prefix: &ast::Path) -> Option<ast::Path> {
let parent = prefix.parent_path()?;
let mut res = make::path_unqualified(parent.segment()?);
let segment = parent.segment()?;
if algo::has_errors(segment.syntax()) {
return None;
}
let mut res = make::path_unqualified(segment);
for p in iter::successors(parent.parent_path(), |it| it.parent_path()) {
res = make::path_qualified(res, p.segment()?);
}