@@ -3,10 +3,11 @@ use std::{fmt, iter, mem};
33
44use base_db:: CrateId ;
55use hir_expand:: { name:: Name , MacroDefId } ;
6- use intern:: sym;
6+ use intern:: { sym, Symbol } ;
77use itertools:: Itertools as _;
88use rustc_hash:: FxHashSet ;
99use smallvec:: { smallvec, SmallVec } ;
10+ use span:: SyntaxContextId ;
1011use triomphe:: Arc ;
1112
1213use crate :: {
@@ -343,15 +344,7 @@ impl Resolver {
343344 }
344345
345346 if n_segments <= 1 {
346- let mut hygiene_info = if !hygiene_id. is_root ( ) {
347- let ctx = hygiene_id. lookup ( db) ;
348- ctx. outer_expn . map ( |expansion| {
349- let expansion = db. lookup_intern_macro_call ( expansion) ;
350- ( ctx. parent , expansion. def )
351- } )
352- } else {
353- None
354- } ;
347+ let mut hygiene_info = hygiene_info ( db, hygiene_id) ;
355348 for scope in self . scopes ( ) {
356349 match scope {
357350 Scope :: ExprScope ( scope) => {
@@ -371,19 +364,7 @@ impl Resolver {
371364 }
372365 }
373366 Scope :: MacroDefScope ( macro_id) => {
374- if let Some ( ( parent_ctx, label_macro_id) ) = hygiene_info {
375- if label_macro_id == * * macro_id {
376- // A macro is allowed to refer to variables from before its declaration.
377- // Therefore, if we got to the rib of its declaration, give up its hygiene
378- // and use its parent expansion.
379- let parent_ctx = db. lookup_intern_syntax_context ( parent_ctx) ;
380- hygiene_id = HygieneId :: new ( parent_ctx. opaque_and_semitransparent ) ;
381- hygiene_info = parent_ctx. outer_expn . map ( |expansion| {
382- let expansion = db. lookup_intern_macro_call ( expansion) ;
383- ( parent_ctx. parent , expansion. def )
384- } ) ;
385- }
386- }
367+ handle_macro_def_scope ( db, & mut hygiene_id, & mut hygiene_info, macro_id)
387368 }
388369 Scope :: GenericParams { params, def } => {
389370 if let Some ( id) = params. find_const_by_name ( first_name, * def) {
@@ -730,6 +711,107 @@ impl Resolver {
730711 } )
731712 }
732713
714+ /// Checks if we rename `renamed` (currently named `current_name`) to `new_name`, will the meaning of this reference
715+ /// (that contains `current_name` path) change from `renamed` to some another variable (returned as `Some`).
716+ pub fn rename_will_conflict_with_another_variable (
717+ & self ,
718+ db : & dyn DefDatabase ,
719+ current_name : & Name ,
720+ current_name_as_path : & ModPath ,
721+ mut hygiene_id : HygieneId ,
722+ new_name : & Symbol ,
723+ to_be_renamed : BindingId ,
724+ ) -> Option < BindingId > {
725+ let mut hygiene_info = hygiene_info ( db, hygiene_id) ;
726+ let mut will_be_resolved_to = None ;
727+ for scope in self . scopes ( ) {
728+ match scope {
729+ Scope :: ExprScope ( scope) => {
730+ for entry in scope. expr_scopes . entries ( scope. scope_id ) {
731+ if entry. hygiene ( ) == hygiene_id {
732+ if entry. binding ( ) == to_be_renamed {
733+ // This currently resolves to our renamed variable, now `will_be_resolved_to`
734+ // contains `Some` if the meaning will change or `None` if not.
735+ return will_be_resolved_to;
736+ } else if entry. name ( ) . symbol ( ) == new_name {
737+ will_be_resolved_to = Some ( entry. binding ( ) ) ;
738+ }
739+ }
740+ }
741+ }
742+ Scope :: MacroDefScope ( macro_id) => {
743+ handle_macro_def_scope ( db, & mut hygiene_id, & mut hygiene_info, macro_id)
744+ }
745+ Scope :: GenericParams { params, def } => {
746+ if params. find_const_by_name ( current_name, * def) . is_some ( ) {
747+ // It does not resolve to our renamed variable.
748+ return None ;
749+ }
750+ }
751+ Scope :: AdtScope ( _) | Scope :: ImplDefScope ( _) => continue ,
752+ Scope :: BlockScope ( m) => {
753+ if m. resolve_path_in_value_ns ( db, current_name_as_path) . is_some ( ) {
754+ // It does not resolve to our renamed variable.
755+ return None ;
756+ }
757+ }
758+ }
759+ }
760+ // It does not resolve to our renamed variable.
761+ None
762+ }
763+
764+ /// Checks if we rename `renamed` to `name`, will the meaning of this reference (that contains `name` path) change
765+ /// from some other variable (returned as `Some`) to `renamed`.
766+ pub fn rename_will_conflict_with_renamed (
767+ & self ,
768+ db : & dyn DefDatabase ,
769+ name : & Name ,
770+ name_as_path : & ModPath ,
771+ mut hygiene_id : HygieneId ,
772+ to_be_renamed : BindingId ,
773+ ) -> Option < BindingId > {
774+ let mut hygiene_info = hygiene_info ( db, hygiene_id) ;
775+ let mut will_resolve_to_renamed = false ;
776+ for scope in self . scopes ( ) {
777+ match scope {
778+ Scope :: ExprScope ( scope) => {
779+ for entry in scope. expr_scopes . entries ( scope. scope_id ) {
780+ if entry. binding ( ) == to_be_renamed {
781+ will_resolve_to_renamed = true ;
782+ } else if entry. hygiene ( ) == hygiene_id && entry. name ( ) == name {
783+ if will_resolve_to_renamed {
784+ // This will resolve to the renamed variable before it resolves to the original variable.
785+ return Some ( entry. binding ( ) ) ;
786+ } else {
787+ // This will resolve to the original variable.
788+ return None ;
789+ }
790+ }
791+ }
792+ }
793+ Scope :: MacroDefScope ( macro_id) => {
794+ handle_macro_def_scope ( db, & mut hygiene_id, & mut hygiene_info, macro_id)
795+ }
796+ Scope :: GenericParams { params, def } => {
797+ if params. find_const_by_name ( name, * def) . is_some ( ) {
798+ // Here and below, it might actually resolve to our renamed variable - in which case it'll
799+ // hide the generic parameter or some other thing (not a variable). We don't check for that
800+ // because due to naming conventions, it is rare that variable will shadow a non-variable.
801+ return None ;
802+ }
803+ }
804+ Scope :: AdtScope ( _) | Scope :: ImplDefScope ( _) => continue ,
805+ Scope :: BlockScope ( m) => {
806+ if m. resolve_path_in_value_ns ( db, name_as_path) . is_some ( ) {
807+ return None ;
808+ }
809+ }
810+ }
811+ }
812+ None
813+ }
814+
733815 /// `expr_id` is required to be an expression id that comes after the top level expression scope in the given resolver
734816 #[ must_use]
735817 pub fn update_to_inner_scope (
@@ -795,6 +877,44 @@ impl Resolver {
795877 }
796878}
797879
880+ #[ inline]
881+ fn handle_macro_def_scope (
882+ db : & dyn DefDatabase ,
883+ hygiene_id : & mut HygieneId ,
884+ hygiene_info : & mut Option < ( SyntaxContextId , MacroDefId ) > ,
885+ macro_id : & MacroDefId ,
886+ ) {
887+ if let Some ( ( parent_ctx, label_macro_id) ) = hygiene_info {
888+ if label_macro_id == macro_id {
889+ // A macro is allowed to refer to variables from before its declaration.
890+ // Therefore, if we got to the rib of its declaration, give up its hygiene
891+ // and use its parent expansion.
892+ let parent_ctx = db. lookup_intern_syntax_context ( * parent_ctx) ;
893+ * hygiene_id = HygieneId :: new ( parent_ctx. opaque_and_semitransparent ) ;
894+ * hygiene_info = parent_ctx. outer_expn . map ( |expansion| {
895+ let expansion = db. lookup_intern_macro_call ( expansion) ;
896+ ( parent_ctx. parent , expansion. def )
897+ } ) ;
898+ }
899+ }
900+ }
901+
902+ #[ inline]
903+ fn hygiene_info (
904+ db : & dyn DefDatabase ,
905+ hygiene_id : HygieneId ,
906+ ) -> Option < ( SyntaxContextId , MacroDefId ) > {
907+ if !hygiene_id. is_root ( ) {
908+ let ctx = hygiene_id. lookup ( db) ;
909+ ctx. outer_expn . map ( |expansion| {
910+ let expansion = db. lookup_intern_macro_call ( expansion) ;
911+ ( ctx. parent , expansion. def )
912+ } )
913+ } else {
914+ None
915+ }
916+ }
917+
798918pub struct UpdateGuard ( usize ) ;
799919
800920impl Resolver {
0 commit comments