Skip to content

Added non-printable-ascii lint #66563

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,12 @@ pub mod parser {
Deny,
"trailing content in included file"
}

declare_lint! {
pub NON_PRINTABLE_ASCII,
Warn,
"non-printable ASCII character in a literal"
}
}

declare_lint! {
Expand Down Expand Up @@ -522,6 +528,7 @@ declare_lint_pass! {
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
parser::ILL_FORMED_ATTRIBUTE_INPUT,
parser::META_VARIABLE_MISUSE,
parser::NON_PRINTABLE_ASCII,
DEPRECATED_IN_FUTURE,
AMBIGUOUS_ASSOCIATED_ITEMS,
MUTABLE_BORROW_RESERVATION_CONFLICT,
Expand Down
5 changes: 3 additions & 2 deletions src/librustc/lint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use crate::hir::def_id::{CrateNum, LOCAL_CRATE};
use crate::hir::intravisit;
use crate::hir;
use crate::lint::builtin::BuiltinLintDiagnostics;
use crate::lint::builtin::parser::{ILL_FORMED_ATTRIBUTE_INPUT, META_VARIABLE_MISUSE};
use crate::lint::builtin::parser::INCOMPLETE_INCLUDE;
use crate::lint::builtin::parser::{ILL_FORMED_ATTRIBUTE_INPUT, META_VARIABLE_MISUSE,
INCOMPLETE_INCLUDE, NON_PRINTABLE_ASCII};
use crate::session::{Session, DiagnosticMessageId};
use crate::ty::TyCtxt;
use crate::ty::query::Providers;
Expand Down Expand Up @@ -111,6 +111,7 @@ impl Lint {
BufferedEarlyLintId::IllFormedAttributeInput => ILL_FORMED_ATTRIBUTE_INPUT,
BufferedEarlyLintId::MetaVariableMisuse => META_VARIABLE_MISUSE,
BufferedEarlyLintId::IncompleteInclude => INCOMPLETE_INCLUDE,
BufferedEarlyLintId::NonPrintableAscii => NON_PRINTABLE_ASCII,
}
}

Expand Down
93 changes: 68 additions & 25 deletions src/librustc_parse/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ use log::debug;
mod tokentrees;
mod unicode_chars;
mod unescape_error_reporting;
use unescape_error_reporting::{emit_unescape_error, push_escaped_char};
use unescape_error_reporting::{emit_unescape_error, push_escaped_char,
lint_unescaped_char, lint_unescaped_byte};

#[derive(Clone, Debug)]
pub struct UnmatchedBrace {
Expand Down Expand Up @@ -532,92 +533,134 @@ impl<'a> StringReader<'a> {

fn validate_char_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
if let Err((off, err)) = unescape::unescape_char(lit) {
emit_unescape_error(
let span = self.mk_sp(content_start - BytePos(1), content_end + BytePos(1));
match unescape::unescape_char(lit) {
Ok(c) => lint_unescaped_char(
&self.sess,
span,
c,
0..lit.len(),
),
Err((off, err)) => emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
span,
unescape::Mode::Char,
0..off,
err,
)
),
}
}

fn validate_byte_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
if let Err((off, err)) = unescape::unescape_byte(lit) {
emit_unescape_error(
let span = self.mk_sp(content_start - BytePos(1), content_end + BytePos(1));
match unescape::unescape_byte(lit) {
Ok(c) => lint_unescaped_byte(
&self.sess,
span,
c,
0..lit.len(),
),
Err((off, err)) => emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
span,
unescape::Mode::Byte,
0..off,
err,
)
),
}
}

fn validate_str_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
let span = self.mk_sp(content_start - BytePos(1), content_end + BytePos(1));
unescape::unescape_str(lit, &mut |range, c| {
if let Err(err) = c {
emit_unescape_error(
match c {
Ok(c) => lint_unescaped_char(
&self.sess,
span,
c,
range,
),
Err(err) => emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
span,
unescape::Mode::Str,
range,
err,
)
),
}
})
}

fn validate_raw_str_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
let span = self.mk_sp(content_start - BytePos(1), content_end + BytePos(1));
unescape::unescape_raw_str(lit, &mut |range, c| {
if let Err(err) = c {
emit_unescape_error(
match c {
Ok(c) => lint_unescaped_char(
&self.sess,
span,
c,
range,
),
Err(err) => emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
span,
unescape::Mode::Str,
range,
err,
)
),
}
})
}

fn validate_raw_byte_str_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
let span = self.mk_sp(content_start - BytePos(1), content_end + BytePos(1));
unescape::unescape_raw_byte_str(lit, &mut |range, c| {
if let Err(err) = c {
emit_unescape_error(
match c {
Ok(c) => lint_unescaped_byte(
&self.sess,
span,
c,
range,
),
Err(err) => emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
span,
unescape::Mode::ByteStr,
range,
err,
)
),
}
})
}

fn validate_byte_str_escape(&self, content_start: BytePos, content_end: BytePos) {
let lit = self.str_from_to(content_start, content_end);
let span = self.mk_sp(content_start - BytePos(1), content_end + BytePos(1));
unescape::unescape_byte_str(lit, &mut |range, c| {
if let Err(err) = c {
emit_unescape_error(
match c {
Ok(c) => lint_unescaped_byte(
&self.sess,
span,
c,
range,
),
Err(err) => emit_unescape_error(
&self.sess.span_diagnostic,
lit,
self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)),
span,
unescape::Mode::ByteStr,
range,
err,
)
),
}
})
}
Expand Down
37 changes: 36 additions & 1 deletion src/librustc_parse/lexer/unescape_error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use std::iter::once;
use rustc_lexer::unescape::{EscapeError, Mode};
use syntax_pos::{Span, BytePos};

use syntax::errors::{Handler, Applicability};
use syntax::{errors::{Handler, Applicability}, sess::ParseSess,
early_buffered_lints::BufferedEarlyLintId, ast};

pub(crate) fn emit_unescape_error(
handler: &Handler,
Expand Down Expand Up @@ -201,6 +202,40 @@ pub(crate) fn emit_unescape_error(
}
}

pub(crate) fn lint_unescaped_char(
sess: &ParseSess,
// full span of the literal, including quotes
span_with_quotes: Span,
// interior part of the literal, without quotes
character: char,
range: Range<usize>,
) {
const ALLOWED_ASCII_CONTROL_CHARS: [char; 2] = ['\t', '\n'];

if range.len() != 1 {
Copy link
Member

Choose a reason for hiding this comment

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

Why this check? Please add a comment.

return;
}
if !ALLOWED_ASCII_CONTROL_CHARS.contains(&character) && character.is_ascii_control() {
sess.buffer_lint(
BufferedEarlyLintId::NonPrintableAscii,
span_with_quotes,
ast::CRATE_NODE_ID,
"non-printable ASCII character in literal"
)
Copy link
Member

@Manishearth Manishearth Nov 30, 2019

Choose a reason for hiding this comment

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

We should add a MachineApplicable suggestion for the appropriate escape sequence.

}
}

pub(crate) fn lint_unescaped_byte(
sess: &ParseSess,
// full span of the literal, including quotes
span_with_quotes: Span,
// interior part of the literal, without quotes
byte: u8,
range: Range<usize>,
) {
lint_unescaped_char(sess, span_with_quotes, char::from(byte), range);
}

/// Pushes a character to a message string for error reporting
pub(crate) fn push_escaped_char(msg: &mut String, c: char) {
match c {
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/early_buffered_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub enum BufferedEarlyLintId {
IllFormedAttributeInput,
MetaVariableMisuse,
IncompleteInclude,
NonPrintableAscii,
}

/// Stores buffered lint info which can later be passed to `librustc`.
Expand Down
Binary file added src/test/ui/lint/non-printable-string-literals.rs
Binary file not shown.
Binary file not shown.
Binary file modified src/test/ui/raw-str.rs
Binary file not shown.