oxc/tasks/prettier_conformance/src/spec.rs
Boshen be6b8b7ce6
[BREAKING CHANGE] Change Atom to Atom<'a> to make it safe (#2497)
Part of #2295

This PR splits the `Atom` type into `Atom<'a>` and `CompactString`.

All the AST node strings now use `Atom<'a>` instead of `Atom` to signify
it belongs to the arena.

It is now up to the user to select which form of the string to use.

This PR essentially removes the really unsafe code 


93742f89e9/crates/oxc_span/src/atom.rs (L98-L107)

which can lead to 

![image](https://github.com/oxc-project/oxc/assets/1430279/8c513c4f-19b0-4b63-b61c-e07c187c95b5)
2024-02-26 19:34:40 +08:00

138 lines
5.7 KiB
Rust

#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
use std::{fs, path::Path, str::FromStr};
use oxc_allocator::Allocator;
use oxc_ast::{
ast::{Argument, ArrayExpressionElement, CallExpression, Expression, ObjectPropertyKind},
VisitMut,
};
use oxc_parser::Parser;
use oxc_prettier::{ArrowParens, EndOfLine, PrettierOptions, QuoteProps, TrailingComma};
use oxc_span::{GetSpan, SourceType};
#[derive(Default)]
pub struct SpecParser {
pub calls: Vec<(PrettierOptions, Vec<(String, String)>)>,
source_text: String,
}
impl SpecParser {
pub fn parse(&mut self, spec: &Path) {
let spec_content = fs::read_to_string(spec).unwrap_or_default();
let allocator = Allocator::default();
let source_type = SourceType::from_path(spec).unwrap_or_default();
let mut ret = Parser::new(&allocator, &spec_content, source_type).parse();
self.source_text = spec_content.clone();
self.calls = vec![];
self.visit_program(&mut ret.program);
}
}
impl VisitMut<'_> for SpecParser {
fn visit_call_expression(&mut self, expr: &mut CallExpression<'_>) {
let Some(ident) = expr.callee.get_identifier_reference() else { return };
// The `runFormatTest` function used on prettier's test cases. We need to collect all `run_spec` calls
// And then parse the arguments to get the options and parsers
// Finally we use this information to generate the snapshot tests
if ident.name != "runFormatTest" {
return;
}
let mut parsers = vec![];
let mut snapshot_options: Vec<(String, String)> = vec![];
let mut options = PrettierOptions::default();
if let Some(argument) = expr.arguments.get(1) {
let Argument::Expression(argument_expr) = argument else {
return;
};
if let Expression::ArrayExpression(arr_expr) = argument_expr {
parsers = arr_expr
.elements
.iter()
.filter_map(|el| {
if let ArrayExpressionElement::Expression(Expression::StringLiteral(
literal,
)) = el
{
return Some(literal.value.to_string());
}
None
})
.collect::<Vec<String>>();
}
} else {
return;
}
if let Some(Argument::Expression(Expression::ObjectExpression(obj_expr))) =
expr.arguments.get(2)
{
obj_expr.properties.iter().for_each(|item| {
if let ObjectPropertyKind::ObjectProperty(obj_prop) = item {
if let Some(name) = obj_prop.key.static_name() {
match &obj_prop.value {
Expression::BooleanLiteral(literal) => {
if name == "semi" {
options.semi = literal.value;
} else if name == "bracketSpacing" {
options.bracket_spacing = literal.value;
} else if name == "singleQuote" {
options.single_quote = literal.value;
}
}
Expression::NumericLiteral(literal) => match name.as_str() {
"printWidth" => options.print_width = literal.value as usize,
"tabWidth" => options.tab_width = literal.value as usize,
_ => {}
},
Expression::StringLiteral(literal) => match name.as_str() {
"trailingComma" => {
options.trailing_comma =
TrailingComma::from_str(literal.value.as_str()).unwrap();
}
"endOfLine" => {
options.end_of_line =
EndOfLine::from_str(literal.value.as_str()).unwrap();
}
"quoteProps" => {
options.quote_props =
QuoteProps::from_str(literal.value.as_str()).unwrap();
}
"arrowParens" => {
options.arrow_parens =
ArrowParens::from_str(literal.value.as_str()).unwrap();
}
_ => {}
},
_ => {}
};
if name != "errors" {
snapshot_options.push((
name.to_string(),
obj_prop.value.span().source_text(&self.source_text).to_string(),
));
}
};
}
});
}
snapshot_options.push((
"parsers".to_string(),
format!(
"[{}]",
parsers.iter().map(|p| format!("\"{p}\"")).collect::<Vec<_>>().join(", ")
),
));
if !snapshot_options.iter().any(|item| item.0 == "printWidth") {
snapshot_options.push(("printWidth".to_string(), "80".into()));
}
snapshot_options.sort_by(|a, b| a.0.cmp(&b.0));
self.calls.push((options, snapshot_options));
}
}