]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide_completion/src/context.rs
NFC: remove redundant clones (clippy::perf)
[rust.git] / crates / ide_completion / src / context.rs
index 66577df941da0e19e48c1f98c348994b24a6ee2c..cb4f08e535c4234e9dcb67a3302dc30f65599ef8 100644 (file)
@@ -17,9 +17,8 @@
 
 use crate::{
     patterns::{
-        for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent,
-        has_impl_parent, has_item_list_or_source_file_parent, has_prev_sibling, has_ref_parent,
-        has_trait_parent, inside_impl_trait_block, is_in_loop_body, is_match_arm, previous_token,
+        determine_location, determine_prev_sibling, for_is_prev2, inside_impl_trait_block,
+        is_in_loop_body, previous_token, ImmediateLocation, ImmediatePrevSibling,
     },
     CompletionConfig,
 };
@@ -30,24 +29,6 @@ pub(crate) enum PatternRefutability {
     Irrefutable,
 }
 
-/// Direct parent container of the cursor position
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub(crate) enum ImmediateLocation {
-    Impl,
-    Trait,
-    RecordFieldList,
-    RefPatOrExpr,
-    IdentPat,
-    BlockExpr,
-    ItemList,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub(crate) enum PrevSibling {
-    Trait,
-    Impl,
-}
-
 /// `CompletionContext` is created early during completion to figure out, where
 /// exactly is the cursor, syntax-wise.
 #[derive(Debug)]
@@ -73,11 +54,6 @@ pub(crate) struct CompletionContext<'a> {
     /// The parent impl of the cursor position if it exists.
     pub(super) impl_def: Option<ast::Impl>,
 
-    /// RecordExpr the token is a field of
-    pub(super) record_lit_syntax: Option<ast::RecordExpr>,
-    /// RecordPat the token is a field of
-    pub(super) record_pat_syntax: Option<ast::RecordPat>,
-
     // potentially set if we are completing a lifetime
     pub(super) lifetime_syntax: Option<ast::Lifetime>,
     pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
@@ -89,6 +65,8 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) is_param: bool,
 
     pub(super) completion_location: Option<ImmediateLocation>,
+    pub(super) prev_sibling: Option<ImmediatePrevSibling>,
+    pub(super) attribute_under_caret: Option<ast::Attr>,
 
     /// FIXME: `ActiveParameter` is string-based, which is very very wrong
     pub(super) active_parameter: Option<ActiveParameter>,
@@ -96,16 +74,10 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) is_trivial_path: bool,
     /// If not a trivial path, the prefix (qualifier).
     pub(super) path_qual: Option<ast::Path>,
-    pub(super) after_if: bool,
     /// `true` if we are a statement or a last expr in the block.
     pub(super) can_be_stmt: bool,
     /// `true` if we expect an expression at the cursor position.
     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.
@@ -114,19 +86,15 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) is_macro_call: bool,
     pub(super) is_path_type: bool,
     pub(super) has_type_args: bool,
-    pub(super) attribute_under_caret: Option<ast::Attr>,
-    pub(super) mod_declaration_under_caret: Option<ast::Module>,
     pub(super) locals: Vec<(String, Local)>,
 
-    // keyword patterns
     pub(super) previous_token: Option<SyntaxToken>,
-    pub(super) prev_sibling: Option<PrevSibling>,
     pub(super) in_loop_body: bool,
-    pub(super) is_match_arm: bool,
     pub(super) incomplete_let: bool,
 
     no_completion_required: bool,
 }
+
 impl<'a> CompletionContext<'a> {
     pub(super) fn new(
         db: &'a RootDatabase,
@@ -176,8 +144,6 @@ pub(super) fn new(
             lifetime_param_syntax: None,
             function_def: None,
             use_item_syntax: None,
-            record_lit_syntax: None,
-            record_pat_syntax: None,
             impl_def: None,
             active_parameter: ActiveParameter::at(db, position),
             is_label_ref: false,
@@ -185,26 +151,20 @@ pub(super) fn new(
             is_pat_or_const: None,
             is_trivial_path: false,
             path_qual: None,
-            after_if: false,
             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,
             is_path_type: false,
             has_type_args: false,
-            attribute_under_caret: None,
-            mod_declaration_under_caret: None,
             previous_token: None,
             in_loop_body: false,
             completion_location: None,
             prev_sibling: None,
-            is_match_arm: false,
             no_completion_required: false,
             incomplete_let: false,
+            attribute_under_caret: None,
             locals,
         };
 
@@ -247,7 +207,6 @@ pub(super) fn new(
                 break;
             }
         }
-        ctx.fill_keyword_patterns(&speculative_file, offset);
         ctx.fill(&original_file, speculative_file, offset);
         Some(ctx)
     }
@@ -281,88 +240,80 @@ pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool {
         self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
     }
 
-    pub(crate) fn has_impl_or_trait_parent(&self) -> bool {
+    pub(crate) fn expects_assoc_item(&self) -> bool {
         matches!(
             self.completion_location,
             Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl)
         )
     }
 
-    pub(crate) fn has_block_expr_parent(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
+    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 has_item_list_parent(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::ItemList))
+    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 has_ident_or_ref_pat_parent(&self) -> bool {
-        matches!(
-            self.completion_location,
-            Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr)
-        )
+    pub(crate) fn expects_use_tree(&self) -> bool {
+        matches!(self.completion_location, Some(ImmediateLocation::Use))
     }
 
-    pub(crate) fn has_impl_parent(&self) -> bool {
+    pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
         matches!(self.completion_location, Some(ImmediateLocation::Impl))
     }
 
-    pub(crate) fn has_field_list_parent(&self) -> bool {
-        matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList))
+    pub(crate) fn expects_item(&self) -> bool {
+        matches!(self.completion_location, Some(ImmediateLocation::ItemList))
     }
 
-    pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
-        self.prev_sibling.is_some()
+    //         fn expects_value(&self) -> bool {
+    pub(crate) fn expects_expression(&self) -> bool {
+        self.is_expr
     }
 
-    pub(crate) fn is_path_disallowed(&self) -> bool {
-        self.record_lit_syntax.is_some()
-            || self.record_pat_syntax.is_some()
-            || self.attribute_under_caret.is_some()
-            || self.mod_declaration_under_caret.is_some()
-            || self.has_impl_or_trait_parent()
+    pub(crate) fn has_block_expr_parent(&self) -> bool {
+        matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
     }
 
-    fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
-        let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
-        let syntax_element = NodeOrToken::Token(fake_ident_token);
-        self.previous_token = previous_token(syntax_element.clone());
-        self.in_loop_body = is_in_loop_body(syntax_element.clone());
-        self.is_match_arm = is_match_arm(syntax_element.clone());
-        if has_prev_sibling(syntax_element.clone(), IMPL) {
-            self.prev_sibling = Some(PrevSibling::Impl)
-        } else if has_prev_sibling(syntax_element.clone(), TRAIT) {
-            self.prev_sibling = Some(PrevSibling::Trait)
-        }
+    pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool {
+        matches!(
+            self.completion_location,
+            Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr)
+        )
+    }
 
-        if has_block_expr_parent(syntax_element.clone()) {
-            self.completion_location = Some(ImmediateLocation::BlockExpr);
-        } else if has_bind_pat_parent(syntax_element.clone()) {
-            self.completion_location = Some(ImmediateLocation::IdentPat);
-        } else if has_ref_parent(syntax_element.clone()) {
-            self.completion_location = Some(ImmediateLocation::RefPatOrExpr);
-        } else if has_impl_parent(syntax_element.clone()) {
-            self.completion_location = Some(ImmediateLocation::Impl);
-        } else if has_field_list_parent(syntax_element.clone()) {
-            self.completion_location = Some(ImmediateLocation::RecordFieldList);
-        } else if has_trait_parent(syntax_element.clone()) {
-            self.completion_location = Some(ImmediateLocation::Trait);
-        } else if has_item_list_or_source_file_parent(syntax_element.clone()) {
-            self.completion_location = Some(ImmediateLocation::ItemList);
-        }
+    pub(crate) fn expect_record_field(&self) -> bool {
+        matches!(self.completion_location, Some(ImmediateLocation::RecordField))
+    }
 
-        self.mod_declaration_under_caret =
-            find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
-                .filter(|module| module.item_list().is_none());
-        self.incomplete_let =
-            syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
-                it.syntax().text_range().end() == syntax_element.text_range().end()
-            });
+    pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
+        matches!(
+            self.prev_sibling,
+            Some(ImmediatePrevSibling::ImplDefType) | Some(ImmediatePrevSibling::TraitDefName)
+        )
+    }
 
-        let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
-        let fn_is_prev = self.previous_token_is(T![fn]);
-        let for_is_prev2 = for_is_prev2(syntax_element.clone());
-        self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2;
+    pub(crate) fn after_if(&self) -> bool {
+        matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
+    }
+
+    pub(crate) fn is_path_disallowed(&self) -> bool {
+        matches!(
+            self.completion_location,
+            Some(ImmediateLocation::Attribute(_))
+                | Some(ImmediateLocation::ModDeclaration(_))
+                | Some(ImmediateLocation::RecordPat(_))
+                | Some(ImmediateLocation::RecordExpr(_))
+        ) || self.attribute_under_caret.is_some()
     }
 
     fn fill_impl_def(&mut self) {
@@ -480,23 +431,43 @@ fn fill(
         file_with_fake_ident: SyntaxNode,
         offset: TextSize,
     ) {
+        let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
+        let syntax_element = NodeOrToken::Token(fake_ident_token);
+        self.previous_token = previous_token(syntax_element.clone());
+        self.attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
+        self.no_completion_required = {
+            let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
+            let fn_is_prev = self.previous_token_is(T![fn]);
+            let for_is_prev2 = for_is_prev2(syntax_element.clone());
+            (fn_is_prev && !inside_impl_trait_block) || for_is_prev2
+        };
+        self.in_loop_body = is_in_loop_body(syntax_element.clone());
+
+        self.incomplete_let =
+            syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
+                it.syntax().text_range().end() == syntax_element.text_range().end()
+            });
+
         let (expected_type, expected_name) = self.expected_type_and_name();
         self.expected_type = expected_type;
         self.expected_name = expected_name;
-        self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
+
         let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) {
             Some(it) => it,
             None => return,
         };
+        self.completion_location =
+            determine_location(&self.sema, original_file, offset, &name_like);
+        self.prev_sibling = determine_prev_sibling(&name_like);
         match name_like {
             ast::NameLike::Lifetime(lifetime) => {
                 self.classify_lifetime(original_file, lifetime, offset);
             }
             ast::NameLike::NameRef(name_ref) => {
-                self.classify_name_ref(original_file, name_ref, offset);
+                self.classify_name_ref(original_file, name_ref);
             }
             ast::NameLike::Name(name) => {
-                self.classify_name(original_file, name, offset);
+                self.classify_name(name);
             }
         }
     }
@@ -530,7 +501,7 @@ fn classify_lifetime(
         }
     }
 
-    fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) {
+    fn classify_name(&mut self, name: ast::Name) {
         if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
             self.is_pat_or_const = Some(PatternRefutability::Refutable);
             // if any of these is here our bind pat can't be a const pat anymore
@@ -568,42 +539,17 @@ fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset:
 
             self.fill_impl_def();
         }
+
         self.is_param |= is_node::<ast::Param>(name.syntax());
-        if ast::RecordPatField::for_field_name(&name).is_some() {
-            self.record_pat_syntax =
-                self.sema.find_node_at_offset_with_macros(&original_file, offset);
-        }
     }
 
-    fn classify_name_ref(
-        &mut self,
-        original_file: &SyntaxNode,
-        name_ref: ast::NameRef,
-        offset: TextSize,
-    ) {
+    fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameRef) {
         self.fill_impl_def();
-        if ast::RecordExprField::for_field_name(&name_ref).is_some() {
-            self.record_lit_syntax =
-                self.sema.find_node_at_offset_with_macros(original_file, offset);
-        }
-        if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() {
-            self.record_pat_syntax =
-                self.sema.find_node_at_offset_with_macros(&original_file, offset);
-        }
 
         self.name_ref_syntax =
             find_node_at_offset(original_file, name_ref.syntax().text_range().start());
 
-        let name_range = name_ref.syntax().text_range();
-        let top_node = name_ref
-            .syntax()
-            .ancestors()
-            .take_while(|it| it.text_range() == name_range)
-            .last()
-            .unwrap();
-
-        if matches!(top_node.parent().map(|it| it.kind()), Some(SOURCE_FILE) | Some(ITEM_LIST)) {
-            self.is_new_item = true;
+        if matches!(self.completion_location, Some(ImmediateLocation::ItemList)) {
             return;
         }
 
@@ -621,7 +567,7 @@ fn classify_name_ref(
             None => return,
         };
 
-        if let Some(segment) = ast::PathSegment::cast(parent.clone()) {
+        if let Some(segment) = ast::PathSegment::cast(parent) {
             let path = segment.parent_path();
             self.is_call = path
                 .syntax()
@@ -676,45 +622,9 @@ fn classify_name_ref(
                 })
                 .unwrap_or(false);
             self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
-
-            if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
-                if let Some(if_expr) =
-                    self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
-                {
-                    if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start()
-                    {
-                        self.after_if = true;
-                    }
-                }
-            }
-        }
-
-        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 { .. }));
     }
 }