@@ -68,6 +68,34 @@ declare_clippy_lint! {
6868 "`pub unsafe fn` without `# Safety` docs"
6969}
7070
71+ declare_clippy_lint ! {
72+ /// **What it does:** Checks for `fn main() { .. }` in doctests
73+ ///
74+ /// **Why is this bad?** The test can be shorter (and likely more readable)
75+ /// if the `fn main()` is left implicit.
76+ ///
77+ /// **Known problems:** None.
78+ ///
79+ /// **Examples:**
80+ /// ``````rust
81+ /// /// An example of a doctest with a `main()` function
82+ /// ///
83+ /// /// # Examples
84+ /// ///
85+ /// /// ```
86+ /// /// fn main() {
87+ /// /// // this needs not be in an `fn`
88+ /// /// }
89+ /// /// ```
90+ /// fn needless_main() {
91+ /// unimplemented!();
92+ /// }
93+ /// ``````
94+ pub NEEDLESS_DOCTEST_MAIN ,
95+ style,
96+ "presence of `fn main() {` in code examples"
97+ }
98+
7199#[ allow( clippy:: module_name_repetitions) ]
72100#[ derive( Clone ) ]
73101pub struct DocMarkdown {
@@ -80,7 +108,7 @@ impl DocMarkdown {
80108 }
81109}
82110
83- impl_lint_pass ! ( DocMarkdown => [ DOC_MARKDOWN , MISSING_SAFETY_DOC ] ) ;
111+ impl_lint_pass ! ( DocMarkdown => [ DOC_MARKDOWN , MISSING_SAFETY_DOC , NEEDLESS_DOCTEST_MAIN ] ) ;
84112
85113impl EarlyLintPass for DocMarkdown {
86114 fn check_crate ( & mut self , cx : & EarlyContext < ' _ > , krate : & ast:: Crate ) {
@@ -245,17 +273,16 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
245273 continue ;
246274 }
247275 safety_header |= in_heading && text. trim ( ) == "Safety" ;
248- if !in_code {
249- let index = match spans . binary_search_by ( |c| c . 0 . cmp ( & range . start ) ) {
250- Ok ( o ) => o ,
251- Err ( e ) => e - 1 ,
252- } ;
253-
254- let ( begin , span) = spans [ index ] ;
255-
276+ let index = match spans . binary_search_by ( |c| c . 0 . cmp ( & range . start ) ) {
277+ Ok ( o ) => o ,
278+ Err ( e ) => e - 1 ,
279+ } ;
280+ let ( begin , span ) = spans [ index ] ;
281+ if in_code {
282+ check_code ( cx , & text , span) ;
283+ } else {
256284 // Adjust for the beginning of the current `Event`
257285 let span = span. with_lo ( span. lo ( ) + BytePos :: from_usize ( range. start - begin) ) ;
258-
259286 check_text ( cx, valid_idents, & text, span) ;
260287 }
261288 } ,
@@ -264,6 +291,12 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
264291 safety_header
265292}
266293
294+ fn check_code ( cx : & EarlyContext < ' _ > , text : & str , span : Span ) {
295+ if text. contains ( "fn main() {" ) {
296+ span_lint ( cx, NEEDLESS_DOCTEST_MAIN , span, "needless `fn main` in doctest" ) ;
297+ }
298+ }
299+
267300fn check_text ( cx : & EarlyContext < ' _ > , valid_idents : & FxHashSet < String > , text : & str , span : Span ) {
268301 for word in text. split ( |c : char | c. is_whitespace ( ) || c == '\'' ) {
269302 // Trim punctuation as in `some comment (see foo::bar).`
0 commit comments