rust-clippy/clippy_lints/src/unicode.rs
xFrednet d647696c1f
Added clippy::version attribute to all normal lints
So, some context for this, well, more a story. I'm not used to scripting, I've never really scripted anything, even if it's a valuable skill. I just never really needed it. Now, `@flip1995` correctly suggested using a script for this in `rust-clippy#7813`...

And I decided to write a script using nushell because why not? This was a mistake... I spend way more time on this than I would like to admit. It has definitely been more than 4 hours. It shouldn't take that long, but me being new to scripting and nushell just wasn't a good mixture... Anyway, here is the script that creates another script which adds the versions. Fun...

Just execute this on the `gh-pages` branch and the resulting `replacer.sh` in `clippy_lints` and it should all work.

```nu
mv v0.0.212 rust-1.00.0;
mv beta rust-1.57.0;
mv master rust-1.58.0;

let paths = (open ./rust-1.58.0/lints.json | select id id_span | flatten | select id path);
let versions = (
    ls | where name =~ "rust-" | select name | format {name}/lints.json |
    each { open $it | select id | insert version $it | str substring "5,11" version} |
    group-by id | rotate counter-clockwise id version |
    update version {get version | first 1} | flatten | select id version);
$paths | each { |row|
    let version = ($versions | where id == ($row.id) | format {version})
    let idu = ($row.id | str upcase)
    $"sed -i '0,/($idu),/{s/pub ($idu),/#[clippy::version = "($version)"]\n    pub ($idu),/}' ($row.path)"
} | str collect ";" | str find-replace --all '1.00.0' 'pre 1.29.0' | save "replacer.sh";
```

And this still has some problems, but at this point I just want to be done -.-
2021-11-10 19:48:31 +01:00

141 lines
4.7 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_lint_allowed;
use clippy_utils::source::snippet;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use unicode_normalization::UnicodeNormalization;
declare_clippy_lint! {
/// ### What it does
/// Checks for invisible Unicode characters in the code.
///
/// ### Why is this bad?
/// Having an invisible character in the code makes for all
/// sorts of April fools, but otherwise is very much frowned upon.
///
/// ### Example
/// You don't see it, but there may be a zero-width space or soft hyphen
/// some­where in this text.
#[clippy::version = "1.49.0"]
pub INVISIBLE_CHARACTERS,
correctness,
"using an invisible character in a string literal, which is confusing"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for non-ASCII characters in string literals.
///
/// ### Why is this bad?
/// Yeah, we know, the 90's called and wanted their charset
/// back. Even so, there still are editors and other programs out there that
/// don't work well with Unicode. So if the code is meant to be used
/// internationally, on multiple operating systems, or has other portability
/// requirements, activating this lint could be useful.
///
/// ### Example
/// ```rust
/// let x = String::from("€");
/// ```
/// Could be written as:
/// ```rust
/// let x = String::from("\u{20ac}");
/// ```
#[clippy::version = "pre 1.29.0"]
pub NON_ASCII_LITERAL,
restriction,
"using any literal non-ASCII chars in a string literal instead of using the `\\u` escape"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for string literals that contain Unicode in a form
/// that is not equal to its
/// [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).
///
/// ### Why is this bad?
/// If such a string is compared to another, the results
/// may be surprising.
///
/// ### Example
/// You may not see it, but "à"" and "à"" aren't the same string. The
/// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`.
#[clippy::version = "pre 1.29.0"]
pub UNICODE_NOT_NFC,
pedantic,
"using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
}
declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_NOT_NFC]);
impl LateLintPass<'_> for Unicode {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
if let ExprKind::Lit(ref lit) = expr.kind {
if let LitKind::Str(_, _) = lit.node {
check_str(cx, lit.span, expr.hir_id);
}
}
}
}
fn escape<T: Iterator<Item = char>>(s: T) -> String {
let mut result = String::new();
for c in s {
if c as u32 > 0x7F {
for d in c.escape_unicode() {
result.push(d);
}
} else {
result.push(c);
}
}
result
}
fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
let string = snippet(cx, span, "");
if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) {
span_lint_and_sugg(
cx,
INVISIBLE_CHARACTERS,
span,
"invisible character detected",
"consider replacing the string with",
string
.replace("\u{200B}", "\\u{200B}")
.replace("\u{ad}", "\\u{AD}")
.replace("\u{2060}", "\\u{2060}"),
Applicability::MachineApplicable,
);
}
if string.chars().any(|c| c as u32 > 0x7F) {
span_lint_and_sugg(
cx,
NON_ASCII_LITERAL,
span,
"literal non-ASCII character detected",
"consider replacing the string with",
if is_lint_allowed(cx, UNICODE_NOT_NFC, id) {
escape(string.chars())
} else {
escape(string.nfc())
},
Applicability::MachineApplicable,
);
}
if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
span_lint_and_sugg(
cx,
UNICODE_NOT_NFC,
span,
"non-NFC Unicode sequence detected",
"consider replacing the string with",
string.nfc().collect::<String>(),
Applicability::MachineApplicable,
);
}
}