Skip to content
6 changes: 5 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ jobs:
clippy:
name: Clippy check
runs-on: ubuntu-latest
env:
RUSTFLAGS: --deny warnings
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- run: cargo clippy --all-targets --all-features -- --deny warnings
- run: cargo clippy --all-targets --all-features
- run: cargo clippy --all-targets
- run: cargo clippy --all-targets --no-default-features

test:
name: Test code
Expand Down
19 changes: 19 additions & 0 deletions scraper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@ atomic = []
errors = []
serde = ["dep:serde"]

[lints.rust]
missing_copy_implementations = "warn"
missing_debug_implementations = "warn"
redundant_imports = "warn"
single_use_lifetimes = "warn"
trivial_casts = "warn"
trivial_numeric_casts = "warn"
unused_extern_crates = "warn"
unused_import_braces = "warn"
unused_qualifications = "warn"
variant_size_differences = "warn"
[lints.clippy]
pedantic = { level = "warn", priority = -1 }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the changes presented are worthwhile, but this is not something I want to maintain here.

@EdJoPaTo EdJoPaTo Nov 11, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not entirely sure whether I understand this comment. Enabling a lint group that hints more things is too annoying?
Not sure what to change here, feel free to drop commits or decline the PR all together?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That specific lint group pedantic. I am unwilling to unconditionally enable it in this project.

It is an opinionated lint group, some projects use it as you do here, some don't. It implies it certain amount of churn. In this project, I do not want that.

So, I am willing to take the move of the Clippy lints to Cargo.toml, the CI extension, but not enabling the pendantic lint group and what follows based on that.

Not sure what to change here, feel free to drop commits or decline the PR all together?

You are capable of reworking the PR. I am not lacking PR to review over various FOSS projects, so if you are not interested in reworking it yourself, please signal that by closing it.

allow_attributes = "warn"

missing_errors_doc = "allow"
missing_panics_doc = "allow"
must_use_candidate = "allow"

[[bin]]
name = "scraper"
path = "src/main.rs"
Expand Down
2 changes: 0 additions & 2 deletions scraper/examples/document.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
extern crate scraper;

use std::io::{self, Read, Write};

use scraper::{Html, Selector};
Expand Down
2 changes: 0 additions & 2 deletions scraper/examples/fragment.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
extern crate scraper;

use std::io::{self, Read, Write};

use scraper::{Html, Selector};
Expand Down
4 changes: 2 additions & 2 deletions scraper/src/element_ref/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl<'a> ElementRef<'a> {
scope: *self,
inner,
selector,
caches: Default::default(),
caches: SelectorCaches::default(),
}
}

Expand Down Expand Up @@ -154,7 +154,7 @@ impl Clone for Select<'_, '_> {
scope: self.scope,
inner: self.inner.clone(),
selector: self.selector,
caches: Default::default(),
caches: SelectorCaches::default(),
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions scraper/src/html/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ impl Html {
/// # }
/// ```
pub fn parse_document(document: &str) -> Self {
let parser =
driver::parse_document(HtmlTreeSink::new(Self::new_document()), Default::default());
let parser = driver::parse_document(
HtmlTreeSink::new(Self::new_document()),
ParseOpts::default(),
);
parser.one(document)
}

Expand All @@ -100,7 +102,7 @@ impl Html {
Select {
inner: self.tree.nodes(),
selector,
caches: Default::default(),
caches: SelectorCaches::default(),
}
}

Expand Down Expand Up @@ -149,7 +151,7 @@ impl Clone for Select<'_, '_> {
Self {
inner: self.inner.clone(),
selector: self.selector,
caches: Default::default(),
caches: SelectorCaches::default(),
}
}
}
Expand Down
9 changes: 4 additions & 5 deletions scraper/src/html/tree_sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,8 @@ impl TreeSink for HtmlTreeSink {
fn add_attrs_if_missing(&self, target: &Self::Handle, attrs: Vec<Attribute>) {
let mut this = self.0.borrow_mut();
let mut node = this.tree.get_mut(*target).unwrap();
let element = match *node.value() {
Node::Element(ref mut e) => e,
_ => unreachable!(),
let Node::Element(ref mut element) = *node.value() else {
unreachable!()
};

for attr in attrs {
Expand Down Expand Up @@ -289,9 +288,9 @@ impl TreeSink for HtmlTreeSink {
.is_some();

if has_parent {
self.append_before_sibling(element, child)
self.append_before_sibling(element, child);
} else {
self.append(prev_element, child)
self.append(prev_element, child);
}
}
}
12 changes: 1 addition & 11 deletions scraper/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
#![doc = include_str!("../README.md")]
#![warn(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
variant_size_differences
)]
#![warn(missing_docs)]

//! # The core types
//!
Expand Down
15 changes: 6 additions & 9 deletions scraper/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
extern crate getopts;
extern crate scraper;

use getopts::Options;
use scraper::{Html, Selector};
use std::env;
Expand Down Expand Up @@ -36,9 +33,9 @@ fn query<T: Read>(input: &Input, output: &Output, selector: &Selector, file: &mu
Input::Fragment => Html::parse_fragment(&html),
};

let mut matched = false;
let mut has_matches = false;
for element in html.select(selector) {
use crate::Output::*;
use crate::Output::{Attr, Classes, Html, Id, InnerHtml, Name, Text};
match *output {
Html => println!("{}", element.html()),
InnerHtml => println!("{}", element.inner_html()),
Expand All @@ -51,9 +48,9 @@ fn query<T: Read>(input: &Input, output: &Output, selector: &Selector, file: &mu
Name => println!("{}", element.value().name()),
Text => println!("{}", element.text().collect::<String>()),
}
matched = true;
has_matches = true;
}
matched
has_matches
}

fn main() {
Expand Down Expand Up @@ -135,7 +132,7 @@ fn main() {
process::exit(USAGE);
});

let matched = if files.is_empty() {
let selector_has_matches = if files.is_empty() {
query(&input, &output, &selector, &mut io::stdin())
} else {
files
Expand All @@ -145,5 +142,5 @@ fn main() {
.any(|mut f| query(&input, &output, &selector, &mut f))
};

process::exit(i32::from(!matched));
process::exit(i32::from(!selector_has_matches));
}
21 changes: 10 additions & 11 deletions scraper/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use std::cell::OnceCell;
/// An HTML node.
// `Element` is usally the most common variant and hence boxing it
// will most likely not improve performance overall.
#[allow(variant_size_differences)]
#[derive(Clone, PartialEq, Eq)]
pub enum Node {
/// The document root.
Expand Down Expand Up @@ -139,17 +138,17 @@ pub struct Doctype {
impl Doctype {
/// Returns the doctype name.
pub fn name(&self) -> &str {
self.name.deref()
&self.name
}

/// Returns the doctype public ID.
pub fn public_id(&self) -> &str {
self.public_id.deref()
&self.public_id
}

/// Returns the doctype system ID.
pub fn system_id(&self) -> &str {
self.system_id.deref()
&self.system_id
}
}

Expand Down Expand Up @@ -182,7 +181,7 @@ impl Deref for Comment {

impl fmt::Debug for Comment {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "<!-- {:?} -->", self.deref())
write!(f, "<!-- {:?} -->", &**self)
}
}

Expand All @@ -203,7 +202,7 @@ impl Deref for Text {

impl fmt::Debug for Text {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{:?}", self.deref())
write!(f, "{:?}", &**self)
}
}

Expand Down Expand Up @@ -240,7 +239,7 @@ pub struct Element {
impl Element {
#[doc(hidden)]
pub fn new(name: QualName, attributes: Vec<Attribute>) -> Self {
#[allow(unused_mut)]
#[cfg_attr(feature = "deterministic", expect(unused_mut))]
let mut attrs = attributes
.into_iter()
.map(|attr| (attr.name, crate::tendril_util::make(attr.value)))
Expand All @@ -259,7 +258,7 @@ impl Element {

/// Returns the element name.
pub fn name(&self) -> &str {
self.name.local.deref()
&self.name.local
}

/// Returns the element ID.
Expand Down Expand Up @@ -327,7 +326,7 @@ impl Element {
}

/// Iterator over classes.
#[allow(missing_debug_implementations)]
#[expect(missing_debug_implementations)]
#[derive(Clone)]
pub struct Classes<'a> {
inner: SliceIter<'a, LocalName>,
Expand All @@ -350,7 +349,7 @@ pub type AttributesIter<'a> = indexmap::map::Iter<'a, QualName, StrTendril>;
pub type AttributesIter<'a> = SliceIter<'a, (QualName, StrTendril)>;

/// Iterator over attributes.
#[allow(missing_debug_implementations)]
#[expect(missing_debug_implementations)]
#[derive(Clone)]
pub struct Attrs<'a> {
inner: AttributesIter<'a>,
Expand All @@ -360,7 +359,7 @@ impl<'a> Iterator for Attrs<'a> {
type Item = (&'a str, &'a str);

fn next(&mut self) -> Option<(&'a str, &'a str)> {
self.inner.next().map(|(k, v)| (k.local.deref(), v.deref()))
self.inner.next().map(|(k, v)| (&*k.local, &**v))
}
}

Expand Down
4 changes: 4 additions & 0 deletions scraper/src/node/serializable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ use html5ever::serialize::{Serializer, TraversalScope};
use crate::Node;

/// Serialize an HTML node using html5ever serializer.
#[expect(
clippy::needless_pass_by_value,
reason = "same as html5ever Serialize trait"
)]
pub(crate) fn serialize<S: Serializer>(
self_node: NodeRef<Node>,
serializer: &mut S,
Expand Down
8 changes: 3 additions & 5 deletions scraper/src/selector.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! CSS selectors.

use std::convert::TryFrom;
use std::fmt;
use std::str::FromStr;

Expand Down Expand Up @@ -46,7 +45,7 @@ impl Selector {
/// The optional `scope` argument is used to specify which element has `:scope` pseudo-class.
/// When it is `None`, `:scope` will match the root element.
pub fn matches_with_scope(&self, element: &ElementRef, scope: Option<ElementRef>) -> bool {
self.matches_with_scope_and_cache(element, scope, &mut Default::default())
self.matches_with_scope_and_cache(element, scope, &mut matching::SelectorCaches::default())
}

// The `nth_index_cache` must not be used after `self` is dropped
Expand Down Expand Up @@ -116,7 +115,7 @@ impl Visitor<'_> for SelectorVisitor {
/// An implementation of `Parser` for `selectors`
#[derive(Clone, Copy, Debug)]
pub struct Parser;
impl<'i> parser::Parser<'i> for Parser {
impl parser::Parser<'_> for Parser {
type Impl = Simple;
type Error = SelectorParseErrorKind;

Expand Down Expand Up @@ -256,7 +255,6 @@ impl FromStr for Selector {
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryInto;

#[test]
fn selector_conversions() {
Expand All @@ -268,7 +266,7 @@ mod tests {
}

#[test]
#[should_panic]
#[should_panic = "UnexpectedSelectorParseError"]
fn invalid_selector_conversions() {
let s = "<failing selector>";
let _sel: Selector = s.try_into().unwrap();
Expand Down
Loading