]> git.lizzy.rs Git - rust.git/commitdiff
Add MethodCall and FieldAccess variants to ImmediateLocation
authorLukas Wirth <lukastw97@gmail.com>
Wed, 2 Jun 2021 13:21:18 +0000 (15:21 +0200)
committerLukas Wirth <lukastw97@gmail.com>
Wed, 2 Jun 2021 13:21:18 +0000 (15:21 +0200)
crates/ide_completion/src/completions/dot.rs
crates/ide_completion/src/completions/flyimport.rs
crates/ide_completion/src/completions/keyword.rs
crates/ide_completion/src/completions/postfix.rs
crates/ide_completion/src/context.rs
crates/ide_completion/src/patterns.rs
crates/ide_completion/src/render/function.rs

index 302c9ccbd369678eeb12ec95891602174396002e..e0a7021fd3e483e98b07be4bb0e8336821f98da2 100644 (file)
@@ -8,7 +8,7 @@
 
 /// Complete dot accesses, i.e. fields or methods.
 pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
-    let dot_receiver = match &ctx.dot_receiver {
+    let dot_receiver = match ctx.dot_receiver() {
         Some(expr) => expr,
         _ => return complete_undotted_self(acc, ctx),
     };
@@ -30,7 +30,10 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
 }
 
 fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
-    if !ctx.is_trivial_path || !ctx.config.enable_self_on_the_fly {
+    if !ctx.config.enable_self_on_the_fly {
+        return;
+    }
+    if !ctx.is_trivial_path || ctx.is_path_disallowed() {
         return;
     }
     ctx.scope.process_all_names(&mut |name, def| {
index df27e7a848a333e08ab431d38ff1a1e82f55eb25..d72bf13d31a37c303131f3dcf0a7aa2c74be8445 100644 (file)
@@ -162,19 +162,19 @@ pub(crate) fn position_for_import<'a>(
     Some(match import_candidate {
         Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
         Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(),
-        Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver.as_ref()?.syntax(),
+        Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
         None => ctx
             .name_ref_syntax
             .as_ref()
             .map(|name_ref| name_ref.syntax())
             .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax()))
-            .or_else(|| ctx.dot_receiver.as_ref().map(|expr| expr.syntax()))?,
+            .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?,
     })
 }
 
 fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
     let current_module = ctx.scope.module()?;
-    if let Some(dot_receiver) = &ctx.dot_receiver {
+    if let Some(dot_receiver) = ctx.dot_receiver() {
         ImportAssets::for_fuzzy_method_call(
             current_module,
             ctx.sema.type_of_expr(dot_receiver)?,
index 0d035c61151ab11330617ff8e9c298b4926323de..1a7a484a4cdd5935a3a41366e5885aef134a3825 100644 (file)
@@ -31,7 +31,7 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
     }
 
     // Suggest .await syntax for types that implement Future trait
-    if let Some(receiver) = &ctx.dot_receiver {
+    if let Some(receiver) = ctx.dot_receiver() {
         if let Some(ty) = ctx.sema.type_of_expr(receiver) {
             if ty.impls_future(ctx.db) {
                 let mut item = kw_completion("await");
index 962aaf0df367856263d9185cc37fe676875ebf71..86bbb58e266399909bf697a7c0b69482c2e6241d 100644 (file)
@@ -14,6 +14,7 @@
     completions::postfix::format_like::add_format_like_completions,
     context::CompletionContext,
     item::{Builder, CompletionKind},
+    patterns::ImmediateLocation,
     CompletionItem, CompletionItemKind, Completions,
 };
 
@@ -22,13 +23,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
         return;
     }
 
-    let dot_receiver = match &ctx.dot_receiver {
-        Some(it) => it,
-        None => return,
+    let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location {
+        Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false),
+        Some(ImmediateLocation::FieldAccess {
+            receiver: Some(it),
+            receiver_is_ambiguous_float_literal,
+        }) => (it, *receiver_is_ambiguous_float_literal),
+        _ => return,
     };
 
-    let receiver_text =
-        get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
+    let receiver_text = get_receiver_text(dot_receiver, receiver_is_ambiguous_float_literal);
 
     let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
         Some(it) => it,
@@ -123,8 +127,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
     // The rest of the postfix completions create an expression that moves an argument,
     // so it's better to consider references now to avoid breaking the compilation
     let dot_receiver = include_references(dot_receiver);
-    let receiver_text =
-        get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
+    let receiver_text = get_receiver_text(&dot_receiver, receiver_is_ambiguous_float_literal);
 
     match try_enum {
         Some(try_enum) => match try_enum {
index 7c46c815d2baf2d17338487c2a7586722f06e2ac..eeb4333f851e7dc25e2dd0116487638d179612fe 100644 (file)
@@ -80,9 +80,6 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) is_expr: bool,
     /// Something is typed at the "top" level, in module or impl/trait.
     pub(super) is_new_item: bool,
-    /// The receiver if this is a field or method access, i.e. writing something.$0
-    pub(super) dot_receiver: Option<ast::Expr>,
-    pub(super) dot_receiver_is_ambiguous_float_literal: bool,
     /// If this is a call (method or function) in particular, i.e. the () are already there.
     pub(super) is_call: bool,
     /// Like `is_call`, but for tuple patterns.
@@ -159,8 +156,6 @@ pub(super) fn new(
             can_be_stmt: false,
             is_expr: false,
             is_new_item: false,
-            dot_receiver: None,
-            dot_receiver_is_ambiguous_float_literal: false,
             is_call: false,
             is_pattern_call: false,
             is_macro_call: false,
@@ -255,6 +250,22 @@ pub(crate) fn expects_assoc_item(&self) -> bool {
         )
     }
 
+    pub(crate) fn has_dot_receiver(&self) -> bool {
+        matches!(
+            &self.completion_location,
+            Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver })
+                if receiver.is_some()
+        )
+    }
+
+    pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
+        match &self.completion_location {
+            Some(ImmediateLocation::MethodCall { receiver })
+            | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(),
+            _ => None,
+        }
+    }
+
     pub(crate) fn expects_use_tree(&self) -> bool {
         matches!(self.completion_location, Some(ImmediateLocation::Use))
     }
@@ -267,6 +278,7 @@ pub(crate) fn expects_item(&self) -> bool {
         matches!(self.completion_location, Some(ImmediateLocation::ItemList))
     }
 
+    //         fn expects_value(&self) -> bool {
     pub(crate) fn expects_expression(&self) -> bool {
         self.is_expr
     }
@@ -623,33 +635,8 @@ fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameR
                 .unwrap_or(false);
             self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
         }
-
-        if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
-            // The receiver comes before the point of insertion of the fake
-            // ident, so it should have the same range in the non-modified file
-            self.dot_receiver = field_expr
-                .expr()
-                .map(|e| e.syntax().text_range())
-                .and_then(|r| find_node_with_range(original_file, r));
-            self.dot_receiver_is_ambiguous_float_literal =
-                if let Some(ast::Expr::Literal(l)) = &self.dot_receiver {
-                    match l.kind() {
-                        ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
-                        _ => false,
-                    }
-                } else {
-                    false
-                };
-        }
-
-        if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
-            // As above
-            self.dot_receiver = method_call_expr
-                .receiver()
-                .map(|e| e.syntax().text_range())
-                .and_then(|r| find_node_with_range(original_file, r));
-            self.is_call = true;
-        }
+        self.is_call |=
+            matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. }));
     }
 }
 
index 26516046bcf32ee8aa583f796ad5faf079409d0c..bf3a3f61ebbb8578ea5807270667d6eadb5c19cc 100644 (file)
@@ -7,7 +7,7 @@
     ast::{self, LoopBodyOwner},
     match_ast, AstNode, Direction, SyntaxElement,
     SyntaxKind::*,
-    SyntaxNode, SyntaxToken, TextSize, T,
+    SyntaxNode, SyntaxToken, TextRange, TextSize, T,
 };
 
 #[cfg(test)]
@@ -37,6 +37,15 @@ pub(crate) enum ImmediateLocation {
     // Fake file ast node
     ModDeclaration(ast::Module),
     // Original file ast node
+    MethodCall {
+        receiver: Option<ast::Expr>,
+    },
+    // Original file ast node
+    FieldAccess {
+        receiver: Option<ast::Expr>,
+        receiver_is_ambiguous_float_literal: bool,
+    },
+    // Original file ast node
     /// The record expr of the field name we are completing
     RecordExpr(ast::RecordExpr),
     // Original file ast node
@@ -164,12 +173,38 @@ pub(crate) fn determine_location(
                 Some(TRAIT) => ImmediateLocation::Trait,
                 _ => return None,
             },
-            ast::Module(it) => if it.item_list().is_none() {
+            ast::Module(it) => {
+                if it.item_list().is_none() {
                     ImmediateLocation::ModDeclaration(it)
                 } else {
-                    return None
+                    return None;
+                }
             },
             ast::Attr(it) => ImmediateLocation::Attribute(it),
+            ast::FieldExpr(it) => {
+                let receiver = it
+                    .expr()
+                    .map(|e| e.syntax().text_range())
+                    .and_then(|r| find_node_with_range(original_file, r));
+                let receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = &receiver {
+                    match l.kind() {
+                        ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'),
+                        _ => false,
+                    }
+                } else {
+                    false
+                };
+                ImmediateLocation::FieldAccess {
+                    receiver,
+                    receiver_is_ambiguous_float_literal,
+                }
+            },
+            ast::MethodCallExpr(it) => ImmediateLocation::MethodCall {
+                receiver: it
+                    .receiver()
+                    .map(|e| e.syntax().text_range())
+                    .and_then(|r| find_node_with_range(original_file, r)),
+            },
             _ => return None,
         }
     };
@@ -194,6 +229,10 @@ fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode {
     name_ref.syntax().clone()
 }
 
+fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
+    syntax.covering_element(range).ancestors().find_map(N::cast)
+}
+
 pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
     // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
     // where we only check the first parent with different text range.
index 3ec77ca0f4a0a409cae19bbb2130df62a25da8d3..1abeed96d7d3a45f25c6c294326a38dd2842deba 100644 (file)
@@ -154,7 +154,7 @@ fn params(&self) -> Params {
         };
 
         let mut params_pats = Vec::new();
-        let params_ty = if self.ctx.completion.dot_receiver.is_some() || self.receiver.is_some() {
+        let params_ty = if self.ctx.completion.has_dot_receiver() || self.receiver.is_some() {
             self.func.method_params(self.ctx.db()).unwrap_or_default()
         } else {
             if let Some(s) = ast_params.self_param() {