Merge Async and Gen into CoroutineKind

This commit is contained in:
Eric Holk 2023-11-30 14:54:39 -08:00
parent c586717cfb
commit b4e3b859f1
2 changed files with 202 additions and 3 deletions

View File

@ -655,6 +655,205 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
headers
}
<<<<<<< HEAD:src/tools/clippy/clippy_lints/src/doc/mod.rs
=======
fn check_link_quotes(cx: &LateContext<'_>, trimmed_text: &str, range: Range<usize>, fragments: Fragments<'_>) {
if trimmed_text.starts_with('\'')
&& trimmed_text.ends_with('\'')
&& let Some(span) = fragments.span(cx, range)
{
span_lint(
cx,
DOC_LINK_WITH_QUOTES,
span,
"possible intra-doc link using quotes instead of backticks",
);
}
}
fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range<usize>, fragments: Fragments<'_>) {
fn has_needless_main(code: String, edition: Edition) -> bool {
rustc_driver::catch_fatal_errors(|| {
rustc_span::create_session_globals_then(edition, || {
let filename = FileName::anon_source_code(&code);
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle);
let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
#[expect(clippy::arc_with_non_send_sync)] // `Lrc` is expected by with_span_handler
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let sess = ParseSess::with_span_handler(handler, sm);
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
Ok(p) => p,
Err(errs) => {
drop(errs);
return false;
},
};
let mut relevant_main_found = false;
loop {
match parser.parse_item(ForceCollect::No) {
Ok(Some(item)) => match &item.kind {
ItemKind::Fn(box Fn {
sig, body: Some(block), ..
}) if item.ident.name == sym::main => {
let is_async = sig.header.coro_kind.is_async();
let returns_nothing = match &sig.decl.output {
FnRetTy::Default(..) => true,
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
FnRetTy::Ty(_) => false,
};
if returns_nothing && !is_async && !block.stmts.is_empty() {
// This main function should be linted, but only if there are no other functions
relevant_main_found = true;
} else {
// This main function should not be linted, we're done
return false;
}
},
// Tests with one of these items are ignored
ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::ExternCrate(..)
| ItemKind::ForeignMod(..)
// Another function was found; this case is ignored
| ItemKind::Fn(..) => return false,
_ => {},
},
Ok(None) => break,
Err(e) => {
e.cancel();
return false;
},
}
}
relevant_main_found
})
})
.ok()
.unwrap_or_default()
}
let trailing_whitespace = text.len() - text.trim_end().len();
// Because of the global session, we need to create a new session in a different thread with
// the edition we need.
let text = text.to_owned();
if thread::spawn(move || has_needless_main(text, edition))
.join()
.expect("thread::spawn failed")
&& let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace)
{
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
}
}
fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
// Trim punctuation as in `some comment (see foo::bar).`
// ^^
// Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix.
let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':');
// Remove leading or trailing single `:` which may be part of a sentence.
if word.starts_with(':') && !word.starts_with("::") {
word = word.trim_start_matches(':');
}
if word.ends_with(':') && !word.ends_with("::") {
word = word.trim_end_matches(':');
}
if valid_idents.contains(word) || word.chars().all(|c| c == ':') {
continue;
}
// Adjust for the current word
let offset = word.as_ptr() as usize - text.as_ptr() as usize;
let span = Span::new(
span.lo() + BytePos::from_usize(offset),
span.lo() + BytePos::from_usize(offset + word.len()),
span.ctxt(),
span.parent(),
);
check_word(cx, word, span);
}
}
fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
/// Checks if a string is upper-camel-case, i.e., starts with an uppercase and
/// contains at least two uppercase letters (`Clippy` is ok) and one lower-case
/// letter (`NASA` is ok).
/// Plurals are also excluded (`IDs` is ok).
fn is_camel_case(s: &str) -> bool {
if s.starts_with(|c: char| c.is_ascii_digit() | c.is_ascii_lowercase()) {
return false;
}
let s = s.strip_suffix('s').unwrap_or(s);
s.chars().all(char::is_alphanumeric)
&& s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
&& s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
}
fn has_underscore(s: &str) -> bool {
s != "_" && !s.contains("\\_") && s.contains('_')
}
fn has_hyphen(s: &str) -> bool {
s != "-" && s.contains('-')
}
if let Ok(url) = Url::parse(word) {
// try to get around the fact that `foo::bar` parses as a valid URL
if !url.cannot_be_a_base() {
span_lint(
cx,
DOC_MARKDOWN,
span,
"you should put bare URLs between `<`/`>` or make a proper Markdown link",
);
return;
}
}
// We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
if has_underscore(word) && has_hyphen(word) {
return;
}
if has_underscore(word) || word.contains("::") || is_camel_case(word) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_then(
cx,
DOC_MARKDOWN,
span,
"item in documentation is missing backticks",
|diag| {
let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
diag.span_suggestion_with_style(
span,
"try",
format!("`{snippet}`"),
applicability,
// always show the suggestion in a separate line, since the
// inline presentation adds another pair of backticks
SuggestionStyle::ShowAlways,
);
},
);
}
}
>>>>>>> d116f1718f1 (Merge Async and Gen into CoroutineKind):src/tools/clippy/clippy_lints/src/doc.rs
struct FindPanicUnwrap<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
panic_span: Option<Span>,

View File

@ -188,7 +188,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
Closure(box ast::Closure {
binder: lb,
capture_clause: lc,
asyncness: la,
coro_kind: la,
movability: lm,
fn_decl: lf,
body: le,
@ -197,7 +197,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
Closure(box ast::Closure {
binder: rb,
capture_clause: rc,
asyncness: ra,
coro_kind: ra,
movability: rm,
fn_decl: rf,
body: re,
@ -565,7 +565,7 @@ pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
&& l.asyncness.is_async() == r.asyncness.is_async()
&& (l.coro_kind.is_async() == r.coro_kind.is_async() || l.coro_kind.is_gen() == r.coro_kind.is_gen())
&& matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
&& eq_ext(&l.ext, &r.ext)
}