Resolve derive attribute input macro paths in ide layer
This commit is contained in:
parent
2e45e47c83
commit
bfe0fa009e
@ -5,7 +5,7 @@ use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
|
||||
use ide_db::{
|
||||
base_db::{AnchoredPath, FileId, FileLoader},
|
||||
defs::{Definition, NameClass, NameRefClass},
|
||||
helpers::pick_best_token,
|
||||
helpers::{pick_best_token, try_resolve_derive_input_at},
|
||||
RootDatabase,
|
||||
};
|
||||
use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
|
||||
@ -78,7 +78,7 @@ pub(crate) fn goto_definition(
|
||||
} else {
|
||||
reference_definition(&sema, Either::Left(<))
|
||||
},
|
||||
ast::TokenTree(tt) => try_lookup_include_path(sema.db, tt, token, position.file_id)?,
|
||||
ast::TokenTree(tt) => try_lookup_include_path_or_derive(&sema, tt, token, position.file_id)?,
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
@ -86,30 +86,41 @@ pub(crate) fn goto_definition(
|
||||
Some(RangeInfo::new(original_token.text_range(), navs))
|
||||
}
|
||||
|
||||
fn try_lookup_include_path(
|
||||
db: &RootDatabase,
|
||||
fn try_lookup_include_path_or_derive(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
tt: ast::TokenTree,
|
||||
token: SyntaxToken,
|
||||
file_id: FileId,
|
||||
) -> Option<Vec<NavigationTarget>> {
|
||||
let path = ast::String::cast(token)?.value()?.into_owned();
|
||||
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
|
||||
let name = macro_call.path()?.segment()?.name_ref()?;
|
||||
if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
|
||||
return None;
|
||||
match ast::String::cast(token.clone()) {
|
||||
Some(token) => {
|
||||
let path = token.value()?.into_owned();
|
||||
let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
|
||||
let name = macro_call.path()?.segment()?.name_ref()?;
|
||||
if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
|
||||
return None;
|
||||
}
|
||||
let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
|
||||
let size = sema.db.file_text(file_id).len().try_into().ok()?;
|
||||
Some(vec![NavigationTarget {
|
||||
file_id,
|
||||
full_range: TextRange::new(0.into(), size),
|
||||
name: path.into(),
|
||||
focus_range: None,
|
||||
kind: None,
|
||||
container_name: None,
|
||||
description: None,
|
||||
docs: None,
|
||||
}])
|
||||
}
|
||||
None => try_resolve_derive_input_at(
|
||||
sema,
|
||||
&tt.syntax().ancestors().nth(2).and_then(ast::Attr::cast)?,
|
||||
&token,
|
||||
)
|
||||
.and_then(|it| it.try_to_nav(sema.db))
|
||||
.map(|it| vec![it]),
|
||||
}
|
||||
let file_id = db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
|
||||
let size = db.file_text(file_id).len().try_into().ok()?;
|
||||
Some(vec![NavigationTarget {
|
||||
file_id,
|
||||
full_range: TextRange::new(0.into(), size),
|
||||
name: path.into(),
|
||||
focus_range: None,
|
||||
kind: None,
|
||||
container_name: None,
|
||||
description: None,
|
||||
docs: None,
|
||||
}])
|
||||
}
|
||||
|
||||
/// finds the trait definition of an impl'd item
|
||||
@ -1383,4 +1394,28 @@ impl Twait for Stwuct {
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_def_derive_input() {
|
||||
check(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
// ^^^^
|
||||
#[derive(Copy$0)]
|
||||
struct Foo;
|
||||
"#,
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
mod foo {
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
// ^^^^
|
||||
}
|
||||
#[derive(foo::Copy$0)]
|
||||
struct Foo;
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use ide_db::{
|
||||
defs::{Definition, NameClass, NameRefClass},
|
||||
helpers::{
|
||||
generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
|
||||
pick_best_token, FamousDefs,
|
||||
pick_best_token, try_resolve_derive_input_at, FamousDefs,
|
||||
},
|
||||
RootDatabase,
|
||||
};
|
||||
@ -129,8 +129,12 @@ pub(crate) fn hover(
|
||||
})?;
|
||||
range = Some(idl_range);
|
||||
resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef)
|
||||
} else if let res@Some(_) = try_hover_for_attribute(&token) {
|
||||
return res;
|
||||
} else if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) {
|
||||
if let res@Some(_) = try_hover_for_lint(&attr, &token) {
|
||||
return res;
|
||||
} else {
|
||||
try_resolve_derive_input_at(&sema, &attr, &token).map(Definition::Macro)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -197,8 +201,7 @@ pub(crate) fn hover(
|
||||
Some(RangeInfo::new(range, res))
|
||||
}
|
||||
|
||||
fn try_hover_for_attribute(token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> {
|
||||
let attr = token.ancestors().find_map(ast::Attr::cast)?;
|
||||
fn try_hover_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<RangeInfo<HoverResult>> {
|
||||
let (path, tt) = attr.as_simple_call()?;
|
||||
if !tt.syntax().text_range().contains(token.text_range().start()) {
|
||||
return None;
|
||||
@ -3839,4 +3842,48 @@ use crate as foo$0;
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_derive_input() {
|
||||
check(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
#[derive(Copy$0)]
|
||||
struct Foo;
|
||||
"#,
|
||||
expect![[r#"
|
||||
*(Copy)*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
pub macro Copy
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
mod foo {
|
||||
#[rustc_builtin_macro]
|
||||
pub macro Copy {}
|
||||
}
|
||||
#[derive(foo::Copy$0)]
|
||||
struct Foo;
|
||||
"#,
|
||||
expect![[r#"
|
||||
*(foo::Copy)*
|
||||
|
||||
```rust
|
||||
test
|
||||
```
|
||||
|
||||
```rust
|
||||
pub macro Copy
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
use hir::{AsAssocItem, HasVisibility, Semantics};
|
||||
use ide_db::{
|
||||
defs::{Definition, NameClass, NameRefClass},
|
||||
helpers::try_resolve_derive_input_at,
|
||||
RootDatabase, SymbolKind,
|
||||
};
|
||||
use rustc_hash::FxHashMap;
|
||||
@ -87,7 +88,18 @@ pub(super) fn element(
|
||||
_ => Highlight::from(SymbolKind::LifetimeParam) | HlMod::Definition,
|
||||
}
|
||||
}
|
||||
IDENT if parent_matches::<ast::TokenTree>(&element) => HlTag::None.into(),
|
||||
IDENT if parent_matches::<ast::TokenTree>(&element) => {
|
||||
if let Some((attr, token)) =
|
||||
element.ancestors().nth(2).and_then(ast::Attr::cast).zip(element.as_token())
|
||||
{
|
||||
match try_resolve_derive_input_at(sema, &attr, token) {
|
||||
Some(makro) => highlight_def(sema.db, krate, Definition::Macro(makro)),
|
||||
None => HlTag::None.into(),
|
||||
}
|
||||
} else {
|
||||
HlTag::None.into()
|
||||
}
|
||||
}
|
||||
p if p.is_punct() => match p {
|
||||
T![&] if parent_matches::<ast::BinExpr>(&element) => HlOperator::Bitwise.into(),
|
||||
T![&] => {
|
||||
|
@ -91,7 +91,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
||||
<span class="brace">}</span>
|
||||
<span class="brace">}</span>
|
||||
|
||||
<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">derive</span><span class="parenthesis attribute">(</span><span class="none attribute">Copy</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
|
||||
<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">derive</span><span class="parenthesis attribute">(</span><span class="macro attribute">Copy</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
|
||||
<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="brace">{</span>
|
||||
<span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
|
||||
<span class="brace">}</span>
|
||||
|
@ -43,8 +43,8 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
||||
let expr = match_expr.expr()?;
|
||||
|
||||
let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
|
||||
if arms.len() == 1 {
|
||||
if let Some(Pat::WildcardPat(..)) = arms[0].pat() {
|
||||
if let [arm] = arms.as_slice() {
|
||||
if let Some(Pat::WildcardPat(..)) = arm.pat() {
|
||||
arms.clear();
|
||||
}
|
||||
}
|
||||
@ -73,9 +73,9 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
||||
.filter_map(|variant| build_pat(ctx.db(), module, variant))
|
||||
.filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat));
|
||||
|
||||
let missing_pats: Box<dyn Iterator<Item = _>> = if Some(enum_def)
|
||||
== FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option().map(lift_enum)
|
||||
{
|
||||
let option_enum =
|
||||
FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option().map(lift_enum);
|
||||
let missing_pats: Box<dyn Iterator<Item = _>> = if Some(enum_def) == option_enum {
|
||||
// Match `Some` variant first.
|
||||
cov_mark::hit!(option_order);
|
||||
Box::new(missing_pats.rev())
|
||||
@ -136,7 +136,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
||||
.arms()
|
||||
.find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
|
||||
if let Some(arm) = catch_all_arm {
|
||||
arm.remove()
|
||||
arm.remove();
|
||||
}
|
||||
let mut first_new_arm = None;
|
||||
for arm in missing_arms {
|
||||
@ -214,13 +214,7 @@ impl ExtendedEnum {
|
||||
fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
|
||||
sema.type_of_expr(expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
|
||||
Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
|
||||
_ => {
|
||||
if ty.is_bool() {
|
||||
Some(ExtendedEnum::Bool)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => ty.is_bool().then(|| ExtendedEnum::Bool),
|
||||
})
|
||||
}
|
||||
|
||||
@ -237,13 +231,7 @@ fn resolve_tuple_of_enum_def(
|
||||
// For now we only handle expansion for a tuple of enums. Here
|
||||
// we map non-enum items to None and rely on `collect` to
|
||||
// convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
|
||||
_ => {
|
||||
if ty.is_bool() {
|
||||
Some(ExtendedEnum::Bool)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => ty.is_bool().then(|| ExtendedEnum::Bool),
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
|
@ -12,7 +12,7 @@ use either::Either;
|
||||
use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
|
||||
use syntax::{
|
||||
ast::{self, make, LoopBodyOwner},
|
||||
AstNode, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent,
|
||||
AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxToken, TokenAtOffset, WalkEvent, T,
|
||||
};
|
||||
|
||||
use crate::RootDatabase;
|
||||
@ -25,6 +25,38 @@ pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the path at the cursor token as a derive macro if it inside a token tree of a derive attribute.
|
||||
pub fn try_resolve_derive_input_at(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
derive_attr: &ast::Attr,
|
||||
cursor: &SyntaxToken,
|
||||
) -> Option<MacroDef> {
|
||||
use itertools::Itertools;
|
||||
if cursor.kind() != T![ident] {
|
||||
return None;
|
||||
}
|
||||
let tt = match derive_attr.as_simple_call() {
|
||||
Some((name, tt))
|
||||
if name == "derive" && tt.syntax().text_range().contains_range(cursor.text_range()) =>
|
||||
{
|
||||
tt
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let tokens: Vec<_> = cursor
|
||||
.siblings_with_tokens(Direction::Prev)
|
||||
.flat_map(SyntaxElement::into_token)
|
||||
.take_while(|tok| tok.kind() != T!['('] && tok.kind() != T![,])
|
||||
.collect();
|
||||
let path = ast::Path::parse(&tokens.into_iter().rev().join("")).ok()?;
|
||||
match sema.scope(tt.syntax()).speculative_resolve(&path) {
|
||||
Some(hir::PathResolution::Macro(makro)) if makro.kind() == hir::MacroKind::Derive => {
|
||||
Some(makro)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Picks the token with the highest rank returned by the passed in function.
|
||||
pub fn pick_best_token(
|
||||
tokens: TokenAtOffset<SyntaxToken>,
|
||||
|
Loading…
x
Reference in New Issue
Block a user