src/tools/clippy/clippy_lints/src/doc/mod.rs RUST 1,408 lines View on github.com → Search inside
1use clippy_config::Conf;2use clippy_utils::attrs::is_doc_hidden;3use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then};4use clippy_utils::macros::root_macro_call;5use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, sym};6use rustc_ast::token::DocFragmentKind;7use rustc_attr_ir::Attribute;8use rustc_data_structures::fx::FxHashSet;9use rustc_errors::Applicability;10use rustc_hir::{FieldDef, ImplItemKind, ItemKind, Node, Safety, TraitItemKind};11use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext as _, impl_lint_pass};12use rustc_resolve::rustdoc::pulldown_cmark::Event::{13    Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start,14    TaskListMarker, Text,15};16use rustc_resolve::rustdoc::pulldown_cmark::Tag::{17    BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph,18};19use rustc_resolve::rustdoc::pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd};20use rustc_resolve::rustdoc::{21    DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, pulldown_cmark,22    source_span_for_markdown_range, span_of_fragments,23};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 comments that use double spaces as hard line break, instead of backslash (`\`).73    ///74    /// ### Why is this bad?75    /// Double spaces, when used as hard line break in doc comments, can be difficult to see, and may76    /// accidentally be removed during automatic formatting or manual refactoring. The use of a backslash (`\`)77    /// is clearer in this regard.78    ///79    /// ### Example80    /// The two replacement dots (`··`) in this example represent a double space.81    /// ```no_run82    /// /// This function adds two numbers and returns the result··83    /// /// Overflow can occur when the max value is exceeded.84    /// fn add(l: i32, r: i32) -> i32 {85    ///     l + r86    /// }87    /// ```88    ///89    /// Use instead:90    /// ```no_run91    /// /// This function adds two numbers and returns the result\92    /// /// Overflow can occur when the max value is exceeded.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 hard line break 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 foo() {}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    /// # fn foo() {}350    /// ```351    /// Use instead:352    /// ```no_run353    /// /// Returns a random number.354    /// ///355    /// /// It was chosen by a fair dice roll.356    /// # fn foo() {}357    /// ```358    ///359    /// ### Terminal punctuation marks360    /// This lint treats these characters as end markers: '.', '?', '!', '…' and ':'.361    ///362    /// The colon is not exactly a terminal punctuation mark, but this is required for paragraphs that363    /// introduce a table or a list for example.364    #[clippy::version = "1.93.0"]365    pub DOC_PARAGRAPHS_MISSING_PUNCTUATION,366    restriction,367    "missing terminal punctuation in doc comments"368}369370declare_clippy_lint! {371    /// ### What it does372    /// Detects syntax that looks like a footnote reference.373    ///374    /// Rustdoc footnotes are compatible with GitHub-Flavored Markdown (GFM).375    /// GFM does not parse a footnote reference unless its definition also376    /// exists. This lint checks for footnote references with missing377    /// definitions, unless it thinks you're writing a regex.378    ///379    /// ### Why is this bad?380    /// This probably means that a footnote was meant to exist,381    /// but was not written.382    ///383    /// ### Example384    /// ```no_run385    /// /// This is not a footnote[^1], because no definition exists.386    /// fn my_fn() {}387    /// ```388    /// Use instead:389    /// ```no_run390    /// /// This is a footnote[^1].391    /// ///392    /// /// [^1]: defined here393    /// fn my_fn() {}394    /// ```395    #[clippy::version = "1.89.0"]396    pub DOC_SUSPICIOUS_FOOTNOTES,397    suspicious,398    "looks like a link or footnote ref, but with no definition"399}400401declare_clippy_lint! {402    /// ### What it does403    /// Detects documentation that is empty.404    /// ### Why is this bad?405    /// Empty docs clutter code without adding value, reducing readability and maintainability.406    /// ### Example407    /// ```no_run408    /// ///409    /// fn returns_true() -> bool {410    ///     true411    /// }412    /// ```413    /// Use instead:414    /// ```no_run415    /// fn returns_true() -> bool {416    ///     true417    /// }418    /// ```419    #[clippy::version = "1.78.0"]420    pub EMPTY_DOCS,421    suspicious,422    "docstrings exist but documentation is empty"423}424425declare_clippy_lint! {426    /// ### What it does427    /// Checks the doc comments of publicly visible functions that428    /// return a `Result` type and warns if there is no `# Errors` section.429    ///430    /// ### Why is this bad?431    /// Documenting the type of errors that can be returned from a432    /// function can help callers write code to handle the errors appropriately.433    ///434    /// ### Examples435    /// Since the following function returns a `Result` it has an `# Errors` section in436    /// its doc comment:437    ///438    /// ```no_run439    ///# use std::io;440    /// /// # Errors441    /// ///442    /// /// Will return `Err` if `filename` does not exist or the user does not have443    /// /// permission to read it.444    /// pub fn read(filename: String) -> io::Result<String> {445    ///     unimplemented!();446    /// }447    /// ```448    #[clippy::version = "1.41.0"]449    pub MISSING_ERRORS_DOC,450    pedantic,451    "`pub fn` returns `Result` without `# Errors` in doc comment"452}453454declare_clippy_lint! {455    /// ### What it does456    /// Checks the doc comments of publicly visible functions that457    /// may panic and warns if there is no `# Panics` section.458    ///459    /// ### Why is this bad?460    /// Documenting the scenarios in which panicking occurs461    /// can help callers who do not want to panic to avoid those situations.462    ///463    /// ### Examples464    /// Since the following function may panic it has a `# Panics` section in465    /// its doc comment:466    ///467    /// ```no_run468    /// /// # Panics469    /// ///470    /// /// Will panic if y is 0471    /// pub fn divide_by(x: i32, y: i32) -> i32 {472    ///     if y == 0 {473    ///         panic!("Cannot divide by 0")474    ///     } else {475    ///         x / y476    ///     }477    /// }478    /// ```479    ///480    /// Individual panics within a function can be ignored with `#[expect]` or481    /// `#[allow]`:482    ///483    /// ```no_run484    /// # use std::num::NonZeroUsize;485    /// pub fn will_not_panic(x: usize) {486    ///     #[expect(clippy::missing_panics_doc, reason = "infallible")]487    ///     let y = NonZeroUsize::new(1).unwrap();488    ///489    ///     // If any panics are added in the future the lint will still catch them490    /// }491    /// ```492    #[clippy::version = "1.51.0"]493    pub MISSING_PANICS_DOC,494    pedantic,495    "`pub fn` may panic without `# Panics` in doc comment"496}497498declare_clippy_lint! {499    /// ### What it does500    /// Checks for the doc comments of publicly visible501    /// unsafe functions and warns if there is no `# Safety` section.502    ///503    /// ### Why is this bad?504    /// Unsafe functions should document their safety505    /// preconditions, so that users can be sure they are using them safely.506    ///507    /// ### Examples508    /// ```no_run509    ///# type Universe = ();510    /// /// This function should really be documented511    /// pub unsafe fn start_apocalypse(u: &mut Universe) {512    ///     unimplemented!();513    /// }514    /// ```515    ///516    /// At least write a line about safety:517    ///518    /// ```no_run519    ///# type Universe = ();520    /// /// # Safety521    /// ///522    /// /// This function should not be called before the horsemen are ready.523    /// pub unsafe fn start_apocalypse(u: &mut Universe) {524    ///     unimplemented!();525    /// }526    /// ```527    #[clippy::version = "1.39.0"]528    pub MISSING_SAFETY_DOC,529    style,530    "`pub unsafe fn` without `# Safety` docs"531}532533declare_clippy_lint! {534    /// ### What it does535    /// Checks for `fn main() { .. }` in doctests536    ///537    /// ### Why is this bad?538    /// The test can be shorter (and likely more readable)539    /// if the `fn main()` is left implicit.540    ///541    /// ### Examples542    /// ```no_run543    /// /// An example of a doctest with a `main()` function544    /// ///545    /// /// # Examples546    /// ///547    /// /// ```548    /// /// fn main() {549    /// ///     // this needs not be in an `fn`550    /// /// }551    /// /// ```552    /// fn needless_main() {553    ///     unimplemented!();554    /// }555    /// ```556    #[clippy::version = "1.40.0"]557    pub NEEDLESS_DOCTEST_MAIN,558    style,559    "presence of `fn main() {` in code examples"560}561562declare_clippy_lint! {563    /// ### What it does564    /// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!`565    ///566    /// ### Why is this bad?567    /// Triple-slash comments (known as "outer doc comments") apply to items that follow it.568    /// An outer doc comment followed by a bang (i.e. `///!`) has no specific meaning.569    ///570    /// The user most likely meant to write an inner doc comment (`//!`, `/*!`), which571    /// applies to the parent item (i.e. the item that the comment is contained in,572    /// usually a module or crate).573    ///574    /// ### Known problems575    /// Inner doc comments can only appear before items, so there are certain cases where the suggestion576    /// made by this lint is not valid code. For example:577    /// ```rust578    /// fn foo() {}579    /// ///!580    /// fn bar() {}581    /// ```582    /// This lint detects the doc comment and suggests changing it to `//!`, but an inner doc comment583    /// is not valid at that position.584    ///585    /// ### Example586    /// In this example, the doc comment is attached to the *function*, rather than the *module*.587    /// ```no_run588    /// pub mod util {589    ///     ///! This module contains utility functions.590    ///591    ///     pub fn dummy() {}592    /// }593    /// ```594    ///595    /// Use instead:596    /// ```no_run597    /// pub mod util {598    ///     //! This module contains utility functions.599    ///600    ///     pub fn dummy() {}601    /// }602    /// ```603    #[clippy::version = "1.70.0"]604    pub SUSPICIOUS_DOC_COMMENTS,605    suspicious,606    "suspicious usage of (outer) doc comments"607}608609declare_clippy_lint! {610    /// ### What it does611    /// Checks for `#[test]` in doctests unless they are marked with612    /// either `ignore`, `no_run` or `compile_fail`.613    ///614    /// ### Why is this bad?615    /// Code in examples marked as `#[test]` will somewhat616    /// surprisingly not be run by `cargo test`. If you really want617    /// to show how to test stuff in an example, mark it `no_run` to618    /// make the intent clear.619    ///620    /// ### Examples621    /// ```no_run622    /// /// An example of a doctest with a `main()` function623    /// ///624    /// /// # Examples625    /// ///626    /// /// ```627    /// /// #[test]628    /// /// fn equality_works() {629    /// ///     assert_eq!(1_u8, 1);630    /// /// }631    /// /// ```632    /// fn test_attr_in_doctest() {633    ///     unimplemented!();634    /// }635    /// ```636    #[clippy::version = "1.76.0"]637    pub TEST_ATTR_IN_DOCTEST,638    suspicious,639    "presence of `#[test]` in code examples"640}641642declare_clippy_lint! {643    /// ### What it does644    /// Checks if the first paragraph in the documentation of items listed in the module page is too long.645    ///646    /// ### Why is this bad?647    /// Documentation will show the first paragraph of the docstring in the summary page of a648    /// module. Having a nice, short summary in the first paragraph is part of writing good docs.649    ///650    /// ### Example651    /// ```no_run652    /// /// A very short summary.653    /// /// A much longer explanation that goes into a lot more detail about654    /// /// how the thing works, possibly with doclinks and so one,655    /// /// and probably spanning a many rows.656    /// struct Foo {}657    /// ```658    /// Use instead:659    /// ```no_run660    /// /// A very short summary.661    /// ///662    /// /// A much longer explanation that goes into a lot more detail about663    /// /// how the thing works, possibly with doclinks and so one,664    /// /// and probably spanning a many rows.665    /// struct Foo {}666    /// ```667    #[clippy::version = "1.82.0"]668    pub TOO_LONG_FIRST_DOC_PARAGRAPH,669    nursery,670    "ensure the first documentation paragraph is short"671}672673declare_clippy_lint! {674    /// ### What it does675    /// Checks for the doc comments of publicly visible676    /// safe functions and traits and warns if there is a `# Safety` section.677    ///678    /// ### Why restrict this?679    /// Safe functions and traits are safe to implement and therefore do not680    /// need to describe safety preconditions that users are required to uphold.681    ///682    /// ### Examples683    /// ```no_run684    ///# type Universe = ();685    /// /// # Safety686    /// ///687    /// /// This function should not be called before the horsemen are ready.688    /// pub fn start_apocalypse_but_safely(u: &mut Universe) {689    ///     unimplemented!();690    /// }691    /// ```692    ///693    /// The function is safe, so there shouldn't be any preconditions694    /// that have to be explained for safety reasons.695    ///696    /// ```no_run697    ///# type Universe = ();698    /// /// This function should really be documented699    /// pub fn start_apocalypse(u: &mut Universe) {700    ///     unimplemented!();701    /// }702    /// ```703    #[clippy::version = "1.67.0"]704    pub UNNECESSARY_SAFETY_DOC,705    restriction,706    "`pub fn` or `pub trait` with `# Safety` docs"707}708709impl_lint_pass!(Documentation => [710    DOC_BROKEN_LINK,711    DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,712    DOC_INCLUDE_WITHOUT_CFG,713    DOC_LAZY_CONTINUATION,714    DOC_LINK_CODE,715    DOC_LINK_WITH_QUOTES,716    DOC_MARKDOWN,717    DOC_NESTED_REFDEFS,718    DOC_OVERINDENTED_LIST_ITEMS,719    DOC_PARAGRAPHS_MISSING_PUNCTUATION,720    DOC_SUSPICIOUS_FOOTNOTES,721    EMPTY_DOCS,722    MISSING_ERRORS_DOC,723    MISSING_PANICS_DOC,724    MISSING_SAFETY_DOC,725    NEEDLESS_DOCTEST_MAIN,726    SUSPICIOUS_DOC_COMMENTS,727    TEST_ATTR_IN_DOCTEST,728    TOO_LONG_FIRST_DOC_PARAGRAPH,729    UNNECESSARY_SAFETY_DOC,730]);731732pub struct Documentation {733    valid_idents: &'static FxHashSet<String>,734    check_private_items: bool,735}736737impl Documentation {738    pub fn new(conf: &'static Conf) -> Self {739        Self {740            valid_idents: &conf.doc_valid_idents,741            check_private_items: conf.check_private_items,742        }743    }744}745746impl EarlyLintPass for Documentation {747    fn check_attributes(&mut self, cx: &EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) {748        include_in_doc_without_cfg::check(cx, attrs);749    }750}751752impl<'tcx> LateLintPass<'tcx> for Documentation {753    fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {754        let Some(headers) = check_attrs(cx, self.valid_idents, attrs) else {755            return;756        };757758        match cx.tcx.hir_node(cx.last_node_with_lint_attrs) {759            Node::Field(FieldDef { span, safety, .. }) => match (headers.safety, safety) {760                (false, Safety::Unsafe) => span_lint(761                    cx,762                    MISSING_SAFETY_DOC,763                    *span,764                    "docs for unsafe field missing `# Safety` section",765                ),766                (true, Safety::Safe) if cx.tcx.features().unsafe_fields() => span_lint_and_help(767                    cx,768                    UNNECESSARY_SAFETY_DOC,769                    *span,770                    "field with `# Safety` documentation is not marked unsafe",771                    None,772                    "if the field has safety invariants, mark it `unsafe`",773                ),774                _ => (),775            },776            Node::Item(item) => {777                too_long_first_doc_paragraph::check(778                    cx,779                    item,780                    attrs,781                    headers.first_paragraph_text_len,782                    headers.first_paragraph_md_len,783                    self.check_private_items,784                );785                match item.kind {786                    ItemKind::Fn { sig, body, .. }787                        if !(is_entrypoint_fn(cx, item.owner_id.to_def_id())788                            || item.span.in_external_macro(cx.tcx.sess.source_map())) =>789                    {790                        missing_headers::check(cx, item.owner_id, sig, headers, Some(body), self.check_private_items);791                    },792                    ItemKind::Trait { safety, .. } => match (headers.safety, safety) {793                        (false, Safety::Unsafe) => span_lint(794                            cx,795                            MISSING_SAFETY_DOC,796                            cx.tcx.def_span(item.owner_id),797                            "docs for unsafe trait missing `# Safety` section",798                        ),799                        (true, Safety::Safe) => span_lint(800                            cx,801                            UNNECESSARY_SAFETY_DOC,802                            cx.tcx.def_span(item.owner_id),803                            "docs for safe trait have unnecessary `# Safety` section",804                        ),805                        _ => (),806                    },807                    _ => (),808                }809            },810            Node::TraitItem(trait_item) => {811                if let TraitItemKind::Fn(sig, ..) = trait_item.kind812                    && !trait_item.span.in_external_macro(cx.tcx.sess.source_map())813                {814                    missing_headers::check(cx, trait_item.owner_id, sig, headers, None, self.check_private_items);815                }816            },817            Node::ImplItem(impl_item) => {818                if let ImplItemKind::Fn(sig, body_id) = impl_item.kind819                    && !impl_item.span.in_external_macro(cx.tcx.sess.source_map())820                    && !is_trait_impl_item(cx, impl_item.hir_id())821                {822                    missing_headers::check(823                        cx,824                        impl_item.owner_id,825                        sig,826                        headers,827                        Some(body_id),828                        self.check_private_items,829                    );830                }831            },832            _ => {},833        }834    }835}836837#[derive(Copy, Clone)]838struct Fragments<'a> {839    doc: &'a str,840    fragments: &'a [DocFragment],841}842843impl Fragments<'_> {844    /// get the span for the markdown range. Note that this function is not cheap, use it with845    /// caution.846    #[must_use]847    fn span(self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> {848        source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments).map(|(sp, _)| sp)849    }850}851852#[derive(Copy, Clone, Default)]853struct DocHeaders {854    safety: bool,855    errors: bool,856    panics: bool,857    first_paragraph_md_len: usize,858    first_paragraph_text_len: usize,859}860861/// Does some pre-processing on raw, desugared `#[doc]` attributes such as parsing them and862/// then delegates to `check_doc`.863/// Some lints are already checked here if they can work with attributes directly and don't need864/// to work with markdown.865/// Others are checked elsewhere, e.g. in `check_doc` if they need access to markdown, or866/// back in the various late lint pass methods if they need the final doc headers, like "Safety" or867/// "Panics" sections.868fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> {869    // We don't want the parser to choke on intra doc links. Since we don't870    // actually care about rendering them, just pretend that all broken links871    // point to a fake address.872    #[expect(clippy::unnecessary_wraps)] // we're following a type signature873    fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {874        Some(("fake".into(), "fake".into()))875    }876877    if suspicious_doc_comments::check(cx, attrs) || is_doc_hidden(attrs) {878        return None;879    }880881    let (fragments, _) = attrs_to_doc_fragments(882        attrs.iter().filter_map(|attr| {883            let (_, kind) = attr.doc_str_and_fragment_kind()?;884            let (is_raw, doc_str_span) = match kind {885                DocFragmentKind::Sugared(_) => (false, attr.span()),886                DocFragmentKind::Raw(span) => (true, span),887            };888            let from_include_str_macro = is_raw889                && !attr.span().in_external_macro(cx.sess().source_map())890                && originated_from_include_str_macro(cx, doc_str_span);891            (from_include_str_macro || !doc_str_span.in_external_macro(cx.sess().source_map())).then_some((attr, None))892        }),893        true,894    );895896    let mut doc = String::with_capacity(fragments.iter().map(|frag| frag.doc.as_str().len() + 1).sum());897898    for fragment in &fragments {899        add_doc_fragment(&mut doc, fragment);900    }901    doc.pop();902903    if doc.trim().is_empty() {904        if let Some(span) = span_of_fragments(&fragments) {905            span_lint_and_help(906                cx,907                EMPTY_DOCS,908                span,909                "empty doc comment",910                None,911                "consider removing or filling it",912            );913        }914        return Some(DocHeaders::default());915    }916917    // Only emits the allow-by-default `DOC_LINK_CODE`; skip its extra markdown reparse when it's off.918    if !clippy_utils::is_lint_allowed(cx, DOC_LINK_CODE, cx.last_node_with_lint_attrs) {919        check_for_code_clusters(920            cx,921            pulldown_cmark::Parser::new_with_broken_link_callback(922                &doc,923                main_body_opts() - Options::ENABLE_SMART_PUNCTUATION,924                Some(&mut fake_broken_link_callback),925            )926            .into_offset_iter(),927            &doc,928            Fragments {929                doc: &doc,930                fragments: &fragments,931            },932        );933    }934935    // Same for the allow-by-default `DOC_PARAGRAPHS_MISSING_PUNCTUATION`, which also reparses.936    if !clippy_utils::is_lint_allowed(cx, DOC_PARAGRAPHS_MISSING_PUNCTUATION, cx.last_node_with_lint_attrs) {937        doc_paragraphs_missing_punctuation::check(938            cx,939            &doc,940            Fragments {941                doc: &doc,942                fragments: &fragments,943            },944        );945    }946947    // NOTE: check_doc uses it own cb function,948    // to avoid causing duplicated diagnostics for the broken link checker.949    let mut full_fake_broken_link_callback = |bl: BrokenLink<'_>| -> Option<(CowStr<'_>, CowStr<'_>)> {950        broken_link::check(cx, &bl, &doc, &fragments);951        Some(("fake".into(), "fake".into()))952    };953954    // disable smart punctuation to pick up ['link'] more easily955    let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION;956    let parser =957        pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut full_fake_broken_link_callback));958959    Some(check_doc(960        cx,961        valid_idents,962        parser.into_offset_iter(),963        &doc,964        Fragments {965            doc: &doc,966            fragments: &fragments,967        },968        attrs,969    ))970}971972fn originated_from_include_str_macro(cx: &LateContext<'_>, span: Span) -> bool {973    root_macro_call(span).is_some_and(|macro_call| cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))974}975976enum Container {977    Blockquote,978    List(usize),979}980981/// Scan the documentation for code links that are back-to-back with code spans.982///983/// This is done separately from the rest of the docs, because that makes it easier to produce984/// the correct messages.985fn check_for_code_clusters<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(986    cx: &LateContext<'_>,987    events: Events,988    doc: &str,989    fragments: Fragments<'_>,990) {991    let mut events = events.peekable();992    let mut code_starts_at = None;993    let mut code_ends_at = None;994    let mut code_includes_link = false;995    while let Some((event, range)) = events.next() {996        match event {997            Start(Link { .. }) if matches!(events.peek(), Some((Code(_), _range))) => {998                if code_starts_at.is_some() {999                    code_ends_at = Some(range.end);1000                } else {1001                    code_starts_at = Some(range.start);1002                }1003                code_includes_link = true;1004                // skip the nested "code", because we're already handling it here1005                let _ = events.next();1006            },1007            Code(_) => {1008                if code_starts_at.is_some() {1009                    code_ends_at = Some(range.end);1010                } else {1011                    code_starts_at = Some(range.start);1012                }1013            },1014            End(TagEnd::Link) => {},1015            _ => {1016                if let Some(start) = code_starts_at1017                    && let Some(end) = code_ends_at1018                    && code_includes_link1019                    && let Some(span) = fragments.span(cx, start..end)1020                {1021                    span_lint_and_then(cx, DOC_LINK_CODE, span, "code link adjacent to code text", |diag| {1022                        let sugg = format!("<code>{}</code>", doc[start..end].replace('`', ""));1023                        diag.span_suggestion_verbose(1024                            span,1025                            "wrap the entire group in `<code>` tags",1026                            sugg,1027                            Applicability::MaybeIncorrect,1028                        );1029                        diag.help("separate code snippets will be shown with a gap");1030                    });1031                }1032                code_includes_link = false;1033                code_starts_at = None;1034                code_ends_at = None;1035            },1036        }1037    }1038}10391040#[derive(Clone, Copy)]1041#[expect(clippy::struct_excessive_bools)]1042struct CodeTags {1043    no_run: bool,1044    ignore: bool,1045    compile_fail: bool,1046    test_harness: bool,10471048    rust: bool,1049}10501051impl Default for CodeTags {1052    fn default() -> Self {1053        Self {1054            no_run: false,1055            ignore: false,1056            compile_fail: false,1057            test_harness: false,10581059            rust: true,1060        }1061    }1062}10631064impl CodeTags {1065    /// Based on <https://github.com/rust-lang/rust/blob/1.90.0/src/librustdoc/html/markdown.rs#L1169>1066    fn parse(lang: &str) -> Self {1067        let mut tags = Self::default();10681069        let mut seen_rust_tags = false;1070        let mut seen_other_tags = false;1071        for item in lang.split([',', ' ', '\t']) {1072            match item.trim() {1073                "" => {},1074                "rust" => {1075                    tags.rust = true;1076                    seen_rust_tags = true;1077                },1078                "ignore" => {1079                    tags.ignore = true;1080                    seen_rust_tags = !seen_other_tags;1081                },1082                "no_run" => {1083                    tags.no_run = true;1084                    seen_rust_tags = !seen_other_tags;1085                },1086                "should_panic" => seen_rust_tags = !seen_other_tags,1087                "compile_fail" => {1088                    tags.compile_fail = true;1089                    seen_rust_tags = !seen_other_tags || seen_rust_tags;1090                },1091                "test_harness" => {1092                    tags.test_harness = true;1093                    seen_rust_tags = !seen_other_tags || seen_rust_tags;1094                },1095                "standalone_crate" => {1096                    seen_rust_tags = !seen_other_tags || seen_rust_tags;1097                },1098                _ if item.starts_with("ignore-") => seen_rust_tags = true,1099                _ if item.starts_with("edition") => {},1100                _ => seen_other_tags = true,1101            }1102        }11031104        tags.rust &= seen_rust_tags || !seen_other_tags;11051106        tags1107    }1108}11091110/// Checks parsed documentation.1111/// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`,1112/// so lints here will generally access that information.1113/// Returns documentation headers -- whether a "Safety", "Errors", "Panic" section was found1114#[expect(clippy::too_many_lines, reason = "big match statement")]1115fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(1116    cx: &LateContext<'_>,1117    valid_idents: &FxHashSet<String>,1118    events: Events,1119    doc: &str,1120    fragments: Fragments<'_>,1121    attrs: &[Attribute],1122) -> DocHeaders {1123    // true if a safety header was found1124    let mut headers = DocHeaders::default();1125    let mut code = None;1126    let mut in_link = None;1127    let mut in_heading = false;1128    let mut in_footnote_definition = false;1129    let mut ticks_unbalanced = false;1130    let mut text_to_check: Vec<(CowStr<'_>, Range<usize>, isize)> = Vec::new();1131    let mut paragraph_range = 0..0;1132    let mut code_level = 0;1133    let mut blockquote_level = 0;1134    let mut collected_breaks: Vec<Span> = Vec::new();1135    let mut is_first_paragraph = true;11361137    let mut containers = Vec::new();11381139    // Skip collecting text and the per-word scan when `DOC_MARKDOWN` (pedantic) is allowed.1140    let check_doc_markdown = !clippy_utils::is_lint_allowed(cx, DOC_MARKDOWN, cx.last_node_with_lint_attrs);11411142    let mut events = events.peekable();11431144    while let Some((event, range)) = events.next() {1145        match event {1146            Html(tag) | InlineHtml(tag) => {1147                if tag.starts_with("<code") {1148                    code_level += 1;1149                } else if tag.starts_with("</code") {1150                    code_level -= 1;1151                } else if tag.starts_with("<blockquote") || tag.starts_with("<q") {1152                    blockquote_level += 1;1153                } else if tag.starts_with("</blockquote") || tag.starts_with("</q") {1154                    blockquote_level -= 1;1155                }1156            },1157            Start(BlockQuote(_)) => {1158                blockquote_level += 1;1159                containers.push(Container::Blockquote);1160                if let Some((next_event, next_range)) = events.peek() {1161                    let next_start = match next_event {1162                        End(TagEnd::BlockQuote) => next_range.end,1163                        _ => next_range.start,1164                    };1165                    if let Some(refdefrange) = looks_like_refdef(doc, range.start..next_start) &&1166                        let Some(refdefspan) = fragments.span(cx, refdefrange.clone())1167                    {1168                        span_lint_and_then(1169                            cx,1170                            DOC_NESTED_REFDEFS,1171                            refdefspan,1172                            "link reference defined in quote",1173                            |diag| {1174                                diag.span_suggestion_short(1175                                    refdefspan.shrink_to_hi(),1176                                    "for an intra-doc link, add `[]` between the label and the colon",1177                                    "[]",1178                                    Applicability::MaybeIncorrect,1179                                );1180                                diag.help("link definitions are not shown in rendered documentation");1181                            }1182                        );1183                    }1184                }1185            },1186            End(TagEnd::BlockQuote) => {1187                blockquote_level -= 1;1188                containers.pop();1189            },1190            Start(CodeBlock(ref kind)) => {1191                code = Some(match kind {1192                    CodeBlockKind::Indented => CodeTags::default(),1193                    CodeBlockKind::Fenced(lang) => CodeTags::parse(lang),1194                });1195            },1196            End(TagEnd::CodeBlock) => code = None,1197            Start(Link { dest_url, .. }) => in_link = Some(dest_url),1198            End(TagEnd::Link) => in_link = None,1199            Start(Heading { .. } | Paragraph | Item) => {1200                if let Start(Heading { .. }) = event {1201                    in_heading = true;1202                }1203                if let Start(Item) = event {1204                    let indent = if let Some((next_event, next_range)) = events.peek() {1205                        let next_start = match next_event {1206                            End(TagEnd::Item) => next_range.end,1207                            _ => next_range.start,1208                        };1209                        if let Some(refdefrange) = looks_like_refdef(doc, range.start..next_start) &&1210                            let Some(refdefspan) = fragments.span(cx, refdefrange.clone())1211                        {1212                            span_lint_and_then(1213                                cx,1214                                DOC_NESTED_REFDEFS,1215                                refdefspan,1216                                "link reference defined in list item",1217                                |diag| {1218                                    diag.span_suggestion_short(1219                                        refdefspan.shrink_to_hi(),1220                                        "for an intra-doc link, add `[]` between the label and the colon",1221                                        "[]",1222                                        Applicability::MaybeIncorrect,1223                                    );1224                                    diag.help("link definitions are not shown in rendered documentation");1225                                }1226                            );1227                            refdefrange.start - range.start1228                        } else {1229                            let mut start = next_range.start;1230                            if start > 0 && doc.as_bytes().get(start - 1) == Some(&b'\\') {1231                                // backslashes aren't in the event stream...1232                                start -= 1;1233                            }12341235                            start.saturating_sub(range.start)1236                        }1237                    } else {1238                        01239                    };1240                    containers.push(Container::List(indent));1241                }1242                ticks_unbalanced = false;1243                paragraph_range = range;1244                if is_first_paragraph {1245                    headers.first_paragraph_md_len = doc[paragraph_range.clone()].chars().count();1246                }1247            },1248            End(TagEnd::Heading(_) | TagEnd::Paragraph | TagEnd::Item) => {1249                if is_first_paragraph {1250                    is_first_paragraph = false;1251                }1252                if let End(TagEnd::Heading(_)) = event {1253                    in_heading = false;1254                }1255                if let End(TagEnd::Item) = event {1256                    containers.pop();1257                }1258                if check_doc_markdown {1259                    if ticks_unbalanced && let Some(span) = fragments.span(cx, paragraph_range.clone())1260                    .or_else(|| span_of_fragments(fragments.fragments)) {1261                        span_lint_and_help(1262                            cx,1263                            DOC_MARKDOWN,1264                            span,1265                            "backticks are unbalanced",1266                            None,1267                            "a backtick may be missing a pair",1268                        );1269                        text_to_check.clear();1270                    } else {1271                        for (text, range, assoc_code_level) in text_to_check.drain(..) {1272                            markdown::check(1273                                cx, valid_idents, &text, &fragments, range, assoc_code_level, blockquote_level1274                            );1275                        }1276                    }1277                }1278            },1279            Start(FootnoteDefinition(..)) => in_footnote_definition = true,1280            End(TagEnd::FootnoteDefinition) => in_footnote_definition = false,1281            Start(_) | End(_)  // We don't care about other tags1282            | TaskListMarker(_) | Rule => (),1283            SoftBreak | HardBreak => {1284                if is_first_paragraph {1285                    headers.first_paragraph_text_len += 1;1286                }1287                if !containers.is_empty()1288                    && !in_footnote_definition1289                    // Tabs aren't handled correctly vvvv1290                    && !doc[range.clone()].contains('\t')1291                    && let Some((next_event, next_range)) = events.peek()1292                    && !matches!(next_event, End(_))1293                {1294                    lazy_continuation::check(1295                        cx,1296                        doc,1297                        range.end..next_range.start,1298                        &fragments,1299                        &containers[..],1300                    );1301                }130213031304                if event == HardBreak1305                    && !doc[range.clone()].trim().starts_with('\\')1306                    && let Some(span) = fragments.span(cx, range.clone())1307                    && !span.from_expansion()1308                    {1309                    collected_breaks.push(span);1310                }1311            },1312            Code(code) | InlineMath(code) | DisplayMath(code) => {1313                if is_first_paragraph {1314                    headers.first_paragraph_text_len += code.chars().count();1315                }1316            }1317            Text(text) => {1318                if is_first_paragraph {1319                    headers.first_paragraph_text_len += text.chars().count();1320                }1321                paragraph_range.end = range.end;1322                let range_ = range.clone();1323                ticks_unbalanced |= text.contains('`')1324                    && code.is_none()1325                    && doc[range.clone()].bytes().enumerate().any(|(i, c)| {1326                        // scan the markdown source code bytes for backquotes that aren't preceded by backslashes1327                        // - use bytes, instead of chars, to avoid utf8 decoding overhead (special chars are ascii)1328                        // - relevant backquotes are within doc[range], but backslashes are not, because they're not1329                        //   actually part of the rendered text (pulldown-cmark doesn't emit any events for escapes)1330                        // - if `range_.start + i == 0`, then `range_.start + i - 1 == -1`, and since we're working in1331                        //   usize, that would underflow and maybe panic1332                        c == b'`' && (range_.start + i == 0 || doc.as_bytes().get(range_.start + i - 1) != Some(&b'\\'))1333                    });1334                if Some(&text) == in_link.as_ref() || ticks_unbalanced {1335                    // Probably a link of the form `<http://example.com>`1336                    // Which are represented as a link to "http://example.com" with1337                    // text "http://example.com" by pulldown-cmark1338                    continue;1339                }1340                let trimmed_text = text.trim();1341                headers.safety |= in_heading && trimmed_text == "Safety";1342                headers.safety |= in_heading && trimmed_text == "SAFETY";1343                headers.safety |= in_heading && trimmed_text == "Implementation safety";1344                headers.safety |= in_heading && trimmed_text == "Implementation Safety";1345                headers.errors |= in_heading && trimmed_text == "Errors";1346                headers.panics |= in_heading && trimmed_text == "Panics";13471348                if let Some(tags) = code {1349                    if tags.rust && !tags.compile_fail && !tags.ignore {1350                        needless_doctest_main::check(cx, &text, range.start, fragments);13511352                        if !tags.no_run && !tags.test_harness {1353                            test_attr_in_doctest::check(cx, &text, range.start, fragments);1354                        }1355                    }1356                } else {1357                    if in_link.is_some() {1358                        link_with_quotes::check(cx, trimmed_text, range.clone(), fragments);1359                    }1360                    if let Some(link) = in_link.as_ref()1361                        && let Ok(url) = Url::parse(link)1362                        && (url.scheme() == "https" || url.scheme() == "http")1363                    {1364                        // Don't check the text associated with external URLs1365                        continue;1366                    }1367                    if check_doc_markdown {1368                        text_to_check.push((text, range.clone(), code_level));1369                    }1370                    doc_suspicious_footnotes::check(cx, doc, range, &fragments, attrs);1371                }1372            }1373            FootnoteReference(_) => {}1374        }1375    }13761377    doc_comment_double_space_linebreaks::check(cx, &collected_breaks);13781379    headers1380}13811382fn looks_like_refdef(doc: &str, range: Range<usize>) -> Option<Range<usize>> {1383    if range.end < range.start {1384        return None;1385    }13861387    let offset = range.start;1388    let mut iterator = doc.as_bytes()[range].iter().copied().enumerate();1389    let mut start = None;1390    while let Some((i, byte)) = iterator.next() {1391        match byte {1392            b'\\' => {1393                iterator.next();1394            },1395            b'[' => {1396                start = Some(i + offset);1397            },1398            b']' if let Some(start) = start1399                && doc.as_bytes().get(i + offset + 1) == Some(&b':') =>1400            {1401                return Some(start..i + offset + 1);1402            },1403            _ => {},1404        }1405    }1406    None1407}

Code quality findings 10

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_md_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
let mut iterator = doc.as_bytes()[range].iter().copied().enumerate();
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.

Generated by searchcode.com · powered by code_get_file + code_get_findings