feat(prettier): check parens for (let)[a] = 1 (#1585)

This commit is contained in:
Boshen 2023-11-29 21:57:59 +08:00 committed by GitHub
parent e6681a8683
commit 1bd1c5b51b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 91 additions and 42 deletions

View file

@ -1248,6 +1248,13 @@ impl<'a> ForStatementInit<'a> {
pub fn is_lexical_declaration(&self) -> bool {
matches!(self, Self::VariableDeclaration(decl) if decl.kind.is_lexical())
}
pub fn expression(&self) -> Option<&Expression<'a>> {
match self {
Self::Expression(e) => Some(e),
_ => None,
}
}
}
/// For-In Statement

View file

@ -28,7 +28,7 @@ use std::borrow::Cow;
use oxc_allocator::{Box, Vec};
use oxc_ast::{ast::*, AstKind};
use oxc_span::GetSpan;
use oxc_syntax::identifier::is_identify_name;
use oxc_syntax::identifier::is_identifier_name;
use crate::{
array,
@ -95,8 +95,8 @@ impl<'a> Format<'a> for Directive {
self.expression.value.as_str(),
p.options.single_quote,
)));
if p.options.semi {
parts.push(ss!(";"));
if let Some(semi) = p.semi() {
parts.push(semi);
}
parts.extend(hardline!());
Doc::Array(parts)
@ -135,8 +135,8 @@ impl<'a> Format<'a> for ExpressionStatement<'a> {
wrap!(p, self, ExpressionStatement, {
let mut parts = p.vec();
parts.push(self.expression.format(p));
if p.options.semi {
parts.push(ss!(";"));
if let Some(semi) = p.semi() {
parts.push(semi);
}
Doc::Array(parts)
})
@ -363,8 +363,8 @@ impl<'a> Format<'a> for DoWhileStatement<'a> {
parts.push(ss!("while ("));
parts.push(group!(p, indent!(p, softline!(), format!(p, self.test)), softline!()));
parts.push(ss!(")"));
if p.options.semi {
parts.push(ss!(";"));
if let Some(semi) = p.semi() {
parts.push(semi);
}
Doc::Array(parts)
@ -651,7 +651,9 @@ impl<'a> Format<'a> for VariableDeclaration<'a> {
}));
if !parent_for_loop.is_some_and(|span| span != self.span) {
parts.push(ss!(";"));
if let Some(semi) = p.semi() {
parts.push(semi);
}
}
Doc::Group(Group::new(parts, false))
@ -674,8 +676,8 @@ impl<'a> Format<'a> for TSTypeAliasDeclaration<'a> {
parts.push(ss!(" = "));
parts.push(format!(p, self.type_annotation));
if p.options.semi {
parts.push(ss!(";"));
if let Some(semi) = p.semi() {
parts.push(semi);
}
Doc::Array(parts)
@ -1047,8 +1049,8 @@ impl<'a> Format<'a> for ImportDeclaration<'a> {
}
parts.push(ss!(" from "));
parts.push(self.source.format(p));
if p.options.semi {
parts.push(ss!(";"));
if let Some(semi) = p.semi() {
parts.push(semi);
}
Doc::Array(parts)
}
@ -1223,7 +1225,7 @@ impl<'a> Format<'a> for Expression<'a> {
impl<'a> Format<'a> for IdentifierReference {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
p.str(self.name.as_str())
wrap!(p, self, IdentifierReference, { p.str(self.name.as_str()) })
}
}
@ -1551,7 +1553,7 @@ impl<'a> Format<'a> for PropertyKey<'a> {
let unquote = if need_quote {
false
} else {
is_identify_name(literal.value.as_str())
is_identifier_name(literal.value.as_str())
};
if !unquote || p.options.quote_props.is_preserve() {

View file

@ -1,12 +1,12 @@
use oxc_ast::ast::{Expression, PropertyKey};
use oxc_syntax::identifier::is_identify_name;
use oxc_syntax::identifier::is_identifier_name;
pub(super) fn is_property_key_has_quote(key: &PropertyKey<'_>) -> bool {
matches!(key, PropertyKey::Expression(Expression::StringLiteral(literal)) if is_string_prop_safe_to_unquote(literal.value.as_str()))
}
pub(super) fn is_string_prop_safe_to_unquote(value: &str) -> bool {
!is_identify_name(value) && !is_simple_number(value)
!is_identifier_name(value) && !is_simple_number(value)
}
// Matches “simple” numbers like `123` and `2.5` but not `1_000`, `1e+100` or `0b10`.

View file

@ -121,6 +121,10 @@ impl<'a> Prettier<'a> {
unsafe { std::mem::transmute(t) }
}
pub fn semi(&self) -> Option<Doc<'a>> {
self.options.semi.then(|| Doc::Str(";"))
}
pub fn should_print_es5_comma(&self) -> bool {
self.should_print_comma_impl(false)
}

View file

@ -11,7 +11,8 @@
use oxc_ast::{
ast::{
AssignmentTarget, AssignmentTargetPattern, ChainElement, ExportDefaultDeclarationKind,
Expression, ModuleDeclaration, ObjectExpression, SimpleAssignmentTarget,
Expression, ForStatementLeft, MemberExpression, ModuleDeclaration, ObjectExpression,
SimpleAssignmentTarget,
},
AstKind,
};
@ -30,34 +31,23 @@ impl<'a> Prettier<'a> {
}
fn need_parens(&mut self, kind: AstKind<'a>) -> bool {
if matches!(kind, AstKind::Program(_)) {
if matches!(kind, AstKind::Program(_)) || kind.is_statement() || kind.is_declaration() {
return false;
}
if kind.is_statement() || kind.is_declaration() {
return false;
}
let parent_kind = self.parent_kind();
if let AstKind::ObjectExpression(e) = kind {
if self.check_object_expression(e) {
return true;
}
}
if self.check_parent_kind(kind, parent_kind) {
return true;
}
if self.check_kind(kind, parent_kind) {
if matches!(kind, AstKind::ObjectExpression(e) if self.check_object_expression(e))
|| self.check_let_object(kind)
|| self.check_parent_kind(kind)
|| self.check_kind(kind)
{
return true;
}
false
}
fn check_kind(&self, kind: AstKind<'a>, parent_kind: AstKind<'a>) -> bool {
fn check_kind(&self, kind: AstKind<'a>) -> bool {
let parent_kind = self.parent_kind();
match kind {
AstKind::NumberLiteral(literal) => {
matches!(parent_kind, AstKind::MemberExpression(e) if e.object().span() == literal.span)
@ -190,8 +180,8 @@ impl<'a> Prettier<'a> {
}
}
fn check_parent_kind(&mut self, kind: AstKind<'a>, parent_kind: AstKind<'a>) -> bool {
match parent_kind {
fn check_parent_kind(&mut self, kind: AstKind<'a>) -> bool {
match self.parent_kind() {
AstKind::Class(class) => {
if let Some(h) = &class.super_class {
match kind {
@ -263,6 +253,54 @@ impl<'a> Prettier<'a> {
false
}
/// `(let)[a] = 1`
fn check_let_object(&self, kind: AstKind<'a>) -> bool {
let AstKind::IdentifierReference(ident) = kind else { return false };
if ident.name != "let" {
return false;
}
let AstKind::MemberExpression(MemberExpression::ComputedMemberExpression(expr)) =
self.parent_kind()
else {
return false;
};
if !matches!(&expr.object, Expression::Identifier(ident) if ident.name == "let") {
return false;
}
let Some(statement) = self.nodes.iter().rev().find(|node| {
matches!(
node,
AstKind::ExpressionStatement(_)
| AstKind::ForStatement(_)
| AstKind::ForInStatement(_)
)
}) else {
return false;
};
match statement {
AstKind::ExpressionStatement(stmt) => {
Self::starts_with_no_lookahead_token(&stmt.expression, ident.span)
}
AstKind::ForStatement(stmt) => stmt
.init
.as_ref()
.and_then(|init| init.expression())
.map_or(false, |e| Self::starts_with_no_lookahead_token(e, ident.span)),
AstKind::ForInStatement(stmt) => {
if let ForStatementLeft::AssignmentTarget(
AssignmentTarget::SimpleAssignmentTarget(
SimpleAssignmentTarget::MemberAssignmentTarget(e),
),
) = &stmt.left
{
return Self::starts_with_no_lookahead_token(e.object(), ident.span);
}
false
}
_ => false,
}
}
fn check_object_function_class(&self, span: Span) -> bool {
for ast_kind in self.nodes.iter().rev() {
if let AstKind::ExpressionStatement(e) = ast_kind {

View file

@ -109,7 +109,7 @@ pub fn is_identifier_part(c: char) -> bool {
is_id_continue_unicode(c) || c == ZWNJ || c == ZWJ
}
pub fn is_identify_name(name: &str) -> bool {
pub fn is_identifier_name(name: &str) -> bool {
let mut chars = name.chars();
chars.next().is_some_and(is_identifier_start_all) && chars.all(is_identifier_part)
}

View file

@ -1,4 +1,4 @@
Compatibility: 201/561 (35.83%)
Compatibility: 203/561 (36.19%)
# Failed
@ -157,7 +157,6 @@ Compatibility: 201/561 (35.83%)
* comments/jsdoc.js
* comments/jsx.js
* comments/last-arg.js
* comments/multi-comments-2.js
* comments/multi-comments-on-same-line-2.js
* comments/multi-comments-on-same-line.js
* comments/multi-comments.js
@ -277,7 +276,6 @@ Compatibility: 201/561 (35.83%)
* identifier/for-of/let.js
### identifier/parentheses
* identifier/parentheses/const.js
* identifier/parentheses/let.js
### if