about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustdoc/core.rs175
-rw-r--r--src/librustdoc/doctest.rs9
-rw-r--r--src/librustdoc/html/markdown.rs2
-rw-r--r--src/librustdoc/lib.rs1
-rw-r--r--src/librustdoc/lint.rs164
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs2
-rw-r--r--src/librustdoc/passes/doc_test_lints.rs6
-rw-r--r--src/librustdoc/passes/html_tags.rs2
-rw-r--r--src/librustdoc/passes/non_autolinks.rs2
9 files changed, 183 insertions, 180 deletions
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 0dc762ea276..5313f8553a8 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -12,8 +12,6 @@ use rustc_hir::{
     Path,
 };
 use rustc_interface::{interface, Queries};
-use rustc_lint::LintStore;
-use rustc_lint_defs::{declare_tool_lint, Lint, LintId};
 use rustc_middle::hir::map::Map;
 use rustc_middle::middle::privacy::AccessLevels;
 use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
@@ -28,7 +26,6 @@ use rustc_span::DUMMY_SP;
 
 use std::cell::RefCell;
 use std::collections::hash_map::Entry;
-use std::lazy::SyncLazy as Lazy;
 use std::mem;
 use std::rc::Rc;
 
@@ -232,164 +229,6 @@ crate fn new_handler(
     )
 }
 
-/// This function is used to setup the lint initialization. By default, in rustdoc, everything
-/// is "allowed". Depending if we run in test mode or not, we want some of them to be at their
-/// default level. For example, the "INVALID_CODEBLOCK_ATTRIBUTES" lint is activated in both
-/// modes.
-///
-/// A little detail easy to forget is that there is a way to set the lint level for all lints
-/// through the "WARNINGS" lint. To prevent this to happen, we set it back to its "normal" level
-/// inside this function.
-///
-/// It returns a tuple containing:
-///  * Vector of tuples of lints' name and their associated "max" level
-///  * HashMap of lint id with their associated "max" level
-pub(crate) fn init_lints<F>(
-    mut allowed_lints: Vec<String>,
-    lint_opts: Vec<(String, lint::Level)>,
-    filter_call: F,
-) -> (Vec<(String, lint::Level)>, FxHashMap<lint::LintId, lint::Level>)
-where
-    F: Fn(&lint::Lint) -> Option<(String, lint::Level)>,
-{
-    let warnings_lint_name = lint::builtin::WARNINGS.name;
-
-    allowed_lints.push(warnings_lint_name.to_owned());
-    allowed_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned());
-
-    let lints = || {
-        lint::builtin::HardwiredLints::get_lints()
-            .into_iter()
-            .chain(rustc_lint::SoftLints::get_lints().into_iter())
-    };
-
-    let lint_opts = lints()
-        .filter_map(|lint| {
-            // Permit feature-gated lints to avoid feature errors when trying to
-            // allow all lints.
-            if lint.feature_gate.is_some() || allowed_lints.iter().any(|l| lint.name == l) {
-                None
-            } else {
-                filter_call(lint)
-            }
-        })
-        .chain(lint_opts.into_iter())
-        .collect::<Vec<_>>();
-
-    let lint_caps = lints()
-        .filter_map(|lint| {
-            // We don't want to allow *all* lints so let's ignore
-            // those ones.
-            if allowed_lints.iter().any(|l| lint.name == l) {
-                None
-            } else {
-                Some((lint::LintId::of(lint), lint::Allow))
-            }
-        })
-        .collect();
-    (lint_opts, lint_caps)
-}
-
-declare_tool_lint! {
-    /// The `broken_intra_doc_links` lint detects failures in resolving
-    /// intra-doc link targets. This is a `rustdoc` only lint, see the
-    /// documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#broken_intra_doc_links
-    pub rustdoc::BROKEN_INTRA_DOC_LINKS,
-    Warn,
-    "failures in resolving intra-doc link targets"
-}
-
-declare_tool_lint! {
-    /// This is a subset of `broken_intra_doc_links` that warns when linking from
-    /// a public item to a private one. This is a `rustdoc` only lint, see the
-    /// documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#private_intra_doc_links
-    pub rustdoc::PRIVATE_INTRA_DOC_LINKS,
-    Warn,
-    "linking from a public item to a private one"
-}
-
-declare_tool_lint! {
-    /// The `invalid_codeblock_attributes` lint detects code block attributes
-    /// in documentation examples that have potentially mis-typed values. This
-    /// is a `rustdoc` only lint, see the documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_codeblock_attributes
-    pub rustdoc::INVALID_CODEBLOCK_ATTRIBUTES,
-    Warn,
-    "codeblock attribute looks a lot like a known one"
-}
-
-declare_tool_lint! {
-    /// The `missing_doc_code_examples` lint detects publicly-exported items
-    /// without code samples in their documentation. This is a `rustdoc` only
-    /// lint, see the documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#missing_doc_code_examples
-    pub rustdoc::MISSING_DOC_CODE_EXAMPLES,
-    Allow,
-    "detects publicly-exported items without code samples in their documentation"
-}
-
-declare_tool_lint! {
-    /// The `private_doc_tests` lint detects code samples in docs of private
-    /// items not documented by `rustdoc`. This is a `rustdoc` only lint, see
-    /// the documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#private_doc_tests
-    pub rustdoc::PRIVATE_DOC_TESTS,
-    Allow,
-    "detects code samples in docs of private items not documented by rustdoc"
-}
-
-declare_tool_lint! {
-    /// The `invalid_html_tags` lint detects invalid HTML tags. This is a
-    /// `rustdoc` only lint, see the documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_html_tags
-    pub rustdoc::INVALID_HTML_TAGS,
-    Allow,
-    "detects invalid HTML tags in doc comments"
-}
-
-declare_tool_lint! {
-    /// The `non_autolinks` lint detects when a URL could be written using
-    /// only angle brackets. This is a `rustdoc` only lint, see the
-    /// documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#non_autolinks
-    pub rustdoc::NON_AUTOLINKS,
-    Warn,
-    "detects URLs that could be written using only angle brackets"
-}
-
-static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
-    vec![
-        BROKEN_INTRA_DOC_LINKS,
-        PRIVATE_INTRA_DOC_LINKS,
-        MISSING_DOC_CODE_EXAMPLES,
-        PRIVATE_DOC_TESTS,
-        INVALID_CODEBLOCK_ATTRIBUTES,
-        INVALID_HTML_TAGS,
-        NON_AUTOLINKS,
-    ]
-});
-
-crate fn register_lints(_sess: &Session, lint_store: &mut LintStore) {
-    lint_store.register_lints(&**RUSTDOC_LINTS);
-    lint_store.register_group(
-        true,
-        "rustdoc",
-        None,
-        RUSTDOC_LINTS.iter().map(|&lint| LintId::of(lint)).collect(),
-    );
-    lint_store
-        .register_renamed("intra_doc_link_resolution_failure", "rustdoc::broken_intra_doc_links");
-}
-
 /// Parse, resolve, and typecheck the given crate.
 crate fn create_config(
     RustdocOptions {
@@ -418,8 +257,8 @@ crate fn create_config(
     let cpath = Some(input.clone());
     let input = Input::File(input);
 
-    // In addition to those specific lints, we also need to allow those given through
-    // command line, otherwise they'll get ignored and we don't want that.
+    // By default, rustdoc ignores all lints.
+    // Specifically unblock lints relevant to documentation or the lint machinery itself.
     let mut lints_to_show = vec![
         // it's unclear whether these should be part of rustdoc directly
         rustc_lint::builtin::MISSING_DOCS.name.to_string(),
@@ -428,12 +267,12 @@ crate fn create_config(
         rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name.to_string(),
         rustc_lint::builtin::UNKNOWN_LINTS.name.to_string(),
     ];
-    lints_to_show.extend(RUSTDOC_LINTS.iter().map(|lint| lint.name.to_string()));
+    lints_to_show.extend(crate::lint::RUSTDOC_LINTS.iter().map(|lint| lint.name.to_string()));
 
-    let (lint_opts, lint_caps) = init_lints(lints_to_show, lint_opts, |lint| {
+    let (lint_opts, lint_caps) = crate::lint::init_lints(lints_to_show, lint_opts, |lint| {
         // FIXME: why is this necessary?
-        if lint.name == BROKEN_INTRA_DOC_LINKS.name
-            || lint.name == INVALID_CODEBLOCK_ATTRIBUTES.name
+        if lint.name == crate::lint::BROKEN_INTRA_DOC_LINKS.name
+            || lint.name == crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name
         {
             None
         } else {
@@ -474,7 +313,7 @@ crate fn create_config(
         diagnostic_output: DiagnosticOutput::Default,
         stderr: None,
         lint_caps,
-        register_lints: Some(box register_lints),
+        register_lints: Some(box crate::lint::register_lints),
         override_queries: Some(|_sess, providers, _external_providers| {
             // Most lints will require typechecking, so just don't run them.
             providers.lint_mod = |_, _| {};
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index eac0ad688d4..27ce064669d 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -26,8 +26,8 @@ use std::str;
 
 use crate::clean::Attributes;
 use crate::config::Options;
-use crate::core::init_lints;
 use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
+use crate::lint::init_lints;
 use crate::passes::span_of_attrs;
 
 #[derive(Clone, Default)]
@@ -44,10 +44,9 @@ crate struct TestOptions {
 crate fn run(options: Options) -> Result<(), ErrorReported> {
     let input = config::Input::File(options.input.clone());
 
-    let invalid_codeblock_attributes_name = crate::core::INVALID_CODEBLOCK_ATTRIBUTES.name;
+    let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name;
 
-    // In addition to those specific lints, we also need to allow those given through
-    // command line, otherwise they'll get ignored and we don't want that.
+    // See core::create_config for what's going on here.
     let allowed_lints = vec![
         invalid_codeblock_attributes_name.to_owned(),
         lint::builtin::UNKNOWN_LINTS.name.to_owned(),
@@ -96,7 +95,7 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
         diagnostic_output: DiagnosticOutput::Default,
         stderr: None,
         lint_caps,
-        register_lints: Some(box crate::core::register_lints),
+        register_lints: Some(box crate::lint::register_lints),
         override_queries: None,
         make_codegen_backend: None,
         registry: rustc_driver::diagnostics_registry(),
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 8c8840e38e6..f8ca259fb9a 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -720,7 +720,7 @@ impl<'tcx> ExtraInfo<'tcx> {
             (None, None) => return,
         };
         self.tcx.struct_span_lint_hir(
-            crate::core::INVALID_CODEBLOCK_ATTRIBUTES,
+            crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
             hir_id,
             self.sp,
             |lint| {
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 6388733c8b2..c152f0fb7f6 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -86,6 +86,7 @@ mod formats;
 // used by the error-index generator, so it needs to be public
 pub mod html;
 mod json;
+crate mod lint;
 mod markdown;
 mod passes;
 mod theme;
diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs
new file mode 100644
index 00000000000..778983868ea
--- /dev/null
+++ b/src/librustdoc/lint.rs
@@ -0,0 +1,164 @@
+use rustc_data_structures::fx::FxHashMap;
+use rustc_lint::LintStore;
+use rustc_lint_defs::{declare_tool_lint, Lint, LintId};
+use rustc_session::{lint, Session};
+
+use std::lazy::SyncLazy as Lazy;
+
+/// This function is used to setup the lint initialization. By default, in rustdoc, everything
+/// is "allowed". Depending if we run in test mode or not, we want some of them to be at their
+/// default level. For example, the "INVALID_CODEBLOCK_ATTRIBUTES" lint is activated in both
+/// modes.
+///
+/// A little detail easy to forget is that there is a way to set the lint level for all lints
+/// through the "WARNINGS" lint. To prevent this to happen, we set it back to its "normal" level
+/// inside this function.
+///
+/// It returns a tuple containing:
+///  * Vector of tuples of lints' name and their associated "max" level
+///  * HashMap of lint id with their associated "max" level
+pub(crate) fn init_lints<F>(
+    mut allowed_lints: Vec<String>,
+    lint_opts: Vec<(String, lint::Level)>,
+    filter_call: F,
+) -> (Vec<(String, lint::Level)>, FxHashMap<lint::LintId, lint::Level>)
+where
+    F: Fn(&lint::Lint) -> Option<(String, lint::Level)>,
+{
+    let warnings_lint_name = lint::builtin::WARNINGS.name;
+
+    allowed_lints.push(warnings_lint_name.to_owned());
+    allowed_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned());
+
+    let lints = || {
+        lint::builtin::HardwiredLints::get_lints()
+            .into_iter()
+            .chain(rustc_lint::SoftLints::get_lints().into_iter())
+    };
+
+    let lint_opts = lints()
+        .filter_map(|lint| {
+            // Permit feature-gated lints to avoid feature errors when trying to
+            // allow all lints.
+            if lint.feature_gate.is_some() || allowed_lints.iter().any(|l| lint.name == l) {
+                None
+            } else {
+                filter_call(lint)
+            }
+        })
+        .chain(lint_opts.into_iter())
+        .collect::<Vec<_>>();
+
+    let lint_caps = lints()
+        .filter_map(|lint| {
+            // We don't want to allow *all* lints so let's ignore
+            // those ones.
+            if allowed_lints.iter().any(|l| lint.name == l) {
+                None
+            } else {
+                Some((lint::LintId::of(lint), lint::Allow))
+            }
+        })
+        .collect();
+    (lint_opts, lint_caps)
+}
+
+declare_tool_lint! {
+    /// The `broken_intra_doc_links` lint detects failures in resolving
+    /// intra-doc link targets. This is a `rustdoc` only lint, see the
+    /// documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#broken_intra_doc_links
+    pub rustdoc::BROKEN_INTRA_DOC_LINKS,
+    Warn,
+    "failures in resolving intra-doc link targets"
+}
+
+declare_tool_lint! {
+    /// This is a subset of `broken_intra_doc_links` that warns when linking from
+    /// a public item to a private one. This is a `rustdoc` only lint, see the
+    /// documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#private_intra_doc_links
+    pub rustdoc::PRIVATE_INTRA_DOC_LINKS,
+    Warn,
+    "linking from a public item to a private one"
+}
+
+declare_tool_lint! {
+    /// The `invalid_codeblock_attributes` lint detects code block attributes
+    /// in documentation examples that have potentially mis-typed values. This
+    /// is a `rustdoc` only lint, see the documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_codeblock_attributes
+    pub rustdoc::INVALID_CODEBLOCK_ATTRIBUTES,
+    Warn,
+    "codeblock attribute looks a lot like a known one"
+}
+
+declare_tool_lint! {
+    /// The `missing_doc_code_examples` lint detects publicly-exported items
+    /// without code samples in their documentation. This is a `rustdoc` only
+    /// lint, see the documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#missing_doc_code_examples
+    pub rustdoc::MISSING_DOC_CODE_EXAMPLES,
+    Allow,
+    "detects publicly-exported items without code samples in their documentation"
+}
+
+declare_tool_lint! {
+    /// The `private_doc_tests` lint detects code samples in docs of private
+    /// items not documented by `rustdoc`. This is a `rustdoc` only lint, see
+    /// the documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#private_doc_tests
+    pub rustdoc::PRIVATE_DOC_TESTS,
+    Allow,
+    "detects code samples in docs of private items not documented by rustdoc"
+}
+
+declare_tool_lint! {
+    /// The `invalid_html_tags` lint detects invalid HTML tags. This is a
+    /// `rustdoc` only lint, see the documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_html_tags
+    pub rustdoc::INVALID_HTML_TAGS,
+    Allow,
+    "detects invalid HTML tags in doc comments"
+}
+
+declare_tool_lint! {
+    /// The `non_autolinks` lint detects when a URL could be written using
+    /// only angle brackets. This is a `rustdoc` only lint, see the
+    /// documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#non_autolinks
+    pub rustdoc::NON_AUTOLINKS,
+    Warn,
+    "detects URLs that could be written using only angle brackets"
+}
+
+crate static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
+    vec![
+        BROKEN_INTRA_DOC_LINKS,
+        PRIVATE_INTRA_DOC_LINKS,
+        MISSING_DOC_CODE_EXAMPLES,
+        PRIVATE_DOC_TESTS,
+        INVALID_CODEBLOCK_ATTRIBUTES,
+        INVALID_HTML_TAGS,
+        NON_AUTOLINKS,
+    ]
+});
+
+crate fn register_lints(_sess: &Session, lint_store: &mut LintStore) {
+    lint_store.register_lints(&**RUSTDOC_LINTS);
+    lint_store.register_group(
+        true,
+        "rustdoc",
+        None,
+        RUSTDOC_LINTS.iter().map(|&lint| LintId::of(lint)).collect(),
+    );
+    lint_store
+        .register_renamed("intra_doc_link_resolution_failure", "rustdoc::broken_intra_doc_links");
+}
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 6619adb5b76..38efecb393b 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -32,9 +32,9 @@ use std::ops::Range;
 
 use crate::clean::{self, utils::find_nearest_parent_module, Crate, Item, ItemLink, PrimitiveType};
 use crate::core::DocContext;
-use crate::core::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS};
 use crate::fold::DocFolder;
 use crate::html::markdown::{markdown_links, MarkdownLink};
+use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS};
 use crate::passes::Pass;
 
 use super::span_of_attrs;
diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs
index 85547e557df..c6b956c6000 100644
--- a/src/librustdoc/passes/doc_test_lints.rs
+++ b/src/librustdoc/passes/doc_test_lints.rs
@@ -68,7 +68,7 @@ crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> boo
         return false;
     }
     let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id.expect_local());
-    let (level, source) = cx.tcx.lint_level_at_node(crate::core::MISSING_DOC_CODE_EXAMPLES, hir_id);
+    let (level, source) = cx.tcx.lint_level_at_node(crate::lint::MISSING_DOC_CODE_EXAMPLES, hir_id);
     level != lint::Level::Allow || matches!(source, LintLevelSource::Default)
 }
 
@@ -90,7 +90,7 @@ crate fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
             debug!("reporting error for {:?} (hir_id={:?})", item, hir_id);
             let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span());
             cx.tcx.struct_span_lint_hir(
-                crate::core::MISSING_DOC_CODE_EXAMPLES,
+                crate::lint::MISSING_DOC_CODE_EXAMPLES,
                 hir_id,
                 sp,
                 |lint| lint.build("missing code example in this documentation").emit(),
@@ -98,7 +98,7 @@ crate fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
         }
     } else if tests.found_tests > 0 && !cx.renderinfo.access_levels.is_public(item.def_id) {
         cx.tcx.struct_span_lint_hir(
-            crate::core::PRIVATE_DOC_TESTS,
+            crate::lint::PRIVATE_DOC_TESTS,
             hir_id,
             span_of_attrs(&item.attrs).unwrap_or(item.source.span()),
             |lint| lint.build("documentation test in private item").emit(),
diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/html_tags.rs
index b97872c6333..27e669aa44f 100644
--- a/src/librustdoc/passes/html_tags.rs
+++ b/src/librustdoc/passes/html_tags.rs
@@ -182,7 +182,7 @@ impl<'a, 'tcx> DocFolder for InvalidHtmlTagsLinter<'a, 'tcx> {
                     Some(sp) => sp,
                     None => span_of_attrs(&item.attrs).unwrap_or(item.source.span()),
                 };
-                cx.tcx.struct_span_lint_hir(crate::core::INVALID_HTML_TAGS, hir_id, sp, |lint| {
+                cx.tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, |lint| {
                     lint.build(msg).emit()
                 });
             };
diff --git a/src/librustdoc/passes/non_autolinks.rs b/src/librustdoc/passes/non_autolinks.rs
index fe21887fc05..09a1959fa11 100644
--- a/src/librustdoc/passes/non_autolinks.rs
+++ b/src/librustdoc/passes/non_autolinks.rs
@@ -73,7 +73,7 @@ impl<'a, 'tcx> DocFolder for NonAutolinksLinter<'a, 'tcx> {
                 let sp = super::source_span_for_markdown_range(cx, &dox, &range, &item.attrs)
                     .or_else(|| span_of_attrs(&item.attrs))
                     .unwrap_or(item.source.span());
-                cx.tcx.struct_span_lint_hir(crate::core::NON_AUTOLINKS, hir_id, sp, |lint| {
+                cx.tcx.struct_span_lint_hir(crate::lint::NON_AUTOLINKS, hir_id, sp, |lint| {
                     lint.build(msg)
                         .span_suggestion(
                             sp,