Skip to content

Commit

Permalink
feat(linter): add rule eslint/new-cap
Browse files Browse the repository at this point in the history
  • Loading branch information
Sysix committed Dec 28, 2024
1 parent cf79497 commit 35bfb86
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 86 deletions.
42 changes: 0 additions & 42 deletions crates/oxc_linter/src/ast_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use oxc_ecmascript::ToBoolean;
use oxc_semantic::{AstNode, IsGlobalReference, NodeId, ReferenceId, Semantic, SymbolId};
use oxc_span::{GetSpan, Span};
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator};
use std::borrow::Cow;

/// Test if an AST node is a boolean value that never changes. Specifically we
/// test for:
Expand Down Expand Up @@ -470,44 +469,3 @@ pub fn leftmost_identifier_reference<'a, 'b: 'a>(
_ => Err(expr),
}
}

fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option<Cow<'a, str>> {
if matches!(key, PropertyKey::NullLiteral(_)) {
return Some("null".into());
}

match key {
PropertyKey::RegExpLiteral(regex) => {
Some(Cow::Owned(format!("/{}/{}", regex.regex.pattern, regex.regex.flags)))
}
PropertyKey::BigIntLiteral(bigint) => Some(Cow::Borrowed(bigint.raw.as_str())),
PropertyKey::TemplateLiteral(template) => {
if template.expressions.len() == 0 && template.quasis.len() == 1 {
if let Some(cooked) = &template.quasis[0].value.cooked {
return Some(Cow::Borrowed(cooked.as_str()));
}
}

None
}
_ => None,
}
}

pub fn get_static_property_name<'a>(kind: &AstKind<'a>) -> Option<Cow<'a, str>> {
let (key, computed) = match kind {
AstKind::PropertyDefinition(definition) => (&definition.key, definition.computed),
AstKind::MethodDefinition(method_definition) => {
(&method_definition.key, method_definition.computed)
}
AstKind::ObjectProperty(property) => (&property.key, property.computed),
// AstKind::MemberExpression(member) => (member., member.is_computed())
_ => return None,
};

if key.is_identifier() && !computed {
return key.name();
}

get_property_key_name(key)
}
48 changes: 44 additions & 4 deletions crates/oxc_linter/src/rules/eslint/func_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::borrow::Cow;
use oxc_ast::{
ast::{
AssignmentTarget, AssignmentTargetProperty, BindingPatternKind, Expression, Function,
FunctionType, MethodDefinitionKind, PropertyKind,
FunctionType, MethodDefinitionKind, PropertyKey, PropertyKind,
},
AstKind,
};
Expand All @@ -14,7 +14,7 @@ use oxc_span::{Atom, GetSpan, Span};
use oxc_syntax::identifier::is_identifier_name;
use phf::phf_set;

use crate::{ast_util::get_static_property_name, context::LintContext, rule::Rule, AstNode};
use crate::{context::LintContext, rule::Rule, AstNode};

fn named_diagnostic(function_name: &str, span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("Unexpected named {function_name}."))
Expand Down Expand Up @@ -232,6 +232,46 @@ fn get_function_identifier<'a>(func: &'a Function<'a>) -> Option<&'a Span> {
func.id.as_ref().map(|id| &id.span)
}

fn get_property_key_name<'a>(key: &PropertyKey<'a>) -> Option<Cow<'a, str>> {
if matches!(key, PropertyKey::NullLiteral(_)) {
return Some("null".into());
}

match key {
PropertyKey::RegExpLiteral(regex) => {
Some(Cow::Owned(format!("/{}/{}", regex.regex.pattern, regex.regex.flags)))
}
PropertyKey::BigIntLiteral(bigint) => Some(Cow::Borrowed(bigint.raw.as_str())),
PropertyKey::TemplateLiteral(template) => {
if template.expressions.len() == 0 && template.quasis.len() == 1 {
if let Some(cooked) = &template.quasis[0].value.cooked {
return Some(Cow::Borrowed(cooked.as_str()));
}
}

None
}
_ => None,
}
}

fn get_static_property_name<'a>(parent_node: &AstNode<'a>) -> Option<Cow<'a, str>> {
let (key, computed) = match parent_node.kind() {
AstKind::PropertyDefinition(definition) => (&definition.key, definition.computed),
AstKind::MethodDefinition(method_definition) => {
(&method_definition.key, method_definition.computed)
}
AstKind::ObjectProperty(property) => (&property.key, property.computed),
_ => return None,
};

if key.is_identifier() && !computed {
return key.name();
}

get_property_key_name(key)
}

/// Gets the name and kind of the given function node.
/// @see <https://github.com/eslint/eslint/blob/48117b27e98639ffe7e78a230bfad9a93039fb7f/lib/rules/utils/ast-utils.js#L1762>
fn get_function_name_with_kind<'a>(func: &Function<'a>, parent_node: &AstNode<'a>) -> Cow<'a, str> {
Expand Down Expand Up @@ -295,14 +335,14 @@ fn get_function_name_with_kind<'a>(func: &Function<'a>, parent_node: &AstNode<'a
if let Some(name) = definition.key.name() {
tokens.push(name);
}
} else if let Some(static_name) = get_static_property_name(&parent_node.kind()) {
} else if let Some(static_name) = get_static_property_name(parent_node) {
tokens.push(static_name);
} else if let Some(name) = func.name() {
tokens.push(Cow::Borrowed(name.as_str()));
}
}
_ => {
if let Some(static_name) = get_static_property_name(&parent_node.kind()) {
if let Some(static_name) = get_static_property_name(parent_node) {
tokens.push(static_name);
} else if let Some(name) = func.name() {
tokens.push(Cow::Borrowed(name.as_str()));
Expand Down
61 changes: 21 additions & 40 deletions crates/oxc_linter/src/rules/eslint/new_cap.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{ast_util::get_static_property_name, context::LintContext, rule::Rule, AstNode};
use crate::{context::LintContext, rule::Rule, AstNode};
use oxc_ast::{
ast::{ChainElement, ComputedMemberExpression, Expression},
AstKind,
Expand Down Expand Up @@ -448,13 +448,11 @@ impl Rule for NewCap {
AstKind::NewExpression(expression) if self.new_is_cap => {
let callee = expression.callee.without_parentheses();

let Some(short_name) = &extract_name_from_new_expression(callee, &node.kind())
else {
let Some(short_name) = &extract_name_from_expression(callee) else {
return;
};

let Some(name) = &extract_name_deep_from_new_expression(callee, &node.kind())
else {
let Some(name) = &extract_name_deep_from_expression(callee) else {
return;
};

Expand All @@ -477,13 +475,11 @@ impl Rule for NewCap {
AstKind::CallExpression(expression) if self.cap_is_new => {
let callee = expression.callee.without_parentheses();

let Some(short_name) = &extract_name_from_new_expression(callee, &node.kind())
else {
let Some(short_name) = &extract_name_from_expression(callee) else {
return;
};

let Some(name) = &extract_name_deep_from_new_expression(callee, &node.kind())
else {
let Some(name) = &extract_name_deep_from_expression(callee) else {
return;
};

Expand Down Expand Up @@ -511,21 +507,16 @@ impl Rule for NewCap {
}
}

fn extract_name_deep_from_new_expression(
expression: &Expression,
kind: &AstKind,
) -> Option<CompactStr> {
fn extract_name_deep_from_expression(expression: &Expression) -> Option<CompactStr> {
if let Some(identifier) = expression.get_identifier_reference() {
return Some(identifier.name.clone().into());
}

match expression.without_parentheses() {
Expression::StaticMemberExpression(expression) => {
let prop_name = expression.property.name.clone().into_compact_str();
let obj_name = extract_name_deep_from_new_expression(
expression.object.without_parentheses(),
kind,
);
let obj_name =
extract_name_deep_from_expression(expression.object.without_parentheses());

if let Some(obj_name) = obj_name {
let new_name = format!("{obj_name}.{prop_name}");
Expand All @@ -536,10 +527,8 @@ fn extract_name_deep_from_new_expression(
}
Expression::ComputedMemberExpression(expression) => {
let prop_name = get_computed_member_name(expression)?;
let obj_name = extract_name_deep_from_new_expression(
expression.object.without_parentheses(),
kind,
);
let obj_name =
extract_name_deep_from_expression(expression.object.without_parentheses());

if let Some(obj_name) = obj_name {
let new_name = format!("{obj_name}.{prop_name}");
Expand All @@ -549,18 +538,14 @@ fn extract_name_deep_from_new_expression(
Some(prop_name)
}
Expression::ChainExpression(chain) => match &chain.expression {
ChainElement::CallExpression(call) => {
extract_name_deep_from_new_expression(&call.callee, kind)
}
ChainElement::CallExpression(call) => extract_name_deep_from_expression(&call.callee),
ChainElement::TSNonNullExpression(non_null) => {
extract_name_deep_from_new_expression(&non_null.expression, kind)
extract_name_deep_from_expression(&non_null.expression)
}
ChainElement::StaticMemberExpression(expression) => {
let prop_name = expression.property.name.clone().into_compact_str();
let obj_name = extract_name_deep_from_new_expression(
expression.object.without_parentheses(),
kind,
);
let obj_name =
extract_name_deep_from_expression(expression.object.without_parentheses());

if let Some(obj_name) = obj_name {
let new_name = format!("{obj_name}.{prop_name}");
Expand All @@ -571,10 +556,8 @@ fn extract_name_deep_from_new_expression(
}
ChainElement::ComputedMemberExpression(expression) => {
let prop_name = get_computed_member_name(expression)?;
let obj_name = extract_name_deep_from_new_expression(
expression.object.without_parentheses(),
kind,
);
let obj_name =
extract_name_deep_from_expression(expression.object.without_parentheses());

if let Some(obj_name) = obj_name {
let new_name = format!("{obj_name}.{prop_name}");
Expand All @@ -585,7 +568,7 @@ fn extract_name_deep_from_new_expression(
}
ChainElement::PrivateFieldExpression(_) => None,
},
_ => get_static_property_name(kind).map(std::convert::Into::into),
_ => None,
}
}

Expand All @@ -602,7 +585,7 @@ fn get_computed_member_name(computed_member: &ComputedMemberExpression) -> Optio
}
}

fn extract_name_from_new_expression(expression: &Expression, kind: &AstKind) -> Option<CompactStr> {
fn extract_name_from_expression(expression: &Expression) -> Option<CompactStr> {
if let Some(identifier) = expression.get_identifier_reference() {
return Some(identifier.name.clone().into());
}
Expand All @@ -613,11 +596,9 @@ fn extract_name_from_new_expression(expression: &Expression, kind: &AstKind) ->
}
Expression::ComputedMemberExpression(expression) => get_computed_member_name(expression),
Expression::ChainExpression(chain) => match &chain.expression {
ChainElement::CallExpression(call) => {
extract_name_from_new_expression(&call.callee, kind)
}
ChainElement::CallExpression(call) => extract_name_from_expression(&call.callee),
ChainElement::TSNonNullExpression(non_null) => {
extract_name_from_new_expression(&non_null.expression, kind)
extract_name_from_expression(&non_null.expression)
}
ChainElement::StaticMemberExpression(expression) => {
Some(expression.property.name.clone().into_compact_str())
Expand All @@ -627,7 +608,7 @@ fn extract_name_from_new_expression(expression: &Expression, kind: &AstKind) ->
}
ChainElement::PrivateFieldExpression(_) => None,
},
_ => get_static_property_name(kind).map(std::convert::Into::into),
_ => None,
}
}

Expand Down

0 comments on commit 35bfb86

Please sign in to comment.