aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir_def/src/nameres/collector.rs293
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs17
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs22
-rw-r--r--crates/hir_expand/src/name.rs2
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs17
-rw-r--r--crates/ide_assists/src/handlers/merge_imports.rs14
-rw-r--r--crates/ide_db/src/helpers/merge_imports.rs15
-rw-r--r--crates/stdx/src/lib.rs28
-rw-r--r--crates/syntax/src/ast/node_ext.rs23
9 files changed, 312 insertions, 119 deletions
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 3896be25d..e76d039b8 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -20,7 +20,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
20use syntax::ast; 20use syntax::ast;
21 21
22use crate::{ 22use crate::{
23 attr::{AttrId, Attrs}, 23 attr::{Attr, AttrId, AttrInput, Attrs},
24 builtin_attr, 24 builtin_attr,
25 db::DefDatabase, 25 db::DefDatabase,
26 derive_macro_as_call_id, 26 derive_macro_as_call_id,
@@ -94,14 +94,16 @@ pub(super) fn collect_defs(
94 unresolved_imports: Vec::new(), 94 unresolved_imports: Vec::new(),
95 resolved_imports: Vec::new(), 95 resolved_imports: Vec::new(),
96 96
97 unexpanded_macros: Vec::new(), 97 unresolved_macros: Vec::new(),
98 mod_dirs: FxHashMap::default(), 98 mod_dirs: FxHashMap::default(),
99 cfg_options, 99 cfg_options,
100 proc_macros, 100 proc_macros,
101 exports_proc_macros: false, 101 exports_proc_macros: false,
102 from_glob_import: Default::default(), 102 from_glob_import: Default::default(),
103 ignore_attrs_on: FxHashSet::default(), 103 skip_attrs: Default::default(),
104 derive_helpers_in_scope: FxHashMap::default(), 104 derive_helpers_in_scope: Default::default(),
105 registered_attrs: Default::default(),
106 registered_tools: Default::default(),
105 }; 107 };
106 match block { 108 match block {
107 Some(block) => { 109 Some(block) => {
@@ -237,7 +239,7 @@ struct DefCollector<'a> {
237 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, 239 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
238 unresolved_imports: Vec<ImportDirective>, 240 unresolved_imports: Vec<ImportDirective>,
239 resolved_imports: Vec<ImportDirective>, 241 resolved_imports: Vec<ImportDirective>,
240 unexpanded_macros: Vec<MacroDirective>, 242 unresolved_macros: Vec<MacroDirective>,
241 mod_dirs: FxHashMap<LocalModuleId, ModDir>, 243 mod_dirs: FxHashMap<LocalModuleId, ModDir>,
242 cfg_options: &'a CfgOptions, 244 cfg_options: &'a CfgOptions,
243 /// List of procedural macros defined by this crate. This is read from the dynamic library 245 /// List of procedural macros defined by this crate. This is read from the dynamic library
@@ -247,10 +249,20 @@ struct DefCollector<'a> {
247 proc_macros: Vec<(Name, ProcMacroExpander)>, 249 proc_macros: Vec<(Name, ProcMacroExpander)>,
248 exports_proc_macros: bool, 250 exports_proc_macros: bool,
249 from_glob_import: PerNsGlobImports, 251 from_glob_import: PerNsGlobImports,
250 ignore_attrs_on: FxHashSet<InFile<ModItem>>, 252 /// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute.
253 /// This map is used to skip all attributes up to and including the one that failed to resolve,
254 /// in order to not expand them twice.
255 ///
256 /// This also stores the attributes to skip when we resolve derive helpers and non-macro
257 /// non-builtin attributes in general.
258 skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
251 /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper 259 /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
252 /// attributes. 260 /// attributes.
253 derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>, 261 derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>,
262 /// Custom attributes registered with `#![register_attr]`.
263 registered_attrs: Vec<String>,
264 /// Custom tool modules registered with `#![register_tool]`.
265 registered_tools: Vec<String>,
254} 266}
255 267
256impl DefCollector<'_> { 268impl DefCollector<'_> {
@@ -259,11 +271,39 @@ impl DefCollector<'_> {
259 let item_tree = self.db.file_item_tree(file_id.into()); 271 let item_tree = self.db.file_item_tree(file_id.into());
260 let module_id = self.def_map.root; 272 let module_id = self.def_map.root;
261 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; 273 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
262 if item_tree 274
263 .top_level_attrs(self.db, self.def_map.krate) 275 let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
264 .cfg() 276 if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) {
265 .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) 277 // Process other crate-level attributes.
266 { 278 for attr in &*attrs {
279 let attr_name = match attr.path.as_ident() {
280 Some(name) => name,
281 None => continue,
282 };
283
284 let registered_name = if *attr_name == hir_expand::name![register_attr]
285 || *attr_name == hir_expand::name![register_tool]
286 {
287 match &attr.input {
288 Some(AttrInput::TokenTree(subtree)) => match &*subtree.token_trees {
289 [tt::TokenTree::Leaf(tt::Leaf::Ident(name))] => name.as_name(),
290 _ => continue,
291 },
292 _ => continue,
293 }
294 } else {
295 continue;
296 };
297
298 if *attr_name == hir_expand::name![register_attr] {
299 self.registered_attrs.push(registered_name.to_string());
300 cov_mark::hit!(register_attr);
301 } else {
302 self.registered_tools.push(registered_name.to_string());
303 cov_mark::hit!(register_tool);
304 }
305 }
306
267 ModCollector { 307 ModCollector {
268 def_collector: &mut *self, 308 def_collector: &mut *self,
269 macro_depth: 0, 309 macro_depth: 0,
@@ -319,7 +359,7 @@ impl DefCollector<'_> {
319 } 359 }
320 } 360 }
321 361
322 if self.reseed_with_unresolved_attributes() == ReachedFixedPoint::Yes { 362 if self.reseed_with_unresolved_attribute() == ReachedFixedPoint::Yes {
323 break; 363 break;
324 } 364 }
325 } 365 }
@@ -362,27 +402,23 @@ impl DefCollector<'_> {
362 } 402 }
363 403
364 /// When the fixed-point loop reaches a stable state, we might still have some unresolved 404 /// When the fixed-point loop reaches a stable state, we might still have some unresolved
365 /// attributes (or unexpanded attribute proc macros) left over. This takes them, and feeds the 405 /// attributes (or unexpanded attribute proc macros) left over. This takes one of them, and
366 /// item they're applied to back into name resolution. 406 /// feeds the item it's applied to back into name resolution.
367 /// 407 ///
368 /// This effectively ignores the fact that the macro is there and just treats the items as 408 /// This effectively ignores the fact that the macro is there and just treats the items as
369 /// normal code. 409 /// normal code.
370 /// 410 ///
371 /// This improves UX when proc macros are turned off or don't work, and replicates the behavior 411 /// This improves UX when proc macros are turned off or don't work, and replicates the behavior
372 /// before we supported proc. attribute macros. 412 /// before we supported proc. attribute macros.
373 fn reseed_with_unresolved_attributes(&mut self) -> ReachedFixedPoint { 413 fn reseed_with_unresolved_attribute(&mut self) -> ReachedFixedPoint {
374 cov_mark::hit!(unresolved_attribute_fallback); 414 cov_mark::hit!(unresolved_attribute_fallback);
375 415
376 let mut added_items = false; 416 let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new());
377 let unexpanded_macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); 417 let pos = unresolved_macros.iter().position(|directive| {
378 for directive in &unexpanded_macros { 418 if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind {
379 if let MacroDirectiveKind::Attr { ast_id, mod_item, .. } = &directive.kind { 419 self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr);
380 // Make sure to only add such items once.
381 if !self.ignore_attrs_on.insert(ast_id.ast_id.with_value(*mod_item)) {
382 continue;
383 }
384 420
385 let file_id = self.def_map[directive.module_id].definition_source(self.db).file_id; 421 let file_id = ast_id.ast_id.file_id;
386 let item_tree = self.db.file_item_tree(file_id); 422 let item_tree = self.db.file_item_tree(file_id);
387 let mod_dir = self.mod_dirs[&directive.module_id].clone(); 423 let mod_dir = self.mod_dirs[&directive.module_id].clone();
388 ModCollector { 424 ModCollector {
@@ -394,14 +430,20 @@ impl DefCollector<'_> {
394 mod_dir, 430 mod_dir,
395 } 431 }
396 .collect(&[*mod_item]); 432 .collect(&[*mod_item]);
397 added_items = true; 433 true
434 } else {
435 false
398 } 436 }
437 });
438
439 if let Some(pos) = pos {
440 unresolved_macros.remove(pos);
399 } 441 }
400 442
401 // The collection above might add new unresolved macros (eg. derives), so merge the lists. 443 // The collection above might add new unresolved macros (eg. derives), so merge the lists.
402 self.unexpanded_macros.extend(unexpanded_macros); 444 self.unresolved_macros.extend(unresolved_macros);
403 445
404 if added_items { 446 if pos.is_some() {
405 // Continue name resolution with the new data. 447 // Continue name resolution with the new data.
406 ReachedFixedPoint::No 448 ReachedFixedPoint::No
407 } else { 449 } else {
@@ -873,7 +915,7 @@ impl DefCollector<'_> {
873 } 915 }
874 916
875 fn resolve_macros(&mut self) -> ReachedFixedPoint { 917 fn resolve_macros(&mut self) -> ReachedFixedPoint {
876 let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); 918 let mut macros = std::mem::replace(&mut self.unresolved_macros, Vec::new());
877 let mut resolved = Vec::new(); 919 let mut resolved = Vec::new();
878 let mut res = ReachedFixedPoint::Yes; 920 let mut res = ReachedFixedPoint::Yes;
879 macros.retain(|directive| { 921 macros.retain(|directive| {
@@ -922,14 +964,43 @@ impl DefCollector<'_> {
922 Err(UnresolvedMacro { .. }) => (), 964 Err(UnresolvedMacro { .. }) => (),
923 } 965 }
924 } 966 }
925 MacroDirectiveKind::Attr { .. } => { 967 MacroDirectiveKind::Attr { ast_id, mod_item, attr } => {
926 // not yet :) 968 if let Some(ident) = ast_id.path.as_ident() {
969 if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id.ast_id) {
970 if helpers.contains(ident) {
971 cov_mark::hit!(resolved_derive_helper);
972
973 // Resolved to derive helper. Collect the item's attributes again,
974 // starting after the derive helper.
975 let file_id = ast_id.ast_id.file_id;
976 let item_tree = self.db.file_item_tree(file_id);
977 let mod_dir = self.mod_dirs[&directive.module_id].clone();
978 self.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr);
979 ModCollector {
980 def_collector: &mut *self,
981 macro_depth: directive.depth,
982 module_id: directive.module_id,
983 file_id,
984 item_tree: &item_tree,
985 mod_dir,
986 }
987 .collect(&[*mod_item]);
988
989 // Remove the original directive since we resolved it.
990 return false;
991 }
992 }
993 }
994
995 // Not resolved to a derive helper, so try to resolve as a macro.
996 // FIXME: not yet :)
927 } 997 }
928 } 998 }
929 999
930 true 1000 true
931 }); 1001 });
932 self.unexpanded_macros = macros; 1002 // Attribute resolution can add unresolved macro invocations, so concatenate the lists.
1003 self.unresolved_macros.extend(macros);
933 1004
934 for (module_id, macro_call_id, depth) in resolved { 1005 for (module_id, macro_call_id, depth) in resolved {
935 self.collect_macro_expansion(module_id, macro_call_id, depth); 1006 self.collect_macro_expansion(module_id, macro_call_id, depth);
@@ -1000,7 +1071,7 @@ impl DefCollector<'_> {
1000 fn finish(mut self) -> DefMap { 1071 fn finish(mut self) -> DefMap {
1001 // Emit diagnostics for all remaining unexpanded macros. 1072 // Emit diagnostics for all remaining unexpanded macros.
1002 1073
1003 for directive in &self.unexpanded_macros { 1074 for directive in &self.unresolved_macros {
1004 match &directive.kind { 1075 match &directive.kind {
1005 MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id( 1076 MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id(
1006 ast_id, 1077 ast_id,
@@ -1102,7 +1173,7 @@ impl ModCollector<'_, '_> {
1102 1173
1103 // Prelude module is always considered to be `#[macro_use]`. 1174 // Prelude module is always considered to be `#[macro_use]`.
1104 if let Some(prelude_module) = self.def_collector.def_map.prelude { 1175 if let Some(prelude_module) = self.def_collector.def_map.prelude {
1105 if prelude_module.krate != self.def_collector.def_map.krate { 1176 if prelude_module.krate != krate {
1106 cov_mark::hit!(prelude_is_macro_use); 1177 cov_mark::hit!(prelude_is_macro_use);
1107 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); 1178 self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
1108 } 1179 }
@@ -1203,11 +1274,6 @@ impl ModCollector<'_, '_> {
1203 ModItem::Struct(id) => { 1274 ModItem::Struct(id) => {
1204 let it = &self.item_tree[id]; 1275 let it = &self.item_tree[id];
1205 1276
1206 // FIXME: check attrs to see if this is an attribute macro invocation;
1207 // in which case we don't add the invocation, just a single attribute
1208 // macro invocation
1209 self.collect_derives(&attrs, it.ast_id.upcast());
1210
1211 def = Some(DefData { 1277 def = Some(DefData {
1212 id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) } 1278 id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1213 .intern(self.def_collector.db) 1279 .intern(self.def_collector.db)
@@ -1220,11 +1286,6 @@ impl ModCollector<'_, '_> {
1220 ModItem::Union(id) => { 1286 ModItem::Union(id) => {
1221 let it = &self.item_tree[id]; 1287 let it = &self.item_tree[id];
1222 1288
1223 // FIXME: check attrs to see if this is an attribute macro invocation;
1224 // in which case we don't add the invocation, just a single attribute
1225 // macro invocation
1226 self.collect_derives(&attrs, it.ast_id.upcast());
1227
1228 def = Some(DefData { 1289 def = Some(DefData {
1229 id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) } 1290 id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1230 .intern(self.def_collector.db) 1291 .intern(self.def_collector.db)
@@ -1237,11 +1298,6 @@ impl ModCollector<'_, '_> {
1237 ModItem::Enum(id) => { 1298 ModItem::Enum(id) => {
1238 let it = &self.item_tree[id]; 1299 let it = &self.item_tree[id];
1239 1300
1240 // FIXME: check attrs to see if this is an attribute macro invocation;
1241 // in which case we don't add the invocation, just a single attribute
1242 // macro invocation
1243 self.collect_derives(&attrs, it.ast_id.upcast());
1244
1245 def = Some(DefData { 1301 def = Some(DefData {
1246 id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) } 1302 id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1247 .intern(self.def_collector.db) 1303 .intern(self.def_collector.db)
@@ -1453,76 +1509,103 @@ impl ModCollector<'_, '_> {
1453 /// 1509 ///
1454 /// Returns `Err` when some attributes could not be resolved to builtins and have been 1510 /// Returns `Err` when some attributes could not be resolved to builtins and have been
1455 /// registered as unresolved. 1511 /// registered as unresolved.
1512 ///
1513 /// If `ignore_up_to` is `Some`, attributes precending and including that attribute will be
1514 /// assumed to be resolved already.
1456 fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> { 1515 fn resolve_attributes(&mut self, attrs: &Attrs, mod_item: ModItem) -> Result<(), ()> {
1457 fn is_builtin_attr(path: &ModPath) -> bool { 1516 let mut ignore_up_to =
1458 if path.kind == PathKind::Plain { 1517 self.def_collector.skip_attrs.get(&InFile::new(self.file_id, mod_item)).copied();
1459 if let Some(tool_module) = path.segments().first() { 1518 for attr in attrs.iter().skip_while(|attr| match ignore_up_to {
1460 let tool_module = tool_module.to_string(); 1519 Some(id) if attr.id == id => {
1461 if builtin_attr::TOOL_MODULES.iter().any(|m| tool_module == *m) { 1520 ignore_up_to = None;
1462 return true; 1521 true
1463 }
1464 }
1465
1466 if let Some(name) = path.as_ident() {
1467 let name = name.to_string();
1468 if builtin_attr::INERT_ATTRIBUTES
1469 .iter()
1470 .chain(builtin_attr::EXTRA_ATTRIBUTES)
1471 .any(|attr| name == *attr)
1472 {
1473 return true;
1474 }
1475 }
1476 } 1522 }
1477 1523 Some(_) => true,
1478 false 1524 None => false,
1479 } 1525 }) {
1480 1526 if attr.path.as_ident() == Some(&hir_expand::name![derive]) {
1481 // We failed to resolve an attribute on this item earlier, and are falling back to treating 1527 self.collect_derive(attr, mod_item);
1482 // the item as-is. 1528 } else if self.is_builtin_or_registered_attr(&attr.path) {
1483 if self.def_collector.ignore_attrs_on.contains(&InFile::new(self.file_id, mod_item)) { 1529 continue;
1484 return Ok(()); 1530 } else {
1485 } 1531 log::debug!("non-builtin attribute {}", attr.path);
1486
1487 match attrs.iter().find(|attr| !is_builtin_attr(&attr.path)) {
1488 Some(non_builtin_attr) => {
1489 log::debug!("non-builtin attribute {}", non_builtin_attr.path);
1490 1532
1491 let ast_id = AstIdWithPath::new( 1533 let ast_id = AstIdWithPath::new(
1492 self.file_id, 1534 self.file_id,
1493 mod_item.ast_id(self.item_tree), 1535 mod_item.ast_id(self.item_tree),
1494 non_builtin_attr.path.as_ref().clone(), 1536 attr.path.as_ref().clone(),
1495 ); 1537 );
1496 self.def_collector.unexpanded_macros.push(MacroDirective { 1538 self.def_collector.unresolved_macros.push(MacroDirective {
1497 module_id: self.module_id, 1539 module_id: self.module_id,
1498 depth: self.macro_depth + 1, 1540 depth: self.macro_depth + 1,
1499 kind: MacroDirectiveKind::Attr { ast_id, attr: non_builtin_attr.id, mod_item }, 1541 kind: MacroDirectiveKind::Attr { ast_id, attr: attr.id, mod_item },
1500 }); 1542 });
1501 1543
1502 Err(()) 1544 return Err(());
1503 } 1545 }
1504 None => Ok(()),
1505 } 1546 }
1547
1548 Ok(())
1506 } 1549 }
1507 1550
1508 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::Item>) { 1551 fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
1509 for derive in attrs.by_key("derive").attrs() { 1552 if path.kind == PathKind::Plain {
1510 match derive.parse_derive() { 1553 if let Some(tool_module) = path.segments().first() {
1511 Some(derive_macros) => { 1554 let tool_module = tool_module.to_string();
1512 for path in derive_macros { 1555 if builtin_attr::TOOL_MODULES
1513 let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); 1556 .iter()
1514 self.def_collector.unexpanded_macros.push(MacroDirective { 1557 .copied()
1515 module_id: self.module_id, 1558 .chain(self.def_collector.registered_tools.iter().map(|s| &**s))
1516 depth: self.macro_depth + 1, 1559 .any(|m| tool_module == *m)
1517 kind: MacroDirectiveKind::Derive { ast_id, derive_attr: derive.id }, 1560 {
1518 }); 1561 return true;
1519 }
1520 } 1562 }
1521 None => { 1563 }
1522 // FIXME: diagnose 1564
1523 log::debug!("malformed derive: {:?}", derive); 1565 if let Some(name) = path.as_ident() {
1566 let name = name.to_string();
1567 if builtin_attr::INERT_ATTRIBUTES
1568 .iter()
1569 .chain(builtin_attr::EXTRA_ATTRIBUTES)
1570 .copied()
1571 .chain(self.def_collector.registered_attrs.iter().map(|s| &**s))
1572 .any(|attr| name == *attr)
1573 {
1574 return true;
1575 }
1576 }
1577 }
1578
1579 false
1580 }
1581
1582 fn collect_derive(&mut self, attr: &Attr, mod_item: ModItem) {
1583 let ast_id: FileAstId<ast::Item> = match mod_item {
1584 ModItem::Struct(it) => self.item_tree[it].ast_id.upcast(),
1585 ModItem::Union(it) => self.item_tree[it].ast_id.upcast(),
1586 ModItem::Enum(it) => self.item_tree[it].ast_id.upcast(),
1587 _ => {
1588 // Cannot use derive on this item.
1589 // FIXME: diagnose
1590 return;
1591 }
1592 };
1593
1594 match attr.parse_derive() {
1595 Some(derive_macros) => {
1596 for path in derive_macros {
1597 let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
1598 self.def_collector.unresolved_macros.push(MacroDirective {
1599 module_id: self.module_id,
1600 depth: self.macro_depth + 1,
1601 kind: MacroDirectiveKind::Derive { ast_id, derive_attr: attr.id },
1602 });
1524 } 1603 }
1525 } 1604 }
1605 None => {
1606 // FIXME: diagnose
1607 log::debug!("malformed derive: {:?}", attr);
1608 }
1526 } 1609 }
1527 } 1610 }
1528 1611
@@ -1686,7 +1769,7 @@ impl ModCollector<'_, '_> {
1686 ast_id.path.kind = PathKind::Super(0); 1769 ast_id.path.kind = PathKind::Super(0);
1687 } 1770 }
1688 1771
1689 self.def_collector.unexpanded_macros.push(MacroDirective { 1772 self.def_collector.unresolved_macros.push(MacroDirective {
1690 module_id: self.module_id, 1773 module_id: self.module_id,
1691 depth: self.macro_depth + 1, 1774 depth: self.macro_depth + 1,
1692 kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment }, 1775 kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment },
@@ -1731,14 +1814,16 @@ mod tests {
1731 glob_imports: FxHashMap::default(), 1814 glob_imports: FxHashMap::default(),
1732 unresolved_imports: Vec::new(), 1815 unresolved_imports: Vec::new(),
1733 resolved_imports: Vec::new(), 1816 resolved_imports: Vec::new(),
1734 unexpanded_macros: Vec::new(), 1817 unresolved_macros: Vec::new(),
1735 mod_dirs: FxHashMap::default(), 1818 mod_dirs: FxHashMap::default(),
1736 cfg_options: &CfgOptions::default(), 1819 cfg_options: &CfgOptions::default(),
1737 proc_macros: Default::default(), 1820 proc_macros: Default::default(),
1738 exports_proc_macros: false, 1821 exports_proc_macros: false,
1739 from_glob_import: Default::default(), 1822 from_glob_import: Default::default(),
1740 ignore_attrs_on: FxHashSet::default(), 1823 skip_attrs: Default::default(),
1741 derive_helpers_in_scope: FxHashMap::default(), 1824 derive_helpers_in_scope: Default::default(),
1825 registered_attrs: Default::default(),
1826 registered_tools: Default::default(),
1742 }; 1827 };
1743 collector.seed_with_top_level(); 1828 collector.seed_with_top_level();
1744 collector.collect(); 1829 collector.collect();
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index 543975e07..75147d973 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -237,3 +237,20 @@ fn good_out_dir_diagnostic() {
237 "#, 237 "#,
238 ); 238 );
239} 239}
240
241#[test]
242fn register_attr_and_tool() {
243 cov_mark::check!(register_attr);
244 cov_mark::check!(register_tool);
245 check_no_diagnostics(
246 r#"
247#![register_tool(tool)]
248#![register_attr(attr)]
249
250#[tool::path]
251#[attr]
252struct S;
253 "#,
254 );
255 // NB: we don't currently emit diagnostics here
256}
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 6eb5f97be..04de107f5 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -736,6 +736,28 @@ fn unresolved_attributes_fall_back_track_per_file_moditems() {
736} 736}
737 737
738#[test] 738#[test]
739fn resolves_derive_helper() {
740 cov_mark::check!(resolved_derive_helper);
741 check(
742 r#"
743//- /main.rs crate:main deps:proc
744#[derive(proc::Derive)]
745#[helper]
746#[unresolved]
747struct S;
748
749//- /proc.rs crate:proc
750#[proc_macro_derive(Derive, attributes(helper))]
751fn derive() {}
752 "#,
753 expect![[r#"
754 crate
755 S: t v
756 "#]],
757 )
758}
759
760#[test]
739fn macro_expansion_overflow() { 761fn macro_expansion_overflow() {
740 cov_mark::check!(macro_expansion_overflow); 762 cov_mark::check!(macro_expansion_overflow);
741 check( 763 check(
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 5a5dc9afd..ef67ea2e9 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -164,6 +164,8 @@ pub mod known {
164 doc, 164 doc,
165 cfg, 165 cfg,
166 cfg_attr, 166 cfg_attr,
167 register_attr,
168 register_tool,
167 // Components of known path (value or mod name) 169 // Components of known path (value or mod name)
168 std, 170 std,
169 core, 171 core,
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index dda5a6631..d4748ef3a 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -33,20 +33,19 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
33// use super::AssistContext; 33// use super::AssistContext;
34// ``` 34// ```
35// 35//
36// .Merge Behavior 36// .Import Granularity
37// 37//
38// It is possible to configure how use-trees are merged with the `importMergeBehavior` setting. 38// It is possible to configure how use-trees are merged with the `importGranularity` setting.
39// It has the following configurations: 39// It has the following configurations:
40// 40//
41// - `full`: This setting will cause auto-import to always completely merge use-trees that share the 41// - `crate`: Merge imports from the same crate into a single use statement. This kind of
42// same path prefix while also merging inner trees that share the same path-prefix. This kind of
43// nesting is only supported in Rust versions later than 1.24. 42// nesting is only supported in Rust versions later than 1.24.
44// - `last`: This setting will cause auto-import to merge use-trees as long as the resulting tree 43// - `module`: Merge imports from the same module into a single use statement.
45// will only contain a nesting of single segment paths at the very end. 44// - `item`: Don't merge imports at all, creating one import per item.
46// - `none`: This setting will cause auto-import to never merge use-trees keeping them as simple 45// - `preserve`: Do not change the granularity of any imports. For auto-import this has the same
47// paths. 46// effect as `item`.
48// 47//
49// In `VS Code` the configuration for this is `rust-analyzer.assist.importMergeBehavior`. 48// In `VS Code` the configuration for this is `rust-analyzer.assist.importGranularity`.
50// 49//
51// .Import Prefix 50// .Import Prefix
52// 51//
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs
index 31854840c..fc117bd9a 100644
--- a/crates/ide_assists/src/handlers/merge_imports.rs
+++ b/crates/ide_assists/src/handlers/merge_imports.rs
@@ -213,6 +213,20 @@ pub(crate) use std::fmt::{Debug, Display};
213 } 213 }
214 214
215 #[test] 215 #[test]
216 fn merge_pub_in_path_crate() {
217 check_assist(
218 merge_imports,
219 r"
220pub(in this::path) use std::fmt$0::Debug;
221pub(in this::path) use std::fmt::Display;
222",
223 r"
224pub(in this::path) use std::fmt::{Debug, Display};
225",
226 )
227 }
228
229 #[test]
216 fn test_merge_nested() { 230 fn test_merge_nested() {
217 check_assist( 231 check_assist(
218 merge_imports, 232 merge_imports,
diff --git a/crates/ide_db/src/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs
index 697e8bcff..0dbabb44f 100644
--- a/crates/ide_db/src/helpers/merge_imports.rs
+++ b/crates/ide_db/src/helpers/merge_imports.rs
@@ -292,9 +292,7 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
292pub fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool { 292pub fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) -> bool {
293 match (vis0, vis1) { 293 match (vis0, vis1) {
294 (None, None) => true, 294 (None, None) => true,
295 // FIXME: Don't use the string representation to check for equality 295 (Some(vis0), Some(vis1)) => vis0.is_eq_to(&vis1),
296 // spaces inside of the node would break this comparison
297 (Some(vis0), Some(vis1)) => vis0.to_string() == vis1.to_string(),
298 _ => false, 296 _ => false,
299 } 297 }
300} 298}
@@ -303,9 +301,14 @@ pub fn eq_attrs(
303 attrs0: impl Iterator<Item = ast::Attr>, 301 attrs0: impl Iterator<Item = ast::Attr>,
304 attrs1: impl Iterator<Item = ast::Attr>, 302 attrs1: impl Iterator<Item = ast::Attr>,
305) -> bool { 303) -> bool {
306 let attrs0 = attrs0.map(|attr| attr.to_string()); 304 // FIXME order of attributes should not matter
307 let attrs1 = attrs1.map(|attr| attr.to_string()); 305 let attrs0 = attrs0
308 attrs0.eq(attrs1) 306 .flat_map(|attr| attr.syntax().descendants_with_tokens())
307 .flat_map(|it| it.into_token());
308 let attrs1 = attrs1
309 .flat_map(|attr| attr.syntax().descendants_with_tokens())
310 .flat_map(|it| it.into_token());
311 stdx::iter_eq_by(attrs0, attrs1, |tok, tok2| tok.text() == tok2.text())
309} 312}
310 313
311fn path_is_self(path: &ast::Path) -> bool { 314fn path_is_self(path: &ast::Path) -> bool {
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 340fcacfa..18d5fadb9 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -140,6 +140,34 @@ impl JodChild {
140 } 140 }
141} 141}
142 142
143// feature: iter_order_by
144// Iterator::eq_by
145pub fn iter_eq_by<I, I2, F>(this: I2, other: I, mut eq: F) -> bool
146where
147 I: IntoIterator,
148 I2: IntoIterator,
149 F: FnMut(I2::Item, I::Item) -> bool,
150{
151 let mut other = other.into_iter();
152 let mut this = this.into_iter();
153
154 loop {
155 let x = match this.next() {
156 None => return other.next().is_none(),
157 Some(val) => val,
158 };
159
160 let y = match other.next() {
161 None => return false,
162 Some(val) => val,
163 };
164
165 if !eq(x, y) {
166 return false;
167 }
168 }
169}
170
143#[cfg(test)] 171#[cfg(test)]
144mod tests { 172mod tests {
145 use super::*; 173 use super::*;
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index bef49238f..df8f98b5b 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -608,6 +608,29 @@ impl ast::Visibility {
608 None => VisibilityKind::Pub, 608 None => VisibilityKind::Pub,
609 } 609 }
610 } 610 }
611
612 pub fn is_eq_to(&self, other: &Self) -> bool {
613 match (self.kind(), other.kind()) {
614 (VisibilityKind::In(this), VisibilityKind::In(other)) => {
615 stdx::iter_eq_by(this.segments(), other.segments(), |lhs, rhs| {
616 lhs.kind().zip(rhs.kind()).map_or(false, |it| match it {
617 (PathSegmentKind::CrateKw, PathSegmentKind::CrateKw)
618 | (PathSegmentKind::SelfKw, PathSegmentKind::SelfKw)
619 | (PathSegmentKind::SuperKw, PathSegmentKind::SuperKw) => true,
620 (PathSegmentKind::Name(lhs), PathSegmentKind::Name(rhs)) => {
621 lhs.text() == rhs.text()
622 }
623 _ => false,
624 })
625 })
626 }
627 (VisibilityKind::PubSelf, VisibilityKind::PubSelf)
628 | (VisibilityKind::PubSuper, VisibilityKind::PubSuper)
629 | (VisibilityKind::PubCrate, VisibilityKind::PubCrate)
630 | (VisibilityKind::Pub, VisibilityKind::Pub) => true,
631 _ => false,
632 }
633 }
611} 634}
612 635
613impl ast::LifetimeParam { 636impl ast::LifetimeParam {