mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-11 20:58:54 +00:00
decl_check: consider outer scopes' allows
Fix #8417. Also makes it less noisy about no_mangle annotated stuff the user can do nothing about. Note: this still is broken with bitfield! macros. A repro in an ignore test is included here. I believe this bug is elsewhere, and I don't think I can work around it here.
This commit is contained in:
parent
855a739ebf
commit
4529f1be81
3 changed files with 133 additions and 11 deletions
|
@ -435,6 +435,16 @@ impl_from!(
|
||||||
for AttrDefId
|
for AttrDefId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
impl From<AssocContainerId> for AttrDefId {
|
||||||
|
fn from(acid: AssocContainerId) -> Self {
|
||||||
|
match acid {
|
||||||
|
AssocContainerId::ModuleId(mid) => AttrDefId::ModuleId(mid),
|
||||||
|
AssocContainerId::ImplId(iid) => AttrDefId::ImplId(iid),
|
||||||
|
AssocContainerId::TraitId(tid) => AttrDefId::TraitId(tid),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum VariantId {
|
pub enum VariantId {
|
||||||
EnumVariantId(EnumVariantId),
|
EnumVariantId(EnumVariantId),
|
||||||
|
|
|
@ -35,6 +35,8 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod allow {
|
mod allow {
|
||||||
|
pub(super) const BAD_STYLE: &str = "bad_style";
|
||||||
|
pub(super) const NONSTANDARD_STYLE: &str = "nonstandard_style";
|
||||||
pub(super) const NON_SNAKE_CASE: &str = "non_snake_case";
|
pub(super) const NON_SNAKE_CASE: &str = "non_snake_case";
|
||||||
pub(super) const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals";
|
pub(super) const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals";
|
||||||
pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types";
|
pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types";
|
||||||
|
@ -83,10 +85,39 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether not following the convention is allowed for this item.
|
/// Checks whether not following the convention is allowed for this item.
|
||||||
///
|
fn allowed(&self, id: AttrDefId, allow_name: &str, recursing: bool) -> bool {
|
||||||
/// Currently this method doesn't check parent attributes.
|
let is_allowed = |def_id| {
|
||||||
fn allowed(&self, id: AttrDefId, allow_name: &str) -> bool {
|
let attrs = self.db.attrs(def_id);
|
||||||
self.db.attrs(id).by_key("allow").tt_values().any(|tt| tt.to_string().contains(allow_name))
|
// don't bug the user about directly no_mangle annotated stuff, they can't do anything about it
|
||||||
|
(!recursing && attrs.by_key("no_mangle").exists())
|
||||||
|
|| attrs.by_key("allow").tt_values().any(|tt| {
|
||||||
|
let allows = tt.to_string();
|
||||||
|
allows.contains(allow_name)
|
||||||
|
|| allows.contains(allow::BAD_STYLE)
|
||||||
|
|| allows.contains(allow::NONSTANDARD_STYLE)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
is_allowed(id)
|
||||||
|
// go upwards one step or give up
|
||||||
|
|| match id {
|
||||||
|
AttrDefId::ModuleId(m) => m.containing_module(self.db.upcast()).map(|v| v.into()),
|
||||||
|
AttrDefId::FunctionId(f) => Some(f.lookup(self.db.upcast()).container.into()),
|
||||||
|
AttrDefId::StaticId(sid) => Some(sid.lookup(self.db.upcast()).container.into()),
|
||||||
|
AttrDefId::ConstId(cid) => Some(cid.lookup(self.db.upcast()).container.into()),
|
||||||
|
AttrDefId::TraitId(tid) => Some(tid.lookup(self.db.upcast()).container.into()),
|
||||||
|
AttrDefId::ImplId(iid) => Some(iid.lookup(self.db.upcast()).container.into()),
|
||||||
|
// These warnings should not explore macro definitions at all
|
||||||
|
AttrDefId::MacroDefId(_) => None,
|
||||||
|
// Will never occur under an enum/struct/union/type alias
|
||||||
|
AttrDefId::AdtId(_) => None,
|
||||||
|
AttrDefId::FieldId(_) => None,
|
||||||
|
AttrDefId::EnumVariantId(_) => None,
|
||||||
|
AttrDefId::TypeAliasId(_) => None,
|
||||||
|
AttrDefId::GenericParamId(_) => None,
|
||||||
|
}
|
||||||
|
.map(|mid| self.allowed(mid, allow_name, true))
|
||||||
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_func(&mut self, func: FunctionId) {
|
fn validate_func(&mut self, func: FunctionId) {
|
||||||
|
@ -109,7 +140,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether non-snake case identifiers are allowed for this function.
|
// Check whether non-snake case identifiers are allowed for this function.
|
||||||
if self.allowed(func.into(), allow::NON_SNAKE_CASE) {
|
if self.allowed(func.into(), allow::NON_SNAKE_CASE, false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,8 +359,9 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
fn validate_struct(&mut self, struct_id: StructId) {
|
fn validate_struct(&mut self, struct_id: StructId) {
|
||||||
let data = self.db.struct_data(struct_id);
|
let data = self.db.struct_data(struct_id);
|
||||||
|
|
||||||
let non_camel_case_allowed = self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES);
|
let non_camel_case_allowed =
|
||||||
let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE);
|
self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES, false);
|
||||||
|
let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false);
|
||||||
|
|
||||||
// Check the structure name.
|
// Check the structure name.
|
||||||
let struct_name = data.name.to_string();
|
let struct_name = data.name.to_string();
|
||||||
|
@ -461,7 +493,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
let data = self.db.enum_data(enum_id);
|
let data = self.db.enum_data(enum_id);
|
||||||
|
|
||||||
// Check whether non-camel case names are allowed for this enum.
|
// Check whether non-camel case names are allowed for this enum.
|
||||||
if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES) {
|
if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES, false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,7 +616,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
fn validate_const(&mut self, const_id: ConstId) {
|
fn validate_const(&mut self, const_id: ConstId) {
|
||||||
let data = self.db.const_data(const_id);
|
let data = self.db.const_data(const_id);
|
||||||
|
|
||||||
if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL) {
|
if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,7 +664,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL) {
|
if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -869,10 +901,36 @@ fn main() {
|
||||||
r#"
|
r#"
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn NonSnakeCaseName(SOME_VAR: u8) -> u8{
|
fn NonSnakeCaseName(SOME_VAR: u8) -> u8{
|
||||||
|
// cov_flags generated output from elsewhere in this file
|
||||||
|
extern "C" {
|
||||||
|
#[no_mangle]
|
||||||
|
static lower_case: u8;
|
||||||
|
}
|
||||||
|
|
||||||
let OtherVar = SOME_VAR + 1;
|
let OtherVar = SOME_VAR + 1;
|
||||||
OtherVar
|
OtherVar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(nonstandard_style)]
|
||||||
|
mod CheckNonstandardStyle {
|
||||||
|
fn HiImABadFnName() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(bad_style)]
|
||||||
|
mod CheckBadStyle {
|
||||||
|
fn HiImABadFnName() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod F {
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait BAD_TRAIT {
|
||||||
|
fn BAD_FUNCTION();
|
||||||
|
fn BadFunction();
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case, non_camel_case_types)]
|
#[allow(non_snake_case, non_camel_case_types)]
|
||||||
pub struct some_type {
|
pub struct some_type {
|
||||||
SOME_FIELD: u8,
|
SOME_FIELD: u8,
|
||||||
|
@ -888,6 +946,60 @@ fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn allow_attributes_crate_attr() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
mod F {
|
||||||
|
fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn bug_trait_inside_fn() {
|
||||||
|
// FIXME:
|
||||||
|
// This is broken, and in fact, should not even be looked at by this
|
||||||
|
// lint in the first place. There's weird stuff going on in the
|
||||||
|
// collection phase.
|
||||||
|
// It's currently being brought in by:
|
||||||
|
// * validate_func on `a` recursing into modules
|
||||||
|
// * then it finds the trait and then the function while iterating
|
||||||
|
// through modules
|
||||||
|
// * then validate_func is called on Dirty
|
||||||
|
// * ... which then proceeds to look at some unknown module taking no
|
||||||
|
// attrs from either the impl or the fn a, and then finally to the root
|
||||||
|
// module
|
||||||
|
//
|
||||||
|
// It should find the attribute on the trait, but it *doesn't even see
|
||||||
|
// the trait* as far as I can tell.
|
||||||
|
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
trait T { fn a(); }
|
||||||
|
struct U {}
|
||||||
|
impl T for U {
|
||||||
|
fn a() {
|
||||||
|
// this comes out of bitflags, mostly
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
trait __BitFlags {
|
||||||
|
const HiImAlsoBad: u8 = 2;
|
||||||
|
#[inline]
|
||||||
|
fn Dirty(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ignores_extern_items() {
|
fn ignores_extern_items() {
|
||||||
cov_mark::check!(extern_func_incorrect_case_ignored);
|
cov_mark::check!(extern_func_incorrect_case_ignored);
|
||||||
|
|
|
@ -541,7 +541,7 @@ include::./generated_assists.adoc[]
|
||||||
== Diagnostics
|
== Diagnostics
|
||||||
|
|
||||||
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.
|
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.
|
||||||
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.
|
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.
|
||||||
|
|
||||||
include::./generated_diagnostic.adoc[]
|
include::./generated_diagnostic.adoc[]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue