9033: Complete keywords in (Assoc)ItemList with leading attribute r=Veykril a=Veykril

Fixes #7673
bors r+

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-05-28 01:21:45 +00:00 committed by GitHub
commit bca00ac340
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 4 deletions

View file

@ -394,6 +394,21 @@ fn quux() -> i32 {
); );
} }
#[test]
fn test_keywords_in_impl_def_with_attr() {
check(
r"impl My { #[foo] $0 }",
expect![[r#"
kw fn
kw const
kw type
kw unsafe
kw pub(crate)
kw pub
"#]],
);
}
#[test] #[test]
fn test_keywords_in_loop() { fn test_keywords_in_loop() {
check( check(

View file

@ -25,9 +25,10 @@ pub(crate) enum ImmediateLocation {
} }
pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> {
// First "expand" the element we are completing to its maximum so that we can check in what // First walk the element we are completing up to its highest node that has the same text range
// context it immediately lies. This for example means if the token is a NameRef at the end of // as the element so that we can check in what context it immediately lies. We only do this for
// a path, we want to look at where the path is in the tree. // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically.
// We only wanna do this if the NameRef is the last segment of the path.
let node = match tok.parent().and_then(ast::NameLike::cast)? { let node = match tok.parent().and_then(ast::NameLike::cast)? {
ast::NameLike::NameRef(name_ref) => { ast::NameLike::NameRef(name_ref) => {
if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) { if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
@ -47,7 +48,20 @@ pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation>
it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(), it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(),
}; };
let parent = match node.parent() { let parent = match node.parent() {
Some(parent) => parent, Some(parent) => match ast::MacroCall::cast(parent.clone()) {
// When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call.
// This is usually fine as the node expansion code above already accounts for that with
// the ancestors call, but there is one exception to this which is that when an attribute
// precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ.
Some(call)
if call.excl_token().is_none()
&& call.token_tree().is_none()
&& call.semicolon_token().is_none() =>
{
call.syntax().parent()?
}
_ => parent,
},
// SourceFile // SourceFile
None => { None => {
return match node.kind() { return match node.kind() {