diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 4e273d9e40..9d0db80249 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -2,6 +2,7 @@ pub(crate) mod src; pub(crate) mod docs; +pub(crate) mod attrs; use std::sync::Arc; diff --git a/crates/ra_hir/src/code_model/attrs.rs b/crates/ra_hir/src/code_model/attrs.rs new file mode 100644 index 0000000000..f7db36b668 --- /dev/null +++ b/crates/ra_hir/src/code_model/attrs.rs @@ -0,0 +1,92 @@ +//! FIXME: write short doc here + +use crate::{ + db::{AstDatabase, DefDatabase, HirDatabase}, + Adt, Const, Enum, EnumVariant, FieldSource, Function, HasSource, MacroDef, Module, Static, + Struct, StructField, Trait, TypeAlias, Union, +}; +use hir_def::attr::Attr; +use hir_expand::hygiene::Hygiene; +use ra_syntax::ast; +use std::sync::Arc; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum AttrDef { + Module(Module), + StructField(StructField), + Adt(Adt), + Function(Function), + EnumVariant(EnumVariant), + Static(Static), + Const(Const), + Trait(Trait), + TypeAlias(TypeAlias), + MacroDef(MacroDef), +} + +impl_froms!( + AttrDef: Module, + StructField, + Adt(Struct, Enum, Union), + EnumVariant, + Static, + Const, + Function, + Trait, + TypeAlias, + MacroDef +); + +pub trait Attrs { + fn attrs(&self, db: &impl HirDatabase) -> Option>; +} + +pub(crate) fn attributes_query( + db: &(impl DefDatabase + AstDatabase), + def: AttrDef, +) -> Option> { + match def { + AttrDef::Module(it) => { + let src = it.declaration_source(db)?; + let hygiene = Hygiene::new(db, src.file_id); + Attr::from_attrs_owner(&src.ast, &hygiene) + } + AttrDef::StructField(it) => match it.source(db).ast { + FieldSource::Named(named) => { + let src = it.source(db); + let hygiene = Hygiene::new(db, src.file_id); + Attr::from_attrs_owner(&named, &hygiene) + } + FieldSource::Pos(..) => None, + }, + AttrDef::Adt(it) => match it { + Adt::Struct(it) => attrs_from_ast(it, db), + Adt::Enum(it) => attrs_from_ast(it, db), + Adt::Union(it) => attrs_from_ast(it, db), + }, + AttrDef::EnumVariant(it) => attrs_from_ast(it, db), + AttrDef::Static(it) => attrs_from_ast(it, db), + AttrDef::Const(it) => attrs_from_ast(it, db), + AttrDef::Function(it) => attrs_from_ast(it, db), + AttrDef::Trait(it) => attrs_from_ast(it, db), + AttrDef::TypeAlias(it) => attrs_from_ast(it, db), + AttrDef::MacroDef(it) => attrs_from_ast(it, db), + } +} + +fn attrs_from_ast(node: T, db: &D) -> Option> +where + T: HasSource, + T::Ast: ast::AttrsOwner, + D: DefDatabase + AstDatabase, +{ + let src = node.source(db); + let hygiene = Hygiene::new(db, src.file_id); + Attr::from_attrs_owner(&src.ast, &hygiene) +} + +impl + Copy> Attrs for T { + fn attrs(&self, db: &impl HirDatabase) -> Option> { + db.attrs((*self).into()) + } +} diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 11b3f94ae9..75c322c999 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -2,6 +2,7 @@ use std::sync::Arc; +use hir_def::attr::Attr; use ra_db::salsa; use ra_syntax::SmolStr; @@ -75,6 +76,9 @@ pub trait DefDatabase: HirDebugDatabase + DefDatabase2 { #[salsa::invoke(crate::code_model::docs::documentation_query)] fn documentation(&self, def: crate::DocDef) -> Option; + + #[salsa::invoke(crate::code_model::attrs::attributes_query)] + fn attrs(&self, def: crate::AttrDef) -> Option>; } #[salsa::query_group(HirDatabaseStorage)] diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 5f2a05e76f..131f6c797a 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -61,6 +61,7 @@ use crate::{ids::MacroFileKind, resolve::Resolver}; pub use crate::{ adt::VariantDef, code_model::{ + attrs::{AttrDef, Attrs}, docs::{DocDef, Docs, Documentation}, src::{HasBodySource, HasSource}, Adt, AssocItem, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum, diff --git a/crates/ra_ide_api/src/completion/complete_postfix.rs b/crates/ra_ide_api/src/completion/complete_postfix.rs index 60ed3518b8..4f95654419 100644 --- a/crates/ra_ide_api/src/completion/complete_postfix.rs +++ b/crates/ra_ide_api/src/completion/complete_postfix.rs @@ -1,5 +1,9 @@ //! FIXME: write short doc here +use hir::{Ty, TypeCtor}; +use ra_syntax::{ast::AstNode, TextRange, TextUnit}; +use ra_text_edit::TextEdit; + use crate::{ completion::{ completion_context::CompletionContext, @@ -7,9 +11,53 @@ use crate::{ }, CompletionItem, }; -use hir::{Ty, TypeCtor}; -use ra_syntax::{ast::AstNode, TextRange, TextUnit}; -use ra_text_edit::TextEdit; + +pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { + let dot_receiver = match &ctx.dot_receiver { + Some(it) => it, + None => return, + }; + + let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal { + let text = dot_receiver.syntax().text(); + let without_dot = ..text.len() - TextUnit::of_char('.'); + text.slice(without_dot).to_string() + } else { + dot_receiver.syntax().text().to_string() + }; + + let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver); + + if is_bool_or_unknown(receiver_ty) { + postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text)) + .add_to(acc); + postfix_snippet( + ctx, + "while", + "while expr {}", + &format!("while {} {{\n$0\n}}", receiver_text), + ) + .add_to(acc); + } + + postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); + + postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); + postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc); + + postfix_snippet( + ctx, + "match", + "match expr {}", + &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), + ) + .add_to(acc); + + postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc); + + postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text)) + .add_to(acc); +} fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { let edit = { @@ -24,62 +72,19 @@ fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: } fn is_bool_or_unknown(ty: Option) -> bool { - if let Some(ty) = ty { - match ty { - Ty::Apply(at) => match at.ctor { - TypeCtor::Bool => true, - _ => false, - }, - Ty::Unknown => true, - _ => false, - } - } else { - true - } -} - -pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { - if let Some(dot_receiver) = &ctx.dot_receiver { - let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal { - let text = dot_receiver.syntax().text(); - let without_dot = ..text.len() - TextUnit::of_char('.'); - text.slice(without_dot).to_string() - } else { - dot_receiver.syntax().text().to_string() - }; - let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver); - if is_bool_or_unknown(receiver_ty) { - postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text)) - .add_to(acc); - postfix_snippet( - ctx, - "while", - "while expr {}", - &format!("while {} {{\n$0\n}}", receiver_text), - ) - .add_to(acc); - } - postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); - postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); - postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc); - postfix_snippet( - ctx, - "match", - "match expr {}", - &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), - ) - .add_to(acc); - postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc); - postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text)) - .add_to(acc); + match &ty { + Some(Ty::Apply(app)) if app.ctor == TypeCtor::Bool => true, + Some(Ty::Unknown) | None => true, + Some(_) => false, } } #[cfg(test)] mod tests { - use crate::completion::{do_completion, CompletionItem, CompletionKind}; use insta::assert_debug_snapshot; + use crate::completion::{do_completion, CompletionItem, CompletionKind}; + fn do_postfix_completion(code: &str) -> Vec { do_completion(code, CompletionKind::Postfix) } diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index cb55d18751..d861303b75 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs @@ -1,8 +1,8 @@ //! This modules takes care of rendering various definitions as completion items. -use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk}; +use hir::{db::HirDatabase, Attrs, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk}; use join_to_string::join; -use ra_syntax::ast::{AttrsOwner, NameOwner}; +use ra_syntax::ast::NameOwner; use test_utils::tested_by; use crate::completion::{ @@ -18,11 +18,7 @@ impl Completions { field: hir::StructField, substs: &hir::Substs, ) { - let ast_node = field.source(ctx.db).ast; - let is_deprecated = match ast_node { - hir::FieldSource::Named(m) => is_deprecated(m), - hir::FieldSource::Pos(m) => is_deprecated(m), - }; + let is_deprecated = is_deprecated(field, ctx.db); CompletionItem::new( CompletionKind::Reference, ctx.source_range(), @@ -185,7 +181,7 @@ impl Completions { CompletionItem::new(CompletionKind::Reference, ctx.source_range(), ¯o_declaration) .kind(CompletionItemKind::Macro) .set_documentation(docs.clone()) - .set_deprecated(is_deprecated(ast_node)) + .set_deprecated(is_deprecated(macro_, ctx.db)) .detail(detail); builder = if ctx.use_item_syntax.is_some() { @@ -218,7 +214,7 @@ impl Completions { CompletionItemKind::Function }) .set_documentation(func.docs(ctx.db)) - .set_deprecated(is_deprecated(ast_node)) + .set_deprecated(is_deprecated(func, ctx.db)) .detail(detail); // Add `<>` for generic types @@ -250,7 +246,7 @@ impl Completions { CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string()) .kind(CompletionItemKind::Const) .set_documentation(constant.docs(ctx.db)) - .set_deprecated(is_deprecated(ast_node)) + .set_deprecated(is_deprecated(constant, ctx.db)) .detail(detail) .add_to(self); } @@ -266,13 +262,13 @@ impl Completions { CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string()) .kind(CompletionItemKind::TypeAlias) .set_documentation(type_alias.docs(ctx.db)) - .set_deprecated(is_deprecated(type_def)) + .set_deprecated(is_deprecated(type_alias, ctx.db)) .detail(detail) .add_to(self); } pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) { - let is_deprecated = is_deprecated(variant.source(ctx.db).ast); + let is_deprecated = is_deprecated(variant, ctx.db); let name = match variant.name(ctx.db) { Some(it) => it, None => return, @@ -291,8 +287,11 @@ impl Completions { } } -fn is_deprecated(node: impl AttrsOwner) -> bool { - node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated") +fn is_deprecated(node: impl Attrs, db: &impl HirDatabase) -> bool { + match node.attrs(db) { + None => false, + Some(attrs) => attrs.iter().any(|x| x.is_simple_atom("deprecated")), + } } fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool { diff --git a/crates/ra_parser/src/grammar/types.rs b/crates/ra_parser/src/grammar/types.rs index d4ca94fca5..9b2e440fb9 100644 --- a/crates/ra_parser/src/grammar/types.rs +++ b/crates/ra_parser/src/grammar/types.rs @@ -28,7 +28,7 @@ fn type_with_bounds_cond(p: &mut Parser, allow_bounds: bool) { T![fn] | T![unsafe] | T![extern] => fn_pointer_type(p), T![for] => for_type(p), T![impl] => impl_trait_type(p), - T![dyn ] => dyn_trait_type(p), + T![dyn] => dyn_trait_type(p), // Some path types are not allowed to have bounds (no plus) T![<] => path_type_(p, allow_bounds), _ if paths::is_use_path_start(p) => path_or_macro_type_(p, allow_bounds), @@ -234,9 +234,9 @@ fn impl_trait_type(p: &mut Parser) { // test dyn_trait_type // type A = dyn Iterator> + 'a; fn dyn_trait_type(p: &mut Parser) { - assert!(p.at(T![dyn ])); + assert!(p.at(T![dyn])); let m = p.start(); - p.bump(T![dyn ]); + p.bump(T![dyn]); type_params::bounds_without_colon(p); m.complete(p, DYN_TRAIT_TYPE); } diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 4ec8ab75af..770b55a9ac 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs @@ -46,7 +46,7 @@ pub enum Mode { /// With verify = false, fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { match fs::read_to_string(path) { - Ok(ref old_contents) if old_contents == contents => { + Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => { return Ok(()); } _ => (), @@ -56,7 +56,11 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { } eprintln!("updating {}", path.display()); fs::write(path, contents)?; - Ok(()) + return Ok(()); + + fn normalize(s: &str) -> String { + s.replace("\r\n", "\n") + } } fn reformat(text: impl std::fmt::Display) -> Result {