Add new lint for automatic_links improvements

This commit is contained in:
Guillaume Gomez 2020-10-12 18:28:57 +02:00
parent 8c20701219
commit 2980367030
5 changed files with 114 additions and 1 deletions

View file

@ -67,7 +67,7 @@ use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::{
BARE_TRAIT_OBJECTS, BROKEN_INTRA_DOC_LINKS, ELIDED_LIFETIMES_IN_PATHS,
AUTOMATIC_LINKS, BARE_TRAIT_OBJECTS, BROKEN_INTRA_DOC_LINKS, ELIDED_LIFETIMES_IN_PATHS,
EXPLICIT_OUTLIVES_REQUIREMENTS, INVALID_CODEBLOCK_ATTRIBUTES, INVALID_HTML_TAGS,
MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS,
};
@ -313,6 +313,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
add_lint_group!(
"rustdoc",
AUTOMATIC_LINKS,
BROKEN_INTRA_DOC_LINKS,
PRIVATE_INTRA_DOC_LINKS,
INVALID_CODEBLOCK_ATTRIBUTES,

View file

@ -1890,6 +1890,17 @@ declare_lint! {
"detects invalid HTML tags in doc comments"
}
declare_lint! {
/// The `automatic_links` lint detects when a URL/email address could be
/// written using only brackets. This is a `rustdoc` only lint, see the
/// documentation in the [rustdoc book].
///
/// [rustdoc book]: ../../../rustdoc/lints.html#automatic_links
pub AUTOMATIC_LINKS,
Allow,
"detects URLs/email adresses that could be written using only brackets"
}
declare_lint! {
/// The `where_clauses_object_safety` lint detects for [object safety] of
/// [where clauses].
@ -2795,6 +2806,7 @@ declare_lint_pass! {
MISSING_DOC_CODE_EXAMPLES,
INVALID_HTML_TAGS,
PRIVATE_DOC_TESTS,
AUTOMATIC_LINKS,
WHERE_CLAUSES_OBJECT_SAFETY,
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
MACRO_USE_EXTERN_CRATE,

View file

@ -330,11 +330,13 @@ pub fn run_core(
let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name;
let invalid_html_tags = rustc_lint::builtin::INVALID_HTML_TAGS.name;
let renamed_and_removed_lints = rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name;
let automatic_links = rustc_lint::builtin::AUTOMATIC_LINKS.name;
let unknown_lints = rustc_lint::builtin::UNKNOWN_LINTS.name;
// In addition to those specific lints, we also need to allow those given through
// command line, otherwise they'll get ignored and we don't want that.
let lints_to_show = vec![
automatic_links.to_owned(),
intra_link_resolution_failure_name.to_owned(),
missing_docs.to_owned(),
missing_doc_example.to_owned(),

View file

@ -0,0 +1,93 @@
use super::{span_of_attrs, Pass};
use crate::clean::*;
use crate::core::DocContext;
use crate::fold::DocFolder;
use crate::html::markdown::opts;
use pulldown_cmark::{Event, Parser, Tag};
use rustc_feature::UnstableFeatures;
use rustc_session::lint;
pub const CHECK_AUTOMATIC_LINKS: Pass = Pass {
name: "check-automatic-links",
run: check_automatic_links,
description: "detects URLS/email addresses that could be written using brackets",
};
struct AutomaticLinksLinter<'a, 'tcx> {
cx: &'a DocContext<'tcx>,
}
impl<'a, 'tcx> AutomaticLinksLinter<'a, 'tcx> {
fn new(cx: &'a DocContext<'tcx>) -> Self {
AutomaticLinksLinter { cx }
}
}
pub fn check_automatic_links(krate: Crate, cx: &DocContext<'_>) -> Crate {
if !UnstableFeatures::from_environment().is_nightly_build() {
krate
} else {
let mut coll = AutomaticLinksLinter::new(cx);
coll.fold_crate(krate)
}
}
impl<'a, 'tcx> DocFolder for AutomaticLinksLinter<'a, 'tcx> {
fn fold_item(&mut self, item: Item) -> Option<Item> {
let hir_id = match self.cx.as_local_hir_id(item.def_id) {
Some(hir_id) => hir_id,
None => {
// If non-local, no need to check anything.
return self.fold_item_recur(item);
}
};
let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
if !dox.is_empty() {
let cx = &self.cx;
let p = Parser::new_ext(&dox, opts()).into_offset_iter();
let mut title = String::new();
let mut in_link = false;
for (event, range) in p {
match event {
Event::Start(Tag::Link(..)) => in_link = true,
Event::End(Tag::Link(_, url, _)) => {
in_link = false;
if url.as_ref() != title {
continue;
}
let sp = match super::source_span_for_markdown_range(
cx,
&dox,
&range,
&item.attrs,
) {
Some(sp) => sp,
None => span_of_attrs(&item.attrs).unwrap_or(item.source.span()),
};
cx.tcx.struct_span_lint_hir(
lint::builtin::AUTOMATIC_LINKS,
hir_id,
sp,
|lint| {
lint.build("Unneeded long form for URL")
.help(&format!("Try with `<{}>` instead", url))
.emit()
},
);
title.clear();
}
Event::Text(s) if in_link => {
title.push_str(&s);
}
_ => {}
}
}
}
self.fold_item_recur(item)
}
}

View file

@ -11,6 +11,9 @@ use crate::core::DocContext;
mod stripper;
pub use stripper::*;
mod automatic_links;
pub use self::automatic_links::CHECK_AUTOMATIC_LINKS;
mod collapse_docs;
pub use self::collapse_docs::COLLAPSE_DOCS;
@ -90,6 +93,7 @@ pub const PASSES: &[Pass] = &[
COLLECT_TRAIT_IMPLS,
CALCULATE_DOC_COVERAGE,
CHECK_INVALID_HTML_TAGS,
CHECK_AUTOMATIC_LINKS,
];
/// The list of passes run by default.
@ -105,6 +109,7 @@ pub const DEFAULT_PASSES: &[ConditionalPass] = &[
ConditionalPass::always(CHECK_CODE_BLOCK_SYNTAX),
ConditionalPass::always(CHECK_INVALID_HTML_TAGS),
ConditionalPass::always(PROPAGATE_DOC_CFG),
ConditionalPass::always(CHECK_AUTOMATIC_LINKS),
];
/// The list of default passes run when `--doc-coverage` is passed to rustdoc.