diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/generate_impl.rs | 42 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/lower.rs | 47 | ||||
-rw-r--r-- | crates/hir_expand/src/name.rs | 36 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check.rs | 28 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check/case_conv.rs | 9 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/unsafe_check.rs | 18 | ||||
-rw-r--r-- | crates/ide/src/doc_links.rs | 28 |
7 files changed, 197 insertions, 11 deletions
diff --git a/crates/assists/src/handlers/generate_impl.rs b/crates/assists/src/handlers/generate_impl.rs index 9989109b5..114974465 100644 --- a/crates/assists/src/handlers/generate_impl.rs +++ b/crates/assists/src/handlers/generate_impl.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use itertools::Itertools; | 1 | use itertools::Itertools; |
2 | use stdx::format_to; | 2 | use stdx::format_to; |
3 | use syntax::ast::{self, AstNode, GenericParamsOwner, NameOwner}; | 3 | use syntax::ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner}; |
4 | 4 | ||
5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
6 | 6 | ||
@@ -27,6 +27,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<() | |||
27 | let nominal = ctx.find_node_at_offset::<ast::AdtDef>()?; | 27 | let nominal = ctx.find_node_at_offset::<ast::AdtDef>()?; |
28 | let name = nominal.name()?; | 28 | let name = nominal.name()?; |
29 | let target = nominal.syntax().text_range(); | 29 | let target = nominal.syntax().text_range(); |
30 | |||
30 | acc.add( | 31 | acc.add( |
31 | AssistId("generate_impl", AssistKind::Generate), | 32 | AssistId("generate_impl", AssistKind::Generate), |
32 | format!("Generate impl for `{}`", name), | 33 | format!("Generate impl for `{}`", name), |
@@ -35,7 +36,15 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<() | |||
35 | let type_params = nominal.generic_param_list(); | 36 | let type_params = nominal.generic_param_list(); |
36 | let start_offset = nominal.syntax().text_range().end(); | 37 | let start_offset = nominal.syntax().text_range().end(); |
37 | let mut buf = String::new(); | 38 | let mut buf = String::new(); |
38 | buf.push_str("\n\nimpl"); | 39 | buf.push_str("\n\n"); |
40 | nominal | ||
41 | .attrs() | ||
42 | .filter(|attr| { | ||
43 | attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false) | ||
44 | }) | ||
45 | .for_each(|attr| buf.push_str(format!("{}\n", attr.to_string()).as_str())); | ||
46 | |||
47 | buf.push_str("impl"); | ||
39 | if let Some(type_params) = &type_params { | 48 | if let Some(type_params) = &type_params { |
40 | format_to!(buf, "{}", type_params.syntax()); | 49 | format_to!(buf, "{}", type_params.syntax()); |
41 | } | 50 | } |
@@ -91,6 +100,35 @@ mod tests { | |||
91 | "struct Foo<'a, T: Foo<'a>> {<|>}", | 100 | "struct Foo<'a, T: Foo<'a>> {<|>}", |
92 | "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}", | 101 | "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}", |
93 | ); | 102 | ); |
103 | check_assist( | ||
104 | generate_impl, | ||
105 | r#" | ||
106 | #[cfg(feature = "foo")] | ||
107 | struct Foo<'a, T: Foo<'a>> {<|>}"#, | ||
108 | r#" | ||
109 | #[cfg(feature = "foo")] | ||
110 | struct Foo<'a, T: Foo<'a>> {} | ||
111 | |||
112 | #[cfg(feature = "foo")] | ||
113 | impl<'a, T: Foo<'a>> Foo<'a, T> { | ||
114 | $0 | ||
115 | }"#, | ||
116 | ); | ||
117 | |||
118 | check_assist( | ||
119 | generate_impl, | ||
120 | r#" | ||
121 | #[cfg(not(feature = "foo"))] | ||
122 | struct Foo<'a, T: Foo<'a>> {<|>}"#, | ||
123 | r#" | ||
124 | #[cfg(not(feature = "foo"))] | ||
125 | struct Foo<'a, T: Foo<'a>> {} | ||
126 | |||
127 | #[cfg(not(feature = "foo"))] | ||
128 | impl<'a, T: Foo<'a>> Foo<'a, T> { | ||
129 | $0 | ||
130 | }"#, | ||
131 | ); | ||
94 | } | 132 | } |
95 | 133 | ||
96 | #[test] | 134 | #[test] |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 3328639cf..ca7fb4a43 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use std::{collections::hash_map::Entry, mem, sync::Arc}; | 3 | use std::{collections::hash_map::Entry, mem, sync::Arc}; |
4 | 4 | ||
5 | use arena::map::ArenaMap; | 5 | use arena::map::ArenaMap; |
6 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId}; | 6 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, name::known, HirFileId}; |
7 | use smallvec::SmallVec; | 7 | use smallvec::SmallVec; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | ast::{self, ModuleItemOwner}, | 9 | ast::{self, ModuleItemOwner}, |
@@ -555,7 +555,8 @@ impl Ctx { | |||
555 | let id: ModItem = match item { | 555 | let id: ModItem = match item { |
556 | ast::ExternItem::Fn(ast) => { | 556 | ast::ExternItem::Fn(ast) => { |
557 | let func = self.lower_function(&ast)?; | 557 | let func = self.lower_function(&ast)?; |
558 | self.data().functions[func.index].is_unsafe = true; | 558 | self.data().functions[func.index].is_unsafe = |
559 | is_intrinsic_fn_unsafe(&self.data().functions[func.index].name); | ||
559 | func.into() | 560 | func.into() |
560 | } | 561 | } |
561 | ast::ExternItem::Static(ast) => { | 562 | ast::ExternItem::Static(ast) => { |
@@ -713,3 +714,45 @@ enum GenericsOwner<'a> { | |||
713 | TypeAlias, | 714 | TypeAlias, |
714 | Impl, | 715 | Impl, |
715 | } | 716 | } |
717 | |||
718 | /// Returns `true` if the given intrinsic is unsafe to call, or false otherwise. | ||
719 | fn is_intrinsic_fn_unsafe(name: &Name) -> bool { | ||
720 | // Should be kept in sync with https://github.com/rust-lang/rust/blob/c6e4db620a7d2f569f11dcab627430921ea8aacf/compiler/rustc_typeck/src/check/intrinsic.rs#L68 | ||
721 | ![ | ||
722 | known::abort, | ||
723 | known::min_align_of, | ||
724 | known::needs_drop, | ||
725 | known::caller_location, | ||
726 | known::size_of_val, | ||
727 | known::min_align_of_val, | ||
728 | known::add_with_overflow, | ||
729 | known::sub_with_overflow, | ||
730 | known::mul_with_overflow, | ||
731 | known::wrapping_add, | ||
732 | known::wrapping_sub, | ||
733 | known::wrapping_mul, | ||
734 | known::saturating_add, | ||
735 | known::saturating_sub, | ||
736 | known::rotate_left, | ||
737 | known::rotate_right, | ||
738 | known::ctpop, | ||
739 | known::ctlz, | ||
740 | known::cttz, | ||
741 | known::bswap, | ||
742 | known::bitreverse, | ||
743 | known::discriminant_value, | ||
744 | known::type_id, | ||
745 | known::likely, | ||
746 | known::unlikely, | ||
747 | known::ptr_guaranteed_eq, | ||
748 | known::ptr_guaranteed_ne, | ||
749 | known::minnumf32, | ||
750 | known::minnumf64, | ||
751 | known::maxnumf32, | ||
752 | known::rustc_peek, | ||
753 | known::maxnumf64, | ||
754 | known::type_name, | ||
755 | known::variant_count, | ||
756 | ] | ||
757 | .contains(&name) | ||
758 | } | ||
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index 63f828707..b26ffa1ef 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -208,6 +208,42 @@ pub mod known { | |||
208 | PartialOrd, | 208 | PartialOrd, |
209 | Eq, | 209 | Eq, |
210 | PartialEq, | 210 | PartialEq, |
211 | // Safe intrinsics | ||
212 | abort, | ||
213 | size_of, | ||
214 | min_align_of, | ||
215 | needs_drop, | ||
216 | caller_location, | ||
217 | size_of_val, | ||
218 | min_align_of_val, | ||
219 | add_with_overflow, | ||
220 | sub_with_overflow, | ||
221 | mul_with_overflow, | ||
222 | wrapping_add, | ||
223 | wrapping_sub, | ||
224 | wrapping_mul, | ||
225 | saturating_add, | ||
226 | saturating_sub, | ||
227 | rotate_left, | ||
228 | rotate_right, | ||
229 | ctpop, | ||
230 | ctlz, | ||
231 | cttz, | ||
232 | bswap, | ||
233 | bitreverse, | ||
234 | discriminant_value, | ||
235 | type_id, | ||
236 | likely, | ||
237 | unlikely, | ||
238 | ptr_guaranteed_eq, | ||
239 | ptr_guaranteed_ne, | ||
240 | minnumf32, | ||
241 | minnumf64, | ||
242 | maxnumf32, | ||
243 | rustc_peek, | ||
244 | maxnumf64, | ||
245 | type_name, | ||
246 | variant_count, | ||
211 | ); | 247 | ); |
212 | 248 | ||
213 | // self/Self cannot be used as an identifier | 249 | // self/Self cannot be used as an identifier |
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index f987636fe..f179c62b7 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -708,11 +708,23 @@ fn foo() { | |||
708 | } | 708 | } |
709 | 709 | ||
710 | #[test] | 710 | #[test] |
711 | fn incorrect_struct_name() { | 711 | fn incorrect_struct_names() { |
712 | check_diagnostics( | 712 | check_diagnostics( |
713 | r#" | 713 | r#" |
714 | struct non_camel_case_name {} | 714 | struct non_camel_case_name {} |
715 | // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` | 715 | // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` |
716 | |||
717 | struct SCREAMING_CASE {} | ||
718 | // ^^^^^^^^^^^^^^ Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase` | ||
719 | "#, | ||
720 | ); | ||
721 | } | ||
722 | |||
723 | #[test] | ||
724 | fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() { | ||
725 | check_diagnostics( | ||
726 | r#" | ||
727 | struct AABB {} | ||
716 | "#, | 728 | "#, |
717 | ); | 729 | ); |
718 | } | 730 | } |
@@ -728,11 +740,23 @@ struct SomeStruct { SomeField: u8 } | |||
728 | } | 740 | } |
729 | 741 | ||
730 | #[test] | 742 | #[test] |
731 | fn incorrect_enum_name() { | 743 | fn incorrect_enum_names() { |
732 | check_diagnostics( | 744 | check_diagnostics( |
733 | r#" | 745 | r#" |
734 | enum some_enum { Val(u8) } | 746 | enum some_enum { Val(u8) } |
735 | // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` | 747 | // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` |
748 | |||
749 | enum SOME_ENUM | ||
750 | // ^^^^^^^^^ Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum` | ||
751 | "#, | ||
752 | ); | ||
753 | } | ||
754 | |||
755 | #[test] | ||
756 | fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() { | ||
757 | check_diagnostics( | ||
758 | r#" | ||
759 | enum AABB {} | ||
736 | "#, | 760 | "#, |
737 | ); | 761 | ); |
738 | } | 762 | } |
diff --git a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs index 3800f2a6b..324d60765 100644 --- a/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs +++ b/crates/hir_ty/src/diagnostics/decl_check/case_conv.rs | |||
@@ -29,7 +29,13 @@ fn detect_case(ident: &str) -> DetectedCase { | |||
29 | 29 | ||
30 | if has_uppercase { | 30 | if has_uppercase { |
31 | if !has_lowercase { | 31 | if !has_lowercase { |
32 | DetectedCase::UpperSnakeCase | 32 | if has_underscore { |
33 | DetectedCase::UpperSnakeCase | ||
34 | } else { | ||
35 | // It has uppercase only and no underscores. Ex: "AABB" | ||
36 | // This is a camel cased acronym. | ||
37 | DetectedCase::UpperCamelCase | ||
38 | } | ||
33 | } else if !has_underscore { | 39 | } else if !has_underscore { |
34 | if first_lowercase { | 40 | if first_lowercase { |
35 | DetectedCase::LowerCamelCase | 41 | DetectedCase::LowerCamelCase |
@@ -180,6 +186,7 @@ mod tests { | |||
180 | check(to_camel_case, "Weird_Case", expect![["WeirdCase"]]); | 186 | check(to_camel_case, "Weird_Case", expect![["WeirdCase"]]); |
181 | check(to_camel_case, "name", expect![["Name"]]); | 187 | check(to_camel_case, "name", expect![["Name"]]); |
182 | check(to_camel_case, "A", expect![[""]]); | 188 | check(to_camel_case, "A", expect![[""]]); |
189 | check(to_camel_case, "AABB", expect![[""]]); | ||
183 | } | 190 | } |
184 | 191 | ||
185 | #[test] | 192 | #[test] |
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs index 21a121aad..2da9688ca 100644 --- a/crates/hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -202,4 +202,22 @@ fn main() { | |||
202 | "#, | 202 | "#, |
203 | ); | 203 | ); |
204 | } | 204 | } |
205 | |||
206 | #[test] | ||
207 | fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { | ||
208 | check_diagnostics( | ||
209 | r#" | ||
210 | extern "rust-intrinsic" { | ||
211 | pub fn bitreverse(x: u32) -> u32; // Safe intrinsic | ||
212 | pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic | ||
213 | } | ||
214 | |||
215 | fn main() { | ||
216 | let _ = bitreverse(12); | ||
217 | let _ = floorf32(12.0); | ||
218 | //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
219 | } | ||
220 | "#, | ||
221 | ); | ||
222 | } | ||
205 | } | 223 | } |
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index b9d8b8a2b..250f10f9f 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -132,7 +132,8 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { | |||
132 | let import_map = db.import_map(krate.into()); | 132 | let import_map = db.import_map(krate.into()); |
133 | let base = once(krate.display_name(db)?.to_string()) | 133 | let base = once(krate.display_name(db)?.to_string()) |
134 | .chain(import_map.path_of(ns)?.segments.iter().map(|name| name.to_string())) | 134 | .chain(import_map.path_of(ns)?.segments.iter().map(|name| name.to_string())) |
135 | .join("/"); | 135 | .join("/") |
136 | + "/"; | ||
136 | 137 | ||
137 | let filename = get_symbol_filename(db, &target_def); | 138 | let filename = get_symbol_filename(db, &target_def); |
138 | let fragment = match definition { | 139 | let fragment = match definition { |
@@ -152,9 +153,16 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { | |||
152 | _ => None, | 153 | _ => None, |
153 | }; | 154 | }; |
154 | 155 | ||
155 | get_doc_url(db, &krate) | 156 | get_doc_url(db, &krate)? |
156 | .and_then(|url| url.join(&base).ok()) | 157 | .join(&base) |
157 | .and_then(|url| filename.as_deref().and_then(|f| url.join(f).ok())) | 158 | .ok() |
159 | .and_then(|mut url| { | ||
160 | if !matches!(definition, Definition::ModuleDef(ModuleDef::Module(..))) { | ||
161 | url.path_segments_mut().ok()?.pop(); | ||
162 | }; | ||
163 | Some(url) | ||
164 | }) | ||
165 | .and_then(|url| url.join(filename.as_deref()?).ok()) | ||
158 | .and_then( | 166 | .and_then( |
159 | |url| if let Some(fragment) = fragment { url.join(&fragment).ok() } else { Some(url) }, | 167 | |url| if let Some(fragment) = fragment { url.join(&fragment).ok() } else { Some(url) }, |
160 | ) | 168 | ) |
@@ -522,6 +530,18 @@ pub struct Foo { | |||
522 | ); | 530 | ); |
523 | } | 531 | } |
524 | 532 | ||
533 | #[test] | ||
534 | fn test_module() { | ||
535 | check( | ||
536 | r#" | ||
537 | pub mod foo { | ||
538 | pub mod ba<|>r {} | ||
539 | } | ||
540 | "#, | ||
541 | expect![[r#"https://docs.rs/test/*/test/foo/bar/index.html"#]], | ||
542 | ) | ||
543 | } | ||
544 | |||
525 | // FIXME: ImportMap will return re-export paths instead of public module | 545 | // FIXME: ImportMap will return re-export paths instead of public module |
526 | // paths. The correct path to documentation will never be a re-export. | 546 | // paths. The correct path to documentation will never be a re-export. |
527 | // This problem stops us from resolving stdlib items included in the prelude | 547 | // This problem stops us from resolving stdlib items included in the prelude |