about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir/src/hir.rs4
-rw-r--r--src/librustdoc/json/conversions.rs48
-rw-r--r--src/rustdoc-json-types/lib.rs43
-rw-r--r--src/test/rustdoc-json/fn_pointer/abi.rs25
-rw-r--r--src/test/rustdoc-json/fn_pointer/header.rs5
-rw-r--r--src/test/rustdoc-json/fn_pointer/qualifiers.rs9
-rw-r--r--src/test/rustdoc-json/fns/abi.rs25
-rw-r--r--src/test/rustdoc-json/fns/header.rs22
-rw-r--r--src/test/rustdoc-json/fns/qualifiers.rs33
-rw-r--r--src/test/rustdoc-json/method_abi.rs25
-rw-r--r--src/test/rustdoc-json/methods/abi.rs55
-rw-r--r--src/test/rustdoc-json/methods/header.rs26
-rw-r--r--src/test/rustdoc-json/methods/qualifiers.rs37
13 files changed, 242 insertions, 115 deletions
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 255e661652d..d48c8e81f54 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2776,6 +2776,10 @@ impl FnHeader {
     pub fn is_const(&self) -> bool {
         matches!(&self.constness, Constness::Const)
     }
+
+    pub fn is_unsafe(&self) -> bool {
+        matches!(&self.unsafety, Unsafety::Unsafe)
+    }
 }
 
 #[derive(Debug, HashStable_Generic)]
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index e77bd5c9223..d4aedb41ddb 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -12,6 +12,7 @@ use rustc_hir::{def::CtorKind, def_id::DefId};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::def_id::CRATE_DEF_INDEX;
 use rustc_span::Pos;
+use rustc_target::spec::abi::Abi as RustcAbi;
 
 use rustdoc_json_types::*;
 
@@ -19,7 +20,6 @@ use crate::clean::utils::print_const_expr;
 use crate::clean::{self, ItemId};
 use crate::formats::item_type::ItemType;
 use crate::json::JsonRenderer;
-use std::collections::HashSet;
 
 impl JsonRenderer<'_> {
     pub(super) fn convert_item(&self, item: clean::Item) -> Option<Item> {
@@ -271,22 +271,28 @@ crate fn from_ctor_kind(struct_type: CtorKind) -> StructType {
     }
 }
 
-crate fn from_fn_header(header: &rustc_hir::FnHeader) -> HashSet<Qualifiers> {
-    let mut v = HashSet::new();
-
-    if let rustc_hir::Unsafety::Unsafe = header.unsafety {
-        v.insert(Qualifiers::Unsafe);
-    }
-
-    if let rustc_hir::IsAsync::Async = header.asyncness {
-        v.insert(Qualifiers::Async);
+crate fn from_fn_header(header: &rustc_hir::FnHeader) -> Header {
+    Header {
+        async_: header.is_async(),
+        const_: header.is_const(),
+        unsafe_: header.is_unsafe(),
+        abi: convert_abi(header.abi),
     }
+}
 
-    if let rustc_hir::Constness::Const = header.constness {
-        v.insert(Qualifiers::Const);
+fn convert_abi(a: RustcAbi) -> Abi {
+    match a {
+        RustcAbi::Rust => Abi::Rust,
+        RustcAbi::C { unwind } => Abi::C { unwind },
+        RustcAbi::Cdecl { unwind } => Abi::Cdecl { unwind },
+        RustcAbi::Stdcall { unwind } => Abi::Stdcall { unwind },
+        RustcAbi::Fastcall { unwind } => Abi::Fastcall { unwind },
+        RustcAbi::Aapcs { unwind } => Abi::Aapcs { unwind },
+        RustcAbi::Win64 { unwind } => Abi::Win64 { unwind },
+        RustcAbi::SysV64 { unwind } => Abi::SysV64 { unwind },
+        RustcAbi::System { unwind } => Abi::System { unwind },
+        _ => Abi::Other(a.to_string()),
     }
-
-    v
 }
 
 impl FromWithTcx<clean::Function> for Function {
@@ -296,7 +302,6 @@ impl FromWithTcx<clean::Function> for Function {
             decl: decl.into_tcx(tcx),
             generics: generics.into_tcx(tcx),
             header: from_fn_header(&header),
-            abi: header.abi.to_string(),
         }
     }
 }
@@ -465,16 +470,14 @@ impl FromWithTcx<clean::BareFunctionDecl> for FunctionPointer {
     fn from_tcx(bare_decl: clean::BareFunctionDecl, tcx: TyCtxt<'_>) -> Self {
         let clean::BareFunctionDecl { unsafety, generic_params, decl, abi } = bare_decl;
         FunctionPointer {
-            header: if let rustc_hir::Unsafety::Unsafe = unsafety {
-                let mut hs = HashSet::new();
-                hs.insert(Qualifiers::Unsafe);
-                hs
-            } else {
-                HashSet::new()
+            header: Header {
+                unsafe_: matches!(unsafety, rustc_hir::Unsafety::Unsafe),
+                const_: false,
+                async_: false,
+                abi: convert_abi(abi),
             },
             generic_params: generic_params.into_iter().map(|x| x.into_tcx(tcx)).collect(),
             decl: decl.into_tcx(tcx),
-            abi: abi.to_string(),
         }
     }
 }
@@ -554,7 +557,6 @@ crate fn from_function_method(
         decl: decl.into_tcx(tcx),
         generics: generics.into_tcx(tcx),
         header: from_fn_header(&header),
-        abi: header.abi.to_string(),
         has_body,
     }
 }
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 600833664be..be9bbc7391d 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -3,13 +3,13 @@
 //! These types are the public API exposed through the `--output-format json` flag. The [`Crate`]
 //! struct is the root of the JSON blob and all other items are contained within.
 
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
 use std::path::PathBuf;
 
 use serde::{Deserialize, Serialize};
 
 /// rustdoc format-version.
-pub const FORMAT_VERSION: u32 = 10;
+pub const FORMAT_VERSION: u32 = 11;
 
 /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
 /// about the language items in the local crate, as well as info about external items to allow
@@ -287,29 +287,45 @@ pub enum StructType {
     Unit,
 }
 
-#[non_exhaustive]
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
-#[serde(rename_all = "snake_case")]
-pub enum Qualifiers {
-    Const,
-    Unsafe,
-    Async,
+pub struct Header {
+    #[serde(rename = "const")]
+    pub const_: bool,
+    #[serde(rename = "unsafe")]
+    pub unsafe_: bool,
+    #[serde(rename = "async")]
+    pub async_: bool,
+    pub abi: Abi,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
+pub enum Abi {
+    // We only have a concrete listing here for stable ABI's because their are so many
+    // See rustc_ast_passes::feature_gate::PostExpansionVisitor::check_abi for the list
+    Rust,
+    C { unwind: bool },
+    Cdecl { unwind: bool },
+    Stdcall { unwind: bool },
+    Fastcall { unwind: bool },
+    Aapcs { unwind: bool },
+    Win64 { unwind: bool },
+    SysV64 { unwind: bool },
+    System { unwind: bool },
+    Other(String),
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
 pub struct Function {
     pub decl: FnDecl,
     pub generics: Generics,
-    pub header: HashSet<Qualifiers>,
-    pub abi: String,
+    pub header: Header,
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
 pub struct Method {
     pub decl: FnDecl,
     pub generics: Generics,
-    pub header: HashSet<Qualifiers>,
-    pub abi: String,
+    pub header: Header,
     pub has_body: bool,
 }
 
@@ -426,8 +442,7 @@ pub enum Type {
 pub struct FunctionPointer {
     pub decl: FnDecl,
     pub generic_params: Vec<GenericParamDef>,
-    pub header: HashSet<Qualifiers>,
-    pub abi: String,
+    pub header: Header,
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
diff --git a/src/test/rustdoc-json/fn_pointer/abi.rs b/src/test/rustdoc-json/fn_pointer/abi.rs
new file mode 100644
index 00000000000..eef20e60a6a
--- /dev/null
+++ b/src/test/rustdoc-json/fn_pointer/abi.rs
@@ -0,0 +1,25 @@
+// ignore-tidy-linelength
+
+#![feature(abi_vectorcall)]
+#![feature(c_unwind)]
+
+// @is abi.json "$.index[*][?(@.name=='AbiRust')].inner.type.inner.header.abi" \"Rust\"
+pub type AbiRust = fn();
+
+// @is - "$.index[*][?(@.name=='AbiC')].inner.type.inner.header.abi" '{"C": {"unwind": false}}'
+pub type AbiC = extern "C" fn();
+
+// @is - "$.index[*][?(@.name=='AbiSystem')].inner.type.inner.header.abi" '{"System": {"unwind": false}}'
+pub type AbiSystem = extern "system" fn();
+
+// @is - "$.index[*][?(@.name=='AbiCUnwind')].inner.type.inner.header.abi" '{"C": {"unwind": true}}'
+pub type AbiCUnwind = extern "C-unwind" fn();
+
+// @is - "$.index[*][?(@.name=='AbiSystemUnwind')].inner.type.inner.header.abi" '{"System": {"unwind": true}}'
+pub type AbiSystemUnwind = extern "system-unwind" fn();
+
+// @is - "$.index[*][?(@.name=='AbiVecorcall')].inner.type.inner.header.abi.Other" '"\"vectorcall\""'
+pub type AbiVecorcall = extern "vectorcall" fn();
+
+// @is - "$.index[*][?(@.name=='AbiVecorcallUnwind')].inner.type.inner.header.abi.Other" '"\"vectorcall-unwind\""'
+pub type AbiVecorcallUnwind = extern "vectorcall-unwind" fn();
diff --git a/src/test/rustdoc-json/fn_pointer/header.rs b/src/test/rustdoc-json/fn_pointer/header.rs
deleted file mode 100644
index a5038e0cd2a..00000000000
--- a/src/test/rustdoc-json/fn_pointer/header.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-// @has header.json "$.index[*][?(@.name=='FnPointer')].inner.type.inner.header" "[]"
-pub type FnPointer = fn();
-
-// @has - "$.index[*][?(@.name=='UnsafePointer')].inner.type.inner.header" '["unsafe"]'
-pub type UnsafePointer = unsafe fn();
diff --git a/src/test/rustdoc-json/fn_pointer/qualifiers.rs b/src/test/rustdoc-json/fn_pointer/qualifiers.rs
new file mode 100644
index 00000000000..38192208536
--- /dev/null
+++ b/src/test/rustdoc-json/fn_pointer/qualifiers.rs
@@ -0,0 +1,9 @@
+// @is qualifiers.json "$.index[*][?(@.name=='FnPointer')].inner.type.inner.header.unsafe" false
+// @is - "$.index[*][?(@.name=='FnPointer')].inner.type.inner.header.const" false
+// @is - "$.index[*][?(@.name=='FnPointer')].inner.type.inner.header.async" false
+pub type FnPointer = fn();
+
+// @is - "$.index[*][?(@.name=='UnsafePointer')].inner.type.inner.header.unsafe" true
+// @is - "$.index[*][?(@.name=='UnsafePointer')].inner.type.inner.header.const" false
+// @is - "$.index[*][?(@.name=='UnsafePointer')].inner.type.inner.header.async" false
+pub type UnsafePointer = unsafe fn();
diff --git a/src/test/rustdoc-json/fns/abi.rs b/src/test/rustdoc-json/fns/abi.rs
new file mode 100644
index 00000000000..16b57913065
--- /dev/null
+++ b/src/test/rustdoc-json/fns/abi.rs
@@ -0,0 +1,25 @@
+// ignore-tidy-linelength
+
+#![feature(abi_vectorcall)]
+#![feature(c_unwind)]
+
+// @is abi.json "$.index[*][?(@.name=='abi_rust')].inner.header.abi" \"Rust\"
+pub fn abi_rust() {}
+
+// @is - "$.index[*][?(@.name=='abi_c')].inner.header.abi" '{"C": {"unwind": false}}'
+pub extern "C" fn abi_c() {}
+
+// @is - "$.index[*][?(@.name=='abi_system')].inner.header.abi" '{"System": {"unwind": false}}'
+pub extern "system" fn abi_system() {}
+
+// @is - "$.index[*][?(@.name=='abi_c_unwind')].inner.header.abi" '{"C": {"unwind": true}}'
+pub extern "C-unwind" fn abi_c_unwind() {}
+
+// @is - "$.index[*][?(@.name=='abi_system_unwind')].inner.header.abi" '{"System": {"unwind": true}}'
+pub extern "system-unwind" fn abi_system_unwind() {}
+
+// @is - "$.index[*][?(@.name=='abi_vectorcall')].inner.header.abi.Other" '"\"vectorcall\""'
+pub extern "vectorcall" fn abi_vectorcall() {}
+
+// @is - "$.index[*][?(@.name=='abi_vectorcall_unwind')].inner.header.abi.Other" '"\"vectorcall-unwind\""'
+pub extern "vectorcall-unwind" fn abi_vectorcall_unwind() {}
diff --git a/src/test/rustdoc-json/fns/header.rs b/src/test/rustdoc-json/fns/header.rs
deleted file mode 100644
index 29741dd50da..00000000000
--- a/src/test/rustdoc-json/fns/header.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-// edition:2018
-
-// @has header.json "$.index[*][?(@.name=='nothing_fn')].inner.header" "[]"
-pub fn nothing_fn() {}
-
-// @has - "$.index[*][?(@.name=='const_fn')].inner.header" '["const"]'
-pub const fn const_fn() {}
-
-// @has - "$.index[*][?(@.name=='async_fn')].inner.header" '["async"]'
-pub async fn async_fn() {}
-
-// @count - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header[*]" 2
-// @has - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header[*]" '"async"'
-// @has - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header[*]" '"unsafe"'
-pub async unsafe fn async_unsafe_fn() {}
-
-// @count - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header[*]" 2
-// @has - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header[*]" '"const"'
-// @has - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header[*]" '"unsafe"'
-pub const unsafe fn const_unsafe_fn() {}
-
-// It's impossible for a function to be both const and async, so no test for that
diff --git a/src/test/rustdoc-json/fns/qualifiers.rs b/src/test/rustdoc-json/fns/qualifiers.rs
new file mode 100644
index 00000000000..5cb3b43e66a
--- /dev/null
+++ b/src/test/rustdoc-json/fns/qualifiers.rs
@@ -0,0 +1,33 @@
+// edition:2018
+
+// @is qualifiers.json "$.index[*][?(@.name=='nothing_fn')].inner.header.async" false
+// @is - "$.index[*][?(@.name=='nothing_fn')].inner.header.const"  false
+// @is - "$.index[*][?(@.name=='nothing_fn')].inner.header.unsafe" false
+pub fn nothing_fn() {}
+
+// @is - "$.index[*][?(@.name=='unsafe_fn')].inner.header.async"  false
+// @is - "$.index[*][?(@.name=='unsafe_fn')].inner.header.const"  false
+// @is - "$.index[*][?(@.name=='unsafe_fn')].inner.header.unsafe" true
+pub unsafe fn unsafe_fn() {}
+
+// @is - "$.index[*][?(@.name=='const_fn')].inner.header.async"  false
+// @is - "$.index[*][?(@.name=='const_fn')].inner.header.const"  true
+// @is - "$.index[*][?(@.name=='const_fn')].inner.header.unsafe" false
+pub const fn const_fn() {}
+
+// @is - "$.index[*][?(@.name=='async_fn')].inner.header.async"  true
+// @is - "$.index[*][?(@.name=='async_fn')].inner.header.const"  false
+// @is - "$.index[*][?(@.name=='async_fn')].inner.header.unsafe" false
+pub async fn async_fn() {}
+
+// @is - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header.async"  true
+// @is - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header.const"  false
+// @is - "$.index[*][?(@.name=='async_unsafe_fn')].inner.header.unsafe" true
+pub async unsafe fn async_unsafe_fn() {}
+
+// @is - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header.async"  false
+// @is - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header.const"  true
+// @is - "$.index[*][?(@.name=='const_unsafe_fn')].inner.header.unsafe" true
+pub const unsafe fn const_unsafe_fn() {}
+
+// It's impossible for a function to be both const and async, so no test for that
diff --git a/src/test/rustdoc-json/method_abi.rs b/src/test/rustdoc-json/method_abi.rs
deleted file mode 100644
index 6fabbc83611..00000000000
--- a/src/test/rustdoc-json/method_abi.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-// @has method_abi.json "$.index[*][?(@.name=='Foo')]"
-pub struct Foo;
-
-impl Foo {
-    // @has - "$.index[*][?(@.name=='abi_rust')].inner.abi" '"\"Rust\""'
-    pub fn abi_rust() {}
-
-    // @has - "$.index[*][?(@.name=='abi_c')].inner.abi" '"\"C\""'
-    pub extern "C" fn abi_c() {}
-
-    // @has - "$.index[*][?(@.name=='abi_system')].inner.abi" '"\"system\""'
-    pub extern "system" fn abi_system() {}
-}
-
-// @has method_abi.json "$.index[*][?(@.name=='Bar')]"
-pub trait Bar {
-    // @has - "$.index[*][?(@.name=='trait_abi_rust')].inner.abi" '"\"Rust\""'
-    fn trait_abi_rust();
-
-    // @has - "$.index[*][?(@.name=='trait_abi_c')].inner.abi" '"\"C\""'
-    extern "C" fn trait_abi_c();
-
-    // @has - "$.index[*][?(@.name=='trait_abi_system')].inner.abi" '"\"system\""'
-    extern "system" fn trait_abi_system();
-}
diff --git a/src/test/rustdoc-json/methods/abi.rs b/src/test/rustdoc-json/methods/abi.rs
new file mode 100644
index 00000000000..07b01d03bf6
--- /dev/null
+++ b/src/test/rustdoc-json/methods/abi.rs
@@ -0,0 +1,55 @@
+// ignore-tidy-linelength
+
+#![feature(abi_vectorcall)]
+#![feature(c_unwind)]
+#![feature(no_core)]
+#![no_core]
+
+// @has abi.json "$.index[*][?(@.name=='Foo')]"
+pub struct Foo;
+
+impl Foo {
+    // @is - "$.index[*][?(@.name=='abi_rust')].inner.header.abi" \"Rust\"
+    pub fn abi_rust() {}
+
+    // @is - "$.index[*][?(@.name=='abi_c')].inner.header.abi" '{"C": {"unwind": false}}'
+    pub extern "C" fn abi_c() {}
+
+    // @is - "$.index[*][?(@.name=='abi_system')].inner.header.abi" '{"System": {"unwind": false}}'
+    pub extern "system" fn abi_system() {}
+
+    // @is - "$.index[*][?(@.name=='abi_c_unwind')].inner.header.abi" '{"C": {"unwind": true}}'
+    pub extern "C-unwind" fn abi_c_unwind() {}
+
+    // @is - "$.index[*][?(@.name=='abi_system_unwind')].inner.header.abi" '{"System": {"unwind": true}}'
+    pub extern "system-unwind" fn abi_system_unwind() {}
+
+    // @is - "$.index[*][?(@.name=='abi_vectorcall')].inner.header.abi.Other" '"\"vectorcall\""'
+    pub extern "vectorcall" fn abi_vectorcall() {}
+
+    // @is - "$.index[*][?(@.name=='abi_vectorcall_unwind')].inner.header.abi.Other" '"\"vectorcall-unwind\""'
+    pub extern "vectorcall-unwind" fn abi_vectorcall_unwind() {}
+}
+
+pub trait Bar {
+    // @is - "$.index[*][?(@.name=='trait_abi_rust')].inner.header.abi" \"Rust\"
+    fn trait_abi_rust() {}
+
+    // @is - "$.index[*][?(@.name=='trait_abi_c')].inner.header.abi" '{"C": {"unwind": false}}'
+    extern "C" fn trait_abi_c() {}
+
+    // @is - "$.index[*][?(@.name=='trait_abi_system')].inner.header.abi" '{"System": {"unwind": false}}'
+    extern "system" fn trait_abi_system() {}
+
+    // @is - "$.index[*][?(@.name=='trait_abi_c_unwind')].inner.header.abi" '{"C": {"unwind": true}}'
+    extern "C-unwind" fn trait_abi_c_unwind() {}
+
+    // @is - "$.index[*][?(@.name=='trait_abi_system_unwind')].inner.header.abi" '{"System": {"unwind": true}}'
+    extern "system-unwind" fn trait_abi_system_unwind() {}
+
+    // @is - "$.index[*][?(@.name=='trait_abi_vectorcall')].inner.header.abi.Other" '"\"vectorcall\""'
+    extern "vectorcall" fn trait_abi_vectorcall() {}
+
+    // @is - "$.index[*][?(@.name=='trait_abi_vectorcall_unwind')].inner.header.abi.Other" '"\"vectorcall-unwind\""'
+    extern "vectorcall-unwind" fn trait_abi_vectorcall_unwind() {}
+}
diff --git a/src/test/rustdoc-json/methods/header.rs b/src/test/rustdoc-json/methods/header.rs
deleted file mode 100644
index 50a3db75ef3..00000000000
--- a/src/test/rustdoc-json/methods/header.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-// edition:2018
-
-pub struct Foo;
-
-impl Foo {
-    // @has header.json "$.index[*][?(@.name=='nothing_meth')].inner.header" "[]"
-    pub fn nothing_meth() {}
-
-    // @has - "$.index[*][?(@.name=='const_meth')].inner.header" '["const"]'
-    pub const fn const_meth() {}
-
-    // @has - "$.index[*][?(@.name=='async_meth')].inner.header" '["async"]'
-    pub async fn async_meth() {}
-
-    // @count - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header[*]" 2
-    // @has - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header[*]" '"async"'
-    // @has - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header[*]" '"unsafe"'
-    pub async unsafe fn async_unsafe_meth() {}
-
-    // @count - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header[*]" 2
-    // @has - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header[*]" '"const"'
-    // @has - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header[*]" '"unsafe"'
-    pub const unsafe fn const_unsafe_meth() {}
-
-    // It's impossible for a method to be both const and async, so no test for that
-}
diff --git a/src/test/rustdoc-json/methods/qualifiers.rs b/src/test/rustdoc-json/methods/qualifiers.rs
new file mode 100644
index 00000000000..af36d36b660
--- /dev/null
+++ b/src/test/rustdoc-json/methods/qualifiers.rs
@@ -0,0 +1,37 @@
+// edition:2018
+
+pub struct Foo;
+
+impl Foo {
+    // @is qualifiers.json "$.index[*][?(@.name=='const_meth')].inner.header.async" false
+    // @is - "$.index[*][?(@.name=='const_meth')].inner.header.const"  true
+    // @is - "$.index[*][?(@.name=='const_meth')].inner.header.unsafe" false
+    pub const fn const_meth() {}
+
+    // @is - "$.index[*][?(@.name=='nothing_meth')].inner.header.async"  false
+    // @is - "$.index[*][?(@.name=='nothing_meth')].inner.header.const"  false
+    // @is - "$.index[*][?(@.name=='nothing_meth')].inner.header.unsafe" false
+    pub fn nothing_meth() {}
+
+    // @is - "$.index[*][?(@.name=='unsafe_meth')].inner.header.async"  false
+    // @is - "$.index[*][?(@.name=='unsafe_meth')].inner.header.const"  false
+    // @is - "$.index[*][?(@.name=='unsafe_meth')].inner.header.unsafe" true
+    pub unsafe fn unsafe_meth() {}
+
+    // @is - "$.index[*][?(@.name=='async_meth')].inner.header.async"  true
+    // @is - "$.index[*][?(@.name=='async_meth')].inner.header.const"  false
+    // @is - "$.index[*][?(@.name=='async_meth')].inner.header.unsafe" false
+    pub async fn async_meth() {}
+
+    // @is - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header.async"  true
+    // @is - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header.const"  false
+    // @is - "$.index[*][?(@.name=='async_unsafe_meth')].inner.header.unsafe" true
+    pub async unsafe fn async_unsafe_meth() {}
+
+    // @is - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header.async"  false
+    // @is - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header.const"  true
+    // @is - "$.index[*][?(@.name=='const_unsafe_meth')].inner.header.unsafe" true
+    pub const unsafe fn const_unsafe_meth() {}
+
+    // It's impossible for a method to be both const and async, so no test for that
+}