Implement type(primitive) (#353)

This commit is contained in:
Charlie Marsh 2022-10-07 16:47:06 -04:00 committed by GitHub
parent e3a7357187
commit 4cf2682cda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 214 additions and 2 deletions

5
resources/test/fixtures/U003.py vendored Normal file
View File

@ -0,0 +1,5 @@
type('')
type(b'')
type(0)
type(0.)
type(0j)

View File

@ -6,6 +6,7 @@ use rustpython_parser::ast::{
Arg, ArgData, Arguments, Cmpop, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Arg, ArgData, Arguments, Cmpop, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind,
Stmt, StmtKind, Unaryop, Stmt, StmtKind, Unaryop,
}; };
use serde::{Deserialize, Serialize};
use crate::ast::types::{ use crate::ast::types::{
Binding, BindingKind, CheckLocator, FunctionScope, Range, Scope, ScopeKind, Binding, BindingKind, CheckLocator, FunctionScope, Range, Scope, ScopeKind,
@ -155,6 +156,65 @@ pub fn check_unnecessary_abspath(func: &Expr, args: &Vec<Expr>, location: Range)
None None
} }
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Primitive {
Bool,
Str,
Bytes,
Int,
Float,
Complex,
}
impl Primitive {
fn from_constant(constant: &Constant) -> Option<Self> {
match constant {
Constant::Bool(_) => Some(Primitive::Bool),
Constant::Str(_) => Some(Primitive::Str),
Constant::Bytes(_) => Some(Primitive::Bytes),
Constant::Int(_) => Some(Primitive::Int),
Constant::Float(_) => Some(Primitive::Float),
Constant::Complex { .. } => Some(Primitive::Complex),
_ => None,
}
}
pub fn builtin(&self) -> String {
match self {
Primitive::Bool => "bool".to_string(),
Primitive::Str => "str".to_string(),
Primitive::Bytes => "bytes".to_string(),
Primitive::Int => "int".to_string(),
Primitive::Float => "float".to_string(),
Primitive::Complex => "complex".to_string(),
}
}
}
/// Check TypeOfPrimitive compliance.
pub fn check_type_of_primitive(func: &Expr, args: &Vec<Expr>, location: Range) -> Option<Check> {
// Validate the arguments.
if args.len() == 1 {
match &func.node {
ExprKind::Attribute { attr: id, .. } | ExprKind::Name { id, .. } => {
if id == "type" {
if let ExprKind::Constant { value, .. } = &args[0].node {
if let Some(primitive) = Primitive::from_constant(value) {
return Some(Check::new(
CheckKind::TypeOfPrimitive(primitive),
location,
));
}
}
}
}
_ => {}
}
}
None
}
fn is_ambiguous_name(name: &str) -> bool { fn is_ambiguous_name(name: &str) -> bool {
name == "l" || name == "I" || name == "O" name == "l" || name == "I" || name == "O"
} }

View File

@ -801,6 +801,10 @@ where
plugins::unnecessary_abspath(self, expr, func, args); plugins::unnecessary_abspath(self, expr, func, args);
} }
if self.settings.enabled.contains(&CheckCode::U003) {
plugins::type_of_primitive(self, expr, func, args);
}
if let ExprKind::Name { id, ctx } = &func.node { if let ExprKind::Name { id, ctx } = &func.node {
if id == "locals" && matches!(ctx, ExprContext::Load) { if id == "locals" && matches!(ctx, ExprContext::Load) {
let scope = &mut self.scopes[*(self let scope = &mut self.scopes[*(self

View File

@ -1,11 +1,13 @@
use std::str::FromStr; use std::str::FromStr;
use crate::ast::types::Range; use crate::ast::checks::Primitive;
use anyhow::Result; use anyhow::Result;
use itertools::Itertools; use itertools::Itertools;
use rustpython_parser::ast::Location; use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::ast::types::Range;
pub const DEFAULT_CHECK_CODES: [CheckCode; 42] = [ pub const DEFAULT_CHECK_CODES: [CheckCode; 42] = [
// pycodestyle // pycodestyle
CheckCode::E402, CheckCode::E402,
@ -53,7 +55,7 @@ pub const DEFAULT_CHECK_CODES: [CheckCode; 42] = [
CheckCode::F901, CheckCode::F901,
]; ];
pub const ALL_CHECK_CODES: [CheckCode; 57] = [ pub const ALL_CHECK_CODES: [CheckCode; 58] = [
// pycodestyle // pycodestyle
CheckCode::E402, CheckCode::E402,
CheckCode::E501, CheckCode::E501,
@ -115,6 +117,7 @@ pub const ALL_CHECK_CODES: [CheckCode; 57] = [
// pyupgrade // pyupgrade
CheckCode::U001, CheckCode::U001,
CheckCode::U002, CheckCode::U002,
CheckCode::U003,
// Refactor // Refactor
CheckCode::R001, CheckCode::R001,
CheckCode::R002, CheckCode::R002,
@ -185,6 +188,7 @@ pub enum CheckCode {
// pyupgrade // pyupgrade
U001, U001,
U002, U002,
U003,
// Refactor // Refactor
R001, R001,
R002, R002,
@ -256,6 +260,7 @@ impl FromStr for CheckCode {
// pyupgrade // pyupgrade
"U001" => Ok(CheckCode::U001), "U001" => Ok(CheckCode::U001),
"U002" => Ok(CheckCode::U002), "U002" => Ok(CheckCode::U002),
"U003" => Ok(CheckCode::U003),
// Refactor // Refactor
"R001" => Ok(CheckCode::R001), "R001" => Ok(CheckCode::R001),
"R002" => Ok(CheckCode::R002), "R002" => Ok(CheckCode::R002),
@ -330,6 +335,7 @@ impl CheckCode {
// pyupgrade // pyupgrade
CheckCode::U001 => "U001", CheckCode::U001 => "U001",
CheckCode::U002 => "U002", CheckCode::U002 => "U002",
CheckCode::U003 => "U003",
// Refactor // Refactor
CheckCode::R001 => "R001", CheckCode::R001 => "R001",
CheckCode::R002 => "R002", CheckCode::R002 => "R002",
@ -413,6 +419,7 @@ impl CheckCode {
// pyupgrade // pyupgrade
CheckCode::U001 => CheckKind::UselessMetaclassType, CheckCode::U001 => CheckKind::UselessMetaclassType,
CheckCode::U002 => CheckKind::UnnecessaryAbspath, CheckCode::U002 => CheckKind::UnnecessaryAbspath,
CheckCode::U003 => CheckKind::UnnecessaryAbspath,
// Refactor // Refactor
CheckCode::R001 => CheckKind::UselessObjectInheritance("...".to_string()), CheckCode::R001 => CheckKind::UselessObjectInheritance("...".to_string()),
CheckCode::R002 => CheckKind::NoAssertEquals, CheckCode::R002 => CheckKind::NoAssertEquals,
@ -494,6 +501,7 @@ pub enum CheckKind {
PrintFound, PrintFound,
PPrintFound, PPrintFound,
// pyupgrade // pyupgrade
TypeOfPrimitive(Primitive),
UnnecessaryAbspath, UnnecessaryAbspath,
UselessMetaclassType, UselessMetaclassType,
// Refactor // Refactor
@ -564,6 +572,7 @@ impl CheckKind {
CheckKind::PrintFound => "PrintFound", CheckKind::PrintFound => "PrintFound",
CheckKind::PPrintFound => "PPrintFound", CheckKind::PPrintFound => "PPrintFound",
// pyupgrade // pyupgrade
CheckKind::TypeOfPrimitive(_) => "TypeOfPrimitive",
CheckKind::UnnecessaryAbspath => "UnnecessaryAbspath", CheckKind::UnnecessaryAbspath => "UnnecessaryAbspath",
CheckKind::UselessMetaclassType => "UselessMetaclassType", CheckKind::UselessMetaclassType => "UselessMetaclassType",
// Refactor // Refactor
@ -634,6 +643,7 @@ impl CheckKind {
CheckKind::PrintFound => &CheckCode::T201, CheckKind::PrintFound => &CheckCode::T201,
CheckKind::PPrintFound => &CheckCode::T203, CheckKind::PPrintFound => &CheckCode::T203,
// pyupgrade // pyupgrade
CheckKind::TypeOfPrimitive(_) => &CheckCode::U003,
CheckKind::UnnecessaryAbspath => &CheckCode::U002, CheckKind::UnnecessaryAbspath => &CheckCode::U002,
CheckKind::UselessMetaclassType => &CheckCode::U001, CheckKind::UselessMetaclassType => &CheckCode::U001,
// Refactor // Refactor
@ -806,6 +816,9 @@ impl CheckKind {
CheckKind::PrintFound => "`print` found".to_string(), CheckKind::PrintFound => "`print` found".to_string(),
CheckKind::PPrintFound => "`pprint` found".to_string(), CheckKind::PPrintFound => "`pprint` found".to_string(),
// pyupgrade // pyupgrade
CheckKind::TypeOfPrimitive(primitive) => {
format!("Use `{}` instead of `type(...)`", primitive.builtin())
}
CheckKind::UnnecessaryAbspath => { CheckKind::UnnecessaryAbspath => {
"`abspath(__file__)` is unnecessary in Python 3.9 and later".to_string() "`abspath(__file__)` is unnecessary in Python 3.9 and later".to_string()
} }
@ -833,6 +846,7 @@ impl CheckKind {
| CheckKind::PPrintFound | CheckKind::PPrintFound
| CheckKind::PrintFound | CheckKind::PrintFound
| CheckKind::SuperCallWithParameters | CheckKind::SuperCallWithParameters
| CheckKind::TypeOfPrimitive(_)
| CheckKind::UnnecessaryAbspath | CheckKind::UnnecessaryAbspath
| CheckKind::UnusedImport(_) | CheckKind::UnusedImport(_)
| CheckKind::UnusedNOQA(_) | CheckKind::UnusedNOQA(_)

View File

@ -893,4 +893,16 @@ mod tests {
insta::assert_yaml_snapshot!(checks); insta::assert_yaml_snapshot!(checks);
Ok(()) Ok(())
} }
#[test]
fn u003() -> Result<()> {
let mut checks = check_path(
Path::new("./resources/test/fixtures/U003.py"),
&settings::Settings::for_rule(CheckCode::U003),
&fixer::Mode::Generate,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
Ok(())
}
} }

View File

@ -4,6 +4,7 @@ mod if_tuple;
mod invalid_print_syntax; mod invalid_print_syntax;
mod print_call; mod print_call;
mod super_call_with_parameters; mod super_call_with_parameters;
mod type_of_primitive;
mod unnecessary_abspath; mod unnecessary_abspath;
mod useless_metaclass_type; mod useless_metaclass_type;
mod useless_object_inheritance; mod useless_object_inheritance;
@ -14,6 +15,7 @@ pub use if_tuple::if_tuple;
pub use invalid_print_syntax::invalid_print_syntax; pub use invalid_print_syntax::invalid_print_syntax;
pub use print_call::print_call; pub use print_call::print_call;
pub use super_call_with_parameters::super_call_with_parameters; pub use super_call_with_parameters::super_call_with_parameters;
pub use type_of_primitive::type_of_primitive;
pub use unnecessary_abspath::unnecessary_abspath; pub use unnecessary_abspath::unnecessary_abspath;
pub use useless_metaclass_type::useless_metaclass_type; pub use useless_metaclass_type::useless_metaclass_type;
pub use useless_object_inheritance::useless_object_inheritance; pub use useless_object_inheritance::useless_object_inheritance;

View File

@ -0,0 +1,25 @@
use rustpython_ast::Expr;
use crate::ast::checks;
use crate::ast::types::{CheckLocator, Range};
use crate::autofix::fixer;
use crate::check_ast::Checker;
use crate::checks::{CheckKind, Fix};
pub fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr, args: &Vec<Expr>) {
if let Some(mut check) =
checks::check_type_of_primitive(func, args, checker.locate_check(Range::from_located(expr)))
{
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
if let CheckKind::TypeOfPrimitive(primitive) = &check.kind {
check.amend(Fix {
content: primitive.builtin(),
location: expr.location,
end_location: expr.end_location,
applied: false,
});
}
}
checker.add_check(check);
}
}

View File

@ -0,0 +1,90 @@
---
source: src/linter.rs
expression: checks
---
- kind:
TypeOfPrimitive: Str
location:
row: 1
column: 1
end_location:
row: 1
column: 9
fix:
content: str
location:
row: 1
column: 1
end_location:
row: 1
column: 9
applied: false
- kind:
TypeOfPrimitive: Bytes
location:
row: 2
column: 1
end_location:
row: 2
column: 10
fix:
content: bytes
location:
row: 2
column: 1
end_location:
row: 2
column: 10
applied: false
- kind:
TypeOfPrimitive: Int
location:
row: 3
column: 1
end_location:
row: 3
column: 8
fix:
content: int
location:
row: 3
column: 1
end_location:
row: 3
column: 8
applied: false
- kind:
TypeOfPrimitive: Float
location:
row: 4
column: 1
end_location:
row: 4
column: 9
fix:
content: float
location:
row: 4
column: 1
end_location:
row: 4
column: 9
applied: false
- kind:
TypeOfPrimitive: Complex
location:
row: 5
column: 1
end_location:
row: 5
column: 9
fix:
content: complex
location:
row: 5
column: 1
end_location:
row: 5
column: 9
applied: false