|
1 | 1 | use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint};
|
2 | 2 | use if_chain::if_chain;
|
3 | 3 | use itertools::Itertools;
|
4 |
| -use rustc_ast::ast::{AttrKind, Attribute}; |
| 4 | +use rustc_ast::ast::{Async, AttrKind, Attribute, FnRetTy, ItemKind}; |
5 | 5 | use rustc_ast::token::CommentKind;
|
6 | 6 | use rustc_data_structures::fx::FxHashSet;
|
| 7 | +use rustc_data_structures::sync::Lrc; |
| 8 | +use rustc_errors::emitter::EmitterWriter; |
| 9 | +use rustc_errors::Handler; |
7 | 10 | use rustc_hir as hir;
|
8 | 11 | use rustc_lint::{LateContext, LateLintPass};
|
9 | 12 | use rustc_middle::lint::in_external_macro;
|
10 | 13 | use rustc_middle::ty;
|
| 14 | +use rustc_parse::maybe_new_parser_from_source_str; |
| 15 | +use rustc_session::parse::ParseSess; |
11 | 16 | use rustc_session::{declare_tool_lint, impl_lint_pass};
|
12 |
| -use rustc_span::source_map::{BytePos, MultiSpan, Span}; |
13 |
| -use rustc_span::Pos; |
| 17 | +use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; |
| 18 | +use rustc_span::{FileName, Pos}; |
| 19 | +use std::io; |
14 | 20 | use std::ops::Range;
|
15 | 21 | use url::Url;
|
16 | 22 |
|
@@ -431,10 +437,67 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
431 | 437 | headers
|
432 | 438 | }
|
433 | 439 |
|
434 |
| -static LEAVE_MAIN_PATTERNS: &[&str] = &["static", "fn main() {}", "extern crate", "async fn main() {"]; |
435 |
| - |
436 | 440 | fn check_code(cx: &LateContext<'_>, text: &str, span: Span) {
|
437 |
| - if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) { |
| 441 | + fn has_needless_main(code: &str) -> bool { |
| 442 | + let filename = FileName::anon_source_code(code); |
| 443 | + |
| 444 | + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); |
| 445 | + let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); |
| 446 | + let handler = Handler::with_emitter(false, None, box emitter); |
| 447 | + let sess = ParseSess::with_span_handler(handler, sm); |
| 448 | + |
| 449 | + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { |
| 450 | + Ok(p) => p, |
| 451 | + Err(errs) => { |
| 452 | + for mut err in errs { |
| 453 | + err.cancel(); |
| 454 | + } |
| 455 | + return false; |
| 456 | + }, |
| 457 | + }; |
| 458 | + |
| 459 | + let mut relevant_main_found = false; |
| 460 | + loop { |
| 461 | + match parser.parse_item() { |
| 462 | + Ok(Some(item)) => match &item.kind { |
| 463 | + // Tests with one of these items are ignored |
| 464 | + ItemKind::Static(..) |
| 465 | + | ItemKind::Const(..) |
| 466 | + | ItemKind::ExternCrate(..) |
| 467 | + | ItemKind::ForeignMod(..) => return false, |
| 468 | + // We found a main function ... |
| 469 | + ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym!(main) => { |
| 470 | + let is_async = matches!(sig.header.asyncness, Async::Yes{..}); |
| 471 | + let returns_nothing = match &sig.decl.output { |
| 472 | + FnRetTy::Default(..) => true, |
| 473 | + FnRetTy::Ty(ty) if ty.kind.is_unit() => true, |
| 474 | + _ => false, |
| 475 | + }; |
| 476 | + |
| 477 | + if returns_nothing && !is_async && !block.stmts.is_empty() { |
| 478 | + // This main function should be linted, but only if there are no other functions |
| 479 | + relevant_main_found = true; |
| 480 | + } else { |
| 481 | + // This main function should not be linted, we're done |
| 482 | + return false; |
| 483 | + } |
| 484 | + }, |
| 485 | + // Another function was found; this case is ignored too |
| 486 | + ItemKind::Fn(..) => return false, |
| 487 | + _ => {}, |
| 488 | + }, |
| 489 | + Ok(None) => break, |
| 490 | + Err(mut e) => { |
| 491 | + e.cancel(); |
| 492 | + return false; |
| 493 | + }, |
| 494 | + } |
| 495 | + } |
| 496 | + |
| 497 | + relevant_main_found |
| 498 | + } |
| 499 | + |
| 500 | + if has_needless_main(text) { |
438 | 501 | span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
|
439 | 502 | }
|
440 | 503 | }
|
|
0 commit comments