Skip to content

Commit da4a985

Browse files
committed
Auto merge of rust-lang#18108 - ChayimFriedman2:lint-level-cfg, r=Veykril
fix: Handle lint attributes that are under `#[cfg_attr]` I forgot `cfg_attr` while working on rust-lang#18099. Although the original code also didn't handle that (although case lints specifically were correct, by virtue of using hir attrs).
2 parents c4949b7 + b97ef38 commit da4a985

File tree

5 files changed

+120
-16
lines changed

5 files changed

+120
-16
lines changed

src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs

+14-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use cfg::{CfgAtom, CfgExpr};
66
use intern::{sym, Symbol};
77
use rustc_hash::FxHashSet;
88
use syntax::{
9-
ast::{self, Attr, HasAttrs, Meta, VariantList},
9+
ast::{self, Attr, HasAttrs, Meta, TokenTree, VariantList},
1010
AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, T,
1111
};
1212
use tracing::{debug, warn};
@@ -17,7 +17,7 @@ fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option<boo
1717
if !attr.simple_name().as_deref().map(|v| v == "cfg")? {
1818
return None;
1919
}
20-
let cfg = parse_from_attr_meta(attr.meta()?)?;
20+
let cfg = parse_from_attr_token_tree(&attr.meta()?.token_tree()?)?;
2121
let enabled = db.crate_graph()[krate].cfg_options.check(&cfg) != Some(false);
2222
Some(enabled)
2323
}
@@ -26,7 +26,15 @@ fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Optio
2626
if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? {
2727
return None;
2828
}
29-
let cfg_expr = parse_from_attr_meta(attr.meta()?)?;
29+
check_cfg_attr_value(db, &attr.token_tree()?, krate)
30+
}
31+
32+
pub fn check_cfg_attr_value(
33+
db: &dyn ExpandDatabase,
34+
attr: &TokenTree,
35+
krate: CrateId,
36+
) -> Option<bool> {
37+
let cfg_expr = parse_from_attr_token_tree(attr)?;
3038
let enabled = db.crate_graph()[krate].cfg_options.check(&cfg_expr) != Some(false);
3139
Some(enabled)
3240
}
@@ -238,8 +246,7 @@ pub(crate) fn process_cfg_attrs(
238246
Some(remove)
239247
}
240248
/// Parses a `cfg` attribute from the meta
241-
fn parse_from_attr_meta(meta: Meta) -> Option<CfgExpr> {
242-
let tt = meta.token_tree()?;
249+
fn parse_from_attr_token_tree(tt: &TokenTree) -> Option<CfgExpr> {
243250
let mut iter = tt
244251
.token_trees_and_tokens()
245252
.filter(is_not_whitespace)
@@ -328,7 +335,7 @@ mod tests {
328335
use expect_test::{expect, Expect};
329336
use syntax::{ast::Attr, AstNode, SourceFile};
330337

331-
use crate::cfg_process::parse_from_attr_meta;
338+
use crate::cfg_process::parse_from_attr_token_tree;
332339

333340
fn check_dnf_from_syntax(input: &str, expect: Expect) {
334341
let parse = SourceFile::parse(input, span::Edition::CURRENT);
@@ -342,7 +349,7 @@ mod tests {
342349
let node = node.clone_subtree();
343350
assert_eq!(node.syntax().text_range().start(), 0.into());
344351

345-
let cfg = parse_from_attr_meta(node.meta().unwrap()).unwrap();
352+
let cfg = parse_from_attr_token_tree(&node.meta().unwrap().token_tree().unwrap()).unwrap();
346353
let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
347354
expect.assert_eq(&actual);
348355
}

src/tools/rust-analyzer/crates/hir-expand/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ use crate::{
5353
};
5454

5555
pub use crate::{
56+
cfg_process::check_cfg_attr_value,
5657
files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile},
5758
prettify_macro_expansion_::prettify_macro_expansion,
5859
};

src/tools/rust-analyzer/crates/hir/src/semantics.rs

+13
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,19 @@ impl<'db> SemanticsImpl<'db> {
386386
Some(node)
387387
}
388388

389+
pub fn check_cfg_attr(&self, attr: &ast::TokenTree) -> Option<bool> {
390+
let file_id = self.find_file(attr.syntax()).file_id;
391+
let krate = match file_id.repr() {
392+
HirFileIdRepr::FileId(file_id) => {
393+
self.file_to_module_defs(file_id.file_id()).next()?.krate().id
394+
}
395+
HirFileIdRepr::MacroFile(macro_file) => {
396+
self.db.lookup_intern_macro_call(macro_file.macro_call_id).krate
397+
}
398+
};
399+
hir_expand::check_cfg_attr_value(self.db.upcast(), attr, krate)
400+
}
401+
389402
/// Expands the macro if it isn't one of the built-in ones that expand to custom syntax or dummy
390403
/// expansions.
391404
pub fn expand_allowed_builtins(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {

src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs

+22
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,28 @@ fn BAR() {
997997
);
998998
}
999999

1000+
#[test]
1001+
fn cfged_lint_attrs() {
1002+
check_diagnostics(
1003+
r#"
1004+
//- /lib.rs cfg:feature=cool_feature
1005+
#[cfg_attr(any(), allow(non_snake_case))]
1006+
fn FOO() {}
1007+
// ^^^ 💡 warn: Function `FOO` should have snake_case name, e.g. `foo`
1008+
1009+
#[cfg_attr(non_existent, allow(non_snake_case))]
1010+
fn BAR() {}
1011+
// ^^^ 💡 warn: Function `BAR` should have snake_case name, e.g. `bar`
1012+
1013+
#[cfg_attr(feature = "cool_feature", allow(non_snake_case))]
1014+
fn BAZ() {}
1015+
1016+
#[cfg_attr(feature = "cool_feature", cfg_attr ( all ( ) , allow ( non_snake_case ) ) ) ]
1017+
fn QUX() {}
1018+
"#,
1019+
);
1020+
}
1021+
10001022
#[test]
10011023
fn allow_with_comment() {
10021024
check_diagnostics(

src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs

+70-9
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ mod handlers {
7676
#[cfg(test)]
7777
mod tests;
7878

79-
use std::{collections::hash_map, sync::LazyLock};
79+
use std::{collections::hash_map, iter, sync::LazyLock};
8080

81+
use either::Either;
8182
use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics};
8283
use ide_db::{
8384
assists::{Assist, AssistId, AssistKind, AssistResolveStrategy},
@@ -92,7 +93,7 @@ use ide_db::{
9293
use itertools::Itertools;
9394
use syntax::{
9495
ast::{self, AstNode, HasAttrs},
95-
AstPtr, Edition, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange,
96+
AstPtr, Edition, NodeOrToken, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, TextRange, T,
9697
};
9798

9899
// FIXME: Make this an enum
@@ -647,6 +648,7 @@ fn find_outline_mod_lint_severity(
647648
let mut result = None;
648649
let lint_groups = lint_groups(&diag.code);
649650
lint_attrs(
651+
sema,
650652
ast::AnyHasAttrs::cast(module_source_file.value).expect("SourceFile always has attrs"),
651653
edition,
652654
)
@@ -763,7 +765,7 @@ fn fill_lint_attrs(
763765

764766
if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) {
765767
// Insert this node's attributes into any outline descendant, including this node.
766-
lint_attrs(ancestor, edition).for_each(|(lint, severity)| {
768+
lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| {
767769
if diag_severity.is_none() && lint_groups(&diag.code).contains(&&*lint) {
768770
diag_severity = Some(severity);
769771
}
@@ -793,7 +795,7 @@ fn fill_lint_attrs(
793795
cache_stack.pop();
794796
return diag_severity;
795797
} else if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) {
796-
lint_attrs(ancestor, edition).for_each(|(lint, severity)| {
798+
lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| {
797799
if diag_severity.is_none() && lint_groups(&diag.code).contains(&&*lint) {
798800
diag_severity = Some(severity);
799801
}
@@ -809,20 +811,27 @@ fn fill_lint_attrs(
809811
}
810812
}
811813

812-
fn lint_attrs(
814+
fn lint_attrs<'a>(
815+
sema: &'a Semantics<'a, RootDatabase>,
813816
ancestor: ast::AnyHasAttrs,
814817
edition: Edition,
815-
) -> impl Iterator<Item = (SmolStr, Severity)> {
818+
) -> impl Iterator<Item = (SmolStr, Severity)> + 'a {
816819
ancestor
817820
.attrs_including_inner()
818821
.filter_map(|attr| {
819822
attr.as_simple_call().and_then(|(name, value)| match &*name {
820-
"allow" | "expect" => Some((Severity::Allow, value)),
821-
"warn" => Some((Severity::Warning, value)),
822-
"forbid" | "deny" => Some((Severity::Error, value)),
823+
"allow" | "expect" => Some(Either::Left(iter::once((Severity::Allow, value)))),
824+
"warn" => Some(Either::Left(iter::once((Severity::Warning, value)))),
825+
"forbid" | "deny" => Some(Either::Left(iter::once((Severity::Error, value)))),
826+
"cfg_attr" => {
827+
let mut lint_attrs = Vec::new();
828+
cfg_attr_lint_attrs(sema, &value, &mut lint_attrs);
829+
Some(Either::Right(lint_attrs.into_iter()))
830+
}
823831
_ => None,
824832
})
825833
})
834+
.flatten()
826835
.flat_map(move |(severity, lints)| {
827836
parse_tt_as_comma_sep_paths(lints, edition).into_iter().flat_map(move |lints| {
828837
// Rejoin the idents with `::`, so we have no spaces in between.
@@ -836,6 +845,58 @@ fn lint_attrs(
836845
})
837846
}
838847

848+
fn cfg_attr_lint_attrs(
849+
sema: &Semantics<'_, RootDatabase>,
850+
value: &ast::TokenTree,
851+
lint_attrs: &mut Vec<(Severity, ast::TokenTree)>,
852+
) {
853+
let prev_len = lint_attrs.len();
854+
855+
let mut iter = value.token_trees_and_tokens().filter(|it| match it {
856+
NodeOrToken::Node(_) => true,
857+
NodeOrToken::Token(it) => !it.kind().is_trivia(),
858+
});
859+
860+
// Skip the condition.
861+
for value in &mut iter {
862+
if value.as_token().is_some_and(|it| it.kind() == T![,]) {
863+
break;
864+
}
865+
}
866+
867+
while let Some(value) = iter.next() {
868+
if let Some(token) = value.as_token() {
869+
if token.kind() == SyntaxKind::IDENT {
870+
let severity = match token.text() {
871+
"allow" | "expect" => Some(Severity::Allow),
872+
"warn" => Some(Severity::Warning),
873+
"forbid" | "deny" => Some(Severity::Error),
874+
"cfg_attr" => {
875+
if let Some(NodeOrToken::Node(value)) = iter.next() {
876+
cfg_attr_lint_attrs(sema, &value, lint_attrs);
877+
}
878+
None
879+
}
880+
_ => None,
881+
};
882+
if let Some(severity) = severity {
883+
let lints = iter.next();
884+
if let Some(NodeOrToken::Node(lints)) = lints {
885+
lint_attrs.push((severity, lints));
886+
}
887+
}
888+
}
889+
}
890+
}
891+
892+
if prev_len != lint_attrs.len() {
893+
if let Some(false) | None = sema.check_cfg_attr(value) {
894+
// Discard the attributes when the condition is false.
895+
lint_attrs.truncate(prev_len);
896+
}
897+
}
898+
}
899+
839900
fn lint_groups(lint: &DiagnosticCode) -> &'static [&'static str] {
840901
match lint {
841902
DiagnosticCode::RustcLint(name) => {

0 commit comments

Comments
 (0)