Merge branch 'master' of https://github.com/rust-analyzer/rust-analyzer into feature/themes

This commit is contained in:
Seivan Heidari 2019-11-11 14:31:09 +01:00
commit 68a5ff050f
34 changed files with 1071 additions and 438 deletions

View file

@ -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:

View file

@ -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

View file

@ -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)))
}
}

View file

@ -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>,
}

View file

@ -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 {

View file

@ -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,
},
};

View file

@ -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()),

View file

@ -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
"###
);
}

View file

@ -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)
}
}

View file

@ -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;

View file

@ -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));
}

View 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)
}

View file

@ -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(&macro_arg.0).map_err(|err| format!("{:?}", err))?;
let tt = macro_rules.0.expand(db, id, &macro_arg.0).map_err(|err| format!("{:?}", err))?;
// Set a hard limit for the expanded tt
let count = tt.count();
if count > 65536 {

View file

@ -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 }

View file

@ -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>,
}

View file

@ -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");

View 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 () ,}}}");
}
}

View file

@ -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 {

View file

@ -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());

View file

@ -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]);
}
}

View file

@ -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]))
}

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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<_>>()
})
}

View file

@ -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,
};

View file

@ -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

View file

@ -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 {

View file

@ -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);
}

View file

@ -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>

View file

@ -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("<", "&lt;").replace(">", "&gt;")

View file

@ -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")
}
}
}
}

View file

@ -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()))
}
}

View file

@ -0,0 +1,6 @@
impl T for () {
fn foo() {}
pub fn bar() {}
pub(crate) type Baz = ();
pub(crate) const C: i32 = 92;
}

View file

@ -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