-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Summary Implementation of Refurb FURB181 Part of #1348 ## Test Plan Test cases from Refurb
- Loading branch information
Showing
8 changed files
with
527 additions
and
0 deletions.
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
crates/ruff_linter/resources/test/fixtures/refurb/FURB181.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import hashlib | ||
from hashlib import ( | ||
blake2b, | ||
blake2s, | ||
md5, | ||
sha1, | ||
sha3_224, | ||
sha3_256, | ||
sha3_384, | ||
sha3_512, | ||
sha224, | ||
) | ||
from hashlib import sha256 | ||
from hashlib import sha256 as hash_algo | ||
from hashlib import sha384, sha512, shake_128, shake_256 | ||
|
||
# these will match | ||
|
||
blake2b().digest().hex() | ||
blake2s().digest().hex() | ||
md5().digest().hex() | ||
sha1().digest().hex() | ||
sha224().digest().hex() | ||
sha256().digest().hex() | ||
sha384().digest().hex() | ||
sha3_224().digest().hex() | ||
sha3_256().digest().hex() | ||
sha3_384().digest().hex() | ||
sha3_512().digest().hex() | ||
sha512().digest().hex() | ||
shake_128().digest(10).hex() | ||
shake_256().digest(10).hex() | ||
|
||
hashlib.sha256().digest().hex() | ||
|
||
sha256(b"text").digest().hex() | ||
|
||
hash_algo().digest().hex() | ||
|
||
# not yet supported | ||
h = sha256() | ||
h.digest().hex() | ||
|
||
|
||
# these will not | ||
|
||
sha256().digest() | ||
sha256().digest().hex("_") | ||
sha256().digest().hex(bytes_per_sep=4) | ||
sha256().hexdigest() | ||
|
||
class Hash: | ||
def digest(self) -> bytes: | ||
return b"" | ||
|
||
|
||
Hash().digest().hex() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
crates/ruff_linter/src/rules/refurb/rules/hashlib_digest_hex.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast::{Expr, ExprAttribute, ExprCall}; | ||
use ruff_text_size::{Ranged, TextRange}; | ||
|
||
use crate::checkers::ast::Checker; | ||
|
||
/// ## What it does | ||
/// Checks for the use of `.digest().hex()` on a hashlib hash, like `sha512`. | ||
/// | ||
/// ## Why is this bad? | ||
/// When generating a hex digest from a hash, it's preferable to use the | ||
/// `.hexdigest()` method, rather than calling `.digest()` and then `.hex()`, | ||
/// as the former is more concise and readable. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// from hashlib import sha512 | ||
/// | ||
/// hashed = sha512(b"some data").digest().hex() | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// from hashlib import sha512 | ||
/// | ||
/// hashed = sha512(b"some data").hexdigest() | ||
/// ``` | ||
/// | ||
/// ## Fix safety | ||
/// This rule's fix is marked as unsafe, as the target of the `.digest()` call | ||
/// could be a user-defined class that implements a `.hex()` method, rather | ||
/// than a hashlib hash object. | ||
/// | ||
/// ## References | ||
/// - [Python documentation: `hashlib`](https://docs.python.org/3/library/hashlib.html) | ||
#[violation] | ||
pub struct HashlibDigestHex; | ||
|
||
impl Violation for HashlibDigestHex { | ||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; | ||
|
||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
format!("Use of hashlib's `.digest().hex()`") | ||
} | ||
|
||
fn fix_title(&self) -> Option<String> { | ||
Some("Replace with `.hexdigest()`".to_string()) | ||
} | ||
} | ||
|
||
/// FURB181 | ||
pub(crate) fn hashlib_digest_hex(checker: &mut Checker, call: &ExprCall) { | ||
if !call.arguments.is_empty() { | ||
return; | ||
} | ||
|
||
let Expr::Attribute(ExprAttribute { attr, value, .. }) = call.func.as_ref() else { | ||
return; | ||
}; | ||
|
||
if attr.as_str() != "hex" { | ||
return; | ||
} | ||
|
||
let Expr::Call(ExprCall { | ||
func, arguments, .. | ||
}) = value.as_ref() | ||
else { | ||
return; | ||
}; | ||
|
||
let Expr::Attribute(ExprAttribute { attr, value, .. }) = func.as_ref() else { | ||
return; | ||
}; | ||
|
||
if attr.as_str() != "digest" { | ||
return; | ||
} | ||
|
||
let Expr::Call(ExprCall { func, .. }) = value.as_ref() else { | ||
return; | ||
}; | ||
|
||
if checker.semantic().resolve_call_path(func).is_some_and( | ||
|call_path: smallvec::SmallVec<[&str; 8]>| { | ||
matches!( | ||
call_path.as_slice(), | ||
[ | ||
"hashlib", | ||
"md5" | ||
| "sha1" | ||
| "sha224" | ||
| "sha256" | ||
| "sha384" | ||
| "sha512" | ||
| "blake2b" | ||
| "blake2s" | ||
| "sha3_224" | ||
| "sha3_256" | ||
| "sha3_384" | ||
| "sha3_512" | ||
| "shake_128" | ||
| "shake_256" | ||
| "_Hash" | ||
] | ||
) | ||
}, | ||
) { | ||
let mut diagnostic = Diagnostic::new(HashlibDigestHex, call.range()); | ||
if arguments.is_empty() { | ||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement( | ||
".hexdigest".to_string(), | ||
TextRange::new(value.end(), call.func.end()), | ||
))); | ||
} | ||
checker.diagnostics.push(diagnostic); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.