Skip to content

Commit 92a6015

Browse files
committed
use syn in needless_doctest_main lint
1 parent d13b678 commit 92a6015

File tree

2 files changed

+32
-8
lines changed

2 files changed

+32
-8
lines changed

clippy_lints/Cargo.toml

+3-4
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@ matches = "0.1.7"
2525
pulldown-cmark = "0.6.0"
2626
quine-mc_cluskey = "0.2.2"
2727
regex-syntax = "0.6"
28+
semver = "0.9.0"
2829
serde = { version = "1.0", features = ["derive"] }
2930
smallvec = { version = "1", features = ["union"] }
31+
syn = { version = "1.0", features = ["full"] }
3032
toml = "0.5.3"
3133
unicode-normalization = "0.1"
32-
semver = "0.9.0"
33-
# NOTE: cargo requires serde feat in its url dep
34-
# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
35-
url = { version = "2.1.0", features = ["serde"] }
34+
url = "2.1.0"
3635

3736
[features]
3837
deny-warnings = []

clippy_lints/src/doc.rs

+29-4
Original file line numberDiff line numberDiff line change
@@ -395,11 +395,36 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
395395
headers
396396
}
397397

398-
static LEAVE_MAIN_PATTERNS: &[&str] = &["static", "fn main() {}", "extern crate"];
398+
fn needs_main(item: &syn::Item) -> bool {
399+
match item {
400+
syn::Item::Const(..) | syn::Item::Static(..) | syn::Item::ExternCrate(..) | syn::Item::ForeignMod(..) => true,
401+
_ => false,
402+
}
403+
}
399404

400-
fn check_code(cx: &LateContext<'_, '_>, text: &str, span: Span) {
401-
if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) {
402-
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
405+
// check a syn Item for non-empty `fn main() { .. }`
406+
fn is_default_main_fn(item: &syn::Item) -> bool {
407+
match item {
408+
syn::Item::Fn(syn::ItemFn { ref sig, ref block, .. }) => {
409+
!block.stmts.is_empty()
410+
&& sig.ident == "main"
411+
&& match sig.output {
412+
syn::ReturnType::Default => true,
413+
syn::ReturnType::Type(_, ref ty) => match **ty {
414+
syn::Type::Tuple(syn::TypeTuple { ref elems, .. }) => elems.is_empty(),
415+
_ => false,
416+
},
417+
}
418+
},
419+
_ => false,
420+
}
421+
}
422+
423+
fn check_code(cx: &LateContext<'_, '_>, code: &str, span: Span) {
424+
if let Ok(file) = syn::parse_file(code) {
425+
if file.items.iter().any(is_default_main_fn) && !file.items.iter().any(needs_main) {
426+
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
427+
}
403428
}
404429
}
405430

0 commit comments

Comments
 (0)