mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-23 19:43:23 +00:00
move raw_items to hir_def
This commit is contained in:
parent
f996b6019b
commit
16e620c052
26 changed files with 1059 additions and 998 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -1019,11 +1019,18 @@ name = "ra_hir_def"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_arena 0.1.0",
|
||||
"ra_cfg 0.1.0",
|
||||
"ra_db 0.1.0",
|
||||
"ra_hir_expand 0.1.0",
|
||||
"ra_mbe 0.1.0",
|
||||
"ra_prof 0.1.0",
|
||||
"ra_syntax 0.1.0",
|
||||
"ra_tt 0.1.0",
|
||||
"relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"test_utils 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -1,90 +1 @@
|
|||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use mbe::ast_to_token_tree;
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, AttrsOwner},
|
||||
SmolStr,
|
||||
};
|
||||
use tt::Subtree;
|
||||
|
||||
use crate::{db::AstDatabase, path::Path, HirFileId, Source};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct Attr {
|
||||
pub(crate) path: Path,
|
||||
pub(crate) input: Option<AttrInput>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AttrInput {
|
||||
Literal(SmolStr),
|
||||
TokenTree(Subtree),
|
||||
}
|
||||
|
||||
impl Attr {
|
||||
pub(crate) fn from_src(
|
||||
Source { file_id, ast }: Source<ast::Attr>,
|
||||
db: &impl AstDatabase,
|
||||
) -> Option<Attr> {
|
||||
let path = Path::from_src(Source { file_id, ast: ast.path()? }, db)?;
|
||||
let input = match ast.input() {
|
||||
None => None,
|
||||
Some(ast::AttrInput::Literal(lit)) => {
|
||||
// FIXME: escape? raw string?
|
||||
let value = lit.syntax().first_token()?.text().trim_matches('"').into();
|
||||
Some(AttrInput::Literal(value))
|
||||
}
|
||||
Some(ast::AttrInput::TokenTree(tt)) => {
|
||||
Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0))
|
||||
}
|
||||
};
|
||||
|
||||
Some(Attr { path, input })
|
||||
}
|
||||
|
||||
pub(crate) fn from_attrs_owner(
|
||||
file_id: HirFileId,
|
||||
owner: &dyn AttrsOwner,
|
||||
db: &impl AstDatabase,
|
||||
) -> Option<Arc<[Attr]>> {
|
||||
let mut attrs = owner.attrs().peekable();
|
||||
if attrs.peek().is_none() {
|
||||
// Avoid heap allocation
|
||||
return None;
|
||||
}
|
||||
Some(attrs.flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect())
|
||||
}
|
||||
|
||||
pub(crate) fn is_simple_atom(&self, name: &str) -> bool {
|
||||
// FIXME: Avoid cloning
|
||||
self.path.as_ident().map_or(false, |s| s.to_string() == name)
|
||||
}
|
||||
|
||||
// FIXME: handle cfg_attr :-)
|
||||
pub(crate) fn as_cfg(&self) -> Option<&Subtree> {
|
||||
if !self.is_simple_atom("cfg") {
|
||||
return None;
|
||||
}
|
||||
match &self.input {
|
||||
Some(AttrInput::TokenTree(subtree)) => Some(subtree),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_path(&self) -> Option<&SmolStr> {
|
||||
if !self.is_simple_atom("path") {
|
||||
return None;
|
||||
}
|
||||
match &self.input {
|
||||
Some(AttrInput::Literal(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> {
|
||||
cfg_options.is_cfg_enabled(self.as_cfg()?)
|
||||
}
|
||||
}
|
||||
pub use hir_def::attr::*;
|
||||
|
|
|
@ -6,7 +6,7 @@ pub(crate) mod docs;
|
|||
use std::sync::Arc;
|
||||
|
||||
use hir_def::{CrateModuleId, ModuleId};
|
||||
use ra_db::{CrateId, Edition, FileId};
|
||||
use ra_db::{CrateId, Edition};
|
||||
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
|
||||
|
||||
use crate::{
|
||||
|
@ -33,7 +33,7 @@ use crate::{
|
|||
},
|
||||
type_ref::Mutability,
|
||||
type_ref::TypeRef,
|
||||
AsName, AstId, Either, HasSource, Name, Ty,
|
||||
AsName, Either, HasSource, Name, Ty,
|
||||
};
|
||||
|
||||
/// hir::Crate describes a single crate. It's the main interface with which
|
||||
|
@ -147,31 +147,7 @@ impl_froms!(
|
|||
BuiltinType
|
||||
);
|
||||
|
||||
pub enum ModuleSource {
|
||||
SourceFile(ast::SourceFile),
|
||||
Module(ast::Module),
|
||||
}
|
||||
|
||||
impl ModuleSource {
|
||||
pub(crate) fn new(
|
||||
db: &(impl DefDatabase + AstDatabase),
|
||||
file_id: Option<FileId>,
|
||||
decl_id: Option<AstId<ast::Module>>,
|
||||
) -> ModuleSource {
|
||||
match (file_id, decl_id) {
|
||||
(Some(file_id), _) => {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
(None, Some(item_id)) => {
|
||||
let module = item_id.to_node(db);
|
||||
assert!(module.item_list().is_some(), "expected inline module");
|
||||
ModuleSource::Module(module)
|
||||
}
|
||||
(None, None) => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use hir_def::ModuleSource;
|
||||
|
||||
impl Module {
|
||||
pub(crate) fn new(krate: Crate, crate_module_id: CrateModuleId) -> Module {
|
||||
|
|
|
@ -12,15 +12,15 @@ use crate::{
|
|||
ids,
|
||||
impl_block::{ImplBlock, ImplSourceMap, ModuleImplBlocks},
|
||||
lang_item::{LangItemTarget, LangItems},
|
||||
nameres::{CrateDefMap, ImportSourceMap, Namespace, RawItems},
|
||||
nameres::{CrateDefMap, Namespace},
|
||||
traits::TraitData,
|
||||
ty::{
|
||||
method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate,
|
||||
InferenceResult, Substs, Ty, TypableDef, TypeCtor,
|
||||
},
|
||||
type_alias::TypeAliasData,
|
||||
Const, ConstData, Crate, DefWithBody, Enum, ExprScopes, FnData, Function, HirFileId, Module,
|
||||
Static, Struct, StructField, Trait, TypeAlias,
|
||||
Const, ConstData, Crate, DefWithBody, Enum, ExprScopes, FnData, Function, Module, Static,
|
||||
Struct, StructField, Trait, TypeAlias,
|
||||
};
|
||||
|
||||
pub use hir_def::db::{InternDatabase, InternDatabaseStorage};
|
||||
|
@ -32,7 +32,7 @@ pub use hir_expand::db::{
|
|||
// This database uses `AstDatabase` internally,
|
||||
#[salsa::query_group(DefDatabaseStorage)]
|
||||
#[salsa::requires(AstDatabase)]
|
||||
pub trait DefDatabase: InternDatabase + HirDebugDatabase + AstDatabase {
|
||||
pub trait DefDatabase: HirDebugDatabase + hir_def::db::DefDatabase2 {
|
||||
#[salsa::invoke(crate::adt::StructData::struct_data_query)]
|
||||
fn struct_data(&self, s: Struct) -> Arc<StructData>;
|
||||
|
||||
|
@ -45,15 +45,6 @@ pub trait DefDatabase: InternDatabase + HirDebugDatabase + AstDatabase {
|
|||
#[salsa::invoke(crate::traits::TraitItemsIndex::trait_items_index)]
|
||||
fn trait_items_index(&self, module: Module) -> crate::traits::TraitItemsIndex;
|
||||
|
||||
#[salsa::invoke(RawItems::raw_items_with_source_map_query)]
|
||||
fn raw_items_with_source_map(
|
||||
&self,
|
||||
file_id: HirFileId,
|
||||
) -> (Arc<RawItems>, Arc<ImportSourceMap>);
|
||||
|
||||
#[salsa::invoke(RawItems::raw_items_query)]
|
||||
fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
|
||||
|
||||
#[salsa::invoke(CrateDefMap::crate_def_map_query)]
|
||||
fn crate_def_map(&self, krate: Crate) -> Arc<CrateDefMap>;
|
||||
|
||||
|
|
|
@ -1,54 +1 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Either<A, B> {
|
||||
A(A),
|
||||
B(B),
|
||||
}
|
||||
|
||||
impl<A, B> Either<A, B> {
|
||||
pub fn either<R, F1, F2>(self, f1: F1, f2: F2) -> R
|
||||
where
|
||||
F1: FnOnce(A) -> R,
|
||||
F2: FnOnce(B) -> R,
|
||||
{
|
||||
match self {
|
||||
Either::A(a) => f1(a),
|
||||
Either::B(b) => f2(b),
|
||||
}
|
||||
}
|
||||
pub fn map<U, V, F1, F2>(self, f1: F1, f2: F2) -> Either<U, V>
|
||||
where
|
||||
F1: FnOnce(A) -> U,
|
||||
F2: FnOnce(B) -> V,
|
||||
{
|
||||
match self {
|
||||
Either::A(a) => Either::A(f1(a)),
|
||||
Either::B(b) => Either::B(f2(b)),
|
||||
}
|
||||
}
|
||||
pub fn map_a<U, F>(self, f: F) -> Either<U, B>
|
||||
where
|
||||
F: FnOnce(A) -> U,
|
||||
{
|
||||
self.map(f, |it| it)
|
||||
}
|
||||
pub fn a(self) -> Option<A> {
|
||||
match self {
|
||||
Either::A(it) => Some(it),
|
||||
Either::B(_) => None,
|
||||
}
|
||||
}
|
||||
pub fn b(self) -> Option<B> {
|
||||
match self {
|
||||
Either::A(_) => None,
|
||||
Either::B(it) => Some(it),
|
||||
}
|
||||
}
|
||||
pub fn as_ref(&self) -> Either<&A, &B> {
|
||||
match self {
|
||||
Either::A(it) => Either::A(it),
|
||||
Either::B(it) => Either::B(it),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use hir_def::either::*;
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use ra_db::{FileId, FilePosition};
|
||||
use ra_syntax::{
|
||||
algo::find_node_at_offset,
|
||||
ast::{self, AstNode, NameOwner},
|
||||
SyntaxNode,
|
||||
};
|
||||
use ra_syntax::ast::{self, AstNode, NameOwner};
|
||||
|
||||
use crate::{
|
||||
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||
|
@ -129,41 +124,6 @@ impl FromSource for StructField {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: simplify it
|
||||
impl ModuleSource {
|
||||
pub fn from_position(
|
||||
db: &(impl DefDatabase + AstDatabase),
|
||||
position: FilePosition,
|
||||
) -> ModuleSource {
|
||||
let parse = db.parse(position.file_id);
|
||||
match &find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset) {
|
||||
Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()),
|
||||
_ => {
|
||||
let source_file = parse.tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_child_node(
|
||||
db: &(impl DefDatabase + AstDatabase),
|
||||
file_id: FileId,
|
||||
child: &SyntaxNode,
|
||||
) -> ModuleSource {
|
||||
if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) {
|
||||
ModuleSource::Module(m)
|
||||
} else {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file_id(db: &(impl DefDatabase + AstDatabase), file_id: FileId) -> ModuleSource {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn from_declaration(db: &impl HirDatabase, src: Source<ast::Module>) -> Option<Self> {
|
||||
let src_parent = Source {
|
||||
|
|
|
@ -59,10 +59,7 @@ pub mod from_source;
|
|||
#[cfg(test)]
|
||||
mod marks;
|
||||
|
||||
use hir_expand::{
|
||||
ast_id_map::{AstIdMap, FileAstId},
|
||||
AstId,
|
||||
};
|
||||
use hir_expand::AstId;
|
||||
|
||||
use crate::{ids::MacroFileKind, name::AsName, resolve::Resolver};
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
test_utils::marks!(
|
||||
bogus_paths
|
||||
// FIXME: restore this mark once hir is split
|
||||
name_res_works_for_broken_modules
|
||||
can_import_enum_variant
|
||||
type_var_cycles_resolve_completely
|
||||
|
|
|
@ -17,6 +17,7 @@ use crate::{db, debug::HirDebugHelper, diagnostics::DiagnosticSink};
|
|||
pub const WORKSPACE: SourceRootId = SourceRootId(0);
|
||||
|
||||
#[salsa::database(
|
||||
hir_def::db::DefDatabase2Storage,
|
||||
ra_db::SourceDatabaseExtStorage,
|
||||
ra_db::SourceDatabaseStorage,
|
||||
db::InternDatabaseStorage,
|
||||
|
|
|
@ -1,142 +1 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use ra_syntax::{ast, SmolStr};
|
||||
|
||||
/// `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
|
||||
/// not there yet!
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct Name(Repr);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
enum Repr {
|
||||
Text(SmolStr),
|
||||
TupleField(usize),
|
||||
}
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.0 {
|
||||
Repr::Text(text) => fmt::Display::fmt(&text, f),
|
||||
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Name {
|
||||
/// Note: this is private to make creating name from random string hard.
|
||||
/// Hopefully, this should allow us to integrate hygiene cleaner in the
|
||||
/// future, and to switch to interned representation of names.
|
||||
const fn new_text(text: SmolStr) -> Name {
|
||||
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 {
|
||||
Name::new_text("[missing name]".into())
|
||||
}
|
||||
|
||||
pub(crate) fn as_tuple_index(&self) -> Option<usize> {
|
||||
match self.0 {
|
||||
Repr::TupleField(idx) => Some(idx),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait AsName {
|
||||
fn as_name(&self) -> Name;
|
||||
}
|
||||
|
||||
impl AsName for ast::NameRef {
|
||||
fn as_name(&self) -> Name {
|
||||
match self.as_tuple_field() {
|
||||
Some(idx) => Name::new_tuple_field(idx),
|
||||
None => Name::resolve(self.text()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsName for ast::Name {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::resolve(self.text())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsName for ast::FieldKind {
|
||||
fn as_name(&self) -> Name {
|
||||
match self {
|
||||
ast::FieldKind::Name(nr) => nr.as_name(),
|
||||
ast::FieldKind::Index(idx) => Name::new_tuple_field(idx.text().parse().unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsName for ra_db::Dependency {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::new_text(self.name.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// Primitives
|
||||
pub(crate) const ISIZE: Name = Name::new_inline_ascii(5, b"isize");
|
||||
pub(crate) const I8: Name = Name::new_inline_ascii(2, b"i8");
|
||||
pub(crate) const I16: Name = Name::new_inline_ascii(3, b"i16");
|
||||
pub(crate) const I32: Name = Name::new_inline_ascii(3, b"i32");
|
||||
pub(crate) const I64: Name = Name::new_inline_ascii(3, b"i64");
|
||||
pub(crate) const I128: Name = Name::new_inline_ascii(4, b"i128");
|
||||
pub(crate) const USIZE: Name = Name::new_inline_ascii(5, b"usize");
|
||||
pub(crate) const U8: Name = Name::new_inline_ascii(2, b"u8");
|
||||
pub(crate) const U16: Name = Name::new_inline_ascii(3, b"u16");
|
||||
pub(crate) const U32: Name = Name::new_inline_ascii(3, b"u32");
|
||||
pub(crate) const U64: Name = Name::new_inline_ascii(3, b"u64");
|
||||
pub(crate) const U128: Name = Name::new_inline_ascii(4, b"u128");
|
||||
pub(crate) const F32: Name = Name::new_inline_ascii(3, b"f32");
|
||||
pub(crate) const F64: Name = Name::new_inline_ascii(3, b"f64");
|
||||
pub(crate) const BOOL: Name = Name::new_inline_ascii(4, b"bool");
|
||||
pub(crate) const CHAR: Name = Name::new_inline_ascii(4, b"char");
|
||||
pub(crate) const STR: Name = Name::new_inline_ascii(3, b"str");
|
||||
|
||||
// Special names
|
||||
pub(crate) const SELF_PARAM: Name = Name::new_inline_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_inline_ascii(11, b"macro_rules");
|
||||
|
||||
// Components of known path (value or mod name)
|
||||
pub(crate) const STD: Name = Name::new_inline_ascii(3, b"std");
|
||||
pub(crate) const ITER: Name = Name::new_inline_ascii(4, b"iter");
|
||||
pub(crate) const OPS: Name = Name::new_inline_ascii(3, b"ops");
|
||||
pub(crate) const FUTURE: Name = Name::new_inline_ascii(6, b"future");
|
||||
pub(crate) const RESULT: Name = Name::new_inline_ascii(6, b"result");
|
||||
pub(crate) const BOXED: Name = Name::new_inline_ascii(5, b"boxed");
|
||||
|
||||
// Components of known path (type name)
|
||||
pub(crate) const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator");
|
||||
pub(crate) const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item");
|
||||
pub(crate) const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try");
|
||||
pub(crate) const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok");
|
||||
pub(crate) const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future");
|
||||
pub(crate) const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
|
||||
pub(crate) const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
|
||||
pub(crate) const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
|
||||
pub(crate) const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
|
||||
pub use hir_def::name::*;
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
//! on the result
|
||||
|
||||
mod per_ns;
|
||||
mod raw;
|
||||
mod collector;
|
||||
mod mod_resolution;
|
||||
#[cfg(test)]
|
||||
|
@ -74,12 +73,9 @@ use crate::{
|
|||
Trait,
|
||||
};
|
||||
|
||||
pub(crate) use self::raw::{ImportSourceMap, RawItems};
|
||||
pub use self::per_ns::{Namespace, PerNs};
|
||||
|
||||
pub use self::{
|
||||
per_ns::{Namespace, PerNs},
|
||||
raw::ImportId,
|
||||
};
|
||||
pub use hir_def::nameres::raw::ImportId;
|
||||
|
||||
/// Contains all top-level defs from a macro-expanded crate
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
@ -328,7 +324,8 @@ impl CrateDefMap {
|
|||
) -> ResolvePathResult {
|
||||
let mut segments = path.segments.iter().enumerate();
|
||||
let mut curr_per_ns: PerNs = match path.kind {
|
||||
PathKind::DollarCrate(krate) => {
|
||||
PathKind::DollarCrate(crate_id) => {
|
||||
let krate = Crate { crate_id };
|
||||
if krate == self.krate {
|
||||
tested_by!(macro_dollar_crate_self);
|
||||
PerNs::types(Module::new(self.krate, self.root).into())
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use hir_def::nameres::raw;
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_db::FileId;
|
||||
use ra_syntax::{ast, SmolStr};
|
||||
|
@ -12,7 +13,7 @@ use crate::{
|
|||
ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind},
|
||||
name::MACRO_RULES,
|
||||
nameres::{
|
||||
diagnostics::DefDiagnostic, mod_resolution::ModDir, raw, Crate, CrateDefMap, CrateModuleId,
|
||||
diagnostics::DefDiagnostic, mod_resolution::ModDir, Crate, CrateDefMap, CrateModuleId,
|
||||
ModuleData, ModuleDef, PerNs, ReachedFixedPoint, Resolution, ResolveMode,
|
||||
},
|
||||
Adt, AstId, Const, Enum, Function, HirFileId, MacroDef, Module, Name, Path, PathKind, Static,
|
||||
|
|
|
@ -2,7 +2,7 @@ use super::*;
|
|||
|
||||
#[test]
|
||||
fn name_res_works_for_broken_modules() {
|
||||
covers!(name_res_works_for_broken_modules);
|
||||
// covers!(name_res_works_for_broken_modules);
|
||||
let map = def_map(
|
||||
"
|
||||
//- /lib.rs
|
||||
|
|
|
@ -1,422 +1 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use std::{iter, sync::Arc};
|
||||
|
||||
use ra_syntax::{
|
||||
ast::{self, NameOwner, TypeAscriptionOwner},
|
||||
AstNode,
|
||||
};
|
||||
|
||||
use crate::{db::AstDatabase, name, type_ref::TypeRef, AsName, Crate, Name, Source};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Path {
|
||||
pub kind: PathKind,
|
||||
pub segments: Vec<PathSegment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct PathSegment {
|
||||
pub name: Name,
|
||||
pub args_and_bindings: Option<Arc<GenericArgs>>,
|
||||
}
|
||||
|
||||
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
|
||||
/// can (in the future) also include bindings of associated types, like in
|
||||
/// `Iterator<Item = Foo>`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct GenericArgs {
|
||||
pub args: Vec<GenericArg>,
|
||||
/// This specifies whether the args contain a Self type as the first
|
||||
/// element. This is the case for path segments like `<T as Trait>`, where
|
||||
/// `T` is actually a type parameter for the path `Trait` specifying the
|
||||
/// Self type. Otherwise, when we have a path `Trait<X, Y>`, the Self type
|
||||
/// is left out.
|
||||
pub has_self_type: bool,
|
||||
/// Associated type bindings like in `Iterator<Item = T>`.
|
||||
pub bindings: Vec<(Name, TypeRef)>,
|
||||
}
|
||||
|
||||
/// A single generic argument.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum GenericArg {
|
||||
Type(TypeRef),
|
||||
// or lifetime...
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum PathKind {
|
||||
Plain,
|
||||
Self_,
|
||||
Super,
|
||||
Crate,
|
||||
// Absolute path
|
||||
Abs,
|
||||
// Type based path like `<T>::foo`
|
||||
Type(Box<TypeRef>),
|
||||
// `$crate` from macro expansion
|
||||
DollarCrate(Crate),
|
||||
}
|
||||
|
||||
impl Path {
|
||||
/// Calls `cb` with all paths, represented by this use item.
|
||||
pub fn expand_use_item(
|
||||
item_src: Source<ast::UseItem>,
|
||||
db: &impl AstDatabase,
|
||||
mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||
) {
|
||||
if let Some(tree) = item_src.ast.use_tree() {
|
||||
expand_use_tree(
|
||||
None,
|
||||
tree,
|
||||
&|| item_src.file_id.macro_crate(db).map(|crate_id| Crate { crate_id }),
|
||||
&mut cb,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_simple_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> Path {
|
||||
Path {
|
||||
kind,
|
||||
segments: segments
|
||||
.into_iter()
|
||||
.map(|name| PathSegment { name, args_and_bindings: None })
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||
/// 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).map(|crate_id| Crate { crate_id }))
|
||||
}
|
||||
|
||||
fn parse(mut path: ast::Path, macro_crate: &impl Fn() -> Option<Crate>) -> Option<Path> {
|
||||
let mut kind = PathKind::Plain;
|
||||
let mut segments = Vec::new();
|
||||
loop {
|
||||
let segment = path.segment()?;
|
||||
|
||||
if segment.has_colon_colon() {
|
||||
kind = PathKind::Abs;
|
||||
}
|
||||
|
||||
match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name) => {
|
||||
if name.text() == "$crate" {
|
||||
if let Some(macro_crate) = macro_crate() {
|
||||
kind = PathKind::DollarCrate(macro_crate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let args = segment
|
||||
.type_arg_list()
|
||||
.and_then(GenericArgs::from_ast)
|
||||
.or_else(|| {
|
||||
GenericArgs::from_fn_like_path_ast(
|
||||
segment.param_list(),
|
||||
segment.ret_type(),
|
||||
)
|
||||
})
|
||||
.map(Arc::new);
|
||||
let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
|
||||
segments.push(segment);
|
||||
}
|
||||
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
|
||||
assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||
|
||||
let self_type = TypeRef::from_ast(type_ref?);
|
||||
|
||||
match trait_ref {
|
||||
// <T>::foo
|
||||
None => {
|
||||
kind = PathKind::Type(Box::new(self_type));
|
||||
}
|
||||
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
||||
Some(trait_ref) => {
|
||||
let path = Path::parse(trait_ref.path()?, macro_crate)?;
|
||||
kind = path.kind;
|
||||
let mut prefix_segments = path.segments;
|
||||
prefix_segments.reverse();
|
||||
segments.extend(prefix_segments);
|
||||
// Insert the type reference (T in the above example) as Self parameter for the trait
|
||||
let mut last_segment = segments.last_mut()?;
|
||||
if last_segment.args_and_bindings.is_none() {
|
||||
last_segment.args_and_bindings =
|
||||
Some(Arc::new(GenericArgs::empty()));
|
||||
};
|
||||
let args = last_segment.args_and_bindings.as_mut().unwrap();
|
||||
let mut args_inner = Arc::make_mut(args);
|
||||
args_inner.has_self_type = true;
|
||||
args_inner.args.insert(0, GenericArg::Type(self_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::PathSegmentKind::CrateKw => {
|
||||
kind = PathKind::Crate;
|
||||
break;
|
||||
}
|
||||
ast::PathSegmentKind::SelfKw => {
|
||||
kind = PathKind::Self_;
|
||||
break;
|
||||
}
|
||||
ast::PathSegmentKind::SuperKw => {
|
||||
kind = PathKind::Super;
|
||||
break;
|
||||
}
|
||||
}
|
||||
path = match qualifier(&path) {
|
||||
Some(it) => it,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
segments.reverse();
|
||||
return Some(Path { kind, segments });
|
||||
|
||||
fn qualifier(path: &ast::Path) -> Option<ast::Path> {
|
||||
if let Some(q) = path.qualifier() {
|
||||
return Some(q);
|
||||
}
|
||||
// FIXME: this bottom up traversal is not too precise.
|
||||
// Should we handle do a top-down analysis, recording results?
|
||||
let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
|
||||
let use_tree = use_tree_list.parent_use_tree();
|
||||
use_tree.path()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts an `ast::NameRef` into a single-identifier `Path`.
|
||||
pub fn from_name_ref(name_ref: &ast::NameRef) -> Path {
|
||||
name_ref.as_name().into()
|
||||
}
|
||||
|
||||
/// `true` is this path is a single identifier, like `foo`
|
||||
pub fn is_ident(&self) -> bool {
|
||||
self.kind == PathKind::Plain && self.segments.len() == 1
|
||||
}
|
||||
|
||||
/// `true` if this path is just a standalone `self`
|
||||
pub fn is_self(&self) -> bool {
|
||||
self.kind == PathKind::Self_ && self.segments.is_empty()
|
||||
}
|
||||
|
||||
/// If this path is a single identifier, like `foo`, return its name.
|
||||
pub fn as_ident(&self) -> Option<&Name> {
|
||||
if self.kind != PathKind::Plain || self.segments.len() > 1 {
|
||||
return None;
|
||||
}
|
||||
self.segments.first().map(|s| &s.name)
|
||||
}
|
||||
|
||||
pub fn expand_macro_expr(&self) -> Option<Name> {
|
||||
self.as_ident().and_then(|name| Some(name.clone()))
|
||||
}
|
||||
|
||||
pub fn is_type_relative(&self) -> bool {
|
||||
match self.kind {
|
||||
PathKind::Type(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericArgs {
|
||||
pub(crate) fn from_ast(node: ast::TypeArgList) -> Option<GenericArgs> {
|
||||
let mut args = Vec::new();
|
||||
for type_arg in node.type_args() {
|
||||
let type_ref = TypeRef::from_ast_opt(type_arg.type_ref());
|
||||
args.push(GenericArg::Type(type_ref));
|
||||
}
|
||||
// lifetimes ignored for now
|
||||
let mut bindings = Vec::new();
|
||||
for assoc_type_arg in node.assoc_type_args() {
|
||||
if let Some(name_ref) = assoc_type_arg.name_ref() {
|
||||
let name = name_ref.as_name();
|
||||
let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref());
|
||||
bindings.push((name, type_ref));
|
||||
}
|
||||
}
|
||||
if args.is_empty() && bindings.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(GenericArgs { args, has_self_type: false, bindings })
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
|
||||
/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
|
||||
pub(crate) fn from_fn_like_path_ast(
|
||||
params: Option<ast::ParamList>,
|
||||
ret_type: Option<ast::RetType>,
|
||||
) -> Option<GenericArgs> {
|
||||
let mut args = Vec::new();
|
||||
let mut bindings = Vec::new();
|
||||
if let Some(params) = params {
|
||||
let mut param_types = Vec::new();
|
||||
for param in params.params() {
|
||||
let type_ref = TypeRef::from_ast_opt(param.ascribed_type());
|
||||
param_types.push(type_ref);
|
||||
}
|
||||
let arg = GenericArg::Type(TypeRef::Tuple(param_types));
|
||||
args.push(arg);
|
||||
}
|
||||
if let Some(ret_type) = ret_type {
|
||||
let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
|
||||
bindings.push((name::OUTPUT_TYPE, type_ref))
|
||||
}
|
||||
if args.is_empty() && bindings.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(GenericArgs { args, has_self_type: false, bindings })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn empty() -> GenericArgs {
|
||||
GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Name> for Path {
|
||||
fn from(name: Name) -> Path {
|
||||
Path::from_simple_segments(PathKind::Plain, iter::once(name))
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_use_tree(
|
||||
prefix: Option<Path>,
|
||||
tree: ast::UseTree,
|
||||
macro_crate: &impl Fn() -> Option<Crate>,
|
||||
cb: &mut impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||
) {
|
||||
if let Some(use_tree_list) = tree.use_tree_list() {
|
||||
let prefix = match tree.path() {
|
||||
// E.g. use something::{{{inner}}};
|
||||
None => prefix,
|
||||
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
|
||||
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
|
||||
Some(path) => match convert_path(prefix, path, macro_crate) {
|
||||
Some(it) => Some(it),
|
||||
None => return, // FIXME: report errors somewhere
|
||||
},
|
||||
};
|
||||
for child_tree in use_tree_list.use_trees() {
|
||||
expand_use_tree(prefix.clone(), child_tree, macro_crate, cb);
|
||||
}
|
||||
} else {
|
||||
let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
|
||||
if let Some(ast_path) = tree.path() {
|
||||
// Handle self in a path.
|
||||
// E.g. `use something::{self, <...>}`
|
||||
if ast_path.qualifier().is_none() {
|
||||
if let Some(segment) = ast_path.segment() {
|
||||
if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
|
||||
if let Some(prefix) = prefix {
|
||||
cb(prefix, &tree, false, alias);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(path) = convert_path(prefix, ast_path, macro_crate) {
|
||||
let is_glob = tree.has_star();
|
||||
cb(path, &tree, is_glob, alias)
|
||||
}
|
||||
// FIXME: report errors somewhere
|
||||
// We get here if we do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_path(
|
||||
prefix: Option<Path>,
|
||||
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 res = match segment.kind()? {
|
||||
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
|
||||
let mut res = prefix
|
||||
.unwrap_or_else(|| Path { kind: PathKind::Plain, segments: Vec::with_capacity(1) });
|
||||
res.segments.push(PathSegment {
|
||||
name: name.as_name(),
|
||||
args_and_bindings: None, // no type args in use
|
||||
});
|
||||
res
|
||||
}
|
||||
ast::PathSegmentKind::CrateKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
Path::from_simple_segments(PathKind::Crate, iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::SelfKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
Path::from_simple_segments(PathKind::Self_, iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::SuperKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
Path::from_simple_segments(PathKind::Super, iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::Type { .. } => {
|
||||
// not allowed in imports
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub mod known {
|
||||
use super::{Path, PathKind};
|
||||
use crate::name;
|
||||
|
||||
pub fn std_iter_into_iterator() -> Path {
|
||||
Path::from_simple_segments(
|
||||
PathKind::Abs,
|
||||
vec![name::STD, name::ITER, name::INTO_ITERATOR_TYPE],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn std_ops_try() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY_TYPE])
|
||||
}
|
||||
|
||||
pub fn std_result_result() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE])
|
||||
}
|
||||
|
||||
pub fn std_future_future() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::FUTURE, name::FUTURE_TYPE])
|
||||
}
|
||||
|
||||
pub fn std_boxed_box() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::BOXED, name::BOX_TYPE])
|
||||
}
|
||||
}
|
||||
pub use hir_def::path::*;
|
||||
|
|
|
@ -1,162 +1 @@
|
|||
//! HIR for references to types. Paths in these are not yet resolved. They can
|
||||
//! be directly created from an ast::TypeRef, without further queries.
|
||||
|
||||
use ra_syntax::ast::{self, TypeAscriptionOwner, TypeBoundsOwner};
|
||||
|
||||
use crate::Path;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Mutability {
|
||||
Shared,
|
||||
Mut,
|
||||
}
|
||||
|
||||
impl Mutability {
|
||||
pub fn from_mutable(mutable: bool) -> Mutability {
|
||||
if mutable {
|
||||
Mutability::Mut
|
||||
} else {
|
||||
Mutability::Shared
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_keyword_for_ref(self) -> &'static str {
|
||||
match self {
|
||||
Mutability::Shared => "",
|
||||
Mutability::Mut => "mut ",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_keyword_for_ptr(self) -> &'static str {
|
||||
match self {
|
||||
Mutability::Shared => "const ",
|
||||
Mutability::Mut => "mut ",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compare ty::Ty
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum TypeRef {
|
||||
Never,
|
||||
Placeholder,
|
||||
Tuple(Vec<TypeRef>),
|
||||
Path(Path),
|
||||
RawPtr(Box<TypeRef>, Mutability),
|
||||
Reference(Box<TypeRef>, Mutability),
|
||||
Array(Box<TypeRef> /*, Expr*/),
|
||||
Slice(Box<TypeRef>),
|
||||
/// A fn pointer. Last element of the vector is the return type.
|
||||
Fn(Vec<TypeRef>),
|
||||
// For
|
||||
ImplTrait(Vec<TypeBound>),
|
||||
DynTrait(Vec<TypeBound>),
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum TypeBound {
|
||||
Path(Path),
|
||||
// also for<> bounds
|
||||
// also Lifetimes
|
||||
Error,
|
||||
}
|
||||
|
||||
impl TypeRef {
|
||||
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
|
||||
pub(crate) fn from_ast(node: ast::TypeRef) -> Self {
|
||||
match node {
|
||||
ast::TypeRef::ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
|
||||
ast::TypeRef::TupleType(inner) => {
|
||||
TypeRef::Tuple(inner.fields().map(TypeRef::from_ast).collect())
|
||||
}
|
||||
ast::TypeRef::NeverType(..) => TypeRef::Never,
|
||||
ast::TypeRef::PathType(inner) => {
|
||||
// FIXME: Use `Path::from_src`
|
||||
inner.path().and_then(Path::from_ast).map(TypeRef::Path).unwrap_or(TypeRef::Error)
|
||||
}
|
||||
ast::TypeRef::PointerType(inner) => {
|
||||
let inner_ty = TypeRef::from_ast_opt(inner.type_ref());
|
||||
let mutability = Mutability::from_mutable(inner.is_mut());
|
||||
TypeRef::RawPtr(Box::new(inner_ty), mutability)
|
||||
}
|
||||
ast::TypeRef::ArrayType(inner) => {
|
||||
TypeRef::Array(Box::new(TypeRef::from_ast_opt(inner.type_ref())))
|
||||
}
|
||||
ast::TypeRef::SliceType(inner) => {
|
||||
TypeRef::Slice(Box::new(TypeRef::from_ast_opt(inner.type_ref())))
|
||||
}
|
||||
ast::TypeRef::ReferenceType(inner) => {
|
||||
let inner_ty = TypeRef::from_ast_opt(inner.type_ref());
|
||||
let mutability = Mutability::from_mutable(inner.is_mut());
|
||||
TypeRef::Reference(Box::new(inner_ty), mutability)
|
||||
}
|
||||
ast::TypeRef::PlaceholderType(_inner) => TypeRef::Placeholder,
|
||||
ast::TypeRef::FnPointerType(inner) => {
|
||||
let ret_ty = TypeRef::from_ast_opt(inner.ret_type().and_then(|rt| rt.type_ref()));
|
||||
let mut params = if let Some(pl) = inner.param_list() {
|
||||
pl.params().map(|p| p.ascribed_type()).map(TypeRef::from_ast_opt).collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
params.push(ret_ty);
|
||||
TypeRef::Fn(params)
|
||||
}
|
||||
// for types are close enough for our purposes to the inner type for now...
|
||||
ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
|
||||
ast::TypeRef::ImplTraitType(inner) => {
|
||||
TypeRef::ImplTrait(type_bounds_from_ast(inner.type_bound_list()))
|
||||
}
|
||||
ast::TypeRef::DynTraitType(inner) => {
|
||||
TypeRef::DynTrait(type_bounds_from_ast(inner.type_bound_list()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_ast_opt(node: Option<ast::TypeRef>) -> Self {
|
||||
if let Some(node) = node {
|
||||
TypeRef::from_ast(node)
|
||||
} else {
|
||||
TypeRef::Error
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unit() -> TypeRef {
|
||||
TypeRef::Tuple(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option<ast::TypeBoundList>) -> Vec<TypeBound> {
|
||||
if let Some(type_bounds) = type_bounds_opt {
|
||||
type_bounds.bounds().map(TypeBound::from_ast).collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeBound {
|
||||
pub(crate) fn from_ast(node: ast::TypeBound) -> Self {
|
||||
match node.kind() {
|
||||
ast::TypeBoundKind::PathType(path_type) => {
|
||||
let path = match path_type.path() {
|
||||
Some(p) => p,
|
||||
None => return TypeBound::Error,
|
||||
};
|
||||
// FIXME: Use `Path::from_src`
|
||||
let path = match Path::from_ast(path) {
|
||||
Some(p) => p,
|
||||
None => return TypeBound::Error,
|
||||
};
|
||||
TypeBound::Path(path)
|
||||
}
|
||||
ast::TypeBoundKind::ForType(_) | ast::TypeBoundKind::Lifetime(_) => TypeBound::Error,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_path(&self) -> Option<&Path> {
|
||||
match self {
|
||||
TypeBound::Path(p) => Some(p),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use hir_def::type_ref::*;
|
||||
|
|
|
@ -6,9 +6,16 @@ authors = ["rust-analyzer developers"]
|
|||
|
||||
[dependencies]
|
||||
log = "0.4.5"
|
||||
once_cell = "1.0.1"
|
||||
relative-path = "1.0.0"
|
||||
rustc-hash = "1.0"
|
||||
|
||||
ra_arena = { path = "../ra_arena" }
|
||||
ra_db = { path = "../ra_db" }
|
||||
ra_syntax = { path = "../ra_syntax" }
|
||||
ra_prof = { path = "../ra_prof" }
|
||||
hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" }
|
||||
test_utils = { path = "../test_utils" }
|
||||
mbe = { path = "../ra_mbe", package = "ra_mbe" }
|
||||
ra_cfg = { path = "../ra_cfg" }
|
||||
tt = { path = "../ra_tt", package = "ra_tt" }
|
||||
|
|
91
crates/ra_hir_def/src/attr.rs
Normal file
91
crates/ra_hir_def/src/attr.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::db::AstDatabase;
|
||||
use mbe::ast_to_token_tree;
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, AttrsOwner},
|
||||
SmolStr,
|
||||
};
|
||||
use tt::Subtree;
|
||||
|
||||
use crate::{path::Path, HirFileId, Source};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Attr {
|
||||
pub(crate) path: Path,
|
||||
pub(crate) input: Option<AttrInput>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AttrInput {
|
||||
Literal(SmolStr),
|
||||
TokenTree(Subtree),
|
||||
}
|
||||
|
||||
impl Attr {
|
||||
pub(crate) fn from_src(
|
||||
Source { file_id, ast }: Source<ast::Attr>,
|
||||
db: &impl AstDatabase,
|
||||
) -> Option<Attr> {
|
||||
let path = Path::from_src(Source { file_id, ast: ast.path()? }, db)?;
|
||||
let input = match ast.input() {
|
||||
None => None,
|
||||
Some(ast::AttrInput::Literal(lit)) => {
|
||||
// FIXME: escape? raw string?
|
||||
let value = lit.syntax().first_token()?.text().trim_matches('"').into();
|
||||
Some(AttrInput::Literal(value))
|
||||
}
|
||||
Some(ast::AttrInput::TokenTree(tt)) => {
|
||||
Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0))
|
||||
}
|
||||
};
|
||||
|
||||
Some(Attr { path, input })
|
||||
}
|
||||
|
||||
pub fn from_attrs_owner(
|
||||
file_id: HirFileId,
|
||||
owner: &dyn AttrsOwner,
|
||||
db: &impl AstDatabase,
|
||||
) -> Option<Arc<[Attr]>> {
|
||||
let mut attrs = owner.attrs().peekable();
|
||||
if attrs.peek().is_none() {
|
||||
// Avoid heap allocation
|
||||
return None;
|
||||
}
|
||||
Some(attrs.flat_map(|ast| Attr::from_src(Source { file_id, ast }, db)).collect())
|
||||
}
|
||||
|
||||
pub fn is_simple_atom(&self, name: &str) -> bool {
|
||||
// FIXME: Avoid cloning
|
||||
self.path.as_ident().map_or(false, |s| s.to_string() == name)
|
||||
}
|
||||
|
||||
// FIXME: handle cfg_attr :-)
|
||||
pub fn as_cfg(&self) -> Option<&Subtree> {
|
||||
if !self.is_simple_atom("cfg") {
|
||||
return None;
|
||||
}
|
||||
match &self.input {
|
||||
Some(AttrInput::TokenTree(subtree)) => Some(subtree),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_path(&self) -> Option<&SmolStr> {
|
||||
if !self.is_simple_atom("path") {
|
||||
return None;
|
||||
}
|
||||
match &self.input {
|
||||
Some(AttrInput::Literal(it)) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> Option<bool> {
|
||||
cfg_options.is_cfg_enabled(self.as_cfg()?)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
//! Defines database & queries for name resolution.
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::{db::AstDatabase, HirFileId};
|
||||
use ra_db::{salsa, SourceDatabase};
|
||||
use ra_syntax::ast;
|
||||
|
||||
use crate::nameres::raw::{ImportSourceMap, RawItems};
|
||||
|
||||
#[salsa::query_group(InternDatabaseStorage)]
|
||||
pub trait InternDatabase: SourceDatabase {
|
||||
#[salsa::interned]
|
||||
|
@ -10,6 +14,8 @@ pub trait InternDatabase: SourceDatabase {
|
|||
#[salsa::interned]
|
||||
fn intern_struct(&self, loc: crate::ItemLoc<ast::StructDef>) -> crate::StructId;
|
||||
#[salsa::interned]
|
||||
fn intern_union(&self, loc: crate::ItemLoc<ast::StructDef>) -> crate::UnionId;
|
||||
#[salsa::interned]
|
||||
fn intern_enum(&self, loc: crate::ItemLoc<ast::EnumDef>) -> crate::EnumId;
|
||||
#[salsa::interned]
|
||||
fn intern_const(&self, loc: crate::ItemLoc<ast::ConstDef>) -> crate::ConstId;
|
||||
|
@ -20,3 +26,15 @@ pub trait InternDatabase: SourceDatabase {
|
|||
#[salsa::interned]
|
||||
fn intern_type_alias(&self, loc: crate::ItemLoc<ast::TypeAliasDef>) -> crate::TypeAliasId;
|
||||
}
|
||||
|
||||
#[salsa::query_group(DefDatabase2Storage)]
|
||||
pub trait DefDatabase2: InternDatabase + AstDatabase {
|
||||
#[salsa::invoke(RawItems::raw_items_with_source_map_query)]
|
||||
fn raw_items_with_source_map(
|
||||
&self,
|
||||
file_id: HirFileId,
|
||||
) -> (Arc<RawItems>, Arc<ImportSourceMap>);
|
||||
|
||||
#[salsa::invoke(RawItems::raw_items_query)]
|
||||
fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
|
||||
}
|
||||
|
|
54
crates/ra_hir_def/src/either.rs
Normal file
54
crates/ra_hir_def/src/either.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Either<A, B> {
|
||||
A(A),
|
||||
B(B),
|
||||
}
|
||||
|
||||
impl<A, B> Either<A, B> {
|
||||
pub fn either<R, F1, F2>(self, f1: F1, f2: F2) -> R
|
||||
where
|
||||
F1: FnOnce(A) -> R,
|
||||
F2: FnOnce(B) -> R,
|
||||
{
|
||||
match self {
|
||||
Either::A(a) => f1(a),
|
||||
Either::B(b) => f2(b),
|
||||
}
|
||||
}
|
||||
pub fn map<U, V, F1, F2>(self, f1: F1, f2: F2) -> Either<U, V>
|
||||
where
|
||||
F1: FnOnce(A) -> U,
|
||||
F2: FnOnce(B) -> V,
|
||||
{
|
||||
match self {
|
||||
Either::A(a) => Either::A(f1(a)),
|
||||
Either::B(b) => Either::B(f2(b)),
|
||||
}
|
||||
}
|
||||
pub fn map_a<U, F>(self, f: F) -> Either<U, B>
|
||||
where
|
||||
F: FnOnce(A) -> U,
|
||||
{
|
||||
self.map(f, |it| it)
|
||||
}
|
||||
pub fn a(self) -> Option<A> {
|
||||
match self {
|
||||
Either::A(it) => Some(it),
|
||||
Either::B(_) => None,
|
||||
}
|
||||
}
|
||||
pub fn b(self) -> Option<B> {
|
||||
match self {
|
||||
Either::A(_) => None,
|
||||
Either::B(it) => Some(it),
|
||||
}
|
||||
}
|
||||
pub fn as_ref(&self) -> Either<&A, &B> {
|
||||
match self {
|
||||
Either::A(it) => Either::A(it),
|
||||
Either::B(it) => Either::B(it),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,12 +8,20 @@
|
|||
//! actually true.
|
||||
|
||||
pub mod db;
|
||||
pub mod either;
|
||||
pub mod attr;
|
||||
pub mod name;
|
||||
pub mod path;
|
||||
pub mod type_ref;
|
||||
|
||||
// FIXME: this should be private
|
||||
pub mod nameres;
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId};
|
||||
use ra_arena::{impl_arena_id, RawId};
|
||||
use ra_db::{salsa, CrateId};
|
||||
use ra_db::{salsa, CrateId, FileId};
|
||||
use ra_syntax::{ast, AstNode, SyntaxNode};
|
||||
|
||||
use crate::db::InternDatabase;
|
||||
|
@ -24,6 +32,68 @@ pub struct Source<T> {
|
|||
pub ast: T,
|
||||
}
|
||||
|
||||
pub enum ModuleSource {
|
||||
SourceFile(ast::SourceFile),
|
||||
Module(ast::Module),
|
||||
}
|
||||
|
||||
impl ModuleSource {
|
||||
pub fn new(
|
||||
db: &impl db::DefDatabase2,
|
||||
file_id: Option<FileId>,
|
||||
decl_id: Option<AstId<ast::Module>>,
|
||||
) -> ModuleSource {
|
||||
match (file_id, decl_id) {
|
||||
(Some(file_id), _) => {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
(None, Some(item_id)) => {
|
||||
let module = item_id.to_node(db);
|
||||
assert!(module.item_list().is_some(), "expected inline module");
|
||||
ModuleSource::Module(module)
|
||||
}
|
||||
(None, None) => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this methods do not belong here
|
||||
pub fn from_position(
|
||||
db: &impl db::DefDatabase2,
|
||||
position: ra_db::FilePosition,
|
||||
) -> ModuleSource {
|
||||
let parse = db.parse(position.file_id);
|
||||
match &ra_syntax::algo::find_node_at_offset::<ast::Module>(
|
||||
parse.tree().syntax(),
|
||||
position.offset,
|
||||
) {
|
||||
Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()),
|
||||
_ => {
|
||||
let source_file = parse.tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_child_node(
|
||||
db: &impl db::DefDatabase2,
|
||||
file_id: FileId,
|
||||
child: &SyntaxNode,
|
||||
) -> ModuleSource {
|
||||
if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) {
|
||||
ModuleSource::Module(m)
|
||||
} else {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file_id(db: &impl db::DefDatabase2, file_id: FileId) -> ModuleSource {
|
||||
let source_file = db.parse(file_id).tree();
|
||||
ModuleSource::SourceFile(source_file)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Source<T> {
|
||||
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
|
||||
Source { file_id: self.file_id, ast: f(self.ast) }
|
||||
|
@ -155,6 +225,18 @@ impl AstItemDef<ast::StructDef> for StructId {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct UnionId(salsa::InternId);
|
||||
impl_intern_key!(UnionId);
|
||||
impl AstItemDef<ast::StructDef> for UnionId {
|
||||
fn intern(db: &impl InternDatabase, loc: ItemLoc<ast::StructDef>) -> Self {
|
||||
db.intern_union(loc)
|
||||
}
|
||||
fn lookup_intern(self, db: &impl InternDatabase) -> ItemLoc<ast::StructDef> {
|
||||
db.lookup_intern_union(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct EnumId(salsa::InternId);
|
||||
impl_intern_key!(EnumId);
|
||||
|
@ -167,6 +249,17 @@ impl AstItemDef<ast::EnumDef> for EnumId {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: rename to `VariantId`, only enums can ave variants
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct EnumVariantId {
|
||||
parent: EnumId,
|
||||
local_id: LocalEnumVariantId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct LocalEnumVariantId(RawId);
|
||||
impl_arena_id!(LocalEnumVariantId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ConstId(salsa::InternId);
|
||||
impl_intern_key!(ConstId);
|
||||
|
|
142
crates/ra_hir_def/src/name.rs
Normal file
142
crates/ra_hir_def/src/name.rs
Normal file
|
@ -0,0 +1,142 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use ra_syntax::{ast, SmolStr};
|
||||
|
||||
/// `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
|
||||
/// not there yet!
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct Name(Repr);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
enum Repr {
|
||||
Text(SmolStr),
|
||||
TupleField(usize),
|
||||
}
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.0 {
|
||||
Repr::Text(text) => fmt::Display::fmt(&text, f),
|
||||
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Name {
|
||||
/// Note: this is private to make creating name from random string hard.
|
||||
/// Hopefully, this should allow us to integrate hygiene cleaner in the
|
||||
/// future, and to switch to interned representation of names.
|
||||
const fn new_text(text: SmolStr) -> Name {
|
||||
Name(Repr::Text(text))
|
||||
}
|
||||
|
||||
pub 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 fn missing() -> Name {
|
||||
Name::new_text("[missing name]".into())
|
||||
}
|
||||
|
||||
pub fn as_tuple_index(&self) -> Option<usize> {
|
||||
match self.0 {
|
||||
Repr::TupleField(idx) => Some(idx),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsName {
|
||||
fn as_name(&self) -> Name;
|
||||
}
|
||||
|
||||
impl AsName for ast::NameRef {
|
||||
fn as_name(&self) -> Name {
|
||||
match self.as_tuple_field() {
|
||||
Some(idx) => Name::new_tuple_field(idx),
|
||||
None => Name::resolve(self.text()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsName for ast::Name {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::resolve(self.text())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsName for ast::FieldKind {
|
||||
fn as_name(&self) -> Name {
|
||||
match self {
|
||||
ast::FieldKind::Name(nr) => nr.as_name(),
|
||||
ast::FieldKind::Index(idx) => Name::new_tuple_field(idx.text().parse().unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsName for ra_db::Dependency {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::new_text(self.name.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// Primitives
|
||||
pub const ISIZE: Name = Name::new_inline_ascii(5, b"isize");
|
||||
pub const I8: Name = Name::new_inline_ascii(2, b"i8");
|
||||
pub const I16: Name = Name::new_inline_ascii(3, b"i16");
|
||||
pub const I32: Name = Name::new_inline_ascii(3, b"i32");
|
||||
pub const I64: Name = Name::new_inline_ascii(3, b"i64");
|
||||
pub const I128: Name = Name::new_inline_ascii(4, b"i128");
|
||||
pub const USIZE: Name = Name::new_inline_ascii(5, b"usize");
|
||||
pub const U8: Name = Name::new_inline_ascii(2, b"u8");
|
||||
pub const U16: Name = Name::new_inline_ascii(3, b"u16");
|
||||
pub const U32: Name = Name::new_inline_ascii(3, b"u32");
|
||||
pub const U64: Name = Name::new_inline_ascii(3, b"u64");
|
||||
pub const U128: Name = Name::new_inline_ascii(4, b"u128");
|
||||
pub const F32: Name = Name::new_inline_ascii(3, b"f32");
|
||||
pub const F64: Name = Name::new_inline_ascii(3, b"f64");
|
||||
pub const BOOL: Name = Name::new_inline_ascii(4, b"bool");
|
||||
pub const CHAR: Name = Name::new_inline_ascii(4, b"char");
|
||||
pub const STR: Name = Name::new_inline_ascii(3, b"str");
|
||||
|
||||
// Special names
|
||||
pub const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self");
|
||||
pub const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self");
|
||||
pub const MACRO_RULES: Name = Name::new_inline_ascii(11, b"macro_rules");
|
||||
|
||||
// Components of known path (value or mod name)
|
||||
pub const STD: Name = Name::new_inline_ascii(3, b"std");
|
||||
pub const ITER: Name = Name::new_inline_ascii(4, b"iter");
|
||||
pub const OPS: Name = Name::new_inline_ascii(3, b"ops");
|
||||
pub const FUTURE: Name = Name::new_inline_ascii(6, b"future");
|
||||
pub const RESULT: Name = Name::new_inline_ascii(6, b"result");
|
||||
pub const BOXED: Name = Name::new_inline_ascii(5, b"boxed");
|
||||
|
||||
// Components of known path (type name)
|
||||
pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator");
|
||||
pub const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item");
|
||||
pub const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try");
|
||||
pub const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok");
|
||||
pub const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future");
|
||||
pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
|
||||
pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
|
||||
pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
|
||||
pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
|
1
crates/ra_hir_def/src/nameres.rs
Normal file
1
crates/ra_hir_def/src/nameres.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod raw;
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use std::{ops::Index, sync::Arc};
|
||||
|
||||
use hir_expand::{ast_id_map::AstIdMap, db::AstDatabase};
|
||||
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
|
||||
use ra_syntax::{
|
||||
ast::{self, AttrsOwner, NameOwner},
|
||||
|
@ -11,8 +12,11 @@ use test_utils::tested_by;
|
|||
|
||||
use crate::{
|
||||
attr::Attr,
|
||||
db::{AstDatabase, DefDatabase},
|
||||
AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source,
|
||||
db::DefDatabase2,
|
||||
either::Either,
|
||||
name::{AsName, Name},
|
||||
path::Path,
|
||||
FileAstId, HirFileId, ModuleSource, Source,
|
||||
};
|
||||
|
||||
/// `RawItems` is a set of top-level items in a file (except for impls).
|
||||
|
@ -48,7 +52,7 @@ impl ImportSourceMap {
|
|||
self.map.insert(import, ptr)
|
||||
}
|
||||
|
||||
pub(crate) fn get(&self, source: &ModuleSource, import: ImportId) -> ImportSource {
|
||||
pub fn get(&self, source: &ModuleSource, import: ImportId) -> ImportSource {
|
||||
let file = match source {
|
||||
ModuleSource::SourceFile(file) => file.clone(),
|
||||
ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
|
||||
|
@ -60,14 +64,14 @@ impl ImportSourceMap {
|
|||
|
||||
impl RawItems {
|
||||
pub(crate) fn raw_items_query(
|
||||
db: &(impl DefDatabase + AstDatabase),
|
||||
db: &(impl DefDatabase2 + AstDatabase),
|
||||
file_id: HirFileId,
|
||||
) -> Arc<RawItems> {
|
||||
db.raw_items_with_source_map(file_id).0
|
||||
}
|
||||
|
||||
pub(crate) fn raw_items_with_source_map_query(
|
||||
db: &(impl DefDatabase + AstDatabase),
|
||||
db: &(impl DefDatabase2 + AstDatabase),
|
||||
file_id: HirFileId,
|
||||
) -> (Arc<RawItems>, Arc<ImportSourceMap>) {
|
||||
let mut collector = RawItemsCollector {
|
||||
|
@ -87,7 +91,7 @@ impl RawItems {
|
|||
(Arc::new(collector.raw_items), Arc::new(collector.source_map))
|
||||
}
|
||||
|
||||
pub(super) fn items(&self) -> &[RawItem] {
|
||||
pub fn items(&self) -> &[RawItem] {
|
||||
&self.items
|
||||
}
|
||||
}
|
||||
|
@ -124,19 +128,19 @@ impl Index<Macro> for RawItems {
|
|||
type Attrs = Option<Arc<[Attr]>>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub(super) struct RawItem {
|
||||
pub struct RawItem {
|
||||
attrs: Attrs,
|
||||
pub(super) kind: RawItemKind,
|
||||
pub kind: RawItemKind,
|
||||
}
|
||||
|
||||
impl RawItem {
|
||||
pub(super) fn attrs(&self) -> &[Attr] {
|
||||
pub fn attrs(&self) -> &[Attr] {
|
||||
self.attrs.as_ref().map_or(&[], |it| &*it)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub(super) enum RawItemKind {
|
||||
pub enum RawItemKind {
|
||||
Module(Module),
|
||||
Import(ImportId),
|
||||
Def(Def),
|
||||
|
@ -144,11 +148,11 @@ pub(super) enum RawItemKind {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(super) struct Module(RawId);
|
||||
pub struct Module(RawId);
|
||||
impl_arena_id!(Module);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) enum ModuleData {
|
||||
pub enum ModuleData {
|
||||
Declaration { name: Name, ast_id: FileAstId<ast::Module> },
|
||||
Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> },
|
||||
}
|
||||
|
@ -159,26 +163,26 @@ impl_arena_id!(ImportId);
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ImportData {
|
||||
pub(super) path: Path,
|
||||
pub(super) alias: Option<Name>,
|
||||
pub(super) is_glob: bool,
|
||||
pub(super) is_prelude: bool,
|
||||
pub(super) is_extern_crate: bool,
|
||||
pub(super) is_macro_use: bool,
|
||||
pub path: Path,
|
||||
pub alias: Option<Name>,
|
||||
pub is_glob: bool,
|
||||
pub is_prelude: bool,
|
||||
pub is_extern_crate: bool,
|
||||
pub is_macro_use: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(super) struct Def(RawId);
|
||||
pub struct Def(RawId);
|
||||
impl_arena_id!(Def);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) struct DefData {
|
||||
pub(super) name: Name,
|
||||
pub(super) kind: DefKind,
|
||||
pub struct DefData {
|
||||
pub name: Name,
|
||||
pub kind: DefKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub(super) enum DefKind {
|
||||
pub enum DefKind {
|
||||
Function(FileAstId<ast::FnDef>),
|
||||
Struct(FileAstId<ast::StructDef>),
|
||||
Union(FileAstId<ast::StructDef>),
|
||||
|
@ -190,15 +194,15 @@ pub(super) enum DefKind {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(super) struct Macro(RawId);
|
||||
pub struct Macro(RawId);
|
||||
impl_arena_id!(Macro);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) struct MacroData {
|
||||
pub(super) ast_id: FileAstId<ast::MacroCall>,
|
||||
pub(super) path: Path,
|
||||
pub(super) name: Option<Name>,
|
||||
pub(super) export: bool,
|
||||
pub struct MacroData {
|
||||
pub ast_id: FileAstId<ast::MacroCall>,
|
||||
pub path: Path,
|
||||
pub name: Option<Name>,
|
||||
pub export: bool,
|
||||
}
|
||||
|
||||
struct RawItemsCollector<DB> {
|
423
crates/ra_hir_def/src/path.rs
Normal file
423
crates/ra_hir_def/src/path.rs
Normal file
|
@ -0,0 +1,423 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use std::{iter, sync::Arc};
|
||||
|
||||
use hir_expand::db::AstDatabase;
|
||||
use ra_db::CrateId;
|
||||
use ra_syntax::{
|
||||
ast::{self, NameOwner, TypeAscriptionOwner},
|
||||
AstNode,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
name::{self, AsName, Name},
|
||||
type_ref::TypeRef,
|
||||
Source,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Path {
|
||||
pub kind: PathKind,
|
||||
pub segments: Vec<PathSegment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct PathSegment {
|
||||
pub name: Name,
|
||||
pub args_and_bindings: Option<Arc<GenericArgs>>,
|
||||
}
|
||||
|
||||
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
|
||||
/// can (in the future) also include bindings of associated types, like in
|
||||
/// `Iterator<Item = Foo>`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct GenericArgs {
|
||||
pub args: Vec<GenericArg>,
|
||||
/// This specifies whether the args contain a Self type as the first
|
||||
/// element. This is the case for path segments like `<T as Trait>`, where
|
||||
/// `T` is actually a type parameter for the path `Trait` specifying the
|
||||
/// Self type. Otherwise, when we have a path `Trait<X, Y>`, the Self type
|
||||
/// is left out.
|
||||
pub has_self_type: bool,
|
||||
/// Associated type bindings like in `Iterator<Item = T>`.
|
||||
pub bindings: Vec<(Name, TypeRef)>,
|
||||
}
|
||||
|
||||
/// A single generic argument.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum GenericArg {
|
||||
Type(TypeRef),
|
||||
// or lifetime...
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum PathKind {
|
||||
Plain,
|
||||
Self_,
|
||||
Super,
|
||||
Crate,
|
||||
// Absolute path
|
||||
Abs,
|
||||
// Type based path like `<T>::foo`
|
||||
Type(Box<TypeRef>),
|
||||
// `$crate` from macro expansion
|
||||
DollarCrate(CrateId),
|
||||
}
|
||||
|
||||
impl Path {
|
||||
/// Calls `cb` with all paths, represented by this use item.
|
||||
pub fn expand_use_item(
|
||||
item_src: Source<ast::UseItem>,
|
||||
db: &impl AstDatabase,
|
||||
mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||
) {
|
||||
if let Some(tree) = item_src.ast.use_tree() {
|
||||
expand_use_tree(None, tree, &|| item_src.file_id.macro_crate(db), &mut cb);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_simple_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> Path {
|
||||
Path {
|
||||
kind,
|
||||
segments: segments
|
||||
.into_iter()
|
||||
.map(|name| PathSegment { name, args_and_bindings: None })
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||
/// 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<CrateId>) -> Option<Path> {
|
||||
let mut kind = PathKind::Plain;
|
||||
let mut segments = Vec::new();
|
||||
loop {
|
||||
let segment = path.segment()?;
|
||||
|
||||
if segment.has_colon_colon() {
|
||||
kind = PathKind::Abs;
|
||||
}
|
||||
|
||||
match segment.kind()? {
|
||||
ast::PathSegmentKind::Name(name) => {
|
||||
if name.text() == "$crate" {
|
||||
if let Some(macro_crate) = macro_crate() {
|
||||
kind = PathKind::DollarCrate(macro_crate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let args = segment
|
||||
.type_arg_list()
|
||||
.and_then(GenericArgs::from_ast)
|
||||
.or_else(|| {
|
||||
GenericArgs::from_fn_like_path_ast(
|
||||
segment.param_list(),
|
||||
segment.ret_type(),
|
||||
)
|
||||
})
|
||||
.map(Arc::new);
|
||||
let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
|
||||
segments.push(segment);
|
||||
}
|
||||
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
|
||||
assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||
|
||||
let self_type = TypeRef::from_ast(type_ref?);
|
||||
|
||||
match trait_ref {
|
||||
// <T>::foo
|
||||
None => {
|
||||
kind = PathKind::Type(Box::new(self_type));
|
||||
}
|
||||
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
||||
Some(trait_ref) => {
|
||||
let path = Path::parse(trait_ref.path()?, macro_crate)?;
|
||||
kind = path.kind;
|
||||
let mut prefix_segments = path.segments;
|
||||
prefix_segments.reverse();
|
||||
segments.extend(prefix_segments);
|
||||
// Insert the type reference (T in the above example) as Self parameter for the trait
|
||||
let mut last_segment = segments.last_mut()?;
|
||||
if last_segment.args_and_bindings.is_none() {
|
||||
last_segment.args_and_bindings =
|
||||
Some(Arc::new(GenericArgs::empty()));
|
||||
};
|
||||
let args = last_segment.args_and_bindings.as_mut().unwrap();
|
||||
let mut args_inner = Arc::make_mut(args);
|
||||
args_inner.has_self_type = true;
|
||||
args_inner.args.insert(0, GenericArg::Type(self_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::PathSegmentKind::CrateKw => {
|
||||
kind = PathKind::Crate;
|
||||
break;
|
||||
}
|
||||
ast::PathSegmentKind::SelfKw => {
|
||||
kind = PathKind::Self_;
|
||||
break;
|
||||
}
|
||||
ast::PathSegmentKind::SuperKw => {
|
||||
kind = PathKind::Super;
|
||||
break;
|
||||
}
|
||||
}
|
||||
path = match qualifier(&path) {
|
||||
Some(it) => it,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
segments.reverse();
|
||||
return Some(Path { kind, segments });
|
||||
|
||||
fn qualifier(path: &ast::Path) -> Option<ast::Path> {
|
||||
if let Some(q) = path.qualifier() {
|
||||
return Some(q);
|
||||
}
|
||||
// FIXME: this bottom up traversal is not too precise.
|
||||
// Should we handle do a top-down analysis, recording results?
|
||||
let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
|
||||
let use_tree = use_tree_list.parent_use_tree();
|
||||
use_tree.path()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts an `ast::NameRef` into a single-identifier `Path`.
|
||||
pub fn from_name_ref(name_ref: &ast::NameRef) -> Path {
|
||||
name_ref.as_name().into()
|
||||
}
|
||||
|
||||
/// `true` is this path is a single identifier, like `foo`
|
||||
pub fn is_ident(&self) -> bool {
|
||||
self.kind == PathKind::Plain && self.segments.len() == 1
|
||||
}
|
||||
|
||||
/// `true` if this path is just a standalone `self`
|
||||
pub fn is_self(&self) -> bool {
|
||||
self.kind == PathKind::Self_ && self.segments.is_empty()
|
||||
}
|
||||
|
||||
/// If this path is a single identifier, like `foo`, return its name.
|
||||
pub fn as_ident(&self) -> Option<&Name> {
|
||||
if self.kind != PathKind::Plain || self.segments.len() > 1 {
|
||||
return None;
|
||||
}
|
||||
self.segments.first().map(|s| &s.name)
|
||||
}
|
||||
|
||||
pub fn expand_macro_expr(&self) -> Option<Name> {
|
||||
self.as_ident().and_then(|name| Some(name.clone()))
|
||||
}
|
||||
|
||||
pub fn is_type_relative(&self) -> bool {
|
||||
match self.kind {
|
||||
PathKind::Type(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericArgs {
|
||||
pub fn from_ast(node: ast::TypeArgList) -> Option<GenericArgs> {
|
||||
let mut args = Vec::new();
|
||||
for type_arg in node.type_args() {
|
||||
let type_ref = TypeRef::from_ast_opt(type_arg.type_ref());
|
||||
args.push(GenericArg::Type(type_ref));
|
||||
}
|
||||
// lifetimes ignored for now
|
||||
let mut bindings = Vec::new();
|
||||
for assoc_type_arg in node.assoc_type_args() {
|
||||
if let Some(name_ref) = assoc_type_arg.name_ref() {
|
||||
let name = name_ref.as_name();
|
||||
let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref());
|
||||
bindings.push((name, type_ref));
|
||||
}
|
||||
}
|
||||
if args.is_empty() && bindings.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(GenericArgs { args, has_self_type: false, bindings })
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
|
||||
/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
|
||||
pub(crate) fn from_fn_like_path_ast(
|
||||
params: Option<ast::ParamList>,
|
||||
ret_type: Option<ast::RetType>,
|
||||
) -> Option<GenericArgs> {
|
||||
let mut args = Vec::new();
|
||||
let mut bindings = Vec::new();
|
||||
if let Some(params) = params {
|
||||
let mut param_types = Vec::new();
|
||||
for param in params.params() {
|
||||
let type_ref = TypeRef::from_ast_opt(param.ascribed_type());
|
||||
param_types.push(type_ref);
|
||||
}
|
||||
let arg = GenericArg::Type(TypeRef::Tuple(param_types));
|
||||
args.push(arg);
|
||||
}
|
||||
if let Some(ret_type) = ret_type {
|
||||
let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
|
||||
bindings.push((name::OUTPUT_TYPE, type_ref))
|
||||
}
|
||||
if args.is_empty() && bindings.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(GenericArgs { args, has_self_type: false, bindings })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn empty() -> GenericArgs {
|
||||
GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Name> for Path {
|
||||
fn from(name: Name) -> Path {
|
||||
Path::from_simple_segments(PathKind::Plain, iter::once(name))
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_use_tree(
|
||||
prefix: Option<Path>,
|
||||
tree: ast::UseTree,
|
||||
macro_crate: &impl Fn() -> Option<CrateId>,
|
||||
cb: &mut impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||
) {
|
||||
if let Some(use_tree_list) = tree.use_tree_list() {
|
||||
let prefix = match tree.path() {
|
||||
// E.g. use something::{{{inner}}};
|
||||
None => prefix,
|
||||
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
|
||||
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
|
||||
Some(path) => match convert_path(prefix, path, macro_crate) {
|
||||
Some(it) => Some(it),
|
||||
None => return, // FIXME: report errors somewhere
|
||||
},
|
||||
};
|
||||
for child_tree in use_tree_list.use_trees() {
|
||||
expand_use_tree(prefix.clone(), child_tree, macro_crate, cb);
|
||||
}
|
||||
} else {
|
||||
let alias = tree.alias().and_then(|a| a.name()).map(|a| a.as_name());
|
||||
if let Some(ast_path) = tree.path() {
|
||||
// Handle self in a path.
|
||||
// E.g. `use something::{self, <...>}`
|
||||
if ast_path.qualifier().is_none() {
|
||||
if let Some(segment) = ast_path.segment() {
|
||||
if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
|
||||
if let Some(prefix) = prefix {
|
||||
cb(prefix, &tree, false, alias);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(path) = convert_path(prefix, ast_path, macro_crate) {
|
||||
let is_glob = tree.has_star();
|
||||
cb(path, &tree, is_glob, alias)
|
||||
}
|
||||
// FIXME: report errors somewhere
|
||||
// We get here if we do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_path(
|
||||
prefix: Option<Path>,
|
||||
path: ast::Path,
|
||||
macro_crate: &impl Fn() -> Option<CrateId>,
|
||||
) -> Option<Path> {
|
||||
let prefix = if let Some(qual) = path.qualifier() {
|
||||
Some(convert_path(prefix, qual, macro_crate)?)
|
||||
} else {
|
||||
prefix
|
||||
};
|
||||
|
||||
let segment = path.segment()?;
|
||||
let res = match segment.kind()? {
|
||||
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
|
||||
let mut res = prefix
|
||||
.unwrap_or_else(|| Path { kind: PathKind::Plain, segments: Vec::with_capacity(1) });
|
||||
res.segments.push(PathSegment {
|
||||
name: name.as_name(),
|
||||
args_and_bindings: None, // no type args in use
|
||||
});
|
||||
res
|
||||
}
|
||||
ast::PathSegmentKind::CrateKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
Path::from_simple_segments(PathKind::Crate, iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::SelfKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
Path::from_simple_segments(PathKind::Self_, iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::SuperKw => {
|
||||
if prefix.is_some() {
|
||||
return None;
|
||||
}
|
||||
Path::from_simple_segments(PathKind::Super, iter::empty())
|
||||
}
|
||||
ast::PathSegmentKind::Type { .. } => {
|
||||
// not allowed in imports
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub mod known {
|
||||
use super::{Path, PathKind};
|
||||
use crate::name;
|
||||
|
||||
pub fn std_iter_into_iterator() -> Path {
|
||||
Path::from_simple_segments(
|
||||
PathKind::Abs,
|
||||
vec![name::STD, name::ITER, name::INTO_ITERATOR_TYPE],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn std_ops_try() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY_TYPE])
|
||||
}
|
||||
|
||||
pub fn std_result_result() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE])
|
||||
}
|
||||
|
||||
pub fn std_future_future() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::FUTURE, name::FUTURE_TYPE])
|
||||
}
|
||||
|
||||
pub fn std_boxed_box() -> Path {
|
||||
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::BOXED, name::BOX_TYPE])
|
||||
}
|
||||
}
|
162
crates/ra_hir_def/src/type_ref.rs
Normal file
162
crates/ra_hir_def/src/type_ref.rs
Normal file
|
@ -0,0 +1,162 @@
|
|||
//! HIR for references to types. Paths in these are not yet resolved. They can
|
||||
//! be directly created from an ast::TypeRef, without further queries.
|
||||
|
||||
use ra_syntax::ast::{self, TypeAscriptionOwner, TypeBoundsOwner};
|
||||
|
||||
use crate::path::Path;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Mutability {
|
||||
Shared,
|
||||
Mut,
|
||||
}
|
||||
|
||||
impl Mutability {
|
||||
pub fn from_mutable(mutable: bool) -> Mutability {
|
||||
if mutable {
|
||||
Mutability::Mut
|
||||
} else {
|
||||
Mutability::Shared
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_keyword_for_ref(self) -> &'static str {
|
||||
match self {
|
||||
Mutability::Shared => "",
|
||||
Mutability::Mut => "mut ",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_keyword_for_ptr(self) -> &'static str {
|
||||
match self {
|
||||
Mutability::Shared => "const ",
|
||||
Mutability::Mut => "mut ",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compare ty::Ty
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum TypeRef {
|
||||
Never,
|
||||
Placeholder,
|
||||
Tuple(Vec<TypeRef>),
|
||||
Path(Path),
|
||||
RawPtr(Box<TypeRef>, Mutability),
|
||||
Reference(Box<TypeRef>, Mutability),
|
||||
Array(Box<TypeRef> /*, Expr*/),
|
||||
Slice(Box<TypeRef>),
|
||||
/// A fn pointer. Last element of the vector is the return type.
|
||||
Fn(Vec<TypeRef>),
|
||||
// For
|
||||
ImplTrait(Vec<TypeBound>),
|
||||
DynTrait(Vec<TypeBound>),
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum TypeBound {
|
||||
Path(Path),
|
||||
// also for<> bounds
|
||||
// also Lifetimes
|
||||
Error,
|
||||
}
|
||||
|
||||
impl TypeRef {
|
||||
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
|
||||
pub fn from_ast(node: ast::TypeRef) -> Self {
|
||||
match node {
|
||||
ast::TypeRef::ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
|
||||
ast::TypeRef::TupleType(inner) => {
|
||||
TypeRef::Tuple(inner.fields().map(TypeRef::from_ast).collect())
|
||||
}
|
||||
ast::TypeRef::NeverType(..) => TypeRef::Never,
|
||||
ast::TypeRef::PathType(inner) => {
|
||||
// FIXME: Use `Path::from_src`
|
||||
inner.path().and_then(Path::from_ast).map(TypeRef::Path).unwrap_or(TypeRef::Error)
|
||||
}
|
||||
ast::TypeRef::PointerType(inner) => {
|
||||
let inner_ty = TypeRef::from_ast_opt(inner.type_ref());
|
||||
let mutability = Mutability::from_mutable(inner.is_mut());
|
||||
TypeRef::RawPtr(Box::new(inner_ty), mutability)
|
||||
}
|
||||
ast::TypeRef::ArrayType(inner) => {
|
||||
TypeRef::Array(Box::new(TypeRef::from_ast_opt(inner.type_ref())))
|
||||
}
|
||||
ast::TypeRef::SliceType(inner) => {
|
||||
TypeRef::Slice(Box::new(TypeRef::from_ast_opt(inner.type_ref())))
|
||||
}
|
||||
ast::TypeRef::ReferenceType(inner) => {
|
||||
let inner_ty = TypeRef::from_ast_opt(inner.type_ref());
|
||||
let mutability = Mutability::from_mutable(inner.is_mut());
|
||||
TypeRef::Reference(Box::new(inner_ty), mutability)
|
||||
}
|
||||
ast::TypeRef::PlaceholderType(_inner) => TypeRef::Placeholder,
|
||||
ast::TypeRef::FnPointerType(inner) => {
|
||||
let ret_ty = TypeRef::from_ast_opt(inner.ret_type().and_then(|rt| rt.type_ref()));
|
||||
let mut params = if let Some(pl) = inner.param_list() {
|
||||
pl.params().map(|p| p.ascribed_type()).map(TypeRef::from_ast_opt).collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
params.push(ret_ty);
|
||||
TypeRef::Fn(params)
|
||||
}
|
||||
// for types are close enough for our purposes to the inner type for now...
|
||||
ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
|
||||
ast::TypeRef::ImplTraitType(inner) => {
|
||||
TypeRef::ImplTrait(type_bounds_from_ast(inner.type_bound_list()))
|
||||
}
|
||||
ast::TypeRef::DynTraitType(inner) => {
|
||||
TypeRef::DynTrait(type_bounds_from_ast(inner.type_bound_list()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_ast_opt(node: Option<ast::TypeRef>) -> Self {
|
||||
if let Some(node) = node {
|
||||
TypeRef::from_ast(node)
|
||||
} else {
|
||||
TypeRef::Error
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unit() -> TypeRef {
|
||||
TypeRef::Tuple(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn type_bounds_from_ast(type_bounds_opt: Option<ast::TypeBoundList>) -> Vec<TypeBound> {
|
||||
if let Some(type_bounds) = type_bounds_opt {
|
||||
type_bounds.bounds().map(TypeBound::from_ast).collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeBound {
|
||||
pub fn from_ast(node: ast::TypeBound) -> Self {
|
||||
match node.kind() {
|
||||
ast::TypeBoundKind::PathType(path_type) => {
|
||||
let path = match path_type.path() {
|
||||
Some(p) => p,
|
||||
None => return TypeBound::Error,
|
||||
};
|
||||
// FIXME: Use `Path::from_src`
|
||||
let path = match Path::from_ast(path) {
|
||||
Some(p) => p,
|
||||
None => return TypeBound::Error,
|
||||
};
|
||||
TypeBound::Path(path)
|
||||
}
|
||||
ast::TypeBoundKind::ForType(_) | ast::TypeBoundKind::Lifetime(_) => TypeBound::Error,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_path(&self) -> Option<&Path> {
|
||||
match self {
|
||||
TypeBound::Path(p) => Some(p),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ use std::hash::{Hash, Hasher};
|
|||
use ra_db::{salsa, CrateId, FileId};
|
||||
use ra_syntax::ast::{self, AstNode};
|
||||
|
||||
use crate::{ast_id_map::FileAstId, db::AstDatabase};
|
||||
use crate::ast_id_map::FileAstId;
|
||||
|
||||
/// Input to the analyzer is a set of files, where each file is identified by
|
||||
/// `FileId` and contains source code. However, another source of source code in
|
||||
|
@ -50,7 +50,7 @@ impl From<MacroFile> for HirFileId {
|
|||
impl HirFileId {
|
||||
/// For macro-expansion files, returns the file original source file the
|
||||
/// expansion originated from.
|
||||
pub fn original_file(self, db: &dyn AstDatabase) -> FileId {
|
||||
pub fn original_file(self, db: &dyn db::AstDatabase) -> FileId {
|
||||
match self.0 {
|
||||
HirFileIdRepr::FileId(file_id) => file_id,
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
|
@ -61,7 +61,7 @@ impl HirFileId {
|
|||
}
|
||||
|
||||
/// Get the crate which the macro lives in, if it is a macro file.
|
||||
pub fn macro_crate(self, db: &dyn AstDatabase) -> Option<CrateId> {
|
||||
pub fn macro_crate(self, db: &dyn db::AstDatabase) -> Option<CrateId> {
|
||||
match self.0 {
|
||||
HirFileIdRepr::FileId(_) => None,
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
|
@ -154,7 +154,7 @@ impl<N: AstNode> AstId<N> {
|
|||
self.file_id
|
||||
}
|
||||
|
||||
pub fn to_node(&self, db: &dyn AstDatabase) -> N {
|
||||
pub fn to_node(&self, db: &dyn db::AstDatabase) -> N {
|
||||
let root = db.parse_or_expand(self.file_id).unwrap();
|
||||
db.ast_id_map(self.file_id).get(self.file_ast_id).to_node(&root)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue