src/tools/clippy/clippy_lints/src/doc/mod.rs RUST 1,350 lines View on github.com → Search inside
1#![allow(clippy::lint_without_lint_pass)]23use clippy_config::Conf;4use clippy_utils::attrs::is_doc_hidden;5use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then};6use clippy_utils::{is_entrypoint_fn, is_trait_impl_item};7use rustc_data_structures::fx::FxHashSet;8use rustc_errors::Applicability;9use rustc_hir::{Attribute, ImplItemKind, ItemKind, Node, Safety, TraitItemKind};10use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};11use rustc_resolve::rustdoc::pulldown_cmark::Event::{12    Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start,13    TaskListMarker, Text,14};15use rustc_resolve::rustdoc::pulldown_cmark::Tag::{16    BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph,17};18use rustc_resolve::rustdoc::pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd};19use rustc_resolve::rustdoc::{20    DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, pulldown_cmark,21    source_span_for_markdown_range, span_of_fragments,22};23use rustc_session::impl_lint_pass;24use rustc_span::Span;25use std::ops::Range;26use url::Url;2728mod broken_link;29mod doc_comment_double_space_linebreaks;30mod doc_paragraphs_missing_punctuation;31mod doc_suspicious_footnotes;32mod include_in_doc_without_cfg;33mod lazy_continuation;34mod link_with_quotes;35mod markdown;36mod missing_headers;37mod needless_doctest_main;38mod suspicious_doc_comments;39mod test_attr_in_doctest;40mod too_long_first_doc_paragraph;4142declare_clippy_lint! {43    /// ### What it does44    /// Checks the doc comments have unbroken links, mostly caused45    /// by bad formatted links such as broken across multiple lines.46    ///47    /// ### Why is this bad?48    /// Because documentation generated by rustdoc will be broken49    /// since expected links won't be links and just text.50    ///51    /// ### Examples52    /// This link is broken:53    /// ```no_run54    /// /// [example of a bad link](https://55    /// /// github.com/rust-lang/rust-clippy/)56    /// pub fn do_something() {}57    /// ```58    ///59    /// It shouldn't be broken across multiple lines to work:60    /// ```no_run61    /// /// [example of a good link](https://github.com/rust-lang/rust-clippy/)62    /// pub fn do_something() {}63    /// ```64    #[clippy::version = "1.90.0"]65    pub DOC_BROKEN_LINK,66    pedantic,67    "broken document link"68}6970declare_clippy_lint! {71    /// ### What it does72    /// Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (`\`).73    ///74    /// ### Why is this bad?75    /// Double spaces, when used as doc comment linebreaks, can be difficult to see, and may76    /// accidentally be removed during automatic formatting or manual refactoring. The use of a back-slash (`\`)77    /// is clearer in this regard.78    ///79    /// ### Example80    /// The two replacement dots in this example represent a double space.81    /// ```no_run82    /// /// This command takes two numbers as inputs and··83    /// /// adds them together, and then returns the result.84    /// fn add(l: i32, r: i32) -> i32 {85    ///     l + r86    /// }87    /// ```88    ///89    /// Use instead:90    /// ```no_run91    /// /// This command takes two numbers as inputs and\92    /// /// adds them together, and then returns the result.93    /// fn add(l: i32, r: i32) -> i32 {94    ///     l + r95    /// }96    /// ```97    #[clippy::version = "1.87.0"]98    pub DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,99    pedantic,100    "double-space used for doc comment linebreak instead of `\\`"101}102103declare_clippy_lint! {104    /// ### What it does105    /// Checks if included files in doc comments are included only for `cfg(doc)`.106    ///107    /// ### Why restrict this?108    /// These files are not useful for compilation but will still be included.109    /// Also, if any of these non-source code file is updated, it will trigger a110    /// recompilation.111    ///112    /// ### Known problems113    ///114    /// Excluding this will currently result in the file being left out if115    /// the item's docs are inlined from another crate. This may be fixed in a116    /// future version of rustdoc.117    ///118    /// ### Example119    /// ```ignore120    /// #![doc = include_str!("some_file.md")]121    /// ```122    /// Use instead:123    /// ```no_run124    /// #![cfg_attr(doc, doc = include_str!("some_file.md"))]125    /// ```126    #[clippy::version = "1.85.0"]127    pub DOC_INCLUDE_WITHOUT_CFG,128    restriction,129    "check if files included in documentation are behind `cfg(doc)`"130}131132declare_clippy_lint! {133    /// ### What it does134    ///135    /// In CommonMark Markdown, the language used to write doc comments, a136    /// paragraph nested within a list or block quote does not need any line137    /// after the first one to be indented or marked. The specification calls138    /// this a "lazy paragraph continuation."139    ///140    /// ### Why is this bad?141    ///142    /// This is easy to write but hard to read. Lazy continuations makes143    /// unintended markers hard to see, and make it harder to deduce the144    /// document's intended structure.145    ///146    /// ### Example147    ///148    /// This table is probably intended to have two rows,149    /// but it does not. It has zero rows, and is followed by150    /// a block quote.151    /// ```no_run152    /// /// Range | Description153    /// /// ----- | -----------154    /// /// >= 1  | fully opaque155    /// /// < 1   | partially see-through156    /// fn set_opacity(opacity: f32) {}157    /// ```158    ///159    /// Fix it by escaping the marker:160    /// ```no_run161    /// /// Range | Description162    /// /// ----- | -----------163    /// /// \>= 1 | fully opaque164    /// /// < 1   | partially see-through165    /// fn set_opacity(opacity: f32) {}166    /// ```167    ///168    /// This example is actually intended to be a list:169    /// ```no_run170    /// /// * Do nothing.171    /// /// * Then do something. Whatever it is needs done,172    /// /// it should be done right now.173    /// # fn do_stuff() {}174    /// ```175    ///176    /// Fix it by indenting the list contents:177    /// ```no_run178    /// /// * Do nothing.179    /// /// * Then do something. Whatever it is needs done,180    /// ///   it should be done right now.181    /// # fn do_stuff() {}182    /// ```183    #[clippy::version = "1.80.0"]184    pub DOC_LAZY_CONTINUATION,185    style,186    "require every line of a paragraph to be indented and marked"187}188189declare_clippy_lint! {190    /// ### What it does191    /// Checks for links with code directly adjacent to code text:192    /// `` [`MyItem`]`<`[`u32`]`>` ``.193    ///194    /// ### Why is this bad?195    /// It can be written more simply using HTML-style `<code>` tags.196    ///197    /// ### Example198    /// ```no_run199    /// //! [`first`](x)`second`200    /// ```201    /// Use instead:202    /// ```no_run203    /// //! <code>[first](x)second</code>204    /// ```205    #[clippy::version = "1.87.0"]206    pub DOC_LINK_CODE,207    nursery,208    "link with code back-to-back with other code"209}210211declare_clippy_lint! {212    /// ### What it does213    /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)214    /// outside of code blocks215    /// ### Why is this bad?216    /// It is likely a typo when defining an intra-doc link217    ///218    /// ### Example219    /// ```no_run220    /// /// See also: ['foo']221    /// fn bar() {}222    /// ```223    /// Use instead:224    /// ```no_run225    /// /// See also: [`foo`]226    /// fn bar() {}227    /// ```228    #[clippy::version = "1.63.0"]229    pub DOC_LINK_WITH_QUOTES,230    pedantic,231    "possible typo for an intra-doc link"232}233234declare_clippy_lint! {235    /// ### What it does236    /// Checks for the presence of `_`, `::` or camel-case words237    /// outside ticks in documentation.238    ///239    /// ### Why is this bad?240    /// *Rustdoc* supports markdown formatting, `_`, `::` and241    /// camel-case probably indicates some code which should be included between242    /// ticks. `_` can also be used for emphasis in markdown, this lint tries to243    /// consider that.244    ///245    /// ### Known problems246    /// Lots of bad docs won’t be fixed, what the lint checks247    /// for is limited, and there are still false positives. HTML elements and their248    /// content are not linted.249    ///250    /// In addition, when writing documentation comments, including `[]` brackets251    /// inside a link text would trip the parser. Therefore, documenting link with252    /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec253    /// would fail.254    ///255    /// ### Examples256    /// ```no_run257    /// /// Do something with the foo_bar parameter. See also258    /// /// that::other::module::foo.259    /// // ^ `foo_bar` and `that::other::module::foo` should be ticked.260    /// fn doit(foo_bar: usize) {}261    /// ```262    ///263    /// ```no_run264    /// // Link text with `[]` brackets should be written as following:265    /// /// Consume the array and return the inner266    /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].267    /// /// [SmallVec]: SmallVec268    /// fn main() {}269    /// ```270    #[clippy::version = "pre 1.29.0"]271    pub DOC_MARKDOWN,272    pedantic,273    "presence of `_`, `::` or camel-case outside backticks in documentation"274}275276declare_clippy_lint! {277    /// ### What it does278    /// Warns if a link reference definition appears at the start of a279    /// list item or quote.280    ///281    /// ### Why is this bad?282    /// This is probably intended as an intra-doc link. If it is really283    /// supposed to be a reference definition, it can be written outside284    /// of the list item or quote.285    ///286    /// ### Example287    /// ```no_run288    /// //! - [link]: description289    /// ```290    /// Use instead:291    /// ```no_run292    /// //! - [link][]: description (for intra-doc link)293    /// //!294    /// //! [link]: destination (for link reference definition)295    /// ```296    #[clippy::version = "1.85.0"]297    pub DOC_NESTED_REFDEFS,298    suspicious,299    "link reference defined in list item or quote"300}301302declare_clippy_lint! {303    /// ### What it does304    ///305    /// Detects overindented list items in doc comments where the continuation306    /// lines are indented more than necessary.307    ///308    /// ### Why is this bad?309    ///310    /// Overindented list items in doc comments can lead to inconsistent and311    /// poorly formatted documentation when rendered. Excessive indentation may312    /// cause the text to be misinterpreted as a nested list item or code block,313    /// affecting readability and the overall structure of the documentation.314    ///315    /// ### Example316    ///317    /// ```no_run318    /// /// - This is the first item in a list319    /// ///      and this line is overindented.320    /// # fn foo() {}321    /// ```322    ///323    /// Fixes this into:324    /// ```no_run325    /// /// - This is the first item in a list326    /// ///   and this line is overindented.327    /// # fn foo() {}328    /// ```329    #[clippy::version = "1.86.0"]330    pub DOC_OVERINDENTED_LIST_ITEMS,331    style,332    "ensure list items are not overindented"333}334335declare_clippy_lint! {336    /// ### What it does337    /// Checks for doc comments whose paragraphs do not end with a period or another punctuation mark.338    /// Various Markdowns constructs are taken into account to avoid false positives.339    ///340    /// ### Why is this bad?341    /// A project may wish to enforce consistent doc comments by making sure paragraphs end with a342    /// punctuation mark.343    ///344    /// ### Example345    /// ```no_run346    /// /// Returns a random number347    /// ///348    /// /// It was chosen by a fair dice roll349    /// ```350    /// Use instead:351    /// ```no_run352    /// /// Returns a random number.353    /// ///354    /// /// It was chosen by a fair dice roll.355    /// ```356    ///357    /// ### Terminal punctuation marks358    /// This lint treats these characters as end markers: '.', '?', '!', '…' and ':'.359    ///360    /// The colon is not exactly a terminal punctuation mark, but this is required for paragraphs that361    /// introduce a table or a list for example.362    #[clippy::version = "1.93.0"]363    pub DOC_PARAGRAPHS_MISSING_PUNCTUATION,364    restriction,365    "missing terminal punctuation in doc comments"366}367368declare_clippy_lint! {369    /// ### What it does370    /// Detects syntax that looks like a footnote reference.371    ///372    /// Rustdoc footnotes are compatible with GitHub-Flavored Markdown (GFM).373    /// GFM does not parse a footnote reference unless its definition also374    /// exists. This lint checks for footnote references with missing375    /// definitions, unless it thinks you're writing a regex.376    ///377    /// ### Why is this bad?378    /// This probably means that a footnote was meant to exist,379    /// but was not written.380    ///381    /// ### Example382    /// ```no_run383    /// /// This is not a footnote[^1], because no definition exists.384    /// fn my_fn() {}385    /// ```386    /// Use instead:387    /// ```no_run388    /// /// This is a footnote[^1].389    /// ///390    /// /// [^1]: defined here391    /// fn my_fn() {}392    /// ```393    #[clippy::version = "1.89.0"]394    pub DOC_SUSPICIOUS_FOOTNOTES,395    suspicious,396    "looks like a link or footnote ref, but with no definition"397}398399declare_clippy_lint! {400    /// ### What it does401    /// Detects documentation that is empty.402    /// ### Why is this bad?403    /// Empty docs clutter code without adding value, reducing readability and maintainability.404    /// ### Example405    /// ```no_run406    /// ///407    /// fn returns_true() -> bool {408    ///     true409    /// }410    /// ```411    /// Use instead:412    /// ```no_run413    /// fn returns_true() -> bool {414    ///     true415    /// }416    /// ```417    #[clippy::version = "1.78.0"]418    pub EMPTY_DOCS,419    suspicious,420    "docstrings exist but documentation is empty"421}422423declare_clippy_lint! {424    /// ### What it does425    /// Checks the doc comments of publicly visible functions that426    /// return a `Result` type and warns if there is no `# Errors` section.427    ///428    /// ### Why is this bad?429    /// Documenting the type of errors that can be returned from a430    /// function can help callers write code to handle the errors appropriately.431    ///432    /// ### Examples433    /// Since the following function returns a `Result` it has an `# Errors` section in434    /// its doc comment:435    ///436    /// ```no_run437    ///# use std::io;438    /// /// # Errors439    /// ///440    /// /// Will return `Err` if `filename` does not exist or the user does not have441    /// /// permission to read it.442    /// pub fn read(filename: String) -> io::Result<String> {443    ///     unimplemented!();444    /// }445    /// ```446    #[clippy::version = "1.41.0"]447    pub MISSING_ERRORS_DOC,448    pedantic,449    "`pub fn` returns `Result` without `# Errors` in doc comment"450}451452declare_clippy_lint! {453    /// ### What it does454    /// Checks the doc comments of publicly visible functions that455    /// may panic and warns if there is no `# Panics` section.456    ///457    /// ### Why is this bad?458    /// Documenting the scenarios in which panicking occurs459    /// can help callers who do not want to panic to avoid those situations.460    ///461    /// ### Examples462    /// Since the following function may panic it has a `# Panics` section in463    /// its doc comment:464    ///465    /// ```no_run466    /// /// # Panics467    /// ///468    /// /// Will panic if y is 0469    /// pub fn divide_by(x: i32, y: i32) -> i32 {470    ///     if y == 0 {471    ///         panic!("Cannot divide by 0")472    ///     } else {473    ///         x / y474    ///     }475    /// }476    /// ```477    ///478    /// Individual panics within a function can be ignored with `#[expect]` or479    /// `#[allow]`:480    ///481    /// ```no_run482    /// # use std::num::NonZeroUsize;483    /// pub fn will_not_panic(x: usize) {484    ///     #[expect(clippy::missing_panics_doc, reason = "infallible")]485    ///     let y = NonZeroUsize::new(1).unwrap();486    ///487    ///     // If any panics are added in the future the lint will still catch them488    /// }489    /// ```490    #[clippy::version = "1.51.0"]491    pub MISSING_PANICS_DOC,492    pedantic,493    "`pub fn` may panic without `# Panics` in doc comment"494}495496declare_clippy_lint! {497    /// ### What it does498    /// Checks for the doc comments of publicly visible499    /// unsafe functions and warns if there is no `# Safety` section.500    ///501    /// ### Why is this bad?502    /// Unsafe functions should document their safety503    /// preconditions, so that users can be sure they are using them safely.504    ///505    /// ### Examples506    /// ```no_run507    ///# type Universe = ();508    /// /// This function should really be documented509    /// pub unsafe fn start_apocalypse(u: &mut Universe) {510    ///     unimplemented!();511    /// }512    /// ```513    ///514    /// At least write a line about safety:515    ///516    /// ```no_run517    ///# type Universe = ();518    /// /// # Safety519    /// ///520    /// /// This function should not be called before the horsemen are ready.521    /// pub unsafe fn start_apocalypse(u: &mut Universe) {522    ///     unimplemented!();523    /// }524    /// ```525    #[clippy::version = "1.39.0"]526    pub MISSING_SAFETY_DOC,527    style,528    "`pub unsafe fn` without `# Safety` docs"529}530531declare_clippy_lint! {532    /// ### What it does533    /// Checks for `fn main() { .. }` in doctests534    ///535    /// ### Why is this bad?536    /// The test can be shorter (and likely more readable)537    /// if the `fn main()` is left implicit.538    ///539    /// ### Examples540    /// ```no_run541    /// /// An example of a doctest with a `main()` function542    /// ///543    /// /// # Examples544    /// ///545    /// /// ```546    /// /// fn main() {547    /// ///     // this needs not be in an `fn`548    /// /// }549    /// /// ```550    /// fn needless_main() {551    ///     unimplemented!();552    /// }553    /// ```554    #[clippy::version = "1.40.0"]555    pub NEEDLESS_DOCTEST_MAIN,556    style,557    "presence of `fn main() {` in code examples"558}559560declare_clippy_lint! {561    /// ### What it does562    /// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!`563    ///564    /// ### Why is this bad?565    /// Triple-slash comments (known as "outer doc comments") apply to items that follow it.566    /// An outer doc comment followed by a bang (i.e. `///!`) has no specific meaning.567    ///568    /// The user most likely meant to write an inner doc comment (`//!`, `/*!`), which569    /// applies to the parent item (i.e. the item that the comment is contained in,570    /// usually a module or crate).571    ///572    /// ### Known problems573    /// Inner doc comments can only appear before items, so there are certain cases where the suggestion574    /// made by this lint is not valid code. For example:575    /// ```rust576    /// fn foo() {}577    /// ///!578    /// fn bar() {}579    /// ```580    /// This lint detects the doc comment and suggests changing it to `//!`, but an inner doc comment581    /// is not valid at that position.582    ///583    /// ### Example584    /// In this example, the doc comment is attached to the *function*, rather than the *module*.585    /// ```no_run586    /// pub mod util {587    ///     ///! This module contains utility functions.588    ///589    ///     pub fn dummy() {}590    /// }591    /// ```592    ///593    /// Use instead:594    /// ```no_run595    /// pub mod util {596    ///     //! This module contains utility functions.597    ///598    ///     pub fn dummy() {}599    /// }600    /// ```601    #[clippy::version = "1.70.0"]602    pub SUSPICIOUS_DOC_COMMENTS,603    suspicious,604    "suspicious usage of (outer) doc comments"605}606607declare_clippy_lint! {608    /// ### What it does609    /// Checks for `#[test]` in doctests unless they are marked with610    /// either `ignore`, `no_run` or `compile_fail`.611    ///612    /// ### Why is this bad?613    /// Code in examples marked as `#[test]` will somewhat614    /// surprisingly not be run by `cargo test`. If you really want615    /// to show how to test stuff in an example, mark it `no_run` to616    /// make the intent clear.617    ///618    /// ### Examples619    /// ```no_run620    /// /// An example of a doctest with a `main()` function621    /// ///622    /// /// # Examples623    /// ///624    /// /// ```625    /// /// #[test]626    /// /// fn equality_works() {627    /// ///     assert_eq!(1_u8, 1);628    /// /// }629    /// /// ```630    /// fn test_attr_in_doctest() {631    ///     unimplemented!();632    /// }633    /// ```634    #[clippy::version = "1.76.0"]635    pub TEST_ATTR_IN_DOCTEST,636    suspicious,637    "presence of `#[test]` in code examples"638}639640declare_clippy_lint! {641    /// ### What it does642    /// Checks if the first paragraph in the documentation of items listed in the module page is too long.643    ///644    /// ### Why is this bad?645    /// Documentation will show the first paragraph of the docstring in the summary page of a646    /// module. Having a nice, short summary in the first paragraph is part of writing good docs.647    ///648    /// ### Example649    /// ```no_run650    /// /// A very short summary.651    /// /// A much longer explanation that goes into a lot more detail about652    /// /// how the thing works, possibly with doclinks and so one,653    /// /// and probably spanning a many rows.654    /// struct Foo {}655    /// ```656    /// Use instead:657    /// ```no_run658    /// /// A very short summary.659    /// ///660    /// /// A much longer explanation that goes into a lot more detail about661    /// /// how the thing works, possibly with doclinks and so one,662    /// /// and probably spanning a many rows.663    /// struct Foo {}664    /// ```665    #[clippy::version = "1.82.0"]666    pub TOO_LONG_FIRST_DOC_PARAGRAPH,667    nursery,668    "ensure the first documentation paragraph is short"669}670671declare_clippy_lint! {672    /// ### What it does673    /// Checks for the doc comments of publicly visible674    /// safe functions and traits and warns if there is a `# Safety` section.675    ///676    /// ### Why restrict this?677    /// Safe functions and traits are safe to implement and therefore do not678    /// need to describe safety preconditions that users are required to uphold.679    ///680    /// ### Examples681    /// ```no_run682    ///# type Universe = ();683    /// /// # Safety684    /// ///685    /// /// This function should not be called before the horsemen are ready.686    /// pub fn start_apocalypse_but_safely(u: &mut Universe) {687    ///     unimplemented!();688    /// }689    /// ```690    ///691    /// The function is safe, so there shouldn't be any preconditions692    /// that have to be explained for safety reasons.693    ///694    /// ```no_run695    ///# type Universe = ();696    /// /// This function should really be documented697    /// pub fn start_apocalypse(u: &mut Universe) {698    ///     unimplemented!();699    /// }700    /// ```701    #[clippy::version = "1.67.0"]702    pub UNNECESSARY_SAFETY_DOC,703    restriction,704    "`pub fn` or `pub trait` with `# Safety` docs"705}706707impl_lint_pass!(Documentation => [708    DOC_BROKEN_LINK,709    DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,710    DOC_INCLUDE_WITHOUT_CFG,711    DOC_LAZY_CONTINUATION,712    DOC_LINK_CODE,713    DOC_LINK_WITH_QUOTES,714    DOC_MARKDOWN,715    DOC_NESTED_REFDEFS,716    DOC_OVERINDENTED_LIST_ITEMS,717    DOC_PARAGRAPHS_MISSING_PUNCTUATION,718    DOC_SUSPICIOUS_FOOTNOTES,719    EMPTY_DOCS,720    MISSING_ERRORS_DOC,721    MISSING_PANICS_DOC,722    MISSING_SAFETY_DOC,723    NEEDLESS_DOCTEST_MAIN,724    SUSPICIOUS_DOC_COMMENTS,725    TEST_ATTR_IN_DOCTEST,726    TOO_LONG_FIRST_DOC_PARAGRAPH,727    UNNECESSARY_SAFETY_DOC,728]);729730pub struct Documentation {731    valid_idents: FxHashSet<String>,732    check_private_items: bool,733}734735impl Documentation {736    pub fn new(conf: &'static Conf) -> Self {737        Self {738            valid_idents: conf.doc_valid_idents.iter().cloned().collect(),739            check_private_items: conf.check_private_items,740        }741    }742}743744impl EarlyLintPass for Documentation {745    fn check_attributes(&mut self, cx: &EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) {746        include_in_doc_without_cfg::check(cx, attrs);747    }748}749750impl<'tcx> LateLintPass<'tcx> for Documentation {751    fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {752        let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {753            return;754        };755756        match cx.tcx.hir_node(cx.last_node_with_lint_attrs) {757            Node::Item(item) => {758                too_long_first_doc_paragraph::check(759                    cx,760                    item,761                    attrs,762                    headers.first_paragraph_len,763                    self.check_private_items,764                );765                match item.kind {766                    ItemKind::Fn { sig, body, .. }767                        if !(is_entrypoint_fn(cx, item.owner_id.to_def_id())768                            || item.span.in_external_macro(cx.tcx.sess.source_map())) =>769                    {770                        missing_headers::check(cx, item.owner_id, sig, headers, Some(body), self.check_private_items);771                    },772                    ItemKind::Trait { safety, .. } => match (headers.safety, safety) {773                        (false, Safety::Unsafe) => span_lint(774                            cx,775                            MISSING_SAFETY_DOC,776                            cx.tcx.def_span(item.owner_id),777                            "docs for unsafe trait missing `# Safety` section",778                        ),779                        (true, Safety::Safe) => span_lint(780                            cx,781                            UNNECESSARY_SAFETY_DOC,782                            cx.tcx.def_span(item.owner_id),783                            "docs for safe trait have unnecessary `# Safety` section",784                        ),785                        _ => (),786                    },787                    _ => (),788                }789            },790            Node::TraitItem(trait_item) => {791                if let TraitItemKind::Fn(sig, ..) = trait_item.kind792                    && !trait_item.span.in_external_macro(cx.tcx.sess.source_map())793                {794                    missing_headers::check(cx, trait_item.owner_id, sig, headers, None, self.check_private_items);795                }796            },797            Node::ImplItem(impl_item) => {798                if let ImplItemKind::Fn(sig, body_id) = impl_item.kind799                    && !impl_item.span.in_external_macro(cx.tcx.sess.source_map())800                    && !is_trait_impl_item(cx, impl_item.hir_id())801                {802                    missing_headers::check(803                        cx,804                        impl_item.owner_id,805                        sig,806                        headers,807                        Some(body_id),808                        self.check_private_items,809                    );810                }811            },812            _ => {},813        }814    }815}816817#[derive(Copy, Clone)]818struct Fragments<'a> {819    doc: &'a str,820    fragments: &'a [DocFragment],821}822823impl Fragments<'_> {824    /// get the span for the markdown range. Note that this function is not cheap, use it with825    /// caution.826    #[must_use]827    fn span(self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> {828        source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments).map(|(sp, _)| sp)829    }830}831832#[derive(Copy, Clone, Default)]833struct DocHeaders {834    safety: bool,835    errors: bool,836    panics: bool,837    first_paragraph_len: usize,838}839840/// Does some pre-processing on raw, desugared `#[doc]` attributes such as parsing them and841/// then delegates to `check_doc`.842/// Some lints are already checked here if they can work with attributes directly and don't need843/// to work with markdown.844/// Others are checked elsewhere, e.g. in `check_doc` if they need access to markdown, or845/// back in the various late lint pass methods if they need the final doc headers, like "Safety" or846/// "Panics" sections.847fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> {848    // We don't want the parser to choke on intra doc links. Since we don't849    // actually care about rendering them, just pretend that all broken links850    // point to a fake address.851    #[expect(clippy::unnecessary_wraps)] // we're following a type signature852    fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {853        Some(("fake".into(), "fake".into()))854    }855856    if suspicious_doc_comments::check(cx, attrs) || is_doc_hidden(attrs) {857        return None;858    }859860    let (fragments, _) = attrs_to_doc_fragments(861        attrs.iter().filter_map(|attr| {862            if attr.doc_str_and_fragment_kind().is_none() || attr.span().in_external_macro(cx.sess().source_map()) {863                None864            } else {865                Some((attr, None))866            }867        }),868        true,869    );870871    let mut doc = String::with_capacity(fragments.iter().map(|frag| frag.doc.as_str().len() + 1).sum());872873    for fragment in &fragments {874        add_doc_fragment(&mut doc, fragment);875    }876    doc.pop();877878    if doc.trim().is_empty() {879        if let Some(span) = span_of_fragments(&fragments) {880            span_lint_and_help(881                cx,882                EMPTY_DOCS,883                span,884                "empty doc comment",885                None,886                "consider removing or filling it",887            );888        }889        return Some(DocHeaders::default());890    }891892    check_for_code_clusters(893        cx,894        pulldown_cmark::Parser::new_with_broken_link_callback(895            &doc,896            main_body_opts() - Options::ENABLE_SMART_PUNCTUATION,897            Some(&mut fake_broken_link_callback),898        )899        .into_offset_iter(),900        &doc,901        Fragments {902            doc: &doc,903            fragments: &fragments,904        },905    );906907    doc_paragraphs_missing_punctuation::check(908        cx,909        &doc,910        Fragments {911            doc: &doc,912            fragments: &fragments,913        },914    );915916    // NOTE: check_doc uses it own cb function,917    // to avoid causing duplicated diagnostics for the broken link checker.918    let mut full_fake_broken_link_callback = |bl: BrokenLink<'_>| -> Option<(CowStr<'_>, CowStr<'_>)> {919        broken_link::check(cx, &bl, &doc, &fragments);920        Some(("fake".into(), "fake".into()))921    };922923    // disable smart punctuation to pick up ['link'] more easily924    let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION;925    let parser =926        pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut full_fake_broken_link_callback));927928    Some(check_doc(929        cx,930        valid_idents,931        parser.into_offset_iter(),932        &doc,933        Fragments {934            doc: &doc,935            fragments: &fragments,936        },937        attrs,938    ))939}940941enum Container {942    Blockquote,943    List(usize),944}945946/// Scan the documentation for code links that are back-to-back with code spans.947///948/// This is done separately from the rest of the docs, because that makes it easier to produce949/// the correct messages.950fn check_for_code_clusters<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(951    cx: &LateContext<'_>,952    events: Events,953    doc: &str,954    fragments: Fragments<'_>,955) {956    let mut events = events.peekable();957    let mut code_starts_at = None;958    let mut code_ends_at = None;959    let mut code_includes_link = false;960    while let Some((event, range)) = events.next() {961        match event {962            Start(Link { .. }) if matches!(events.peek(), Some((Code(_), _range))) => {963                if code_starts_at.is_some() {964                    code_ends_at = Some(range.end);965                } else {966                    code_starts_at = Some(range.start);967                }968                code_includes_link = true;969                // skip the nested "code", because we're already handling it here970                let _ = events.next();971            },972            Code(_) => {973                if code_starts_at.is_some() {974                    code_ends_at = Some(range.end);975                } else {976                    code_starts_at = Some(range.start);977                }978            },979            End(TagEnd::Link) => {},980            _ => {981                if let Some(start) = code_starts_at982                    && let Some(end) = code_ends_at983                    && code_includes_link984                    && let Some(span) = fragments.span(cx, start..end)985                {986                    span_lint_and_then(cx, DOC_LINK_CODE, span, "code link adjacent to code text", |diag| {987                        let sugg = format!("<code>{}</code>", doc[start..end].replace('`', ""));988                        diag.span_suggestion_verbose(989                            span,990                            "wrap the entire group in `<code>` tags",991                            sugg,992                            Applicability::MaybeIncorrect,993                        );994                        diag.help("separate code snippets will be shown with a gap");995                    });996                }997                code_includes_link = false;998                code_starts_at = None;999                code_ends_at = None;1000            },1001        }1002    }1003}10041005#[derive(Clone, Copy)]1006#[expect(clippy::struct_excessive_bools)]1007struct CodeTags {1008    no_run: bool,1009    ignore: bool,1010    compile_fail: bool,1011    test_harness: bool,10121013    rust: bool,1014}10151016impl Default for CodeTags {1017    fn default() -> Self {1018        Self {1019            no_run: false,1020            ignore: false,1021            compile_fail: false,1022            test_harness: false,10231024            rust: true,1025        }1026    }1027}10281029impl CodeTags {1030    /// Based on <https://github.com/rust-lang/rust/blob/1.90.0/src/librustdoc/html/markdown.rs#L1169>1031    fn parse(lang: &str) -> Self {1032        let mut tags = Self::default();10331034        let mut seen_rust_tags = false;1035        let mut seen_other_tags = false;1036        for item in lang.split([',', ' ', '\t']) {1037            match item.trim() {1038                "" => {},1039                "rust" => {1040                    tags.rust = true;1041                    seen_rust_tags = true;1042                },1043                "ignore" => {1044                    tags.ignore = true;1045                    seen_rust_tags = !seen_other_tags;1046                },1047                "no_run" => {1048                    tags.no_run = true;1049                    seen_rust_tags = !seen_other_tags;1050                },1051                "should_panic" => seen_rust_tags = !seen_other_tags,1052                "compile_fail" => {1053                    tags.compile_fail = true;1054                    seen_rust_tags = !seen_other_tags || seen_rust_tags;1055                },1056                "test_harness" => {1057                    tags.test_harness = true;1058                    seen_rust_tags = !seen_other_tags || seen_rust_tags;1059                },1060                "standalone_crate" => {1061                    seen_rust_tags = !seen_other_tags || seen_rust_tags;1062                },1063                _ if item.starts_with("ignore-") => seen_rust_tags = true,1064                _ if item.starts_with("edition") => {},1065                _ => seen_other_tags = true,1066            }1067        }10681069        tags.rust &= seen_rust_tags || !seen_other_tags;10701071        tags1072    }1073}10741075/// Checks parsed documentation.1076/// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`,1077/// so lints here will generally access that information.1078/// Returns documentation headers -- whether a "Safety", "Errors", "Panic" section was found1079#[expect(clippy::too_many_lines, reason = "big match statement")]1080fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(1081    cx: &LateContext<'_>,1082    valid_idents: &FxHashSet<String>,1083    events: Events,1084    doc: &str,1085    fragments: Fragments<'_>,1086    attrs: &[Attribute],1087) -> DocHeaders {1088    // true if a safety header was found1089    let mut headers = DocHeaders::default();1090    let mut code = None;1091    let mut in_link = None;1092    let mut in_heading = false;1093    let mut in_footnote_definition = false;1094    let mut ticks_unbalanced = false;1095    let mut text_to_check: Vec<(CowStr<'_>, Range<usize>, isize)> = Vec::new();1096    let mut paragraph_range = 0..0;1097    let mut code_level = 0;1098    let mut blockquote_level = 0;1099    let mut collected_breaks: Vec<Span> = Vec::new();1100    let mut is_first_paragraph = true;11011102    let mut containers = Vec::new();11031104    let mut events = events.peekable();11051106    while let Some((event, range)) = events.next() {1107        match event {1108            Html(tag) | InlineHtml(tag) => {1109                if tag.starts_with("<code") {1110                    code_level += 1;1111                } else if tag.starts_with("</code") {1112                    code_level -= 1;1113                } else if tag.starts_with("<blockquote") || tag.starts_with("<q") {1114                    blockquote_level += 1;1115                } else if tag.starts_with("</blockquote") || tag.starts_with("</q") {1116                    blockquote_level -= 1;1117                }1118            },1119            Start(BlockQuote(_)) => {1120                blockquote_level += 1;1121                containers.push(Container::Blockquote);1122                if let Some((next_event, next_range)) = events.peek() {1123                    let next_start = match next_event {1124                        End(TagEnd::BlockQuote) => next_range.end,1125                        _ => next_range.start,1126                    };1127                    if let Some(refdefrange) = looks_like_refdef(doc, range.start..next_start) &&1128                        let Some(refdefspan) = fragments.span(cx, refdefrange.clone())1129                    {1130                        span_lint_and_then(1131                            cx,1132                            DOC_NESTED_REFDEFS,1133                            refdefspan,1134                            "link reference defined in quote",1135                            |diag| {1136                                diag.span_suggestion_short(1137                                    refdefspan.shrink_to_hi(),1138                                    "for an intra-doc link, add `[]` between the label and the colon",1139                                    "[]",1140                                    Applicability::MaybeIncorrect,1141                                );1142                                diag.help("link definitions are not shown in rendered documentation");1143                            }1144                        );1145                    }1146                }1147            },1148            End(TagEnd::BlockQuote) => {1149                blockquote_level -= 1;1150                containers.pop();1151            },1152            Start(CodeBlock(ref kind)) => {1153                code = Some(match kind {1154                    CodeBlockKind::Indented => CodeTags::default(),1155                    CodeBlockKind::Fenced(lang) => CodeTags::parse(lang),1156                });1157            },1158            End(TagEnd::CodeBlock) => code = None,1159            Start(Link { dest_url, .. }) => in_link = Some(dest_url),1160            End(TagEnd::Link) => in_link = None,1161            Start(Heading { .. } | Paragraph | Item) => {1162                if let Start(Heading { .. }) = event {1163                    in_heading = true;1164                }1165                if let Start(Item) = event {1166                    let indent = if let Some((next_event, next_range)) = events.peek() {1167                        let next_start = match next_event {1168                            End(TagEnd::Item) => next_range.end,1169                            _ => next_range.start,1170                        };1171                        if let Some(refdefrange) = looks_like_refdef(doc, range.start..next_start) &&1172                            let Some(refdefspan) = fragments.span(cx, refdefrange.clone())1173                        {1174                            span_lint_and_then(1175                                cx,1176                                DOC_NESTED_REFDEFS,1177                                refdefspan,1178                                "link reference defined in list item",1179                                |diag| {1180                                    diag.span_suggestion_short(1181                                        refdefspan.shrink_to_hi(),1182                                        "for an intra-doc link, add `[]` between the label and the colon",1183                                        "[]",1184                                        Applicability::MaybeIncorrect,1185                                    );1186                                    diag.help("link definitions are not shown in rendered documentation");1187                                }1188                            );1189                            refdefrange.start - range.start1190                        } else {1191                            let mut start = next_range.start;1192                            if start > 0 && doc.as_bytes().get(start - 1) == Some(&b'\\') {1193                                // backslashes aren't in the event stream...1194                                start -= 1;1195                            }11961197                            start.saturating_sub(range.start)1198                        }1199                    } else {1200                        01201                    };1202                    containers.push(Container::List(indent));1203                }1204                ticks_unbalanced = false;1205                paragraph_range = range;1206                if is_first_paragraph {1207                    headers.first_paragraph_len = doc[paragraph_range.clone()].chars().count();1208                    is_first_paragraph = false;1209                }1210            },1211            End(TagEnd::Heading(_) | TagEnd::Paragraph | TagEnd::Item) => {1212                if let End(TagEnd::Heading(_)) = event {1213                    in_heading = false;1214                }1215                if let End(TagEnd::Item) = event {1216                    containers.pop();1217                }1218                if ticks_unbalanced && let Some(span) = fragments.span(cx, paragraph_range.clone()) {1219                    span_lint_and_help(1220                        cx,1221                        DOC_MARKDOWN,1222                        span,1223                        "backticks are unbalanced",1224                        None,1225                        "a backtick may be missing a pair",1226                    );1227                    text_to_check.clear();1228                } else {1229                    for (text, range, assoc_code_level) in text_to_check.drain(..) {1230                        markdown::check(cx, valid_idents, &text, &fragments, range, assoc_code_level, blockquote_level);1231                    }1232                }1233            },1234            Start(FootnoteDefinition(..)) => in_footnote_definition = true,1235            End(TagEnd::FootnoteDefinition) => in_footnote_definition = false,1236            Start(_) | End(_)  // We don't care about other tags1237            | TaskListMarker(_) | Code(_) | Rule | InlineMath(..) | DisplayMath(..) => (),1238            SoftBreak | HardBreak => {1239                if !containers.is_empty()1240                    && !in_footnote_definition1241                    // Tabs aren't handled correctly vvvv1242                    && !doc[range.clone()].contains('\t')1243                    && let Some((next_event, next_range)) = events.peek()1244                    && !matches!(next_event, End(_))1245                {1246                    lazy_continuation::check(1247                        cx,1248                        doc,1249                        range.end..next_range.start,1250                        &fragments,1251                        &containers[..],1252                    );1253                }125412551256                if event == HardBreak1257                    && !doc[range.clone()].trim().starts_with('\\')1258                    && let Some(span) = fragments.span(cx, range.clone())1259                    && !span.from_expansion()1260                    {1261                    collected_breaks.push(span);1262                }1263            },1264            Text(text) => {1265                paragraph_range.end = range.end;1266                let range_ = range.clone();1267                ticks_unbalanced |= text.contains('`')1268                    && code.is_none()1269                    && doc[range.clone()].bytes().enumerate().any(|(i, c)| {1270                        // scan the markdown source code bytes for backquotes that aren't preceded by backslashes1271                        // - use bytes, instead of chars, to avoid utf8 decoding overhead (special chars are ascii)1272                        // - relevant backquotes are within doc[range], but backslashes are not, because they're not1273                        //   actually part of the rendered text (pulldown-cmark doesn't emit any events for escapes)1274                        // - if `range_.start + i == 0`, then `range_.start + i - 1 == -1`, and since we're working in1275                        //   usize, that would underflow and maybe panic1276                        c == b'`' && (range_.start + i == 0 || doc.as_bytes().get(range_.start + i - 1) != Some(&b'\\'))1277                    });1278                if Some(&text) == in_link.as_ref() || ticks_unbalanced {1279                    // Probably a link of the form `<http://example.com>`1280                    // Which are represented as a link to "http://example.com" with1281                    // text "http://example.com" by pulldown-cmark1282                    continue;1283                }1284                let trimmed_text = text.trim();1285                headers.safety |= in_heading && trimmed_text == "Safety";1286                headers.safety |= in_heading && trimmed_text == "SAFETY";1287                headers.safety |= in_heading && trimmed_text == "Implementation safety";1288                headers.safety |= in_heading && trimmed_text == "Implementation Safety";1289                headers.errors |= in_heading && trimmed_text == "Errors";1290                headers.panics |= in_heading && trimmed_text == "Panics";12911292                if let Some(tags) = code {1293                    if tags.rust && !tags.compile_fail && !tags.ignore {1294                        needless_doctest_main::check(cx, &text, range.start, fragments);12951296                        if !tags.no_run && !tags.test_harness {1297                            test_attr_in_doctest::check(cx, &text, range.start, fragments);1298                        }1299                    }1300                } else {1301                    if in_link.is_some() {1302                        link_with_quotes::check(cx, trimmed_text, range.clone(), fragments);1303                    }1304                    if let Some(link) = in_link.as_ref()1305                        && let Ok(url) = Url::parse(link)1306                        && (url.scheme() == "https" || url.scheme() == "http")1307                    {1308                        // Don't check the text associated with external URLs1309                        continue;1310                    }1311                    text_to_check.push((text, range.clone(), code_level));1312                    doc_suspicious_footnotes::check(cx, doc, range, &fragments, attrs);1313                }1314            }1315            FootnoteReference(_) => {}1316        }1317    }13181319    doc_comment_double_space_linebreaks::check(cx, &collected_breaks);13201321    headers1322}13231324fn looks_like_refdef(doc: &str, range: Range<usize>) -> Option<Range<usize>> {1325    if range.end < range.start {1326        return None;1327    }13281329    let offset = range.start;1330    let mut iterator = doc.as_bytes()[range].iter().copied().enumerate();1331    let mut start = None;1332    while let Some((i, byte)) = iterator.next() {1333        match byte {1334            b'\\' => {1335                iterator.next();1336            },1337            b'[' => {1338                start = Some(i + offset);1339            },1340            b']' if let Some(start) = start1341                && doc.as_bytes().get(i + offset + 1) == Some(&b':') =>1342            {1343                return Some(start..i + offset + 1);1344            },1345            _ => {},1346        }1347    }1348    None1349}

Code quality findings 34

Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
/// unsafe functions and warns if there is no `# Safety` section.
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
"`pub unsafe fn` without `# Safety` docs"
Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
"docs for unsafe trait missing `# Safety` section",
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// //! - [link][]: description (for intra-doc link)
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// /// This is not a footnote[^1], because no definition exists.
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// /// This is a footnote[^1].
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
/// let y = NonZeroUsize::new(1).unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
fragments: &'a [DocFragment],
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
// disable smart punctuation to pick up ['link'] more easily
Warning: Ignoring a Result or Option using 'let _ =' can hide errors or unexpected None values. Ensure the value is handled appropriately (match, if let, ?, expect) unless intentionally discarded with justification.
warning correctness discarded-result
let _ = events.next();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let sugg = format!("<code>{}</code>", doc[start..end].replace('`', ""));
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
headers.first_paragraph_len = doc[paragraph_range.clone()].chars().count();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&& !doc[range.clone()].contains('\t')
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&containers[..],
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&& !doc[range.clone()].trim().starts_with('\\')
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&& doc[range.clone()].bytes().enumerate().any(|(i, c)| {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
// - relevant backquotes are within doc[range], but backslashes are not, because they're not
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let mut iterator = doc.as_bytes()[range].iter().copied().enumerate();
Info: Including file content directly into the binary at compile time. Ensure this is intended, as it increases binary size and doesn't reflect runtime file changes.
info maintainability include-in-binary
/// #![doc = include_str!("some_file.md")]
Info: Including file content directly into the binary at compile time. Ensure this is intended, as it increases binary size and doesn't reflect runtime file changes.
info maintainability include-in-binary
/// #![cfg_attr(doc, doc = include_str!("some_file.md"))]
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
/// unimplemented!();
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
/// unimplemented!();
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
/// unimplemented!();
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
/// unimplemented!();
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
/// unimplemented!();
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
/// unimplemented!();
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info correctness todo-unimplemented
/// unimplemented!();
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let next_start = match next_event {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let next_start = match next_event {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.