@@ -68,6 +68,34 @@ declare_clippy_lint! {
68
68
"`pub unsafe fn` without `# Safety` docs"
69
69
}
70
70
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
+
71
99
#[ allow( clippy:: module_name_repetitions) ]
72
100
#[ derive( Clone ) ]
73
101
pub struct DocMarkdown {
@@ -80,7 +108,7 @@ impl DocMarkdown {
80
108
}
81
109
}
82
110
83
- impl_lint_pass ! ( DocMarkdown => [ DOC_MARKDOWN , MISSING_SAFETY_DOC ] ) ;
111
+ impl_lint_pass ! ( DocMarkdown => [ DOC_MARKDOWN , MISSING_SAFETY_DOC , NEEDLESS_DOCTEST_MAIN ] ) ;
84
112
85
113
impl EarlyLintPass for DocMarkdown {
86
114
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
245
273
continue ;
246
274
}
247
275
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 {
256
284
// Adjust for the beginning of the current `Event`
257
285
let span = span. with_lo ( span. lo ( ) + BytePos :: from_usize ( range. start - begin) ) ;
258
-
259
286
check_text ( cx, valid_idents, & text, span) ;
260
287
}
261
288
} ,
@@ -264,6 +291,12 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
264
291
safety_header
265
292
}
266
293
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
+
267
300
fn check_text ( cx : & EarlyContext < ' _ > , valid_idents : & FxHashSet < String > , text : & str , span : Span ) {
268
301
for word in text. split ( |c : char | c. is_whitespace ( ) || c == '\'' ) {
269
302
// Trim punctuation as in `some comment (see foo::bar).`
0 commit comments