about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGeorg Semmler <github@weiznich.de>2023-04-28 13:04:35 +0200
committerGeorg Semmler <github@weiznich.de>2023-07-28 13:28:02 +0200
commit5b576665e50752b89e96e5d3043b99a9558e881c (patch)
tree2bcea71a57e7924343d8d8c1995321bd7b06d4b8
parentd150dbb067e66f351a0b33a54e7d4b464ef51e47 (diff)
downloadrust-5b576665e50752b89e96e5d3043b99a9558e881c.tar.gz
rust-5b576665e50752b89e96e5d3043b99a9558e881c.zip
Introduce the `#[diagnostic]` attribute namespace
Co-authored-by: est31 <est31@users.noreply.github.com>

Co-authored-by: Esteban Kuber <estebank@users.noreply.github.com>

Co-authored-by: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs13
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs25
-rw-r--r--compiler/rustc_resolve/src/macros.rs20
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--src/tools/tidy/src/ui_tests.rs2
-rw-r--r--tests/ui/diagnostic_namespace/auxiliary/proc-macro-helper.rs12
-rw-r--r--tests/ui/diagnostic_namespace/can_use_the_diagnostic_name_in_other_places.rs13
-rw-r--r--tests/ui/diagnostic_namespace/existing_proc_macros.rs24
-rw-r--r--tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs13
-rw-r--r--tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr35
-rw-r--r--tests/ui/diagnostic_namespace/non_existing_attributes_accepted.rs13
-rw-r--r--tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr16
-rw-r--r--tests/ui/diagnostic_namespace/requires_path.rs9
-rw-r--r--tests/ui/diagnostic_namespace/requires_path.stderr8
15 files changed, 202 insertions, 4 deletions
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index b0dbc2c2340..c0d2e76f310 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -218,6 +218,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 }
             }
         }
+        if !attr.is_doc_comment()
+            && attr.get_normal_item().path.segments.len() == 2
+            && attr.get_normal_item().path.segments[0].ident.name == sym::diagnostic
+            && !self.features.diagnostic_namespace
+        {
+            let msg = "`#[diagnostic]` attribute name space is experimental";
+            gate_feature_post!(
+                self,
+                diagnostic_namespace,
+                attr.get_normal_item().path.segments[0].ident.span,
+                msg
+            );
+        }
 
         // Emit errors for non-staged-api crates.
         if !self.features.staged_api {
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 56a2c5eff3d..c03789f500a 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -379,6 +379,8 @@ declare_features! (
     (active, deprecated_safe, "1.61.0", Some(94978), None),
     /// Allows having using `suggestion` in the `#[deprecated]` attribute.
     (active, deprecated_suggestion, "1.61.0", Some(94785), None),
+    /// Allows using the `#[diagnostic]` attribute tool namespace
+    (active, diagnostic_namespace, "CURRENT_RUSTC_VERSION", Some(94785), None),
     /// Controls errors in trait implementations.
     (active, do_not_recommend, "1.67.0", Some(51992), None),
     /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index a0f2e9aed81..cfb40a3b65c 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3400,6 +3400,7 @@ declare_lint_pass! {
         UNFULFILLED_LINT_EXPECTATIONS,
         UNINHABITED_STATIC,
         UNKNOWN_CRATE_TYPES,
+        UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
         UNKNOWN_LINTS,
         UNNAMEABLE_TYPES,
         UNREACHABLE_CODE,
@@ -4380,3 +4381,27 @@ declare_lint! {
     "effective visibility of a type is larger than the area in which it can be named",
     @feature_gate = sym::type_privacy_lints;
 }
+
+declare_lint! {
+    /// The `unknown_diagnostic_attributes` lint detects unrecognized diagnostic attributes.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #![feature(diagnostic_namespace)]
+    /// #[diagnostic::does_not_exist]
+    /// struct Foo;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is usually a mistake to specify a diagnostic attribute that does not exist. Check
+    /// the spelling, and check the diagnostic attribute listing for the correct name. Also
+    /// consider if you are using an old version of the compiler, and the attribute
+    /// is only available in a newer version.
+    pub UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
+    Warn,
+    "unrecognized diagnostic attribute"
+}
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 266e37e4cef..d456cc9a9a0 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -24,7 +24,9 @@ use rustc_hir::def_id::{CrateNum, LocalDefId};
 use rustc_middle::middle::stability;
 use rustc_middle::ty::RegisteredTools;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE};
+use rustc_session::lint::builtin::{
+    LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
+};
 use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES};
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::parse::feature_err;
@@ -140,9 +142,9 @@ pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools {
             }
         }
     }
-    // We implicitly add `rustfmt` and `clippy` to known tools,
+    // We implicitly add `rustfmt`, `clippy`, `diagnostic` to known tools,
     // but it's not an error to register them explicitly.
-    let predefined_tools = [sym::clippy, sym::rustfmt];
+    let predefined_tools = [sym::clippy, sym::rustfmt, sym::diagnostic];
     registered_tools.extend(predefined_tools.iter().cloned().map(Ident::with_dummy_span));
     registered_tools
 }
@@ -595,6 +597,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
         }
 
+        if res == Res::NonMacroAttr(NonMacroAttrKind::Tool)
+            && path.segments.len() >= 2
+            && path.segments[0].ident.name == sym::diagnostic
+        {
+            self.tcx.sess.parse_sess.buffer_lint(
+                UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
+                path.segments[1].span(),
+                node_id,
+                "unknown diagnostic attribute",
+            );
+        }
+
         Ok((ext, res))
     }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 54eb7bef5f2..1b426ef2048 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -620,6 +620,7 @@ symbols! {
         destruct,
         destructuring_assignment,
         diagnostic,
+        diagnostic_namespace,
         direct,
         discriminant_kind,
         discriminant_type,
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index f9a90bf3a00..d1ae88fcd80 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -13,7 +13,7 @@ use std::path::{Path, PathBuf};
 const ENTRY_LIMIT: usize = 900;
 // FIXME: The following limits should be reduced eventually.
 const ISSUES_ENTRY_LIMIT: usize = 1893;
-const ROOT_ENTRY_LIMIT: usize = 870;
+const ROOT_ENTRY_LIMIT: usize = 871;
 
 const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
     "rs",     // test source files
diff --git a/tests/ui/diagnostic_namespace/auxiliary/proc-macro-helper.rs b/tests/ui/diagnostic_namespace/auxiliary/proc-macro-helper.rs
new file mode 100644
index 00000000000..759c32c8453
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/auxiliary/proc-macro-helper.rs
@@ -0,0 +1,12 @@
+// force-host
+// no-prefer-dynamic
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+
+#[proc_macro_attribute]
+pub fn diagnostic(i: TokenStream, _: TokenStream) -> TokenStream {
+    i
+}
diff --git a/tests/ui/diagnostic_namespace/can_use_the_diagnostic_name_in_other_places.rs b/tests/ui/diagnostic_namespace/can_use_the_diagnostic_name_in_other_places.rs
new file mode 100644
index 00000000000..08b4d68779c
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/can_use_the_diagnostic_name_in_other_places.rs
@@ -0,0 +1,13 @@
+// check-pass
+
+mod diagnostic {}
+
+macro_rules! diagnostic{
+    () => {}
+}
+
+#[allow(non_upper_case_globals)]
+const diagnostic: () = ();
+
+fn main() {
+}
diff --git a/tests/ui/diagnostic_namespace/existing_proc_macros.rs b/tests/ui/diagnostic_namespace/existing_proc_macros.rs
new file mode 100644
index 00000000000..d6d1fb01496
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/existing_proc_macros.rs
@@ -0,0 +1,24 @@
+#![feature(diagnostic_namespace)]
+// check-pass
+// aux-build:proc-macro-helper.rs
+
+extern crate proc_macro_helper;
+
+mod test1 {
+    use proc_macro_helper::diagnostic;
+
+    #[diagnostic]
+    struct Foo;
+
+}
+
+mod test2 {
+    mod diagnostic {
+        pub use proc_macro_helper::diagnostic as on_unimplemented;
+    }
+
+    #[diagnostic::on_unimplemented]
+    trait Foo {}
+}
+
+fn main() {}
diff --git a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs
new file mode 100644
index 00000000000..a686ed9c84e
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs
@@ -0,0 +1,13 @@
+#[diagnostic::non_existing_attribute]
+//~^ERROR `#[diagnostic]` attribute name space is experimental [E0658]
+//~|WARNING unknown diagnostic attribute [unknown_diagnostic_attributes]
+pub trait Bar {
+}
+
+#[diagnostic::non_existing_attribute(with_option = "foo")]
+//~^ERROR `#[diagnostic]` attribute name space is experimental [E0658]
+//~|WARNING unknown diagnostic attribute [unknown_diagnostic_attributes]
+struct Foo;
+
+fn main() {
+}
diff --git a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr
new file mode 100644
index 00000000000..45c95cbb3c7
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr
@@ -0,0 +1,35 @@
+error[E0658]: `#[diagnostic]` attribute name space is experimental
+  --> $DIR/feature-gate-diagnostic_namespace.rs:1:3
+   |
+LL | #[diagnostic::non_existing_attribute]
+   |   ^^^^^^^^^^
+   |
+   = note: see issue #94785 <https://github.com/rust-lang/rust/issues/94785> for more information
+   = help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable
+
+error[E0658]: `#[diagnostic]` attribute name space is experimental
+  --> $DIR/feature-gate-diagnostic_namespace.rs:7:3
+   |
+LL | #[diagnostic::non_existing_attribute(with_option = "foo")]
+   |   ^^^^^^^^^^
+   |
+   = note: see issue #94785 <https://github.com/rust-lang/rust/issues/94785> for more information
+   = help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable
+
+warning: unknown diagnostic attribute
+  --> $DIR/feature-gate-diagnostic_namespace.rs:1:15
+   |
+LL | #[diagnostic::non_existing_attribute]
+   |               ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(unknown_diagnostic_attributes)]` on by default
+
+warning: unknown diagnostic attribute
+  --> $DIR/feature-gate-diagnostic_namespace.rs:7:15
+   |
+LL | #[diagnostic::non_existing_attribute(with_option = "foo")]
+   |               ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors; 2 warnings emitted
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.rs b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.rs
new file mode 100644
index 00000000000..677bd5a7343
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.rs
@@ -0,0 +1,13 @@
+#![feature(diagnostic_namespace)]
+// check-pass
+#[diagnostic::non_existing_attribute]
+//~^WARN unknown diagnostic attribute
+pub trait Bar {
+}
+
+#[diagnostic::non_existing_attribute(with_option = "foo")]
+//~^WARN unknown diagnostic attribute
+struct Foo;
+
+fn main() {
+}
diff --git a/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr
new file mode 100644
index 00000000000..4f9b7ba2bcf
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr
@@ -0,0 +1,16 @@
+warning: unknown diagnostic attribute
+  --> $DIR/non_existing_attributes_accepted.rs:3:15
+   |
+LL | #[diagnostic::non_existing_attribute]
+   |               ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(unknown_diagnostic_attributes)]` on by default
+
+warning: unknown diagnostic attribute
+  --> $DIR/non_existing_attributes_accepted.rs:8:15
+   |
+LL | #[diagnostic::non_existing_attribute(with_option = "foo")]
+   |               ^^^^^^^^^^^^^^^^^^^^^^
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/diagnostic_namespace/requires_path.rs b/tests/ui/diagnostic_namespace/requires_path.rs
new file mode 100644
index 00000000000..e8d6ca73ad0
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/requires_path.rs
@@ -0,0 +1,9 @@
+#![feature(diagnostic_namespace)]
+
+#[diagnostic]
+//~^ERROR cannot find attribute `diagnostic` in this scope
+pub struct Bar;
+
+
+fn main() {
+}
diff --git a/tests/ui/diagnostic_namespace/requires_path.stderr b/tests/ui/diagnostic_namespace/requires_path.stderr
new file mode 100644
index 00000000000..ce867621daa
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/requires_path.stderr
@@ -0,0 +1,8 @@
+error: cannot find attribute `diagnostic` in this scope
+  --> $DIR/requires_path.rs:3:3
+   |
+LL | #[diagnostic]
+   |   ^^^^^^^^^^
+
+error: aborting due to previous error
+