mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
Merge #1815
1815: Support correct `$crate` expansion in macros r=uHOOCCOOHu a=uHOOCCOOHu This PR makes normal use cases of `$crate` from macros work as expected. It makes more macros from `std` work. Type inference works well with `panic`, `unimplemented`, `format`, and maybe more. Sadly that `vec![1, 2, 3]` still not works, but it is not longer an issue about macro. Screenshot:  Co-authored-by: uHOOCCOOHu <hooccooh1896@gmail.com>
This commit is contained in:
commit
2b69c84396
20 changed files with 374 additions and 127 deletions
|
@ -511,7 +511,7 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
|
||||||
hir::PathKind::Plain => {}
|
hir::PathKind::Plain => {}
|
||||||
hir::PathKind::Self_ => ps.push("self".into()),
|
hir::PathKind::Self_ => ps.push("self".into()),
|
||||||
hir::PathKind::Super => ps.push("super".into()),
|
hir::PathKind::Super => ps.push("super".into()),
|
||||||
hir::PathKind::Type(_) => return None,
|
hir::PathKind::Type(_) | hir::PathKind::DollarCrate(_) => return None,
|
||||||
}
|
}
|
||||||
for s in path.segments.iter() {
|
for s in path.segments.iter() {
|
||||||
ps.push(s.name.to_string().into());
|
ps.push(s.name.to_string().into());
|
||||||
|
|
|
@ -133,7 +133,7 @@ impl VariantData {
|
||||||
.fields()
|
.fields()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, fd)| StructFieldData {
|
.map(|(i, fd)| StructFieldData {
|
||||||
name: Name::tuple_field_name(i),
|
name: Name::new_tuple_field(i),
|
||||||
type_ref: TypeRef::from_ast_opt(fd.type_ref()),
|
type_ref: TypeRef::from_ast_opt(fd.type_ref()),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -119,7 +119,7 @@ impl HasSource for TypeAlias {
|
||||||
impl HasSource for MacroDef {
|
impl HasSource for MacroDef {
|
||||||
type Ast = ast::MacroCall;
|
type Ast = ast::MacroCall;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::MacroCall> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::MacroCall> {
|
||||||
Source { file_id: self.id.0.file_id(), ast: self.id.0.to_node(db) }
|
Source { file_id: self.id.ast_id.file_id(), ast: self.id.ast_id.to_node(db) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -272,8 +272,11 @@ where
|
||||||
self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
|
self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::PathExpr(e) => {
|
ast::Expr::PathExpr(e) => {
|
||||||
let path =
|
let path = e
|
||||||
e.path().and_then(Path::from_ast).map(Expr::Path).unwrap_or(Expr::Missing);
|
.path()
|
||||||
|
.and_then(|path| self.parse_path(path))
|
||||||
|
.map(Expr::Path)
|
||||||
|
.unwrap_or(Expr::Missing);
|
||||||
self.alloc_expr(path, syntax_ptr)
|
self.alloc_expr(path, syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::ContinueExpr(_e) => {
|
ast::Expr::ContinueExpr(_e) => {
|
||||||
|
@ -295,7 +298,7 @@ where
|
||||||
self.alloc_expr(Expr::Return { expr }, syntax_ptr)
|
self.alloc_expr(Expr::Return { expr }, syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::RecordLit(e) => {
|
ast::Expr::RecordLit(e) => {
|
||||||
let path = e.path().and_then(Path::from_ast);
|
let path = e.path().and_then(|path| self.parse_path(path));
|
||||||
let mut field_ptrs = Vec::new();
|
let mut field_ptrs = Vec::new();
|
||||||
let record_lit = if let Some(nfl) = e.record_field_list() {
|
let record_lit = if let Some(nfl) = e.record_field_list() {
|
||||||
let fields = nfl
|
let fields = nfl
|
||||||
|
@ -459,7 +462,7 @@ where
|
||||||
.ast_id(&e)
|
.ast_id(&e)
|
||||||
.with_file_id(self.current_file_id);
|
.with_file_id(self.current_file_id);
|
||||||
|
|
||||||
if let Some(path) = e.path().and_then(Path::from_ast) {
|
if let Some(path) = e.path().and_then(|path| self.parse_path(path)) {
|
||||||
if let Some(def) = self.resolver.resolve_path_as_macro(self.db, &path) {
|
if let Some(def) = self.resolver.resolve_path_as_macro(self.db, &path) {
|
||||||
let call_id = MacroCallLoc { def: def.id, ast_id }.id(self.db);
|
let call_id = MacroCallLoc { def: def.id, ast_id }.id(self.db);
|
||||||
let file_id = call_id.as_file(MacroFileKind::Expr);
|
let file_id = call_id.as_file(MacroFileKind::Expr);
|
||||||
|
@ -529,7 +532,7 @@ where
|
||||||
Pat::Bind { name, mode: annotation, subpat }
|
Pat::Bind { name, mode: annotation, subpat }
|
||||||
}
|
}
|
||||||
ast::Pat::TupleStructPat(p) => {
|
ast::Pat::TupleStructPat(p) => {
|
||||||
let path = p.path().and_then(Path::from_ast);
|
let path = p.path().and_then(|path| self.parse_path(path));
|
||||||
let args = p.args().map(|p| self.collect_pat(p)).collect();
|
let args = p.args().map(|p| self.collect_pat(p)).collect();
|
||||||
Pat::TupleStruct { path, args }
|
Pat::TupleStruct { path, args }
|
||||||
}
|
}
|
||||||
|
@ -539,7 +542,7 @@ where
|
||||||
Pat::Ref { pat, mutability }
|
Pat::Ref { pat, mutability }
|
||||||
}
|
}
|
||||||
ast::Pat::PathPat(p) => {
|
ast::Pat::PathPat(p) => {
|
||||||
let path = p.path().and_then(Path::from_ast);
|
let path = p.path().and_then(|path| self.parse_path(path));
|
||||||
path.map(Pat::Path).unwrap_or(Pat::Missing)
|
path.map(Pat::Path).unwrap_or(Pat::Missing)
|
||||||
}
|
}
|
||||||
ast::Pat::TuplePat(p) => {
|
ast::Pat::TuplePat(p) => {
|
||||||
|
@ -548,7 +551,7 @@ where
|
||||||
}
|
}
|
||||||
ast::Pat::PlaceholderPat(_) => Pat::Wild,
|
ast::Pat::PlaceholderPat(_) => Pat::Wild,
|
||||||
ast::Pat::RecordPat(p) => {
|
ast::Pat::RecordPat(p) => {
|
||||||
let path = p.path().and_then(Path::from_ast);
|
let path = p.path().and_then(|path| self.parse_path(path));
|
||||||
let record_field_pat_list =
|
let record_field_pat_list =
|
||||||
p.record_field_pat_list().expect("every struct should have a field list");
|
p.record_field_pat_list().expect("every struct should have a field list");
|
||||||
let mut fields: Vec<_> = record_field_pat_list
|
let mut fields: Vec<_> = record_field_pat_list
|
||||||
|
@ -589,6 +592,10 @@ where
|
||||||
self.missing_pat()
|
self.missing_pat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
|
||||||
|
Path::from_src(Source { ast: path, file_id: self.current_file_id }, self.db)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ast::BinOp> for BinaryOp {
|
impl From<ast::BinOp> for BinaryOp {
|
||||||
|
|
|
@ -132,6 +132,7 @@ impl GenericParams {
|
||||||
fn fill_params(&mut self, params: ast::TypeParamList, start: u32) {
|
fn fill_params(&mut self, params: ast::TypeParamList, start: u32) {
|
||||||
for (idx, type_param) in params.type_params().enumerate() {
|
for (idx, type_param) in params.type_params().enumerate() {
|
||||||
let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
|
let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
|
||||||
|
// FIXME: Use `Path::from_src`
|
||||||
let default = type_param.default_type().and_then(|t| t.path()).and_then(Path::from_ast);
|
let default = type_param.default_type().and_then(|t| t.path()).and_then(Path::from_ast);
|
||||||
|
|
||||||
let param = GenericParam { idx: idx as u32 + start, name: name.clone(), default };
|
let param = GenericParam { idx: idx as u32 + start, name: name.clone(), default };
|
||||||
|
|
|
@ -10,7 +10,7 @@ use ra_syntax::{ast, AstNode, Parse, SyntaxNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{AstDatabase, DefDatabase, InternDatabase},
|
db::{AstDatabase, DefDatabase, InternDatabase},
|
||||||
AstId, FileAstId, Module, Source,
|
AstId, Crate, FileAstId, Module, Source,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// hir makes heavy use of ids: integer (u32) handlers to various things. You
|
/// hir makes heavy use of ids: integer (u32) handlers to various things. You
|
||||||
|
@ -58,6 +58,17 @@ impl HirFileId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the crate which the macro lives in, if it is a macro file.
|
||||||
|
pub(crate) fn macro_crate(self, db: &impl AstDatabase) -> Option<Crate> {
|
||||||
|
match self.0 {
|
||||||
|
HirFileIdRepr::File(_) => None,
|
||||||
|
HirFileIdRepr::Macro(macro_file) => {
|
||||||
|
let loc = macro_file.macro_call_id.loc(db);
|
||||||
|
Some(loc.def.krate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_or_expand_query(
|
pub(crate) fn parse_or_expand_query(
|
||||||
db: &impl AstDatabase,
|
db: &impl AstDatabase,
|
||||||
file_id: HirFileId,
|
file_id: HirFileId,
|
||||||
|
@ -121,10 +132,13 @@ impl From<FileId> for HirFileId {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct MacroDefId(pub(crate) AstId<ast::MacroCall>);
|
pub struct MacroDefId {
|
||||||
|
pub(crate) ast_id: AstId<ast::MacroCall>,
|
||||||
|
pub(crate) krate: Crate,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn macro_def_query(db: &impl AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> {
|
pub(crate) fn macro_def_query(db: &impl AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> {
|
||||||
let macro_call = id.0.to_node(db);
|
let macro_call = id.ast_id.to_node(db);
|
||||||
let arg = macro_call.token_tree()?;
|
let arg = macro_call.token_tree()?;
|
||||||
let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| {
|
let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| {
|
||||||
log::warn!("fail on macro_def to token tree: {:#?}", arg);
|
log::warn!("fail on macro_def to token tree: {:#?}", arg);
|
||||||
|
|
|
@ -218,7 +218,10 @@ impl ModuleImplBlocks {
|
||||||
ast::ItemOrMacro::Macro(macro_call) => {
|
ast::ItemOrMacro::Macro(macro_call) => {
|
||||||
//FIXME: we should really cut down on the boilerplate required to process a macro
|
//FIXME: we should really cut down on the boilerplate required to process a macro
|
||||||
let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id);
|
let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id);
|
||||||
if let Some(path) = macro_call.path().and_then(Path::from_ast) {
|
if let Some(path) = macro_call
|
||||||
|
.path()
|
||||||
|
.and_then(|path| Path::from_src(Source { ast: path, file_id }, db))
|
||||||
|
{
|
||||||
if let Some(def) = self.module.resolver(db).resolve_path_as_macro(db, &path)
|
if let Some(def) = self.module.resolver(db).resolve_path_as_macro(db, &path)
|
||||||
{
|
{
|
||||||
let call_id = MacroCallLoc { def: def.id, ast_id }.id(db);
|
let call_id = MacroCallLoc { def: def.id, ast_id }.id(db);
|
||||||
|
|
|
@ -13,4 +13,6 @@ test_utils::marks!(
|
||||||
macro_rules_from_other_crates_are_visible_with_macro_use
|
macro_rules_from_other_crates_are_visible_with_macro_use
|
||||||
prelude_is_macro_use
|
prelude_is_macro_use
|
||||||
coerce_merge_fail_fallback
|
coerce_merge_fail_fallback
|
||||||
|
macro_dollar_crate_self
|
||||||
|
macro_dollar_crate_other
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,20 +5,21 @@ use ra_syntax::{ast, SmolStr};
|
||||||
/// `Name` is a wrapper around string, which is used in hir for both references
|
/// `Name` is a wrapper around string, which is used in hir for both references
|
||||||
/// and declarations. In theory, names should also carry hygiene info, but we are
|
/// and declarations. In theory, names should also carry hygiene info, but we are
|
||||||
/// not there yet!
|
/// not there yet!
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct Name {
|
pub struct Name(Repr);
|
||||||
text: SmolStr,
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
enum Repr {
|
||||||
|
Text(SmolStr),
|
||||||
|
TupleField(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Name {
|
impl fmt::Display for Name {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
fmt::Display::fmt(&self.text, f)
|
match &self.0 {
|
||||||
}
|
Repr::Text(text) => fmt::Display::fmt(&text, f),
|
||||||
}
|
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
|
||||||
|
}
|
||||||
impl fmt::Debug for Name {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(&self.text, f)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,29 +27,38 @@ impl Name {
|
||||||
/// Note: this is private to make creating name from random string hard.
|
/// Note: this is private to make creating name from random string hard.
|
||||||
/// Hopefully, this should allow us to integrate hygiene cleaner in the
|
/// Hopefully, this should allow us to integrate hygiene cleaner in the
|
||||||
/// future, and to switch to interned representation of names.
|
/// future, and to switch to interned representation of names.
|
||||||
const fn new(text: SmolStr) -> Name {
|
const fn new_text(text: SmolStr) -> Name {
|
||||||
Name { text }
|
Name(Repr::Text(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_tuple_field(idx: usize) -> Name {
|
||||||
|
Name(Repr::TupleField(idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut to create inline plain text name
|
||||||
|
const fn new_inline_ascii(len: usize, text: &[u8]) -> Name {
|
||||||
|
Name::new_text(SmolStr::new_inline_from_ascii(len, text))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve a name from the text of token.
|
||||||
|
fn resolve(raw_text: &SmolStr) -> Name {
|
||||||
|
let raw_start = "r#";
|
||||||
|
if raw_text.as_str().starts_with(raw_start) {
|
||||||
|
Name::new_text(SmolStr::new(&raw_text[raw_start.len()..]))
|
||||||
|
} else {
|
||||||
|
Name::new_text(raw_text.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn missing() -> Name {
|
pub(crate) fn missing() -> Name {
|
||||||
Name::new("[missing name]".into())
|
Name::new_text("[missing name]".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn tuple_field_name(idx: usize) -> Name {
|
pub(crate) fn as_tuple_index(&self) -> Option<usize> {
|
||||||
Name::new(idx.to_string().into())
|
match self.0 {
|
||||||
}
|
Repr::TupleField(idx) => Some(idx),
|
||||||
|
_ => None,
|
||||||
// There's should be no way to extract a string out of `Name`: `Name` in the
|
}
|
||||||
// future, `Name` will include hygiene information, and you can't encode
|
|
||||||
// hygiene into a String.
|
|
||||||
//
|
|
||||||
// If you need to compare something with `Name`, compare `Name`s directly.
|
|
||||||
//
|
|
||||||
// If you need to render `Name` for the user, use the `Display` impl, but be
|
|
||||||
// aware that it strips hygiene info.
|
|
||||||
#[deprecated(note = "use to_string instead")]
|
|
||||||
pub fn as_smolstr(&self) -> &SmolStr {
|
|
||||||
&self.text
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,15 +68,16 @@ pub(crate) trait AsName {
|
||||||
|
|
||||||
impl AsName for ast::NameRef {
|
impl AsName for ast::NameRef {
|
||||||
fn as_name(&self) -> Name {
|
fn as_name(&self) -> Name {
|
||||||
let name = resolve_name(self.text());
|
match self.as_tuple_field() {
|
||||||
Name::new(name)
|
Some(idx) => Name::new_tuple_field(idx),
|
||||||
|
None => Name::resolve(self.text()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsName for ast::Name {
|
impl AsName for ast::Name {
|
||||||
fn as_name(&self) -> Name {
|
fn as_name(&self) -> Name {
|
||||||
let name = resolve_name(self.text());
|
Name::resolve(self.text())
|
||||||
Name::new(name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,66 +85,56 @@ impl AsName for ast::FieldKind {
|
||||||
fn as_name(&self) -> Name {
|
fn as_name(&self) -> Name {
|
||||||
match self {
|
match self {
|
||||||
ast::FieldKind::Name(nr) => nr.as_name(),
|
ast::FieldKind::Name(nr) => nr.as_name(),
|
||||||
ast::FieldKind::Index(idx) => Name::new(idx.text().clone()),
|
ast::FieldKind::Index(idx) => Name::new_tuple_field(idx.text().parse().unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsName for ra_db::Dependency {
|
impl AsName for ra_db::Dependency {
|
||||||
fn as_name(&self) -> Name {
|
fn as_name(&self) -> Name {
|
||||||
Name::new(self.name.clone())
|
Name::new_text(self.name.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Primitives
|
// Primitives
|
||||||
pub(crate) const ISIZE: Name = Name::new(SmolStr::new_inline_from_ascii(5, b"isize"));
|
pub(crate) const ISIZE: Name = Name::new_inline_ascii(5, b"isize");
|
||||||
pub(crate) const I8: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"i8"));
|
pub(crate) const I8: Name = Name::new_inline_ascii(2, b"i8");
|
||||||
pub(crate) const I16: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"i16"));
|
pub(crate) const I16: Name = Name::new_inline_ascii(3, b"i16");
|
||||||
pub(crate) const I32: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"i32"));
|
pub(crate) const I32: Name = Name::new_inline_ascii(3, b"i32");
|
||||||
pub(crate) const I64: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"i64"));
|
pub(crate) const I64: Name = Name::new_inline_ascii(3, b"i64");
|
||||||
pub(crate) const I128: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"i128"));
|
pub(crate) const I128: Name = Name::new_inline_ascii(4, b"i128");
|
||||||
pub(crate) const USIZE: Name = Name::new(SmolStr::new_inline_from_ascii(5, b"usize"));
|
pub(crate) const USIZE: Name = Name::new_inline_ascii(5, b"usize");
|
||||||
pub(crate) const U8: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"u8"));
|
pub(crate) const U8: Name = Name::new_inline_ascii(2, b"u8");
|
||||||
pub(crate) const U16: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"u16"));
|
pub(crate) const U16: Name = Name::new_inline_ascii(3, b"u16");
|
||||||
pub(crate) const U32: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"u32"));
|
pub(crate) const U32: Name = Name::new_inline_ascii(3, b"u32");
|
||||||
pub(crate) const U64: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"u64"));
|
pub(crate) const U64: Name = Name::new_inline_ascii(3, b"u64");
|
||||||
pub(crate) const U128: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"u128"));
|
pub(crate) const U128: Name = Name::new_inline_ascii(4, b"u128");
|
||||||
pub(crate) const F32: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"f32"));
|
pub(crate) const F32: Name = Name::new_inline_ascii(3, b"f32");
|
||||||
pub(crate) const F64: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"f64"));
|
pub(crate) const F64: Name = Name::new_inline_ascii(3, b"f64");
|
||||||
pub(crate) const BOOL: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"bool"));
|
pub(crate) const BOOL: Name = Name::new_inline_ascii(4, b"bool");
|
||||||
pub(crate) const CHAR: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"char"));
|
pub(crate) const CHAR: Name = Name::new_inline_ascii(4, b"char");
|
||||||
pub(crate) const STR: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"str"));
|
pub(crate) const STR: Name = Name::new_inline_ascii(3, b"str");
|
||||||
|
|
||||||
// Special names
|
// Special names
|
||||||
pub(crate) const SELF_PARAM: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"self"));
|
pub(crate) const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self");
|
||||||
pub(crate) const SELF_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"Self"));
|
pub(crate) const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self");
|
||||||
pub(crate) const MACRO_RULES: Name = Name::new(SmolStr::new_inline_from_ascii(11, b"macro_rules"));
|
pub(crate) const MACRO_RULES: Name = Name::new_inline_ascii(11, b"macro_rules");
|
||||||
|
|
||||||
// Components of known path (value or mod name)
|
// Components of known path (value or mod name)
|
||||||
pub(crate) const STD: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"std"));
|
pub(crate) const STD: Name = Name::new_inline_ascii(3, b"std");
|
||||||
pub(crate) const ITER: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"iter"));
|
pub(crate) const ITER: Name = Name::new_inline_ascii(4, b"iter");
|
||||||
pub(crate) const OPS: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"ops"));
|
pub(crate) const OPS: Name = Name::new_inline_ascii(3, b"ops");
|
||||||
pub(crate) const FUTURE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"future"));
|
pub(crate) const FUTURE: Name = Name::new_inline_ascii(6, b"future");
|
||||||
pub(crate) const RESULT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"result"));
|
pub(crate) const RESULT: Name = Name::new_inline_ascii(6, b"result");
|
||||||
pub(crate) const BOXED: Name = Name::new(SmolStr::new_inline_from_ascii(5, b"boxed"));
|
pub(crate) const BOXED: Name = Name::new_inline_ascii(5, b"boxed");
|
||||||
|
|
||||||
// Components of known path (type name)
|
// Components of known path (type name)
|
||||||
pub(crate) const INTO_ITERATOR_TYPE: Name =
|
pub(crate) const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator");
|
||||||
Name::new(SmolStr::new_inline_from_ascii(12, b"IntoIterator"));
|
pub(crate) const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item");
|
||||||
pub(crate) const ITEM_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"Item"));
|
pub(crate) const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try");
|
||||||
pub(crate) const TRY_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Try"));
|
pub(crate) const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok");
|
||||||
pub(crate) const OK_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok"));
|
pub(crate) const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future");
|
||||||
pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Future"));
|
pub(crate) const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
|
||||||
pub(crate) const RESULT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Result"));
|
pub(crate) const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
|
||||||
pub(crate) const OUTPUT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output"));
|
pub(crate) const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
|
||||||
pub(crate) const TARGET_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Target"));
|
pub(crate) const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
|
||||||
pub(crate) const BOX_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Box"));
|
|
||||||
|
|
||||||
fn resolve_name(text: &SmolStr) -> SmolStr {
|
|
||||||
let raw_start = "r#";
|
|
||||||
if text.as_str().starts_with(raw_start) {
|
|
||||||
SmolStr::new(&text[raw_start.len()..])
|
|
||||||
} else {
|
|
||||||
text.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -332,6 +332,20 @@ impl CrateDefMap {
|
||||||
) -> ResolvePathResult {
|
) -> ResolvePathResult {
|
||||||
let mut segments = path.segments.iter().enumerate();
|
let mut segments = path.segments.iter().enumerate();
|
||||||
let mut curr_per_ns: PerNs = match path.kind {
|
let mut curr_per_ns: PerNs = match path.kind {
|
||||||
|
PathKind::DollarCrate(krate) => {
|
||||||
|
if krate == self.krate {
|
||||||
|
tested_by!(macro_dollar_crate_self);
|
||||||
|
PerNs::types(Module { krate: self.krate, module_id: self.root }.into())
|
||||||
|
} else {
|
||||||
|
match krate.root_module(db) {
|
||||||
|
Some(module) => {
|
||||||
|
tested_by!(macro_dollar_crate_other);
|
||||||
|
PerNs::types(module.into())
|
||||||
|
}
|
||||||
|
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
PathKind::Crate => {
|
PathKind::Crate => {
|
||||||
PerNs::types(Module { krate: self.krate, module_id: self.root }.into())
|
PerNs::types(Module { krate: self.krate, module_id: self.root }.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -662,7 +662,10 @@ where
|
||||||
// Case 1: macro rules, define a macro in crate-global mutable scope
|
// Case 1: macro rules, define a macro in crate-global mutable scope
|
||||||
if is_macro_rules(&mac.path) {
|
if is_macro_rules(&mac.path) {
|
||||||
if let Some(name) = &mac.name {
|
if let Some(name) = &mac.name {
|
||||||
let macro_id = MacroDefId(mac.ast_id.with_file_id(self.file_id));
|
let macro_id = MacroDefId {
|
||||||
|
ast_id: mac.ast_id.with_file_id(self.file_id),
|
||||||
|
krate: self.def_collector.def_map.krate,
|
||||||
|
};
|
||||||
let macro_ = MacroDef { id: macro_id };
|
let macro_ = MacroDef { id: macro_id };
|
||||||
self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export);
|
self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{AstDatabase, DefDatabase},
|
db::{AstDatabase, DefDatabase},
|
||||||
AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path,
|
AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// `RawItems` is a set of top-level items in a file (except for impls).
|
/// `RawItems` is a set of top-level items in a file (except for impls).
|
||||||
|
@ -71,6 +71,8 @@ impl RawItems {
|
||||||
raw_items: RawItems::default(),
|
raw_items: RawItems::default(),
|
||||||
source_ast_id_map: db.ast_id_map(file_id),
|
source_ast_id_map: db.ast_id_map(file_id),
|
||||||
source_map: ImportSourceMap::default(),
|
source_map: ImportSourceMap::default(),
|
||||||
|
file_id,
|
||||||
|
db,
|
||||||
};
|
};
|
||||||
if let Some(node) = db.parse_or_expand(file_id) {
|
if let Some(node) = db.parse_or_expand(file_id) {
|
||||||
if let Some(source_file) = ast::SourceFile::cast(node.clone()) {
|
if let Some(source_file) = ast::SourceFile::cast(node.clone()) {
|
||||||
|
@ -192,13 +194,15 @@ pub(super) struct MacroData {
|
||||||
pub(super) export: bool,
|
pub(super) export: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RawItemsCollector {
|
struct RawItemsCollector<DB> {
|
||||||
raw_items: RawItems,
|
raw_items: RawItems,
|
||||||
source_ast_id_map: Arc<AstIdMap>,
|
source_ast_id_map: Arc<AstIdMap>,
|
||||||
source_map: ImportSourceMap,
|
source_map: ImportSourceMap,
|
||||||
|
file_id: HirFileId,
|
||||||
|
db: DB,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawItemsCollector {
|
impl<DB: AstDatabase> RawItemsCollector<&DB> {
|
||||||
fn process_module(&mut self, current_module: Option<Module>, body: impl ast::ModuleItemOwner) {
|
fn process_module(&mut self, current_module: Option<Module>, body: impl ast::ModuleItemOwner) {
|
||||||
for item_or_macro in body.items_with_macros() {
|
for item_or_macro in body.items_with_macros() {
|
||||||
match item_or_macro {
|
match item_or_macro {
|
||||||
|
@ -300,17 +304,21 @@ impl RawItemsCollector {
|
||||||
fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
|
fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
|
||||||
let is_prelude = use_item.has_atom_attr("prelude_import");
|
let is_prelude = use_item.has_atom_attr("prelude_import");
|
||||||
|
|
||||||
Path::expand_use_item(&use_item, |path, use_tree, is_glob, alias| {
|
Path::expand_use_item(
|
||||||
let import_data = ImportData {
|
Source { ast: use_item, file_id: self.file_id },
|
||||||
path,
|
self.db,
|
||||||
alias,
|
|path, use_tree, is_glob, alias| {
|
||||||
is_glob,
|
let import_data = ImportData {
|
||||||
is_prelude,
|
path,
|
||||||
is_extern_crate: false,
|
alias,
|
||||||
is_macro_use: false,
|
is_glob,
|
||||||
};
|
is_prelude,
|
||||||
self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree)));
|
is_extern_crate: false,
|
||||||
})
|
is_macro_use: false,
|
||||||
|
};
|
||||||
|
self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree)));
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_extern_crate_item(
|
fn add_extern_crate_item(
|
||||||
|
@ -335,7 +343,10 @@ impl RawItemsCollector {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) {
|
fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) {
|
||||||
let path = match m.path().and_then(Path::from_ast) {
|
let path = match m
|
||||||
|
.path()
|
||||||
|
.and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db))
|
||||||
|
{
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
|
@ -515,3 +515,108 @@ fn path_qualified_macros() {
|
||||||
⋮not_found: _
|
⋮not_found: _
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_dollar_crate_is_correct_in_item() {
|
||||||
|
covers!(macro_dollar_crate_self);
|
||||||
|
covers!(macro_dollar_crate_other);
|
||||||
|
let map = def_map_with_crate_graph(
|
||||||
|
"
|
||||||
|
//- /main.rs
|
||||||
|
#[macro_use]
|
||||||
|
extern crate foo;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod m {
|
||||||
|
macro_rules! current {
|
||||||
|
() => {
|
||||||
|
use $crate::Foo as FooSelf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
current!();
|
||||||
|
not_current1!();
|
||||||
|
foo::not_current2!();
|
||||||
|
|
||||||
|
//- /lib.rs
|
||||||
|
mod m {
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! not_current1 {
|
||||||
|
() => {
|
||||||
|
use $crate::Bar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! not_current2 {
|
||||||
|
() => {
|
||||||
|
use $crate::Baz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bar;
|
||||||
|
struct Baz;
|
||||||
|
",
|
||||||
|
crate_graph! {
|
||||||
|
"main": ("/main.rs", ["foo"]),
|
||||||
|
"foo": ("/lib.rs", []),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_snapshot!(map, @r###"
|
||||||
|
⋮crate
|
||||||
|
⋮Bar: t v
|
||||||
|
⋮Baz: t v
|
||||||
|
⋮Foo: t v
|
||||||
|
⋮FooSelf: t v
|
||||||
|
⋮foo: t
|
||||||
|
⋮m: t
|
||||||
|
⋮
|
||||||
|
⋮crate::m
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_dollar_crate_is_correct_in_indirect_deps() {
|
||||||
|
covers!(macro_dollar_crate_other);
|
||||||
|
// From std
|
||||||
|
let map = def_map_with_crate_graph(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
foo!();
|
||||||
|
|
||||||
|
//- /std.rs
|
||||||
|
#[prelude_import]
|
||||||
|
use self::prelude::*;
|
||||||
|
|
||||||
|
pub use core::foo;
|
||||||
|
|
||||||
|
mod prelude {}
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod std_macros;
|
||||||
|
|
||||||
|
//- /core.rs
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! foo {
|
||||||
|
() => {
|
||||||
|
use $crate::bar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct bar;
|
||||||
|
"#,
|
||||||
|
crate_graph! {
|
||||||
|
"main": ("/main.rs", ["std"]),
|
||||||
|
"std": ("/std.rs", ["core"]),
|
||||||
|
"core": ("/core.rs", []),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_snapshot!(map, @r###"
|
||||||
|
⋮crate
|
||||||
|
⋮bar: t v
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use ra_syntax::{
|
||||||
AstNode,
|
AstNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{name, type_ref::TypeRef, AsName, Name};
|
use crate::{db::AstDatabase, name, type_ref::TypeRef, AsName, Crate, Name, Source};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Path {
|
pub struct Path {
|
||||||
|
@ -52,16 +52,19 @@ pub enum PathKind {
|
||||||
Abs,
|
Abs,
|
||||||
// Type based path like `<T>::foo`
|
// Type based path like `<T>::foo`
|
||||||
Type(Box<TypeRef>),
|
Type(Box<TypeRef>),
|
||||||
|
// `$crate` from macro expansion
|
||||||
|
DollarCrate(Crate),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Path {
|
impl Path {
|
||||||
/// Calls `cb` with all paths, represented by this use item.
|
/// Calls `cb` with all paths, represented by this use item.
|
||||||
pub fn expand_use_item(
|
pub fn expand_use_item(
|
||||||
item: &ast::UseItem,
|
item_src: Source<ast::UseItem>,
|
||||||
|
db: &impl AstDatabase,
|
||||||
mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||||
) {
|
) {
|
||||||
if let Some(tree) = item.use_tree() {
|
if let Some(tree) = item_src.ast.use_tree() {
|
||||||
expand_use_tree(None, tree, &mut cb);
|
expand_use_tree(None, tree, &|| item_src.file_id.macro_crate(db), &mut cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +79,19 @@ impl Path {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||||
pub fn from_ast(mut path: ast::Path) -> Option<Path> {
|
/// DEPRECATED: It does not handle `$crate` from macro call.
|
||||||
|
pub fn from_ast(path: ast::Path) -> Option<Path> {
|
||||||
|
Path::parse(path, &|| None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||||
|
/// It correctly handles `$crate` based path from macro call.
|
||||||
|
pub fn from_src(source: Source<ast::Path>, db: &impl AstDatabase) -> Option<Path> {
|
||||||
|
let file_id = source.file_id;
|
||||||
|
Path::parse(source.ast, &|| file_id.macro_crate(db))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(mut path: ast::Path, macro_crate: &impl Fn() -> Option<Crate>) -> Option<Path> {
|
||||||
let mut kind = PathKind::Plain;
|
let mut kind = PathKind::Plain;
|
||||||
let mut segments = Vec::new();
|
let mut segments = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
|
@ -88,6 +103,13 @@ impl Path {
|
||||||
|
|
||||||
match segment.kind()? {
|
match segment.kind()? {
|
||||||
ast::PathSegmentKind::Name(name) => {
|
ast::PathSegmentKind::Name(name) => {
|
||||||
|
if name.text() == "$crate" {
|
||||||
|
if let Some(macro_crate) = macro_crate() {
|
||||||
|
kind = PathKind::DollarCrate(macro_crate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let args = segment
|
let args = segment
|
||||||
.type_arg_list()
|
.type_arg_list()
|
||||||
.and_then(GenericArgs::from_ast)
|
.and_then(GenericArgs::from_ast)
|
||||||
|
@ -113,7 +135,7 @@ impl Path {
|
||||||
}
|
}
|
||||||
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
||||||
Some(trait_ref) => {
|
Some(trait_ref) => {
|
||||||
let path = Path::from_ast(trait_ref.path()?)?;
|
let path = Path::parse(trait_ref.path()?, macro_crate)?;
|
||||||
kind = path.kind;
|
kind = path.kind;
|
||||||
let mut prefix_segments = path.segments;
|
let mut prefix_segments = path.segments;
|
||||||
prefix_segments.reverse();
|
prefix_segments.reverse();
|
||||||
|
@ -264,6 +286,7 @@ impl From<Name> for Path {
|
||||||
fn expand_use_tree(
|
fn expand_use_tree(
|
||||||
prefix: Option<Path>,
|
prefix: Option<Path>,
|
||||||
tree: ast::UseTree,
|
tree: ast::UseTree,
|
||||||
|
macro_crate: &impl Fn() -> Option<Crate>,
|
||||||
cb: &mut impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
cb: &mut impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||||
) {
|
) {
|
||||||
if let Some(use_tree_list) = tree.use_tree_list() {
|
if let Some(use_tree_list) = tree.use_tree_list() {
|
||||||
|
@ -272,13 +295,13 @@ fn expand_use_tree(
|
||||||
None => prefix,
|
None => prefix,
|
||||||
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
|
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
|
||||||
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
|
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
|
||||||
Some(path) => match convert_path(prefix, path) {
|
Some(path) => match convert_path(prefix, path, macro_crate) {
|
||||||
Some(it) => Some(it),
|
Some(it) => Some(it),
|
||||||
None => return, // FIXME: report errors somewhere
|
None => return, // FIXME: report errors somewhere
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
for child_tree in use_tree_list.use_trees() {
|
for child_tree in use_tree_list.use_trees() {
|
||||||
expand_use_tree(prefix.clone(), child_tree, cb);
|
expand_use_tree(prefix.clone(), child_tree, macro_crate, cb);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
|
let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
|
||||||
|
@ -295,7 +318,7 @@ fn expand_use_tree(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(path) = convert_path(prefix, ast_path) {
|
if let Some(path) = convert_path(prefix, ast_path, macro_crate) {
|
||||||
let is_glob = tree.has_star();
|
let is_glob = tree.has_star();
|
||||||
cb(path, &tree, is_glob, alias)
|
cb(path, &tree, is_glob, alias)
|
||||||
}
|
}
|
||||||
|
@ -305,12 +328,29 @@ fn expand_use_tree(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
|
fn convert_path(
|
||||||
let prefix =
|
prefix: Option<Path>,
|
||||||
if let Some(qual) = path.qualifier() { Some(convert_path(prefix, qual)?) } else { prefix };
|
path: ast::Path,
|
||||||
|
macro_crate: &impl Fn() -> Option<Crate>,
|
||||||
|
) -> Option<Path> {
|
||||||
|
let prefix = if let Some(qual) = path.qualifier() {
|
||||||
|
Some(convert_path(prefix, qual, macro_crate)?)
|
||||||
|
} else {
|
||||||
|
prefix
|
||||||
|
};
|
||||||
|
|
||||||
let segment = path.segment()?;
|
let segment = path.segment()?;
|
||||||
let res = match segment.kind()? {
|
let res = match segment.kind()? {
|
||||||
ast::PathSegmentKind::Name(name) => {
|
ast::PathSegmentKind::Name(name) => {
|
||||||
|
if name.text() == "$crate" {
|
||||||
|
if let Some(krate) = macro_crate() {
|
||||||
|
return Some(Path::from_simple_segments(
|
||||||
|
PathKind::DollarCrate(krate),
|
||||||
|
iter::empty(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// no type args in use
|
// no type args in use
|
||||||
let mut res = prefix
|
let mut res = prefix
|
||||||
.unwrap_or_else(|| Path { kind: PathKind::Plain, segments: Vec::with_capacity(1) });
|
.unwrap_or_else(|| Path { kind: PathKind::Plain, segments: Vec::with_capacity(1) });
|
||||||
|
|
|
@ -203,6 +203,7 @@ impl SourceAnalyzer {
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
macro_call: &ast::MacroCall,
|
macro_call: &ast::MacroCall,
|
||||||
) -> Option<MacroDef> {
|
) -> Option<MacroDef> {
|
||||||
|
// This must be a normal source file rather than macro file.
|
||||||
let path = macro_call.path().and_then(Path::from_ast)?;
|
let path = macro_call.path().and_then(Path::from_ast)?;
|
||||||
self.resolver.resolve_path_as_macro(db, &path)
|
self.resolver.resolve_path_as_macro(db, &path)
|
||||||
}
|
}
|
||||||
|
@ -261,6 +262,7 @@ impl SourceAnalyzer {
|
||||||
return Some(PathResolution::AssocItem(assoc));
|
return Some(PathResolution::AssocItem(assoc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// This must be a normal source file rather than macro file.
|
||||||
let hir_path = crate::Path::from_ast(path.clone())?;
|
let hir_path = crate::Path::from_ast(path.clone())?;
|
||||||
self.resolve_hir_path(db, &hir_path)
|
self.resolve_hir_path(db, &hir_path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -609,7 +609,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
|
|
||||||
for (i, &subpat) in subpats.iter().enumerate() {
|
for (i, &subpat) in subpats.iter().enumerate() {
|
||||||
let expected_ty = def
|
let expected_ty = def
|
||||||
.and_then(|d| d.field(self.db, &Name::tuple_field_name(i)))
|
.and_then(|d| d.field(self.db, &Name::new_tuple_field(i)))
|
||||||
.map_or(Ty::Unknown, |field| field.ty(self.db))
|
.map_or(Ty::Unknown, |field| field.ty(self.db))
|
||||||
.subst(&substs);
|
.subst(&substs);
|
||||||
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
||||||
|
@ -1374,10 +1374,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
)
|
)
|
||||||
.find_map(|derefed_ty| match canonicalized.decanonicalize_ty(derefed_ty.value) {
|
.find_map(|derefed_ty| match canonicalized.decanonicalize_ty(derefed_ty.value) {
|
||||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||||
TypeCtor::Tuple { .. } => {
|
TypeCtor::Tuple { .. } => name
|
||||||
let i = name.to_string().parse::<usize>().ok();
|
.as_tuple_index()
|
||||||
i.and_then(|i| a_ty.parameters.0.get(i).cloned())
|
.and_then(|idx| a_ty.parameters.0.get(idx).cloned()),
|
||||||
}
|
|
||||||
TypeCtor::Adt(Adt::Struct(s)) => s.field(self.db, name).map(|field| {
|
TypeCtor::Adt(Adt::Struct(s)) => s.field(self.db, name).map(|field| {
|
||||||
self.write_field_resolution(tgt_expr, field);
|
self.write_field_resolution(tgt_expr, field);
|
||||||
field.ty(self.db).subst(&a_ty.parameters)
|
field.ty(self.db).subst(&a_ty.parameters)
|
||||||
|
|
|
@ -3130,6 +3130,39 @@ fn test() { S.foo()<|>; }
|
||||||
assert_eq!(t, "u128");
|
assert_eq!(t, "u128");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_macro_with_dollar_crate_is_correct_in_expr() {
|
||||||
|
covers!(macro_dollar_crate_other);
|
||||||
|
let (mut db, pos) = MockDatabase::with_position(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
fn test() {
|
||||||
|
let x = (foo::foo!(1), foo::foo!(2));
|
||||||
|
x<|>;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /lib.rs
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! foo {
|
||||||
|
(1) => { $crate::bar!() };
|
||||||
|
(2) => { 1 + $crate::baz() };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bar {
|
||||||
|
() => { 42 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn baz() -> usize { 31usize }
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
db.set_crate_graph_from_fixture(crate_graph! {
|
||||||
|
"main": ("/main.rs", ["foo"]),
|
||||||
|
"foo": ("/lib.rs", []),
|
||||||
|
});
|
||||||
|
assert_eq!("(i32, usize)", type_at_pos(&db, pos));
|
||||||
|
}
|
||||||
|
|
||||||
#[ignore]
|
#[ignore]
|
||||||
#[test]
|
#[test]
|
||||||
fn method_resolution_trait_before_autoref() {
|
fn method_resolution_trait_before_autoref() {
|
||||||
|
|
|
@ -72,6 +72,7 @@ impl TypeRef {
|
||||||
}
|
}
|
||||||
ast::TypeRef::NeverType(..) => TypeRef::Never,
|
ast::TypeRef::NeverType(..) => TypeRef::Never,
|
||||||
ast::TypeRef::PathType(inner) => {
|
ast::TypeRef::PathType(inner) => {
|
||||||
|
// FIXME: Use `Path::from_src`
|
||||||
inner.path().and_then(Path::from_ast).map(TypeRef::Path).unwrap_or(TypeRef::Error)
|
inner.path().and_then(Path::from_ast).map(TypeRef::Path).unwrap_or(TypeRef::Error)
|
||||||
}
|
}
|
||||||
ast::TypeRef::PointerType(inner) => {
|
ast::TypeRef::PointerType(inner) => {
|
||||||
|
@ -141,6 +142,7 @@ impl TypeBound {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => return TypeBound::Error,
|
None => return TypeBound::Error,
|
||||||
};
|
};
|
||||||
|
// FIXME: Use `Path::from_src`
|
||||||
let path = match Path::from_ast(path) {
|
let path = match Path::from_ast(path) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => return TypeBound::Error,
|
None => return TypeBound::Error,
|
||||||
|
|
|
@ -86,7 +86,7 @@ fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> Result<tt::Sub
|
||||||
|
|
||||||
fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> Result<Fragment, ExpandError> {
|
fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> Result<Fragment, ExpandError> {
|
||||||
let res = if v == "crate" {
|
let res = if v == "crate" {
|
||||||
// FIXME: Properly handle $crate token
|
// We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
|
||||||
let tt =
|
let tt =
|
||||||
tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() })
|
tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() })
|
||||||
.into();
|
.into();
|
||||||
|
|
|
@ -21,6 +21,16 @@ impl ast::NameRef {
|
||||||
pub fn text(&self) -> &SmolStr {
|
pub fn text(&self) -> &SmolStr {
|
||||||
text_of_first_token(self.syntax())
|
text_of_first_token(self.syntax())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_tuple_field(&self) -> Option<usize> {
|
||||||
|
self.syntax().children_with_tokens().find_map(|c| {
|
||||||
|
if c.kind() == SyntaxKind::INT_NUMBER {
|
||||||
|
c.as_token().and_then(|tok| tok.text().as_str().parse().ok())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn text_of_first_token(node: &SyntaxNode) -> &SmolStr {
|
fn text_of_first_token(node: &SyntaxNode) -> &SmolStr {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue