diff options
-rw-r--r-- | crates/hir_def/src/lib.rs | 10 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/decl_check.rs | 132 | ||||
-rw-r--r-- | docs/user/manual.adoc | 2 |
3 files changed, 133 insertions, 11 deletions
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index e2af0e514..eefc75706 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -435,6 +435,16 @@ impl_from!( | |||
435 | for AttrDefId | 435 | for AttrDefId |
436 | ); | 436 | ); |
437 | 437 | ||
438 | impl From<AssocContainerId> for AttrDefId { | ||
439 | fn from(acid: AssocContainerId) -> Self { | ||
440 | match acid { | ||
441 | AssocContainerId::ModuleId(mid) => AttrDefId::ModuleId(mid), | ||
442 | AssocContainerId::ImplId(iid) => AttrDefId::ImplId(iid), | ||
443 | AssocContainerId::TraitId(tid) => AttrDefId::TraitId(tid), | ||
444 | } | ||
445 | } | ||
446 | } | ||
447 | |||
438 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 448 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
439 | pub enum VariantId { | 449 | pub enum VariantId { |
440 | EnumVariantId(EnumVariantId), | 450 | EnumVariantId(EnumVariantId), |
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index 1c9f9ede7..31532f327 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -35,6 +35,8 @@ use crate::{ | |||
35 | }; | 35 | }; |
36 | 36 | ||
37 | mod allow { | 37 | mod allow { |
38 | pub(super) const BAD_STYLE: &str = "bad_style"; | ||
39 | pub(super) const NONSTANDARD_STYLE: &str = "nonstandard_style"; | ||
38 | pub(super) const NON_SNAKE_CASE: &str = "non_snake_case"; | 40 | pub(super) const NON_SNAKE_CASE: &str = "non_snake_case"; |
39 | pub(super) const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals"; | 41 | pub(super) const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals"; |
40 | pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; | 42 | pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; |
@@ -83,10 +85,39 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
83 | } | 85 | } |
84 | 86 | ||
85 | /// Checks whether not following the convention is allowed for this item. | 87 | /// Checks whether not following the convention is allowed for this item. |
86 | /// | 88 | fn allowed(&self, id: AttrDefId, allow_name: &str, recursing: bool) -> bool { |
87 | /// Currently this method doesn't check parent attributes. | 89 | let is_allowed = |def_id| { |
88 | fn allowed(&self, id: AttrDefId, allow_name: &str) -> bool { | 90 | let attrs = self.db.attrs(def_id); |
89 | self.db.attrs(id).by_key("allow").tt_values().any(|tt| tt.to_string().contains(allow_name)) | 91 | // don't bug the user about directly no_mangle annotated stuff, they can't do anything about it |
92 | (!recursing && attrs.by_key("no_mangle").exists()) | ||
93 | || attrs.by_key("allow").tt_values().any(|tt| { | ||
94 | let allows = tt.to_string(); | ||
95 | allows.contains(allow_name) | ||
96 | || allows.contains(allow::BAD_STYLE) | ||
97 | || allows.contains(allow::NONSTANDARD_STYLE) | ||
98 | }) | ||
99 | }; | ||
100 | |||
101 | is_allowed(id) | ||
102 | // go upwards one step or give up | ||
103 | || match id { | ||
104 | AttrDefId::ModuleId(m) => m.containing_module(self.db.upcast()).map(|v| v.into()), | ||
105 | AttrDefId::FunctionId(f) => Some(f.lookup(self.db.upcast()).container.into()), | ||
106 | AttrDefId::StaticId(sid) => Some(sid.lookup(self.db.upcast()).container.into()), | ||
107 | AttrDefId::ConstId(cid) => Some(cid.lookup(self.db.upcast()).container.into()), | ||
108 | AttrDefId::TraitId(tid) => Some(tid.lookup(self.db.upcast()).container.into()), | ||
109 | AttrDefId::ImplId(iid) => Some(iid.lookup(self.db.upcast()).container.into()), | ||
110 | // These warnings should not explore macro definitions at all | ||
111 | AttrDefId::MacroDefId(_) => None, | ||
112 | // Will never occur under an enum/struct/union/type alias | ||
113 | AttrDefId::AdtId(_) => None, | ||
114 | AttrDefId::FieldId(_) => None, | ||
115 | AttrDefId::EnumVariantId(_) => None, | ||
116 | AttrDefId::TypeAliasId(_) => None, | ||
117 | AttrDefId::GenericParamId(_) => None, | ||
118 | } | ||
119 | .map(|mid| self.allowed(mid, allow_name, true)) | ||
120 | .unwrap_or(false) | ||
90 | } | 121 | } |
91 | 122 | ||
92 | fn validate_func(&mut self, func: FunctionId) { | 123 | fn validate_func(&mut self, func: FunctionId) { |
@@ -109,7 +140,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
109 | } | 140 | } |
110 | 141 | ||
111 | // Check whether non-snake case identifiers are allowed for this function. | 142 | // Check whether non-snake case identifiers are allowed for this function. |
112 | if self.allowed(func.into(), allow::NON_SNAKE_CASE) { | 143 | if self.allowed(func.into(), allow::NON_SNAKE_CASE, false) { |
113 | return; | 144 | return; |
114 | } | 145 | } |
115 | 146 | ||
@@ -328,8 +359,9 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
328 | fn validate_struct(&mut self, struct_id: StructId) { | 359 | fn validate_struct(&mut self, struct_id: StructId) { |
329 | let data = self.db.struct_data(struct_id); | 360 | let data = self.db.struct_data(struct_id); |
330 | 361 | ||
331 | let non_camel_case_allowed = self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES); | 362 | let non_camel_case_allowed = |
332 | let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE); | 363 | self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES, false); |
364 | let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false); | ||
333 | 365 | ||
334 | // Check the structure name. | 366 | // Check the structure name. |
335 | let struct_name = data.name.to_string(); | 367 | let struct_name = data.name.to_string(); |
@@ -461,7 +493,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
461 | let data = self.db.enum_data(enum_id); | 493 | let data = self.db.enum_data(enum_id); |
462 | 494 | ||
463 | // Check whether non-camel case names are allowed for this enum. | 495 | // Check whether non-camel case names are allowed for this enum. |
464 | if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES) { | 496 | if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { |
465 | return; | 497 | return; |
466 | } | 498 | } |
467 | 499 | ||
@@ -584,7 +616,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
584 | fn validate_const(&mut self, const_id: ConstId) { | 616 | fn validate_const(&mut self, const_id: ConstId) { |
585 | let data = self.db.const_data(const_id); | 617 | let data = self.db.const_data(const_id); |
586 | 618 | ||
587 | if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL) { | 619 | if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { |
588 | return; | 620 | return; |
589 | } | 621 | } |
590 | 622 | ||
@@ -632,7 +664,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
632 | return; | 664 | return; |
633 | } | 665 | } |
634 | 666 | ||
635 | if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL) { | 667 | if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { |
636 | return; | 668 | return; |
637 | } | 669 | } |
638 | 670 | ||
@@ -869,10 +901,36 @@ fn main() { | |||
869 | r#" | 901 | r#" |
870 | #[allow(non_snake_case)] | 902 | #[allow(non_snake_case)] |
871 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8{ | 903 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8{ |
904 | // cov_flags generated output from elsewhere in this file | ||
905 | extern "C" { | ||
906 | #[no_mangle] | ||
907 | static lower_case: u8; | ||
908 | } | ||
909 | |||
872 | let OtherVar = SOME_VAR + 1; | 910 | let OtherVar = SOME_VAR + 1; |
873 | OtherVar | 911 | OtherVar |
874 | } | 912 | } |
875 | 913 | ||
914 | #[allow(nonstandard_style)] | ||
915 | mod CheckNonstandardStyle { | ||
916 | fn HiImABadFnName() {} | ||
917 | } | ||
918 | |||
919 | #[allow(bad_style)] | ||
920 | mod CheckBadStyle { | ||
921 | fn HiImABadFnName() {} | ||
922 | } | ||
923 | |||
924 | mod F { | ||
925 | #![allow(non_snake_case)] | ||
926 | fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {} | ||
927 | } | ||
928 | |||
929 | trait BAD_TRAIT { | ||
930 | fn BAD_FUNCTION(); | ||
931 | fn BadFunction(); | ||
932 | } | ||
933 | |||
876 | #[allow(non_snake_case, non_camel_case_types)] | 934 | #[allow(non_snake_case, non_camel_case_types)] |
877 | pub struct some_type { | 935 | pub struct some_type { |
878 | SOME_FIELD: u8, | 936 | SOME_FIELD: u8, |
@@ -889,6 +947,60 @@ fn main() { | |||
889 | } | 947 | } |
890 | 948 | ||
891 | #[test] | 949 | #[test] |
950 | fn allow_attributes_crate_attr() { | ||
951 | check_diagnostics( | ||
952 | r#" | ||
953 | #![allow(non_snake_case)] | ||
954 | |||
955 | mod F { | ||
956 | fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {} | ||
957 | } | ||
958 | |||
959 | "#, | ||
960 | ); | ||
961 | } | ||
962 | |||
963 | #[test] | ||
964 | #[ignore] | ||
965 | fn bug_trait_inside_fn() { | ||
966 | // FIXME: | ||
967 | // This is broken, and in fact, should not even be looked at by this | ||
968 | // lint in the first place. There's weird stuff going on in the | ||
969 | // collection phase. | ||
970 | // It's currently being brought in by: | ||
971 | // * validate_func on `a` recursing into modules | ||
972 | // * then it finds the trait and then the function while iterating | ||
973 | // through modules | ||
974 | // * then validate_func is called on Dirty | ||
975 | // * ... which then proceeds to look at some unknown module taking no | ||
976 | // attrs from either the impl or the fn a, and then finally to the root | ||
977 | // module | ||
978 | // | ||
979 | // It should find the attribute on the trait, but it *doesn't even see | ||
980 | // the trait* as far as I can tell. | ||
981 | |||
982 | check_diagnostics( | ||
983 | r#" | ||
984 | trait T { fn a(); } | ||
985 | struct U {} | ||
986 | impl T for U { | ||
987 | fn a() { | ||
988 | // this comes out of bitflags, mostly | ||
989 | #[allow(non_snake_case)] | ||
990 | trait __BitFlags { | ||
991 | const HiImAlsoBad: u8 = 2; | ||
992 | #[inline] | ||
993 | fn Dirty(&self) -> bool { | ||
994 | false | ||
995 | } | ||
996 | } | ||
997 | |||
998 | } | ||
999 | }"#, | ||
1000 | ); | ||
1001 | } | ||
1002 | |||
1003 | #[test] | ||
892 | fn ignores_extern_items() { | 1004 | fn ignores_extern_items() { |
893 | cov_mark::check!(extern_func_incorrect_case_ignored); | 1005 | cov_mark::check!(extern_func_incorrect_case_ignored); |
894 | cov_mark::check!(extern_static_incorrect_case_ignored); | 1006 | cov_mark::check!(extern_static_incorrect_case_ignored); |
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index e74b287fb..b86e91772 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -541,7 +541,7 @@ include::./generated_assists.adoc[] | |||
541 | == Diagnostics | 541 | == Diagnostics |
542 | 542 | ||
543 | While most errors and warnings provided by rust-analyzer come from the `cargo check` integration, there's a growing number of diagnostics implemented using rust-analyzer's own analysis. | 543 | While most errors and warnings provided by rust-analyzer come from the `cargo check` integration, there's a growing number of diagnostics implemented using rust-analyzer's own analysis. |
544 | These diagnostics don't respect `#[allow]` or `#[deny]` attributes yet, but can be turned off using the `rust-analyzer.diagnostics.enable`, `rust-analyzer.diagnostics.enableExperimental` or `rust-analyzer.diagnostics.disabled` settings. | 544 | Some of these diagnostics don't respect `\#[allow]` or `\#[deny]` attributes yet, but can be turned off using the `rust-analyzer.diagnostics.enable`, `rust-analyzer.diagnostics.enableExperimental` or `rust-analyzer.diagnostics.disabled` settings. |
545 | 545 | ||
546 | include::./generated_diagnostic.adoc[] | 546 | include::./generated_diagnostic.adoc[] |
547 | 547 | ||