use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
-use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
+use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
use rustc_ast::token::{self, LitKind};
use rustc_ast::tokenstream::TokenStream;
-use rustc_errors::Applicability;
+use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_lexer::unescape::{self, EscapeError};
-use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_parse::parser;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, Symbol};
/// // Good
/// println!();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub PRINTLN_EMPTY_STRING,
style,
"using `println!(\"\")` with an empty string"
/// # let name = "World";
/// println!("Hello {}!", name);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub PRINT_WITH_NEWLINE,
style,
"using `print!()` with a format string that ends in a single newline"
/// application and might forget to remove those prints afterward.
///
/// ### Known problems
- /// Only catches `print!` and `println!` calls.
+ /// * Only catches `print!` and `println!` calls.
+ /// * The lint level is unaffected by crate attributes. The level can still
+ /// be set for functions, modules and other items. To change the level for
+ /// the entire crate, please use command line flags. More information and a
+ /// configuration example can be found in [clippy#6610].
+ ///
+ /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
///
/// ### Example
/// ```rust
/// println!("Hello world!");
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub PRINT_STDOUT,
restriction,
"printing on stdout"
/// application and might forget to remove those prints afterward.
///
/// ### Known problems
- /// Only catches `eprint!` and `eprintln!` calls.
+ /// * Only catches `eprint!` and `eprintln!` calls.
+ /// * The lint level is unaffected by crate attributes. The level can still
+ /// be set for functions, modules and other items. To change the level for
+ /// the entire crate, please use command line flags. More information and a
+ /// configuration example can be found in [clippy#6610].
+ ///
+ /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
///
/// ### Example
/// ```rust
/// eprintln!("Hello world!");
/// ```
+ #[clippy::version = "1.50.0"]
pub PRINT_STDERR,
restriction,
"printing on stderr"
/// # let foo = "bar";
/// println!("{:?}", foo);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub USE_DEBUG,
restriction,
"use of `Debug`-based formatting"
/// ```rust
/// println!("foo");
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub PRINT_LITERAL,
style,
"printing a literal with a format string"
/// // Good
/// writeln!(buf);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub WRITELN_EMPTY_STRING,
style,
"using `writeln!(buf, \"\")` with an empty string"
/// // Good
/// writeln!(buf, "Hello {}!", name);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub WRITE_WITH_NEWLINE,
style,
"using `write!()` with a format string that ends in a single newline"
/// // Good
/// writeln!(buf, "foo");
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub WRITE_LITERAL,
style,
"writing a literal with a format string"
impl EarlyLintPass for Write {
fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) {
- if let ItemKind::Impl(box ImplKind {
+ if let ItemKind::Impl(box Impl {
of_trait: Some(trait_ref),
..
}) = &item.kind
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
fn is_build_script(cx: &EarlyContext<'_>) -> bool {
// Cargo sets the crate name for build scripts to `build_script_build`
- cx.sess
+ cx.sess()
.opts
.crate_name
.as_ref()
let nl_span = match (dest, only_nl) {
// Special case of `write!(buf, "\n")`: Mark everything from the end of
// `buf` for removal so no trailing comma [`writeln!(buf, )`] remains.
- (Some(dest_expr), true) => Span::new(dest_expr.span.hi(), nl_span.hi(), nl_span.ctxt()),
+ (Some(dest_expr), true) => nl_span.with_lo(dest_expr.span.hi()),
_ => nl_span,
};
span_lint_and_then(
/// Return this and a boolean indicating whether it only consisted of a newline.
fn newline_span(fmtstr: &StrLit) -> (Span, bool) {
let sp = fmtstr.span;
- let contents = &fmtstr.symbol.as_str();
+ let contents = fmtstr.symbol.as_str();
- if *contents == r"\n" {
+ if contents == r"\n" {
return (sp, true);
}
}
}
},
- ArgumentNamed(n) => {
+ ArgumentNamed(n, _) => {
if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
match x.1.as_slice() {
// A non-empty format string has been seen already.
StrStyle::Raw(n) => Some(n as usize),
};
- let mut parser = Parser::new(&str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format);
+ let mut parser = Parser::new(str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format);
let mut args = SimpleFormatArgs::default();
while let Some(arg) = parser.next() {
/// ```
#[allow(clippy::too_many_lines)]
fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
- let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None);
+ let mut parser = parser::Parser::new(&cx.sess().parse_sess, tts, false, None);
let expr = if is_write {
match parser
.parse_expr()
.map(rustc_ast::ptr::P::into_inner)
- .map_err(|mut e| e.cancel())
+ .map_err(DiagnosticBuilder::cancel)
{
// write!(e, ...)
Ok(p) if parser.eat(&token::Comma) => Some(p),
}
let comma_span = parser.prev_token.span;
- let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) {
+ let token_expr = if let Ok(expr) = parser.parse_expr().map_err(DiagnosticBuilder::cancel) {
expr
} else {
return (Some(fmtstr), None);
};
let replacement: String = match lit.token.kind {
- LitKind::Integer | LitKind::Float | LitKind::Err => continue,
LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
- lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+ lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
},
LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
- lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+ lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
},
- LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue,
- LitKind::Byte | LitKind::Char => match &*lit.token.symbol.as_str() {
+ LitKind::StrRaw(_)
+ | LitKind::Str
+ | LitKind::ByteStrRaw(_)
+ | LitKind::ByteStr
+ | LitKind::Integer
+ | LitKind::Float
+ | LitKind::Err => continue,
+ LitKind::Byte | LitKind::Char => match lit.token.symbol.as_str() {
"\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"",
"\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue,
"\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\",
let mut last_was_cr = false;
let mut should_lint = false;
- let contents = &fmtstr.symbol.as_str();
+ let contents = fmtstr.symbol.as_str();
let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
let c = c.unwrap();