Merge branch 'master' of https://github.com/rust-analyzer/rust-analyzer into feature/themes
This commit is contained in:
commit
68a5ff050f
34 changed files with 1071 additions and 438 deletions
|
|
@ -14,7 +14,6 @@ matrix:
|
|||
script:
|
||||
- rustup component add rustfmt
|
||||
- rustup component add rust-src
|
||||
- sed -i "s/debug = 1/debug = false/g" Cargo.toml
|
||||
- cargo test --no-run # let's measure compile time separately
|
||||
- cargo test
|
||||
env:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
members = [ "crates/*", "xtask/" ]
|
||||
|
||||
[profile.dev]
|
||||
debug = 1 # only line info
|
||||
# disabling debug info speeds up builds a bunch,
|
||||
# and we don't rely on it for debugging that much.
|
||||
debug = 0
|
||||
|
||||
[profile.release]
|
||||
incremental = true
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
|
|||
use crate::{
|
||||
adt::VariantDef,
|
||||
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||
expr::{validation::ExprValidator, Body, BodySourceMap},
|
||||
expr::{validation::ExprValidator, BindingAnnotation, Body, BodySourceMap, Pat, PatId},
|
||||
generics::HasGenericParams,
|
||||
ids::{
|
||||
AstItemDef, ConstId, EnumId, FunctionId, MacroDefId, StaticId, StructId, TraitId,
|
||||
|
|
@ -32,7 +32,7 @@ use crate::{
|
|||
resolve::{Resolver, Scope, TypeNs},
|
||||
traits::TraitData,
|
||||
ty::{InferenceResult, Namespace, TraitRef},
|
||||
Either, HasSource, ImportId, Name, ScopeDef, Ty,
|
||||
Either, HasSource, ImportId, Name, ScopeDef, Source, Ty,
|
||||
};
|
||||
|
||||
/// hir::Crate describes a single crate. It's the main interface with which
|
||||
|
|
@ -1070,3 +1070,54 @@ impl AssocItem {
|
|||
.expect("AssocItem without container")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Local {
|
||||
pub(crate) parent: DefWithBody,
|
||||
pub(crate) pat_id: PatId,
|
||||
}
|
||||
|
||||
impl Local {
|
||||
pub fn name(self, db: &impl HirDatabase) -> Option<Name> {
|
||||
let body = db.body_hir(self.parent);
|
||||
match &body[self.pat_id] {
|
||||
Pat::Bind { name, .. } => Some(name.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_self(self, db: &impl HirDatabase) -> bool {
|
||||
self.name(db) == Some(name::SELF_PARAM)
|
||||
}
|
||||
|
||||
pub fn is_mut(self, db: &impl HirDatabase) -> bool {
|
||||
let body = db.body_hir(self.parent);
|
||||
match &body[self.pat_id] {
|
||||
Pat::Bind { mode, .. } => match mode {
|
||||
BindingAnnotation::Mutable | BindingAnnotation::RefMut => true,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent(self, _db: &impl HirDatabase) -> DefWithBody {
|
||||
self.parent
|
||||
}
|
||||
|
||||
pub fn module(self, db: &impl HirDatabase) -> Module {
|
||||
self.parent.module(db)
|
||||
}
|
||||
|
||||
pub fn ty(self, db: &impl HirDatabase) -> Ty {
|
||||
let infer = db.infer(self.parent);
|
||||
infer[self.pat_id].clone()
|
||||
}
|
||||
|
||||
pub fn source(self, db: &impl HirDatabase) -> Source<Either<ast::BindPat, ast::SelfParam>> {
|
||||
let (_body, source_map) = db.body_with_source_map(self.parent);
|
||||
let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm...
|
||||
let root = src.file_syntax(db);
|
||||
src.map(|ast| ast.map(|it| it.cast().unwrap().to_node(&root), |it| it.to_node(&root)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ impl_arena_id!(ScopeId);
|
|||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ExprScopes {
|
||||
body: Arc<Body>,
|
||||
pub(crate) body: Arc<Body>,
|
||||
scopes: Arena<ScopeId, ScopeData>,
|
||||
scope_by_expr: FxHashMap<ExprId, ScopeId>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,17 @@
|
|||
|
||||
use hir_def::{StructId, StructOrUnionId, UnionId};
|
||||
use hir_expand::name::AsName;
|
||||
use ra_syntax::ast::{self, AstNode, NameOwner};
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode, NameOwner},
|
||||
match_ast,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
db::{AstDatabase, DefDatabase, HirDatabase},
|
||||
ids::{AstItemDef, LocationCtx},
|
||||
AstId, Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module,
|
||||
ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef,
|
||||
AstId, Const, Crate, DefWithBody, Enum, EnumVariant, FieldSource, Function, HasSource,
|
||||
ImplBlock, Local, Module, ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias,
|
||||
Union, VariantDef,
|
||||
};
|
||||
|
||||
pub trait FromSource: Sized {
|
||||
|
|
@ -126,6 +130,26 @@ impl FromSource for StructField {
|
|||
}
|
||||
}
|
||||
|
||||
impl Local {
|
||||
pub fn from_source(db: &impl HirDatabase, src: Source<ast::BindPat>) -> Option<Self> {
|
||||
let file_id = src.file_id;
|
||||
let parent: DefWithBody = src.ast.syntax().ancestors().find_map(|it| {
|
||||
let res = match_ast! {
|
||||
match it {
|
||||
ast::ConstDef(ast) => { Const::from_source(db, Source { ast, file_id})?.into() },
|
||||
ast::StaticDef(ast) => { Static::from_source(db, Source { ast, file_id})?.into() },
|
||||
ast::FnDef(ast) => { Function::from_source(db, Source { ast, file_id})?.into() },
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
})?;
|
||||
let (_body, source_map) = db.body_with_source_map(parent);
|
||||
let pat_id = source_map.node_pat(&src.ast.into())?;
|
||||
Some(Local { parent, pat_id })
|
||||
}
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn from_declaration(db: &impl HirDatabase, src: Source<ast::Module>) -> Option<Self> {
|
||||
let src_parent = Source {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ pub use crate::{
|
|||
docs::{DocDef, Docs, Documentation},
|
||||
src::{HasBodySource, HasSource},
|
||||
Adt, AssocItem, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum,
|
||||
EnumVariant, FieldSource, FnData, Function, HasBody, MacroDef, Module, ModuleDef,
|
||||
EnumVariant, FieldSource, FnData, Function, HasBody, Local, MacroDef, Module, ModuleDef,
|
||||
ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union,
|
||||
},
|
||||
expr::ExprScopes,
|
||||
|
|
@ -76,7 +76,11 @@ pub use crate::{
|
|||
resolve::ScopeDef,
|
||||
source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer},
|
||||
ty::{
|
||||
display::HirDisplay, ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
display::HirDisplay,
|
||||
primitive::{
|
||||
FloatBitness, FloatTy, IntBitness, IntTy, Signedness, UncertainFloatTy, UncertainIntTy,
|
||||
},
|
||||
ApplicationTy, CallableDef, Substs, TraitRef, Ty, TypeCtor, TypeWalk,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use crate::{
|
|||
ids::LocationCtx,
|
||||
resolve::{ScopeDef, TypeNs, ValueNs},
|
||||
ty::method_resolution::{self, implements_trait},
|
||||
AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId,
|
||||
AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, Local,
|
||||
MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty,
|
||||
};
|
||||
|
||||
|
|
@ -94,6 +94,7 @@ fn def_with_body_from_child_node(
|
|||
#[derive(Debug)]
|
||||
pub struct SourceAnalyzer {
|
||||
resolver: Resolver,
|
||||
body_owner: Option<DefWithBody>,
|
||||
body_source_map: Option<Arc<BodySourceMap>>,
|
||||
infer: Option<Arc<crate::ty::InferenceResult>>,
|
||||
scopes: Option<Arc<crate::expr::ExprScopes>>,
|
||||
|
|
@ -104,7 +105,7 @@ pub enum PathResolution {
|
|||
/// An item
|
||||
Def(crate::ModuleDef),
|
||||
/// A local binding (only value namespace)
|
||||
LocalBinding(Either<AstPtr<ast::BindPat>, AstPtr<ast::SelfParam>>),
|
||||
Local(Local),
|
||||
/// A generic parameter
|
||||
GenericParam(u32),
|
||||
SelfType(crate::ImplBlock),
|
||||
|
|
@ -152,6 +153,7 @@ impl SourceAnalyzer {
|
|||
let resolver = expr::resolver_for_scope(def.body(db), db, scope);
|
||||
SourceAnalyzer {
|
||||
resolver,
|
||||
body_owner: Some(def),
|
||||
body_source_map: Some(source_map),
|
||||
infer: Some(def.infer(db)),
|
||||
scopes: Some(scopes),
|
||||
|
|
@ -162,6 +164,7 @@ impl SourceAnalyzer {
|
|||
.ancestors()
|
||||
.find_map(|node| try_get_resolver_for_node(db, file_id, &node))
|
||||
.unwrap_or_default(),
|
||||
body_owner: None,
|
||||
body_source_map: None,
|
||||
infer: None,
|
||||
scopes: None,
|
||||
|
|
@ -233,16 +236,9 @@ impl SourceAnalyzer {
|
|||
});
|
||||
let values = self.resolver.resolve_path_in_value_ns_fully(db, &path).and_then(|val| {
|
||||
let res = match val {
|
||||
ValueNs::LocalBinding(it) => {
|
||||
// We get a `PatId` from resolver, but it actually can only
|
||||
// point at `BindPat`, and not at the arbitrary pattern.
|
||||
let pat_ptr = self
|
||||
.body_source_map
|
||||
.as_ref()?
|
||||
.pat_syntax(it)?
|
||||
.ast // FIXME: ignoring file_id here is definitelly wrong
|
||||
.map_a(|ptr| ptr.cast::<ast::BindPat>().unwrap());
|
||||
PathResolution::LocalBinding(pat_ptr)
|
||||
ValueNs::LocalBinding(pat_id) => {
|
||||
let var = Local { parent: self.body_owner?, pat_id };
|
||||
PathResolution::Local(var)
|
||||
}
|
||||
ValueNs::Function(it) => PathResolution::Def(it.into()),
|
||||
ValueNs::Const(it) => PathResolution::Def(it.into()),
|
||||
|
|
|
|||
|
|
@ -4810,3 +4810,22 @@ fn no_such_field_diagnostics() {
|
|||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_builtin_macros_line() {
|
||||
assert_snapshot!(
|
||||
infer(r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! line {() => {}}
|
||||
|
||||
fn main() {
|
||||
let x = line!();
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
![0; 1) '6': i32
|
||||
[64; 88) '{ ...!(); }': ()
|
||||
[74; 75) 'x': i32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
//! A peculiarity of built-in types is that they are always available and are
|
||||
//! not associated with any particular crate.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use hir_expand::name::{self, Name};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
|
|
@ -61,3 +63,33 @@ impl BuiltinType {
|
|||
(name::F64, BuiltinType::Float { bitness: FloatBitness::X64 }),
|
||||
];
|
||||
}
|
||||
|
||||
impl fmt::Display for BuiltinType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let type_name = match self {
|
||||
BuiltinType::Char => "char",
|
||||
BuiltinType::Bool => "bool",
|
||||
BuiltinType::Str => "str",
|
||||
BuiltinType::Int { signedness, bitness } => match (signedness, bitness) {
|
||||
(Signedness::Signed, IntBitness::Xsize) => "isize",
|
||||
(Signedness::Signed, IntBitness::X8) => "i8",
|
||||
(Signedness::Signed, IntBitness::X16) => "i16",
|
||||
(Signedness::Signed, IntBitness::X32) => "i32",
|
||||
(Signedness::Signed, IntBitness::X64) => "i64",
|
||||
(Signedness::Signed, IntBitness::X128) => "i128",
|
||||
|
||||
(Signedness::Unsigned, IntBitness::Xsize) => "usize",
|
||||
(Signedness::Unsigned, IntBitness::X8) => "u8",
|
||||
(Signedness::Unsigned, IntBitness::X16) => "u16",
|
||||
(Signedness::Unsigned, IntBitness::X32) => "u32",
|
||||
(Signedness::Unsigned, IntBitness::X64) => "u64",
|
||||
(Signedness::Unsigned, IntBitness::X128) => "u128",
|
||||
},
|
||||
BuiltinType::Float { bitness } => match bitness {
|
||||
FloatBitness::X32 => "f32",
|
||||
FloatBitness::X64 => "f64",
|
||||
},
|
||||
};
|
||||
f.write_str(type_name)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use hir_expand::{
|
||||
builtin_macro::find_builtin_macro,
|
||||
name::{self, AsName, Name},
|
||||
HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind,
|
||||
HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFileKind,
|
||||
};
|
||||
use ra_cfg::CfgOptions;
|
||||
use ra_db::{CrateId, FileId};
|
||||
|
|
@ -692,10 +693,30 @@ where
|
|||
fn collect_macro(&mut self, mac: &raw::MacroData) {
|
||||
let ast_id = AstId::new(self.file_id, mac.ast_id);
|
||||
|
||||
// Case 0: builtin macros
|
||||
if mac.builtin {
|
||||
if let Some(name) = &mac.name {
|
||||
let krate = self.def_collector.def_map.krate;
|
||||
if let Some(macro_id) = find_builtin_macro(name, krate, ast_id) {
|
||||
self.def_collector.define_macro(
|
||||
self.module_id,
|
||||
name.clone(),
|
||||
macro_id,
|
||||
mac.export,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Case 1: macro rules, define a macro in crate-global mutable scope
|
||||
if is_macro_rules(&mac.path) {
|
||||
if let Some(name) = &mac.name {
|
||||
let macro_id = MacroDefId { ast_id, krate: self.def_collector.def_map.krate };
|
||||
let macro_id = MacroDefId {
|
||||
ast_id,
|
||||
krate: self.def_collector.def_map.krate,
|
||||
kind: MacroDefKind::Declarative,
|
||||
};
|
||||
self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export);
|
||||
}
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ pub(super) struct MacroData {
|
|||
pub(super) path: Path,
|
||||
pub(super) name: Option<Name>,
|
||||
pub(super) export: bool,
|
||||
pub(super) builtin: bool,
|
||||
}
|
||||
|
||||
struct RawItemsCollector {
|
||||
|
|
@ -367,7 +368,11 @@ impl RawItemsCollector {
|
|||
// FIXME: cfg_attr
|
||||
let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export");
|
||||
|
||||
let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export });
|
||||
// FIXME: cfg_attr
|
||||
let builtin =
|
||||
m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "rustc_builtin_macro");
|
||||
|
||||
let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export, builtin });
|
||||
self.push_item(current_module, attrs, RawItemKind::Macro(m));
|
||||
}
|
||||
|
||||
|
|
|
|||
80
crates/ra_hir_expand/src/builtin_macro.rs
Normal file
80
crates/ra_hir_expand/src/builtin_macro.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
//! Builtin macro
|
||||
use crate::db::AstDatabase;
|
||||
use crate::{
|
||||
ast::{self, AstNode},
|
||||
name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind,
|
||||
TextUnit,
|
||||
};
|
||||
|
||||
use crate::quote;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum BuiltinExpander {
|
||||
Line,
|
||||
}
|
||||
|
||||
impl BuiltinExpander {
|
||||
pub fn expand(
|
||||
&self,
|
||||
db: &dyn AstDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
match self {
|
||||
BuiltinExpander::Line => line_expand(db, id, tt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_builtin_macro(
|
||||
ident: &name::Name,
|
||||
krate: CrateId,
|
||||
ast_id: AstId<ast::MacroCall>,
|
||||
) -> Option<MacroDefId> {
|
||||
// FIXME: Better registering method
|
||||
if ident == &name::LINE_MACRO {
|
||||
Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Line) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
|
||||
// FIXME: Use expansion info
|
||||
let file_id = file.original_file(db);
|
||||
let text = db.file_text(file_id);
|
||||
let mut line_num = 1;
|
||||
|
||||
// Count line end
|
||||
for (i, c) in text.chars().enumerate() {
|
||||
if i == pos.to_usize() {
|
||||
break;
|
||||
}
|
||||
if c == '\n' {
|
||||
line_num += 1;
|
||||
}
|
||||
}
|
||||
|
||||
line_num
|
||||
}
|
||||
|
||||
fn line_expand(
|
||||
db: &dyn AstDatabase,
|
||||
id: MacroCallId,
|
||||
_tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
let loc = db.lookup_intern_macro(id);
|
||||
let macro_call = loc.ast_id.to_node(db);
|
||||
|
||||
let arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
|
||||
let arg_start = arg.syntax().text_range().start();
|
||||
|
||||
let file = id.as_file(MacroFileKind::Expr);
|
||||
let line_num = to_line_number(db, file, arg_start);
|
||||
|
||||
let expanded = quote! {
|
||||
#line_num
|
||||
};
|
||||
|
||||
Ok(expanded)
|
||||
}
|
||||
|
|
@ -9,10 +9,37 @@ use ra_prof::profile;
|
|||
use ra_syntax::{AstNode, Parse, SyntaxNode};
|
||||
|
||||
use crate::{
|
||||
ast_id_map::AstIdMap, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId,
|
||||
MacroFile, MacroFileKind,
|
||||
ast_id_map::AstIdMap, BuiltinExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc,
|
||||
MacroDefId, MacroDefKind, MacroFile, MacroFileKind,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum TokenExpander {
|
||||
MacroRules(mbe::MacroRules),
|
||||
Builtin(BuiltinExpander),
|
||||
}
|
||||
|
||||
impl TokenExpander {
|
||||
pub fn expand(
|
||||
&self,
|
||||
db: &dyn AstDatabase,
|
||||
id: MacroCallId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
match self {
|
||||
TokenExpander::MacroRules(it) => it.expand(tt),
|
||||
TokenExpander::Builtin(it) => it.expand(db, id, tt),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shift(&self) -> u32 {
|
||||
match self {
|
||||
TokenExpander::MacroRules(it) => it.shift(),
|
||||
TokenExpander::Builtin(_) => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: rename to ExpandDatabase
|
||||
#[salsa::query_group(AstDatabaseStorage)]
|
||||
pub trait AstDatabase: SourceDatabase {
|
||||
|
|
@ -24,7 +51,7 @@ pub trait AstDatabase: SourceDatabase {
|
|||
#[salsa::interned]
|
||||
fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId;
|
||||
fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
|
||||
fn macro_def(&self, id: MacroDefId) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>>;
|
||||
fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
|
||||
fn parse_macro(
|
||||
&self,
|
||||
macro_file: MacroFile,
|
||||
|
|
@ -41,18 +68,25 @@ pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdM
|
|||
pub(crate) fn macro_def(
|
||||
db: &dyn AstDatabase,
|
||||
id: MacroDefId,
|
||||
) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>> {
|
||||
let macro_call = id.ast_id.to_node(db);
|
||||
let arg = macro_call.token_tree()?;
|
||||
let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
|
||||
log::warn!("fail on macro_def to token tree: {:#?}", arg);
|
||||
None
|
||||
})?;
|
||||
let rules = MacroRules::parse(&tt).ok().or_else(|| {
|
||||
log::warn!("fail on macro_def parse: {:#?}", tt);
|
||||
None
|
||||
})?;
|
||||
Some(Arc::new((rules, tmap)))
|
||||
) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
|
||||
match id.kind {
|
||||
MacroDefKind::Declarative => {
|
||||
let macro_call = id.ast_id.to_node(db);
|
||||
let arg = macro_call.token_tree()?;
|
||||
let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
|
||||
log::warn!("fail on macro_def to token tree: {:#?}", arg);
|
||||
None
|
||||
})?;
|
||||
let rules = MacroRules::parse(&tt).ok().or_else(|| {
|
||||
log::warn!("fail on macro_def parse: {:#?}", tt);
|
||||
None
|
||||
})?;
|
||||
Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
|
||||
}
|
||||
MacroDefKind::BuiltIn(expander) => {
|
||||
Some(Arc::new((TokenExpander::Builtin(expander.clone()), mbe::TokenMap::default())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn macro_arg(
|
||||
|
|
@ -74,7 +108,7 @@ pub(crate) fn macro_expand(
|
|||
let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
|
||||
|
||||
let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
|
||||
let tt = macro_rules.0.expand(¯o_arg.0).map_err(|err| format!("{:?}", err))?;
|
||||
let tt = macro_rules.0.expand(db, id, ¯o_arg.0).map_err(|err| format!("{:?}", err))?;
|
||||
// Set a hard limit for the expanded tt
|
||||
let count = tt.count();
|
||||
if count > 65536 {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
db::AstDatabase,
|
||||
either::Either,
|
||||
name::{AsName, Name},
|
||||
HirFileId, HirFileIdRepr,
|
||||
HirFileId, HirFileIdRepr, MacroDefKind,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -24,7 +24,10 @@ impl Hygiene {
|
|||
HirFileIdRepr::FileId(_) => None,
|
||||
HirFileIdRepr::MacroFile(macro_file) => {
|
||||
let loc = db.lookup_intern_macro(macro_file.macro_call_id);
|
||||
Some(loc.def.krate)
|
||||
match loc.def.kind {
|
||||
MacroDefKind::Declarative => Some(loc.def.krate),
|
||||
MacroDefKind::BuiltIn(_) => None,
|
||||
}
|
||||
}
|
||||
};
|
||||
Hygiene { def_crate }
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ pub mod either;
|
|||
pub mod name;
|
||||
pub mod hygiene;
|
||||
pub mod diagnostics;
|
||||
pub mod builtin_macro;
|
||||
pub mod quote;
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::Arc;
|
||||
|
|
@ -21,6 +23,7 @@ use ra_syntax::{
|
|||
};
|
||||
|
||||
use crate::ast_id_map::FileAstId;
|
||||
use crate::builtin_macro::BuiltinExpander;
|
||||
|
||||
/// 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
|
||||
|
|
@ -122,6 +125,13 @@ impl salsa::InternKey for MacroCallId {
|
|||
pub struct MacroDefId {
|
||||
pub krate: CrateId,
|
||||
pub ast_id: AstId<ast::MacroCall>,
|
||||
pub kind: MacroDefKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum MacroDefKind {
|
||||
Declarative,
|
||||
BuiltIn(BuiltinExpander),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
|
@ -144,7 +154,7 @@ pub struct ExpansionInfo {
|
|||
pub(crate) def_start: (HirFileId, TextUnit),
|
||||
pub(crate) shift: u32,
|
||||
|
||||
pub(crate) macro_def: Arc<(mbe::MacroRules, mbe::TokenMap)>,
|
||||
pub(crate) macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>,
|
||||
pub(crate) macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
|
||||
pub(crate) exp_map: Arc<mbe::RevTokenMap>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,3 +140,6 @@ 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");
|
||||
|
||||
// Builtin Macros
|
||||
pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line");
|
||||
|
|
|
|||
261
crates/ra_hir_expand/src/quote.rs
Normal file
261
crates/ra_hir_expand/src/quote.rs
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
//! A simplified version of quote-crate like quasi quote macro
|
||||
|
||||
// A helper macro quote macro
|
||||
// FIXME:
|
||||
// 1. Not all puncts are handled
|
||||
// 2. #()* pattern repetition not supported now
|
||||
// * But we can do it manually, see `test_quote_derive_copy_hack`
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __quote {
|
||||
() => {
|
||||
Vec::<tt::TokenTree>::new()
|
||||
};
|
||||
|
||||
( @SUBTREE $delim:ident $($tt:tt)* ) => {
|
||||
{
|
||||
let children = $crate::__quote!($($tt)*);
|
||||
let subtree = tt::Subtree {
|
||||
delimiter: tt::Delimiter::$delim,
|
||||
token_trees: $crate::quote::IntoTt::to_tokens(children),
|
||||
};
|
||||
subtree
|
||||
}
|
||||
};
|
||||
|
||||
( @PUNCT $first:literal ) => {
|
||||
{
|
||||
vec![
|
||||
tt::Leaf::Punct(tt::Punct {
|
||||
char: $first,
|
||||
spacing: tt::Spacing::Alone,
|
||||
}).into()
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
( @PUNCT $first:literal, $sec:literal ) => {
|
||||
{
|
||||
vec![
|
||||
tt::Leaf::Punct(tt::Punct {
|
||||
char: $first,
|
||||
spacing: tt::Spacing::Joint,
|
||||
}).into(),
|
||||
tt::Leaf::Punct(tt::Punct {
|
||||
char: $sec,
|
||||
spacing: tt::Spacing::Alone,
|
||||
}).into()
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// hash variable
|
||||
( # $first:ident $($tail:tt)* ) => {
|
||||
{
|
||||
let token = $crate::quote::ToTokenTree::to_token($first);
|
||||
let mut tokens = vec![token.into()];
|
||||
let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
|
||||
tokens.append(&mut tail_tokens);
|
||||
tokens
|
||||
}
|
||||
};
|
||||
|
||||
// Brace
|
||||
( { $($tt:tt)* } ) => { $crate::__quote!(@SUBTREE Brace $($tt)*) };
|
||||
// Bracket
|
||||
( [ $($tt:tt)* ] ) => { $crate::__quote!(@SUBTREE Bracket $($tt)*) };
|
||||
// Parenthesis
|
||||
( ( $($tt:tt)* ) ) => { $crate::__quote!(@SUBTREE Parenthesis $($tt)*) };
|
||||
|
||||
// Literal
|
||||
( $tt:literal ) => { vec![$crate::quote::ToTokenTree::to_token($tt).into()] };
|
||||
// Ident
|
||||
( $tt:ident ) => {
|
||||
vec![ {
|
||||
tt::Leaf::Ident(tt::Ident {
|
||||
text: stringify!($tt).into(),
|
||||
id: tt::TokenId::unspecified(),
|
||||
}).into()
|
||||
}]
|
||||
};
|
||||
|
||||
// Puncts
|
||||
// FIXME: Not all puncts are handled
|
||||
( -> ) => {$crate::__quote!(@PUNCT '-', '>')};
|
||||
( & ) => {$crate::__quote!(@PUNCT '&')};
|
||||
( , ) => {$crate::__quote!(@PUNCT ',')};
|
||||
( : ) => {$crate::__quote!(@PUNCT ':')};
|
||||
( . ) => {$crate::__quote!(@PUNCT '.')};
|
||||
|
||||
( $first:tt $($tail:tt)+ ) => {
|
||||
{
|
||||
let mut tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($first));
|
||||
let mut tail_tokens = $crate::quote::IntoTt::to_tokens($crate::__quote!($($tail)*));
|
||||
|
||||
tokens.append(&mut tail_tokens);
|
||||
tokens
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// FIXME:
|
||||
/// It probably should implement in proc-macro
|
||||
#[macro_export]
|
||||
macro_rules! quote {
|
||||
( $($tt:tt)* ) => {
|
||||
$crate::quote::IntoTt::to_subtree($crate::__quote!($($tt)*))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait IntoTt {
|
||||
fn to_subtree(self) -> tt::Subtree;
|
||||
fn to_tokens(self) -> Vec<tt::TokenTree>;
|
||||
}
|
||||
|
||||
impl IntoTt for Vec<tt::TokenTree> {
|
||||
fn to_subtree(self) -> tt::Subtree {
|
||||
tt::Subtree { delimiter: tt::Delimiter::None, token_trees: self }
|
||||
}
|
||||
|
||||
fn to_tokens(self) -> Vec<tt::TokenTree> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoTt for tt::Subtree {
|
||||
fn to_subtree(self) -> tt::Subtree {
|
||||
self
|
||||
}
|
||||
|
||||
fn to_tokens(self) -> Vec<tt::TokenTree> {
|
||||
vec![tt::TokenTree::Subtree(self)]
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait ToTokenTree {
|
||||
fn to_token(self) -> tt::TokenTree;
|
||||
}
|
||||
|
||||
impl ToTokenTree for tt::TokenTree {
|
||||
fn to_token(self) -> tt::TokenTree {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokenTree for tt::Subtree {
|
||||
fn to_token(self) -> tt::TokenTree {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_to_to_tokentrees {
|
||||
($($ty:ty => $this:ident $im:block);*) => {
|
||||
$(
|
||||
impl ToTokenTree for $ty {
|
||||
fn to_token($this) -> tt::TokenTree {
|
||||
let leaf: tt::Leaf = $im.into();
|
||||
leaf.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokenTree for &$ty {
|
||||
fn to_token($this) -> tt::TokenTree {
|
||||
let leaf: tt::Leaf = $im.clone().into();
|
||||
leaf.into()
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_to_to_tokentrees! {
|
||||
u32 => self { tt::Literal{text: self.to_string().into()} };
|
||||
usize => self { tt::Literal{text: self.to_string().into()}};
|
||||
i32 => self { tt::Literal{text: self.to_string().into()}};
|
||||
&str => self { tt::Literal{text: self.to_string().into()}};
|
||||
String => self { tt::Literal{text: self.into()}};
|
||||
tt::Leaf => self { self };
|
||||
tt::Literal => self { self };
|
||||
tt::Ident => self { self };
|
||||
tt::Punct => self { self }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_quote_delimiters() {
|
||||
assert_eq!(quote!({}).to_string(), "{}");
|
||||
assert_eq!(quote!(()).to_string(), "()");
|
||||
assert_eq!(quote!([]).to_string(), "[]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quote_idents() {
|
||||
assert_eq!(quote!(32).to_string(), "32");
|
||||
assert_eq!(quote!(struct).to_string(), "struct");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quote_hash_simple_literal() {
|
||||
let a = 20;
|
||||
assert_eq!(quote!(#a).to_string(), "20");
|
||||
let s: String = "hello".into();
|
||||
assert_eq!(quote!(#s).to_string(), "hello");
|
||||
}
|
||||
|
||||
fn mk_ident(name: &str) -> tt::Ident {
|
||||
tt::Ident { text: name.into(), id: tt::TokenId::unspecified() }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quote_hash_token_tree() {
|
||||
let a = mk_ident("hello");
|
||||
|
||||
let quoted = quote!(#a);
|
||||
assert_eq!(quoted.to_string(), "hello");
|
||||
let t = format!("{:?}", quoted);
|
||||
assert_eq!(t, "Subtree { delimiter: None, token_trees: [Leaf(Ident(Ident { text: \"hello\", id: TokenId(4294967295) }))] }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quote_simple_derive_copy() {
|
||||
let name = mk_ident("Foo");
|
||||
|
||||
let quoted = quote! {
|
||||
impl Clone for #name {
|
||||
fn clone(&self) -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {}}}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quote_derive_copy_hack() {
|
||||
// Assume the given struct is:
|
||||
// struct Foo {
|
||||
// name: String,
|
||||
// id: u32,
|
||||
// }
|
||||
let struct_name = mk_ident("Foo");
|
||||
let fields = [mk_ident("name"), mk_ident("id")];
|
||||
let fields = fields
|
||||
.into_iter()
|
||||
.map(|it| quote!(#it: self.#it.clone(), ).token_trees.clone())
|
||||
.flatten();
|
||||
|
||||
let list = tt::Subtree { delimiter: tt::Delimiter::Brace, token_trees: fields.collect() };
|
||||
|
||||
let quoted = quote! {
|
||||
impl Clone for #struct_name {
|
||||
fn clone(&self) -> Self {
|
||||
Self #list
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(quoted.to_string(), "impl Clone for Foo {fn clone (& self) -> Self {Self {name : self . name . clone () , id : self . id . clone () ,}}}");
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ pub use function_signature::FunctionSignature;
|
|||
pub use navigation_target::NavigationTarget;
|
||||
pub use structure::{file_structure, StructureNode};
|
||||
|
||||
pub(crate) use navigation_target::{description_from_symbol, docs_from_symbol};
|
||||
pub(crate) use navigation_target::{description_from_symbol, docs_from_symbol, ToNav};
|
||||
pub(crate) use short_label::ShortLabel;
|
||||
|
||||
pub(crate) fn function_label(node: &ast::FnDef) -> String {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use hir::{AssocItem, FieldSource, HasSource, ModuleSource};
|
||||
use hir::{AssocItem, Either, FieldSource, HasSource, ModuleSource};
|
||||
use ra_db::{FileId, SourceDatabase};
|
||||
use ra_syntax::{
|
||||
ast::{self, DocCommentsOwner},
|
||||
match_ast, AstNode, AstPtr, SmolStr,
|
||||
SyntaxKind::{self, NAME},
|
||||
ast::{self, DocCommentsOwner, NameOwner},
|
||||
match_ast, AstNode, SmolStr,
|
||||
SyntaxKind::{self, BIND_PAT},
|
||||
SyntaxNode, TextRange,
|
||||
};
|
||||
|
||||
|
|
@ -29,19 +29,8 @@ pub struct NavigationTarget {
|
|||
docs: Option<String>,
|
||||
}
|
||||
|
||||
fn find_range_from_node(
|
||||
db: &RootDatabase,
|
||||
src: hir::HirFileId,
|
||||
node: &SyntaxNode,
|
||||
) -> (FileId, TextRange) {
|
||||
let text_range = node.text_range();
|
||||
let (file_id, text_range) = src
|
||||
.expansion_info(db)
|
||||
.and_then(|expansion_info| expansion_info.find_range(text_range))
|
||||
.unwrap_or((src, text_range));
|
||||
|
||||
// FIXME: handle recursive macro generated macro
|
||||
(file_id.original_file(db), text_range)
|
||||
pub(crate) trait ToNav {
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget;
|
||||
}
|
||||
|
||||
impl NavigationTarget {
|
||||
|
|
@ -87,88 +76,6 @@ impl NavigationTarget {
|
|||
self.focus_range
|
||||
}
|
||||
|
||||
pub(crate) fn from_bind_pat(
|
||||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
pat: &ast::BindPat,
|
||||
) -> NavigationTarget {
|
||||
NavigationTarget::from_named(db, file_id.into(), pat, None, None)
|
||||
}
|
||||
|
||||
pub(crate) fn from_symbol(db: &RootDatabase, symbol: FileSymbol) -> NavigationTarget {
|
||||
NavigationTarget {
|
||||
file_id: symbol.file_id,
|
||||
name: symbol.name.clone(),
|
||||
kind: symbol.ptr.kind(),
|
||||
full_range: symbol.ptr.range(),
|
||||
focus_range: symbol.name_range,
|
||||
container_name: symbol.container_name.clone(),
|
||||
description: description_from_symbol(db, &symbol),
|
||||
docs: docs_from_symbol(db, &symbol),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_pat(
|
||||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
pat: AstPtr<ast::BindPat>,
|
||||
) -> NavigationTarget {
|
||||
let parse = db.parse(file_id);
|
||||
let pat = pat.to_node(parse.tree().syntax());
|
||||
NavigationTarget::from_bind_pat(db, file_id, &pat)
|
||||
}
|
||||
|
||||
pub(crate) fn from_self_param(
|
||||
file_id: FileId,
|
||||
par: AstPtr<ast::SelfParam>,
|
||||
) -> NavigationTarget {
|
||||
let (name, full_range) = ("self".into(), par.syntax_node_ptr().range());
|
||||
|
||||
NavigationTarget {
|
||||
file_id,
|
||||
name,
|
||||
full_range,
|
||||
focus_range: None,
|
||||
kind: NAME,
|
||||
container_name: None,
|
||||
description: None, //< No document node for SelfParam
|
||||
docs: None, //< No document node for SelfParam
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
|
||||
let src = module.definition_source(db);
|
||||
let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
|
||||
match src.ast {
|
||||
ModuleSource::SourceFile(node) => {
|
||||
let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
|
||||
|
||||
NavigationTarget::from_syntax(
|
||||
file_id,
|
||||
name,
|
||||
None,
|
||||
text_range,
|
||||
node.syntax(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
ModuleSource::Module(node) => {
|
||||
let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
|
||||
|
||||
NavigationTarget::from_syntax(
|
||||
file_id,
|
||||
name,
|
||||
None,
|
||||
text_range,
|
||||
node.syntax(),
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
|
||||
let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
|
||||
if let Some(src) = module.declaration_source(db) {
|
||||
|
|
@ -183,55 +90,7 @@ impl NavigationTarget {
|
|||
src.ast.short_label(),
|
||||
);
|
||||
}
|
||||
NavigationTarget::from_module(db, module)
|
||||
}
|
||||
|
||||
pub(crate) fn from_field(db: &RootDatabase, field: hir::StructField) -> NavigationTarget {
|
||||
let src = field.source(db);
|
||||
match src.ast {
|
||||
FieldSource::Named(it) => NavigationTarget::from_named(
|
||||
db,
|
||||
src.file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
),
|
||||
FieldSource::Pos(it) => {
|
||||
let (file_id, text_range) = find_range_from_node(db, src.file_id, it.syntax());
|
||||
NavigationTarget::from_syntax(
|
||||
file_id,
|
||||
"".into(),
|
||||
None,
|
||||
text_range,
|
||||
it.syntax(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_def_source<A, D>(db: &RootDatabase, def: D) -> NavigationTarget
|
||||
where
|
||||
D: HasSource<Ast = A>,
|
||||
A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
|
||||
{
|
||||
let src = def.source(db);
|
||||
NavigationTarget::from_named(
|
||||
db,
|
||||
src.file_id,
|
||||
&src.ast,
|
||||
src.ast.doc_comment_text(),
|
||||
src.ast.short_label(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn from_adt_def(db: &RootDatabase, adt_def: hir::Adt) -> NavigationTarget {
|
||||
match adt_def {
|
||||
hir::Adt::Struct(it) => NavigationTarget::from_def_source(db, it),
|
||||
hir::Adt::Union(it) => NavigationTarget::from_def_source(db, it),
|
||||
hir::Adt::Enum(it) => NavigationTarget::from_def_source(db, it),
|
||||
}
|
||||
module.to_nav(db)
|
||||
}
|
||||
|
||||
pub(crate) fn from_def(
|
||||
|
|
@ -239,14 +98,14 @@ impl NavigationTarget {
|
|||
module_def: hir::ModuleDef,
|
||||
) -> Option<NavigationTarget> {
|
||||
let nav = match module_def {
|
||||
hir::ModuleDef::Module(module) => NavigationTarget::from_module(db, module),
|
||||
hir::ModuleDef::Function(func) => NavigationTarget::from_def_source(db, func),
|
||||
hir::ModuleDef::Adt(it) => NavigationTarget::from_adt_def(db, it),
|
||||
hir::ModuleDef::Const(it) => NavigationTarget::from_def_source(db, it),
|
||||
hir::ModuleDef::Static(it) => NavigationTarget::from_def_source(db, it),
|
||||
hir::ModuleDef::EnumVariant(it) => NavigationTarget::from_def_source(db, it),
|
||||
hir::ModuleDef::Trait(it) => NavigationTarget::from_def_source(db, it),
|
||||
hir::ModuleDef::TypeAlias(it) => NavigationTarget::from_def_source(db, it),
|
||||
hir::ModuleDef::Module(module) => module.to_nav(db),
|
||||
hir::ModuleDef::Function(it) => it.to_nav(db),
|
||||
hir::ModuleDef::Adt(it) => it.to_nav(db),
|
||||
hir::ModuleDef::Const(it) => it.to_nav(db),
|
||||
hir::ModuleDef::Static(it) => it.to_nav(db),
|
||||
hir::ModuleDef::EnumVariant(it) => it.to_nav(db),
|
||||
hir::ModuleDef::Trait(it) => it.to_nav(db),
|
||||
hir::ModuleDef::TypeAlias(it) => it.to_nav(db),
|
||||
hir::ModuleDef::BuiltinType(..) => {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -254,41 +113,6 @@ impl NavigationTarget {
|
|||
Some(nav)
|
||||
}
|
||||
|
||||
pub(crate) fn from_impl_block(
|
||||
db: &RootDatabase,
|
||||
impl_block: hir::ImplBlock,
|
||||
) -> NavigationTarget {
|
||||
let src = impl_block.source(db);
|
||||
let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());
|
||||
|
||||
NavigationTarget::from_syntax(
|
||||
file_id,
|
||||
"impl".into(),
|
||||
None,
|
||||
text_range,
|
||||
src.ast.syntax(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn from_assoc_item(
|
||||
db: &RootDatabase,
|
||||
assoc_item: hir::AssocItem,
|
||||
) -> NavigationTarget {
|
||||
match assoc_item {
|
||||
AssocItem::Function(it) => NavigationTarget::from_def_source(db, it),
|
||||
AssocItem::Const(it) => NavigationTarget::from_def_source(db, it),
|
||||
AssocItem::TypeAlias(it) => NavigationTarget::from_def_source(db, it),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_macro_def(db: &RootDatabase, macro_call: hir::MacroDef) -> NavigationTarget {
|
||||
let src = macro_call.source(db);
|
||||
log::debug!("nav target {:#?}", src.ast.syntax());
|
||||
NavigationTarget::from_named(db, src.file_id, &src.ast, src.ast.doc_comment_text(), None)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn assert_match(&self, expected: &str) {
|
||||
let actual = self.debug_render();
|
||||
|
|
@ -359,6 +183,198 @@ impl NavigationTarget {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToNav for FileSymbol {
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||
NavigationTarget {
|
||||
file_id: self.file_id,
|
||||
name: self.name.clone(),
|
||||
kind: self.ptr.kind(),
|
||||
full_range: self.ptr.range(),
|
||||
focus_range: self.name_range,
|
||||
container_name: self.container_name.clone(),
|
||||
description: description_from_symbol(db, self),
|
||||
docs: docs_from_symbol(db, self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait ToNavFromAst {}
|
||||
impl ToNavFromAst for hir::Function {}
|
||||
impl ToNavFromAst for hir::Const {}
|
||||
impl ToNavFromAst for hir::Static {}
|
||||
impl ToNavFromAst for hir::Struct {}
|
||||
impl ToNavFromAst for hir::Enum {}
|
||||
impl ToNavFromAst for hir::EnumVariant {}
|
||||
impl ToNavFromAst for hir::Union {}
|
||||
impl ToNavFromAst for hir::TypeAlias {}
|
||||
impl ToNavFromAst for hir::Trait {}
|
||||
|
||||
impl<D> ToNav for D
|
||||
where
|
||||
D: HasSource + ToNavFromAst + Copy,
|
||||
D::Ast: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
|
||||
{
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||
let src = self.source(db);
|
||||
NavigationTarget::from_named(
|
||||
db,
|
||||
src.file_id,
|
||||
&src.ast,
|
||||
src.ast.doc_comment_text(),
|
||||
src.ast.short_label(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToNav for hir::Module {
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||
let src = self.definition_source(db);
|
||||
let name = self.name(db).map(|it| it.to_string().into()).unwrap_or_default();
|
||||
match src.ast {
|
||||
ModuleSource::SourceFile(node) => {
|
||||
let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
|
||||
|
||||
NavigationTarget::from_syntax(
|
||||
file_id,
|
||||
name,
|
||||
None,
|
||||
text_range,
|
||||
node.syntax(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
ModuleSource::Module(node) => {
|
||||
let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
|
||||
|
||||
NavigationTarget::from_syntax(
|
||||
file_id,
|
||||
name,
|
||||
None,
|
||||
text_range,
|
||||
node.syntax(),
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToNav for hir::ImplBlock {
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||
let src = self.source(db);
|
||||
let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());
|
||||
|
||||
NavigationTarget::from_syntax(
|
||||
file_id,
|
||||
"impl".into(),
|
||||
None,
|
||||
text_range,
|
||||
src.ast.syntax(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToNav for hir::StructField {
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||
let src = self.source(db);
|
||||
|
||||
match src.ast {
|
||||
FieldSource::Named(it) => NavigationTarget::from_named(
|
||||
db,
|
||||
src.file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
),
|
||||
FieldSource::Pos(it) => {
|
||||
let (file_id, text_range) = find_range_from_node(db, src.file_id, it.syntax());
|
||||
NavigationTarget::from_syntax(
|
||||
file_id,
|
||||
"".into(),
|
||||
None,
|
||||
text_range,
|
||||
it.syntax(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToNav for hir::MacroDef {
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||
let src = self.source(db);
|
||||
log::debug!("nav target {:#?}", src.ast.syntax());
|
||||
NavigationTarget::from_named(db, src.file_id, &src.ast, src.ast.doc_comment_text(), None)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToNav for hir::Adt {
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||
match self {
|
||||
hir::Adt::Struct(it) => it.to_nav(db),
|
||||
hir::Adt::Union(it) => it.to_nav(db),
|
||||
hir::Adt::Enum(it) => it.to_nav(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToNav for hir::AssocItem {
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||
match self {
|
||||
AssocItem::Function(it) => it.to_nav(db),
|
||||
AssocItem::Const(it) => it.to_nav(db),
|
||||
AssocItem::TypeAlias(it) => it.to_nav(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToNav for hir::Local {
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||
let src = self.source(db);
|
||||
let (full_range, focus_range) = match src.ast {
|
||||
Either::A(it) => {
|
||||
(it.syntax().text_range(), it.name().map(|it| it.syntax().text_range()))
|
||||
}
|
||||
Either::B(it) => (it.syntax().text_range(), Some(it.self_kw_token().text_range())),
|
||||
};
|
||||
let name = match self.name(db) {
|
||||
Some(it) => it.to_string().into(),
|
||||
None => "".into(),
|
||||
};
|
||||
NavigationTarget {
|
||||
file_id: src.file_id.original_file(db),
|
||||
name,
|
||||
kind: BIND_PAT,
|
||||
full_range,
|
||||
focus_range,
|
||||
container_name: None,
|
||||
description: None,
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_range_from_node(
|
||||
db: &RootDatabase,
|
||||
src: hir::HirFileId,
|
||||
node: &SyntaxNode,
|
||||
) -> (FileId, TextRange) {
|
||||
let text_range = node.text_range();
|
||||
let (file_id, text_range) = src
|
||||
.expansion_info(db)
|
||||
.and_then(|expansion_info| expansion_info.find_range(text_range))
|
||||
.unwrap_or((src, text_range));
|
||||
|
||||
// FIXME: handle recursive macro generated macro
|
||||
(file_id.original_file(db), text_range)
|
||||
}
|
||||
|
||||
pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
|
||||
let parse = db.parse(symbol.file_id);
|
||||
let node = symbol.ptr.to_node(parse.tree().syntax());
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use ra_syntax::{
|
|||
|
||||
use crate::{
|
||||
db::RootDatabase,
|
||||
display::ShortLabel,
|
||||
display::{ShortLabel, ToNav},
|
||||
references::{classify_name_ref, NameKind::*},
|
||||
FilePosition, NavigationTarget, RangeInfo,
|
||||
};
|
||||
|
|
@ -56,20 +56,19 @@ pub(crate) fn reference_definition(
|
|||
|
||||
let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind);
|
||||
match name_kind {
|
||||
Some(Macro(mac)) => return Exact(NavigationTarget::from_macro_def(db, mac)),
|
||||
Some(Field(field)) => return Exact(NavigationTarget::from_field(db, field)),
|
||||
Some(AssocItem(assoc)) => return Exact(NavigationTarget::from_assoc_item(db, assoc)),
|
||||
Some(Macro(mac)) => return Exact(mac.to_nav(db)),
|
||||
Some(Field(field)) => return Exact(field.to_nav(db)),
|
||||
Some(AssocItem(assoc)) => return Exact(assoc.to_nav(db)),
|
||||
Some(Def(def)) => match NavigationTarget::from_def(db, def) {
|
||||
Some(nav) => return Exact(nav),
|
||||
None => return Approximate(vec![]),
|
||||
},
|
||||
Some(SelfType(ty)) => {
|
||||
if let Some((def_id, _)) = ty.as_adt() {
|
||||
return Exact(NavigationTarget::from_adt_def(db, def_id));
|
||||
if let Some((adt, _)) = ty.as_adt() {
|
||||
return Exact(adt.to_nav(db));
|
||||
}
|
||||
}
|
||||
Some(Pat((_, pat))) => return Exact(NavigationTarget::from_pat(db, file_id, pat)),
|
||||
Some(SelfParam(par)) => return Exact(NavigationTarget::from_self_param(file_id, par)),
|
||||
Some(Local(local)) => return Exact(local.to_nav(db)),
|
||||
Some(GenericParam(_)) => {
|
||||
// FIXME: go to the generic param def
|
||||
}
|
||||
|
|
@ -79,7 +78,7 @@ pub(crate) fn reference_definition(
|
|||
// Fallback index based approach:
|
||||
let navs = crate::symbol_index::index_resolve(db, name_ref)
|
||||
.into_iter()
|
||||
.map(|s| NavigationTarget::from_symbol(db, s))
|
||||
.map(|s| s.to_nav(db))
|
||||
.collect();
|
||||
Approximate(navs)
|
||||
}
|
||||
|
|
@ -95,7 +94,7 @@ pub(crate) fn name_definition(
|
|||
if module.has_semi() {
|
||||
let src = hir::Source { file_id: file_id.into(), ast: module };
|
||||
if let Some(child_module) = hir::Module::from_declaration(db, src) {
|
||||
let nav = NavigationTarget::from_module(db, child_module);
|
||||
let nav = child_module.to_nav(db);
|
||||
return Some(vec![nav]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use ra_db::SourceDatabase;
|
||||
use ra_syntax::{ast, AstNode};
|
||||
|
||||
use crate::{db::RootDatabase, FilePosition, NavigationTarget, RangeInfo};
|
||||
use crate::{db::RootDatabase, display::ToNav, FilePosition, NavigationTarget, RangeInfo};
|
||||
|
||||
pub(crate) fn goto_type_definition(
|
||||
db: &RootDatabase,
|
||||
|
|
@ -33,7 +33,7 @@ pub(crate) fn goto_type_definition(
|
|||
|
||||
let adt_def = analyzer.autoderef(db, ty).find_map(|ty| ty.as_adt().map(|adt| adt.0))?;
|
||||
|
||||
let nav = NavigationTarget::from_adt_def(db, adt_def);
|
||||
let nav = adt_def.to_nav(db);
|
||||
Some(RangeInfo::new(node.text_range(), vec![nav]))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,27 +117,23 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
|
|||
hir::AssocItem::Const(it) => from_def_source(db, it),
|
||||
hir::AssocItem::TypeAlias(it) => from_def_source(db, it),
|
||||
}),
|
||||
Some(Def(it)) => {
|
||||
match it {
|
||||
hir::ModuleDef::Module(it) => {
|
||||
if let hir::ModuleSource::Module(it) = it.definition_source(db).ast {
|
||||
res.extend(hover_text(it.doc_comment_text(), it.short_label()))
|
||||
}
|
||||
}
|
||||
hir::ModuleDef::Function(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Adt(Adt::Struct(it)) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Adt(Adt::Union(it)) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Adt(Adt::Enum(it)) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::EnumVariant(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Const(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Static(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Trait(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::TypeAlias(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::BuiltinType(_) => {
|
||||
// FIXME: hover for builtin Type ?
|
||||
Some(Def(it)) => match it {
|
||||
hir::ModuleDef::Module(it) => {
|
||||
if let hir::ModuleSource::Module(it) = it.definition_source(db).ast {
|
||||
res.extend(hover_text(it.doc_comment_text(), it.short_label()))
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::ModuleDef::Function(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Adt(Adt::Struct(it)) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Adt(Adt::Union(it)) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Adt(Adt::Enum(it)) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::EnumVariant(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Const(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Static(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::Trait(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::TypeAlias(it) => res.extend(from_def_source(db, it)),
|
||||
hir::ModuleDef::BuiltinType(it) => res.extend(Some(it.to_string())),
|
||||
},
|
||||
Some(SelfType(ty)) => {
|
||||
if let Some((adt_def, _)) = ty.as_adt() {
|
||||
res.extend(match adt_def {
|
||||
|
|
@ -147,7 +143,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
|
|||
})
|
||||
}
|
||||
}
|
||||
Some(Pat(_)) | Some(SelfParam(_)) => {
|
||||
Some(Local(_)) => {
|
||||
// Hover for these shows type names
|
||||
no_fallback = true;
|
||||
}
|
||||
|
|
@ -722,4 +718,16 @@ fn func(foo: i32) { if true { <|>foo; }; }
|
|||
assert_eq!(trim_markup_opt(hover.info.first()), Some("macro_rules! foo"));
|
||||
assert_eq!(hover.info.is_exact(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hover_tuple_field() {
|
||||
let (analysis, position) = single_file_with_position(
|
||||
"
|
||||
struct TS(String, i32<|>);
|
||||
",
|
||||
);
|
||||
let hover = analysis.hover(position).unwrap().unwrap();
|
||||
assert_eq!(trim_markup_opt(hover.info.first()), Some("i32"));
|
||||
assert_eq!(hover.info.is_exact(), true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use hir::{db::HirDatabase, ApplicationTy, FromSource, Ty, TypeCtor};
|
|||
use ra_db::SourceDatabase;
|
||||
use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
|
||||
|
||||
use crate::{db::RootDatabase, FilePosition, NavigationTarget, RangeInfo};
|
||||
use crate::{db::RootDatabase, display::ToNav, FilePosition, NavigationTarget, RangeInfo};
|
||||
|
||||
pub(crate) fn goto_implementation(
|
||||
db: &RootDatabase,
|
||||
|
|
@ -58,7 +58,7 @@ fn impls_for_def(
|
|||
impls
|
||||
.all_impls()
|
||||
.filter(|impl_block| is_equal_for_find_impls(&ty, &impl_block.target_ty(db)))
|
||||
.map(|imp| NavigationTarget::from_impl_block(db, imp))
|
||||
.map(|imp| imp.to_nav(db))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
|
@ -75,12 +75,7 @@ fn impls_for_trait(
|
|||
let krate = module.krate();
|
||||
let impls = db.impls_in_crate(krate);
|
||||
|
||||
Some(
|
||||
impls
|
||||
.lookup_impl_blocks_for_trait(tr)
|
||||
.map(|imp| NavigationTarget::from_impl_block(db, imp))
|
||||
.collect(),
|
||||
)
|
||||
Some(impls.lookup_impl_blocks_for_trait(tr).map(|imp| imp.to_nav(db)).collect())
|
||||
}
|
||||
|
||||
fn is_equal_for_find_impls(original_ty: &Ty, impl_ty: &Ty) -> bool {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ use ra_db::{
|
|||
};
|
||||
use ra_syntax::{SourceFile, TextRange, TextUnit};
|
||||
|
||||
use crate::{db::LineIndexDatabase, symbol_index::FileSymbol};
|
||||
use crate::{db::LineIndexDatabase, display::ToNav, symbol_index::FileSymbol};
|
||||
|
||||
pub use crate::{
|
||||
assists::{Assist, AssistId},
|
||||
|
|
@ -351,7 +351,7 @@ impl Analysis {
|
|||
self.with_db(|db| {
|
||||
symbol_index::world_symbols(db, query)
|
||||
.into_iter()
|
||||
.map(|s| NavigationTarget::from_symbol(db, s))
|
||||
.map(|s| s.to_nav(db))
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ use ra_db::{SourceDatabase, SourceDatabaseExt};
|
|||
use ra_prof::profile;
|
||||
use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit};
|
||||
|
||||
use crate::{db::RootDatabase, FilePosition, FileRange, NavigationTarget, RangeInfo};
|
||||
use crate::{
|
||||
db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo,
|
||||
};
|
||||
|
||||
pub(crate) use self::{
|
||||
classify::{classify_name, classify_name_ref},
|
||||
|
|
@ -76,16 +78,15 @@ pub(crate) fn find_all_refs(
|
|||
let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?;
|
||||
|
||||
let declaration = match def.kind {
|
||||
NameKind::Macro(mac) => NavigationTarget::from_macro_def(db, mac),
|
||||
NameKind::Field(field) => NavigationTarget::from_field(db, field),
|
||||
NameKind::AssocItem(assoc) => NavigationTarget::from_assoc_item(db, assoc),
|
||||
NameKind::Macro(mac) => mac.to_nav(db),
|
||||
NameKind::Field(field) => field.to_nav(db),
|
||||
NameKind::AssocItem(assoc) => assoc.to_nav(db),
|
||||
NameKind::Def(def) => NavigationTarget::from_def(db, def)?,
|
||||
NameKind::SelfType(ref ty) => match ty.as_adt() {
|
||||
Some((def_id, _)) => NavigationTarget::from_adt_def(db, def_id),
|
||||
Some((adt, _)) => adt.to_nav(db),
|
||||
None => return None,
|
||||
},
|
||||
NameKind::Pat((_, pat)) => NavigationTarget::from_pat(db, position.file_id, pat),
|
||||
NameKind::SelfParam(par) => NavigationTarget::from_self_param(position.file_id, par),
|
||||
NameKind::Local(local) => local.to_nav(db),
|
||||
NameKind::GenericParam(_) => return None,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
//! Functions that are used to classify an element from its definition or reference.
|
||||
|
||||
use hir::{Either, FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer};
|
||||
use hir::{FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer};
|
||||
use ra_db::FileId;
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{ast, match_ast, AstNode, AstPtr};
|
||||
use ra_syntax::{ast, match_ast, AstNode};
|
||||
use test_utils::tested_by;
|
||||
|
||||
use super::{
|
||||
name_definition::{from_assoc_item, from_module_def, from_pat, from_struct_field},
|
||||
name_definition::{from_assoc_item, from_module_def, from_struct_field},
|
||||
NameDefinition, NameKind,
|
||||
};
|
||||
use crate::db::RootDatabase;
|
||||
|
|
@ -25,7 +25,13 @@ pub(crate) fn classify_name(
|
|||
match_ast! {
|
||||
match parent {
|
||||
ast::BindPat(it) => {
|
||||
from_pat(db, file_id, AstPtr::new(&it))
|
||||
let src = hir::Source { file_id, ast: it };
|
||||
let local = hir::Local::from_source(db, src)?;
|
||||
Some(NameDefinition {
|
||||
visibility: None,
|
||||
container: local.module(db),
|
||||
kind: NameKind::Local(local),
|
||||
})
|
||||
},
|
||||
ast::RecordFieldDef(it) => {
|
||||
let ast = hir::FieldSource::Named(it);
|
||||
|
|
@ -159,10 +165,10 @@ pub(crate) fn classify_name_ref(
|
|||
match resolved {
|
||||
Def(def) => Some(from_module_def(db, def, Some(container))),
|
||||
AssocItem(item) => Some(from_assoc_item(db, item)),
|
||||
LocalBinding(Either::A(pat)) => from_pat(db, file_id, pat),
|
||||
LocalBinding(Either::B(par)) => {
|
||||
let kind = NameKind::SelfParam(par);
|
||||
Some(NameDefinition { kind, container, visibility })
|
||||
Local(local) => {
|
||||
let container = local.module(db);
|
||||
let kind = NameKind::Local(local);
|
||||
Some(NameDefinition { kind, container, visibility: None })
|
||||
}
|
||||
GenericParam(par) => {
|
||||
// FIXME: get generic param def
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@
|
|||
//! Note that the reference search is possible for not all of the classified items.
|
||||
|
||||
use hir::{
|
||||
db::AstDatabase, Adt, AssocItem, DefWithBody, FromSource, HasSource, HirFileId, MacroDef,
|
||||
Module, ModuleDef, StructField, Ty, VariantDef,
|
||||
Adt, AssocItem, HasSource, Local, MacroDef, Module, ModuleDef, StructField, Ty, VariantDef,
|
||||
};
|
||||
use ra_syntax::{ast, ast::VisibilityOwner, match_ast, AstNode, AstPtr};
|
||||
use ra_syntax::{ast, ast::VisibilityOwner};
|
||||
|
||||
use crate::db::RootDatabase;
|
||||
|
||||
|
|
@ -18,8 +17,7 @@ pub enum NameKind {
|
|||
AssocItem(AssocItem),
|
||||
Def(ModuleDef),
|
||||
SelfType(Ty),
|
||||
Pat((DefWithBody, AstPtr<ast::BindPat>)),
|
||||
SelfParam(AstPtr<ast::SelfParam>),
|
||||
Local(Local),
|
||||
GenericParam(u32),
|
||||
}
|
||||
|
||||
|
|
@ -30,36 +28,6 @@ pub(crate) struct NameDefinition {
|
|||
pub kind: NameKind,
|
||||
}
|
||||
|
||||
pub(super) fn from_pat(
|
||||
db: &RootDatabase,
|
||||
file_id: HirFileId,
|
||||
pat: AstPtr<ast::BindPat>,
|
||||
) -> Option<NameDefinition> {
|
||||
let root = db.parse_or_expand(file_id)?;
|
||||
let def = pat.to_node(&root).syntax().ancestors().find_map(|node| {
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::FnDef(it) => {
|
||||
let src = hir::Source { file_id, ast: it };
|
||||
Some(hir::Function::from_source(db, src)?.into())
|
||||
},
|
||||
ast::ConstDef(it) => {
|
||||
let src = hir::Source { file_id, ast: it };
|
||||
Some(hir::Const::from_source(db, src)?.into())
|
||||
},
|
||||
ast::StaticDef(it) => {
|
||||
let src = hir::Source { file_id, ast: it };
|
||||
Some(hir::Static::from_source(db, src)?.into())
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
})?;
|
||||
let kind = NameKind::Pat((def, pat));
|
||||
let container = def.module(db);
|
||||
Some(NameDefinition { kind, container, visibility: None })
|
||||
}
|
||||
|
||||
pub(super) fn from_assoc_item(db: &RootDatabase, item: AssocItem) -> NameDefinition {
|
||||
let container = item.module(db);
|
||||
let visibility = match item {
|
||||
|
|
|
|||
|
|
@ -71,13 +71,13 @@ impl NameDefinition {
|
|||
let module_src = self.container.definition_source(db);
|
||||
let file_id = module_src.file_id.original_file(db);
|
||||
|
||||
if let NameKind::Pat((def, _)) = self.kind {
|
||||
let mut res = FxHashMap::default();
|
||||
let range = match def {
|
||||
if let NameKind::Local(var) = self.kind {
|
||||
let range = match var.parent(db) {
|
||||
DefWithBody::Function(f) => f.source(db).ast.syntax().text_range(),
|
||||
DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(),
|
||||
DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(),
|
||||
};
|
||||
let mut res = FxHashMap::default();
|
||||
res.insert(file_id, Some(range));
|
||||
return SearchScope::new(res);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,14 +20,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
.keyword\.control { color: #F0DFAF; font-weight: bold; }
|
||||
</style>
|
||||
<pre><code><span class="keyword">fn</span> <span class="function">main</span>() {
|
||||
<span class="keyword">let</span> <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span> = <span class="string">"hello"</span>;
|
||||
<span class="keyword">let</span> <span class="variable" data-binding-hash="5695551762718493399" style="color: hsl(272,48%,45%);">x</span> = <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span>.<span class="text">to_string</span>();
|
||||
<span class="keyword">let</span> <span class="variable" data-binding-hash="5435401749617022797" style="color: hsl(353,77%,74%);">y</span> = <span class="variable" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span>.<span class="text">to_string</span>();
|
||||
<span class="keyword">let</span> <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>;
|
||||
<span class="keyword">let</span> <span class="variable" data-binding-hash="14702933417323009544" style="color: hsl(108,90%,49%);">x</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.<span class="text">to_string</span>();
|
||||
<span class="keyword">let</span> <span class="variable" data-binding-hash="5443150872754369068" style="color: hsl(215,43%,43%);">y</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.<span class="text">to_string</span>();
|
||||
|
||||
<span class="keyword">let</span> <span class="variable" data-binding-hash="1903207544374197704" style="color: hsl(58,61%,61%);">x</span> = <span class="string">"other color please!"</span>;
|
||||
<span class="keyword">let</span> <span class="variable" data-binding-hash="14878783531007968800" style="color: hsl(265,73%,83%);">y</span> = <span class="variable" data-binding-hash="1903207544374197704" style="color: hsl(58,61%,61%);">x</span>.<span class="text">to_string</span>();
|
||||
<span class="keyword">let</span> <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span> = <span class="string">"other color please!"</span>;
|
||||
<span class="keyword">let</span> <span class="variable" data-binding-hash="2073121142529774969" style="color: hsl(320,43%,74%);">y</span> = <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span>.<span class="text">to_string</span>();
|
||||
}
|
||||
|
||||
<span class="keyword">fn</span> <span class="function">bar</span>() {
|
||||
<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="3888301305669440875" style="color: hsl(242,59%,59%);">hello</span> = <span class="string">"hello"</span>;
|
||||
<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>;
|
||||
}</code></pre>
|
||||
|
|
@ -2,19 +2,17 @@
|
|||
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use hir::{Mutability, Ty};
|
||||
use hir::{Mutability, Name};
|
||||
use ra_db::SourceDatabase;
|
||||
use ra_prof::profile;
|
||||
use ra_syntax::{
|
||||
ast::{self, NameOwner},
|
||||
AstNode, Direction, SmolStr, SyntaxElement, SyntaxKind,
|
||||
SyntaxKind::*,
|
||||
TextRange, T,
|
||||
};
|
||||
use ra_syntax::{ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, TextRange, T};
|
||||
|
||||
use crate::{
|
||||
db::RootDatabase,
|
||||
references::{classify_name_ref, NameKind::*},
|
||||
references::{
|
||||
classify_name, classify_name_ref,
|
||||
NameKind::{self, *},
|
||||
},
|
||||
FileId,
|
||||
};
|
||||
|
||||
|
|
@ -40,32 +38,12 @@ fn is_control_keyword(kind: SyntaxKind) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_variable_mutable(
|
||||
db: &RootDatabase,
|
||||
analyzer: &hir::SourceAnalyzer,
|
||||
pat: ast::BindPat,
|
||||
) -> bool {
|
||||
if pat.is_mutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let ty = analyzer.type_of_pat(db, &pat.into()).unwrap_or(Ty::Unknown);
|
||||
if let Some((_, mutability)) = ty.as_reference() {
|
||||
match mutability {
|
||||
Mutability::Shared => false,
|
||||
Mutability::Mut => true,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
|
||||
let _p = profile("highlight");
|
||||
let parse = db.parse(file_id);
|
||||
let root = parse.tree().syntax().clone();
|
||||
|
||||
fn calc_binding_hash(file_id: FileId, text: &SmolStr, shadow_count: u32) -> u64 {
|
||||
fn calc_binding_hash(file_id: FileId, name: &Name, shadow_count: u32) -> u64 {
|
||||
fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
|
||||
use std::{collections::hash_map::DefaultHasher, hash::Hasher};
|
||||
|
||||
|
|
@ -74,13 +52,13 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
|
|||
hasher.finish()
|
||||
}
|
||||
|
||||
hash((file_id, text, shadow_count))
|
||||
hash((file_id, name, shadow_count))
|
||||
}
|
||||
|
||||
// Visited nodes to handle highlighting priorities
|
||||
// FIXME: retain only ranges here
|
||||
let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default();
|
||||
let mut bindings_shadow_count: FxHashMap<SmolStr, u32> = FxHashMap::default();
|
||||
let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
|
||||
|
||||
let mut res = Vec::new();
|
||||
for node in root.descendants_with_tokens() {
|
||||
|
|
@ -100,81 +78,38 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
|
|||
if node.ancestors().any(|it| it.kind() == ATTR) {
|
||||
continue;
|
||||
}
|
||||
if let Some(name_ref) = node.as_node().cloned().and_then(ast::NameRef::cast) {
|
||||
let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind);
|
||||
match name_kind {
|
||||
Some(Macro(_)) => "macro",
|
||||
Some(Field(_)) => "field",
|
||||
Some(AssocItem(hir::AssocItem::Function(_))) => "function",
|
||||
Some(AssocItem(hir::AssocItem::Const(_))) => "constant",
|
||||
Some(AssocItem(hir::AssocItem::TypeAlias(_))) => "type",
|
||||
Some(Def(hir::ModuleDef::Module(_))) => "module",
|
||||
Some(Def(hir::ModuleDef::Function(_))) => "function",
|
||||
Some(Def(hir::ModuleDef::Adt(_))) => "type",
|
||||
Some(Def(hir::ModuleDef::EnumVariant(_))) => "constant",
|
||||
Some(Def(hir::ModuleDef::Const(_))) => "constant",
|
||||
Some(Def(hir::ModuleDef::Static(_))) => "constant",
|
||||
Some(Def(hir::ModuleDef::Trait(_))) => "type",
|
||||
Some(Def(hir::ModuleDef::TypeAlias(_))) => "type",
|
||||
Some(Def(hir::ModuleDef::BuiltinType(_))) => "type",
|
||||
Some(SelfType(_)) => "type",
|
||||
Some(Pat((_, ptr))) => {
|
||||
let pat = ptr.to_node(&root);
|
||||
if let Some(name) = pat.name() {
|
||||
let text = name.text();
|
||||
let shadow_count =
|
||||
bindings_shadow_count.entry(text.clone()).or_default();
|
||||
binding_hash =
|
||||
Some(calc_binding_hash(file_id, &text, *shadow_count))
|
||||
}
|
||||
|
||||
let analyzer =
|
||||
hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
|
||||
if is_variable_mutable(db, &analyzer, ptr.to_node(&root)) {
|
||||
"variable.mut"
|
||||
} else {
|
||||
"variable"
|
||||
}
|
||||
}
|
||||
Some(SelfParam(_)) => "type",
|
||||
Some(GenericParam(_)) => "type",
|
||||
None => "text",
|
||||
let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
|
||||
let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind);
|
||||
|
||||
if let Some(Local(local)) = &name_kind {
|
||||
if let Some(name) = local.name(db) {
|
||||
let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
|
||||
binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
|
||||
}
|
||||
} else {
|
||||
"text"
|
||||
}
|
||||
};
|
||||
|
||||
name_kind.map_or("text", |it| highlight_name(db, it))
|
||||
}
|
||||
NAME => {
|
||||
if let Some(name) = node.as_node().cloned().and_then(ast::Name::cast) {
|
||||
let analyzer = hir::SourceAnalyzer::new(db, file_id, name.syntax(), None);
|
||||
if let Some(pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) {
|
||||
if let Some(name) = pat.name() {
|
||||
let text = name.text();
|
||||
let shadow_count =
|
||||
bindings_shadow_count.entry(text.clone()).or_default();
|
||||
*shadow_count += 1;
|
||||
binding_hash = Some(calc_binding_hash(file_id, &text, *shadow_count))
|
||||
}
|
||||
let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
|
||||
let name_kind = classify_name(db, file_id, &name).map(|d| d.kind);
|
||||
|
||||
if is_variable_mutable(db, &analyzer, pat) {
|
||||
"variable.mut"
|
||||
} else {
|
||||
"variable"
|
||||
}
|
||||
} else {
|
||||
name.syntax()
|
||||
.parent()
|
||||
.map(|x| match x.kind() {
|
||||
TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => {
|
||||
"type"
|
||||
}
|
||||
RECORD_FIELD_DEF => "field",
|
||||
_ => "function",
|
||||
})
|
||||
.unwrap_or("function")
|
||||
if let Some(Local(local)) = &name_kind {
|
||||
if let Some(name) = local.name(db) {
|
||||
let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
|
||||
*shadow_count += 1;
|
||||
binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
|
||||
}
|
||||
} else {
|
||||
"text"
|
||||
};
|
||||
|
||||
match name_kind {
|
||||
Some(name_kind) => highlight_name(db, name_kind),
|
||||
None => name.syntax().parent().map_or("function", |x| match x.kind() {
|
||||
TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => "type",
|
||||
RECORD_FIELD_DEF => "field",
|
||||
_ => "function",
|
||||
}),
|
||||
}
|
||||
}
|
||||
INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
|
||||
|
|
@ -272,6 +207,37 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
|
|||
buf
|
||||
}
|
||||
|
||||
fn highlight_name(db: &RootDatabase, name_kind: NameKind) -> &'static str {
|
||||
match name_kind {
|
||||
Macro(_) => "macro",
|
||||
Field(_) => "field",
|
||||
AssocItem(hir::AssocItem::Function(_)) => "function",
|
||||
AssocItem(hir::AssocItem::Const(_)) => "constant",
|
||||
AssocItem(hir::AssocItem::TypeAlias(_)) => "type",
|
||||
Def(hir::ModuleDef::Module(_)) => "module",
|
||||
Def(hir::ModuleDef::Function(_)) => "function",
|
||||
Def(hir::ModuleDef::Adt(_)) => "type",
|
||||
Def(hir::ModuleDef::EnumVariant(_)) => "constant",
|
||||
Def(hir::ModuleDef::Const(_)) => "constant",
|
||||
Def(hir::ModuleDef::Static(_)) => "constant",
|
||||
Def(hir::ModuleDef::Trait(_)) => "type",
|
||||
Def(hir::ModuleDef::TypeAlias(_)) => "type",
|
||||
Def(hir::ModuleDef::BuiltinType(_)) => "type",
|
||||
SelfType(_) => "type",
|
||||
GenericParam(_) => "type",
|
||||
Local(local) => {
|
||||
if local.is_mut(db) {
|
||||
"variable.mut"
|
||||
} else {
|
||||
match local.ty(db).as_reference() {
|
||||
Some((_, Mutability::Mut)) => "variable.mut",
|
||||
_ => "variable",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME: like, real html escaping
|
||||
fn html_escape(text: &str) -> String {
|
||||
text.replace("<", "<").replace(">", ">")
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ pub enum SyntaxErrorKind {
|
|||
InvalidBlockAttr,
|
||||
InvalidMatchInnerAttr,
|
||||
InvalidTupleIndexFormat,
|
||||
VisibilityNotAllowed,
|
||||
}
|
||||
|
||||
impl fmt::Display for SyntaxErrorKind {
|
||||
|
|
@ -99,6 +100,9 @@ impl fmt::Display for SyntaxErrorKind {
|
|||
}
|
||||
ParseError(msg) => write!(f, "{}", msg.0),
|
||||
EscapeError(err) => write!(f, "{}", err),
|
||||
VisibilityNotAllowed => {
|
||||
write!(f, "unnecessary visibility qualifier")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_lexer::unescape;
|
|||
|
||||
use crate::{
|
||||
ast, match_ast, AstNode, SyntaxError, SyntaxErrorKind,
|
||||
SyntaxKind::{BYTE, BYTE_STRING, CHAR, INT_NUMBER, STRING},
|
||||
SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF},
|
||||
SyntaxNode, SyntaxToken, TextUnit, T,
|
||||
};
|
||||
|
||||
|
|
@ -102,6 +102,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
|
|||
ast::BlockExpr(it) => { block::validate_block_expr(it, &mut errors) },
|
||||
ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) },
|
||||
ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) },
|
||||
ast::Visibility(it) => { validate_visibility(it, &mut errors) },
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -206,3 +207,23 @@ fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<Syntax
|
|||
name_ref?.syntax().first_child_or_token()?.into_token().filter(|it| it.kind() == INT_NUMBER)
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
|
||||
let parent = match vis.syntax().parent() {
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
match parent.kind() {
|
||||
FN_DEF | CONST_DEF | TYPE_ALIAS_DEF => (),
|
||||
_ => return,
|
||||
}
|
||||
let impl_block = match parent.parent().and_then(|it| it.parent()).and_then(ast::ImplBlock::cast)
|
||||
{
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
if impl_block.target_trait().is_some() {
|
||||
errors
|
||||
.push(SyntaxError::new(SyntaxErrorKind::VisibilityNotAllowed, vis.syntax.text_range()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
impl T for () {
|
||||
fn foo() {}
|
||||
pub fn bar() {}
|
||||
pub(crate) type Baz = ();
|
||||
pub(crate) const C: i32 = 92;
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
SOURCE_FILE@[0; 118)
|
||||
IMPL_BLOCK@[0; 117)
|
||||
IMPL_KW@[0; 4) "impl"
|
||||
WHITESPACE@[4; 5) " "
|
||||
PATH_TYPE@[5; 6)
|
||||
PATH@[5; 6)
|
||||
PATH_SEGMENT@[5; 6)
|
||||
NAME_REF@[5; 6)
|
||||
IDENT@[5; 6) "T"
|
||||
WHITESPACE@[6; 7) " "
|
||||
FOR_KW@[7; 10) "for"
|
||||
WHITESPACE@[10; 11) " "
|
||||
TUPLE_TYPE@[11; 13)
|
||||
L_PAREN@[11; 12) "("
|
||||
R_PAREN@[12; 13) ")"
|
||||
WHITESPACE@[13; 14) " "
|
||||
ITEM_LIST@[14; 117)
|
||||
L_CURLY@[14; 15) "{"
|
||||
WHITESPACE@[15; 20) "\n "
|
||||
FN_DEF@[20; 31)
|
||||
FN_KW@[20; 22) "fn"
|
||||
WHITESPACE@[22; 23) " "
|
||||
NAME@[23; 26)
|
||||
IDENT@[23; 26) "foo"
|
||||
PARAM_LIST@[26; 28)
|
||||
L_PAREN@[26; 27) "("
|
||||
R_PAREN@[27; 28) ")"
|
||||
WHITESPACE@[28; 29) " "
|
||||
BLOCK_EXPR@[29; 31)
|
||||
BLOCK@[29; 31)
|
||||
L_CURLY@[29; 30) "{"
|
||||
R_CURLY@[30; 31) "}"
|
||||
WHITESPACE@[31; 36) "\n "
|
||||
FN_DEF@[36; 51)
|
||||
VISIBILITY@[36; 39)
|
||||
PUB_KW@[36; 39) "pub"
|
||||
WHITESPACE@[39; 40) " "
|
||||
FN_KW@[40; 42) "fn"
|
||||
WHITESPACE@[42; 43) " "
|
||||
NAME@[43; 46)
|
||||
IDENT@[43; 46) "bar"
|
||||
PARAM_LIST@[46; 48)
|
||||
L_PAREN@[46; 47) "("
|
||||
R_PAREN@[47; 48) ")"
|
||||
WHITESPACE@[48; 49) " "
|
||||
BLOCK_EXPR@[49; 51)
|
||||
BLOCK@[49; 51)
|
||||
L_CURLY@[49; 50) "{"
|
||||
R_CURLY@[50; 51) "}"
|
||||
WHITESPACE@[51; 56) "\n "
|
||||
TYPE_ALIAS_DEF@[56; 81)
|
||||
VISIBILITY@[56; 66)
|
||||
PUB_KW@[56; 59) "pub"
|
||||
L_PAREN@[59; 60) "("
|
||||
CRATE_KW@[60; 65) "crate"
|
||||
R_PAREN@[65; 66) ")"
|
||||
WHITESPACE@[66; 67) " "
|
||||
TYPE_KW@[67; 71) "type"
|
||||
WHITESPACE@[71; 72) " "
|
||||
NAME@[72; 75)
|
||||
IDENT@[72; 75) "Baz"
|
||||
WHITESPACE@[75; 76) " "
|
||||
EQ@[76; 77) "="
|
||||
WHITESPACE@[77; 78) " "
|
||||
TUPLE_TYPE@[78; 80)
|
||||
L_PAREN@[78; 79) "("
|
||||
R_PAREN@[79; 80) ")"
|
||||
SEMI@[80; 81) ";"
|
||||
WHITESPACE@[81; 86) "\n "
|
||||
CONST_DEF@[86; 115)
|
||||
VISIBILITY@[86; 96)
|
||||
PUB_KW@[86; 89) "pub"
|
||||
L_PAREN@[89; 90) "("
|
||||
CRATE_KW@[90; 95) "crate"
|
||||
R_PAREN@[95; 96) ")"
|
||||
WHITESPACE@[96; 97) " "
|
||||
CONST_KW@[97; 102) "const"
|
||||
WHITESPACE@[102; 103) " "
|
||||
NAME@[103; 104)
|
||||
IDENT@[103; 104) "C"
|
||||
COLON@[104; 105) ":"
|
||||
WHITESPACE@[105; 106) " "
|
||||
PATH_TYPE@[106; 109)
|
||||
PATH@[106; 109)
|
||||
PATH_SEGMENT@[106; 109)
|
||||
NAME_REF@[106; 109)
|
||||
IDENT@[106; 109) "i32"
|
||||
WHITESPACE@[109; 110) " "
|
||||
EQ@[110; 111) "="
|
||||
WHITESPACE@[111; 112) " "
|
||||
LITERAL@[112; 114)
|
||||
INT_NUMBER@[112; 114) "92"
|
||||
SEMI@[114; 115) ";"
|
||||
WHITESPACE@[115; 116) "\n"
|
||||
R_CURLY@[116; 117) "}"
|
||||
WHITESPACE@[117; 118) "\n"
|
||||
error [36; 39): unnecessary visibility qualifier
|
||||
error [56; 66): unnecessary visibility qualifier
|
||||
error [86; 96): unnecessary visibility qualifier
|
||||
Loading…
Add table
Add a link
Reference in a new issue