Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1 @@
{"files":{"Cargo.toml":"04276bd9c80435b465ef21047532fccd4d98b9d4ac10cfbea6851171c8ebf199","benches/serde.rs":"14c09feab0336e7fc6492b35c60d6758b04ed557867244a2aff0a141e1952f2c","src/collections.rs":"653a45b5ec102fd58bcddb4442e1f93c32019f83bf908a1fcb976af98b49d21d","src/comments.rs":"2868051ab0f545564cf18577919e2bb200161785fa0f8a2f50d23dc68a2ba4a8","src/eq.rs":"a418e6043b6127d48bbf9d37bec03d771f2ff28b2c1c3257e74aa6a0ff2fac27","src/errors/diagnostic.rs":"ebedd825054a9e16330b0acc475743effc9e1caafcb5b50eec702a0b8d1379f3","src/errors/diagnostic_builder.rs":"0af3b5d81325703db3e3fe9bb2bf91b1d531d7000119b7cc405d967a81a594ba","src/errors/emitter.rs":"b7fb7f579db38c3377939fe02173f12c99ec55782a4fd964c697a7041214f07f","src/errors/lock.rs":"7c586f265fcebbe03c9e7e79cbb903ce854d0e97ab799487a14e2d602d72aff2","src/errors/mod.rs":"b90768616b132b028c626929cb0ec61763859042b4bdc703a556bba469aac5ed","src/errors/snippet.rs":"ab806111208f3b68dbd00322ebdf7d0d25d10b255186ec415e67843e89db3e31","src/errors/styled_buffer.rs":"f10de25391cc0eb8269d500e5851931be7deb0a1826c8098771059a899b20138","src/errors/tests.rs":"8b49cc549453e4f38d4fcae2a8f434239fbf89ff0cb495220bb56e8914bd2bef","src/input.rs":"7ddf8537120df1731e0cb6e14c3578abd991ef25cf12ac6d70249c27dcfdbfd7","src/iter.rs":"b13a6f180264e6b17dec3183f7c76e2401b214862661837ce53370d57a16c25a","src/lib.rs":"4e91b0ee8cd7cd5a8ebd8286c05dd864602ab8db65c814a34f38c8d6d00cf9ed","src/pass.rs":"4ec031d53186af0043854c011351ebc7a788fd807f5a1baea648049b5a3f500e","src/plugin/diagnostics.rs":"201a5027967f1f64dfcad298d51cd36b40fa5715f08853a7a4925cb441120ec4","src/plugin/metadata.rs":"a1d780b48a8ba3d085d7ff077bcd4fee84e4787dade06ca4288975dfc890c387","src/plugin/mod.rs":"a871819e79631771f8795ebf6269676b03adb314e3998c1e5ca9076632d6413b","src/plugin/serialized.rs":"3752970a82520e9016e4736e86890a2f410c704c7bd3dd74e298e9c2c6769b71","src/pos.rs":"94eaee8fc19c55c80c36151d1d80879fe691801ff8484e6b1ef3fa42b9a1ffa2","src/private/mod.rs":"b97e4dbbdf488d16796d7dd7350fd1c5891292ee4adf9ba34af9444346f68fb3","src/rustc_data_structures.rs":"a156eb041a92793c2452a5aa3d372a02d7d8b1c116b80169f1179b32a4408e3e","src/rustc_data_structures/stable_hasher.rs":"d3f164b74d355c04d069ddbe5757203a818f4c4cb818a959517d0bb8b36d30e8","src/serializer.rs":"ed3343a0c5560eedf231f7c7a2204d1333a139fddf61ff03d5ebbdcf6196646c","src/source_map.rs":"08edfc6c9b47d03380c1fa6194237213b52748cddd774dfb14171ea4e591c578","src/sync.rs":"16f8178d95970fd3255c098f864aa4d93cb6d4888eb84591e4ad73b70f422ca2","src/syntax_pos.rs":"81802095f35dbc1acc8075f73cd417d2c3c089f560b3c9798784e69b448fe8b1","src/syntax_pos/analyze_source_file.rs":"308199df826016d1a55a79148cc1a98e4dafb7460145f7a7ac6f0f822bfb5e5b","src/syntax_pos/hygiene.rs":"24a16ef2eda974b1d6f6cf69d600e9fcb8dcdf75b372d250c7329af4098cfd2a","src/util/iter.rs":"4a2310afe42287e4980fdb93a44ac2d17f89ea2f3f86a3f3e2d01b495a6c14c7","src/util/map.rs":"47692e3ca61a40b263aa2caedd79369615494f30355cf7276b5e0573ace4e774","src/util/mod.rs":"ae5f49e6c24a0c0a0ec3ccd9a5625a44a6cf0dfb98f4e814c4ba33bacfa5be62","src/util/move_map.rs":"2dc1db8d437f1dcf0e8c46c95a7e9f91f981700c27f01b5efd65c07942d380ec","src/util/take.rs":"686c4de62c9c251d708279cb0f83c110bbbd7a9e3dc95ef19f5c496311ea3e39","tests/ast_serde.rs":"c1e60e98b8f982f7c603c5dfd0295c4aea4f0a02e5531e9b32875d7d1732ff13","tests/attr_interop.rs":"975d73e20b58a34f19f295de86bb452ea20610122dce0dba03024dc3aae1929d","tests/concurrent.js":"03931ddf1633a856c1a995ebbd2f0791d669bf7130cedb447b003a7fd0110b12","tests/concurrent.rs":"cb1722104847d986fbc0c9f8cc031b32b68b472da621bba9323112932136dd06","tests/empty.rs":"4797b297c9c4ffb03ea3c2633be9f1a388218d2bab77e8b6644825fbe8ccb1f9","tests/issue_3159.rs":"6163196b89c925d5f515a072a46191c5644e03b3bd76aba122951b8f6dabb64e","tests/source_map.rs":"b15d4f77d29dbfc966ece2b0599f04ea7c3687232d0fbe7baa4056aa83fa7e8c","tests/spanned_attr.rs":"bff3bca5bf8bd69e4d4f0f334784959f598cc8dcc7f67c2cabb3c3bdd7680272"},"package":"39cb7fcd56655c8ae7dcf2344f0be6cbff4d9c7cb401fe3ec8e56e1de8dfe582"}

176
third-party/vendor/swc_common/Cargo.toml vendored Normal file
View file

@ -0,0 +1,176 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "swc_common"
version = "0.32.0"
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
description = "Common utilities for the swc project."
documentation = "https://rustdoc.swc.rs/swc_common/"
license = "Apache-2.0"
repository = "https://github.com/swc-project/swc.git"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"--cfg",
"docsrs",
]
[lib]
bench = false
[[bench]]
name = "serde"
harness = false
[dependencies.ahash]
version = "0.8.3"
optional = true
[dependencies.anyhow]
version = "1.0.71"
optional = true
[dependencies.arbitrary]
version = "1"
features = ["derive"]
optional = true
[dependencies.ast_node]
version = "0.9.5"
[dependencies.atty]
version = "0.2"
optional = true
[dependencies.better_scoped_tls]
version = "0.1.1"
[dependencies.bytecheck]
version = "0.6.10"
optional = true
[dependencies.cfg-if]
version = "1.0.0"
[dependencies.either]
version = "1.5"
[dependencies.from_variant]
version = "0.1.6"
[dependencies.new_debug_unreachable]
version = "1.0.4"
[dependencies.num-bigint]
version = "0.4"
[dependencies.once_cell]
version = "1.18.0"
[dependencies.parking_lot]
version = "0.12.1"
optional = true
[dependencies.rkyv]
version = "=0.7.42"
features = [
"strict",
"validation",
]
optional = true
[dependencies.rustc-hash]
version = "1.1.0"
[dependencies.serde]
version = "1.0.119"
features = ["derive"]
[dependencies.siphasher]
version = "0.3.9"
[dependencies.sourcemap]
version = "6"
optional = true
[dependencies.string_cache]
version = "0.8.7"
[dependencies.swc_atoms]
version = "0.5.9"
[dependencies.swc_eq_ignore_macros]
version = "0.1.2"
[dependencies.swc_visit]
version = "0.5.7"
[dependencies.termcolor]
version = "1.0"
optional = true
[dependencies.tracing]
version = "0.1.37"
[dependencies.unicode-width]
version = "0.1.4"
[dependencies.url]
version = "2.4.0"
[dev-dependencies.criterion]
version = "0.5"
[dev-dependencies.rayon]
version = "1"
[dev-dependencies.serde_json]
version = "1"
[features]
__plugin = []
__plugin_mode = []
__plugin_rt = []
__rkyv = []
ahash = ["dep:ahash"]
concurrent = ["parking_lot"]
debug = []
default = []
diagnostic-serde = []
plugin-base = [
"__plugin",
"anyhow",
"rkyv-impl",
"diagnostic-serde",
]
plugin-mode = [
"__plugin_mode",
"plugin-base",
]
plugin-rt = [
"__plugin_rt",
"plugin-base",
]
plugin_transform_schema_v1 = []
plugin_transform_schema_vtest = []
rkyv-impl = [
"__rkyv",
"rkyv",
"swc_atoms/rkyv-impl",
"bytecheck",
]
tty-emitter = [
"atty",
"termcolor",
]

View file

@ -0,0 +1,96 @@
#![cfg_attr(not(feature = "serde-impl"), allow(unused))]
use ast_node::ast_node;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use serde::{Deserialize, Serialize};
use swc_common::{Span, DUMMY_SP};
#[derive(Serialize, Deserialize)]
pub struct SerdeStr {
span: Span,
value: String,
}
#[ast_node("String")]
pub struct Str {
span: Span,
value: String,
}
#[derive(Serialize, Deserialize)]
pub struct SerdeNum {
span: Span,
value: u64,
}
#[ast_node("Number")]
pub struct Num {
span: Span,
value: u64,
}
#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Serde {
Number(SerdeNum),
String(SerdeStr),
}
#[ast_node]
pub enum AstNode {
#[tag("Number")]
Number(Num),
#[tag("String")]
String(Str),
}
fn bench_serde(c: &mut Criterion) {
let src = Serde::String(SerdeStr {
span: DUMMY_SP,
value: String::from("perf-diff"),
});
c.bench_function("serialization of serde", |b| {
b.iter(|| black_box(serde_json::to_string(&src).unwrap()));
});
c.bench_function("deserialization of serde", |b| {
let src = serde_json::to_string(&Serde::String(SerdeStr {
span: DUMMY_SP,
value: String::from("perf-diff"),
}))
.unwrap();
println!("{}", src);
b.iter(|| black_box(serde_json::to_string(&src).unwrap()));
});
}
fn bench_ast_node(c: &mut Criterion) {
#[cfg(feature = "serde-impl")]
c.bench_function("serialization of ast node", |b| {
let src = AstNode::String(Str {
span: DUMMY_SP,
value: String::from("perf-diff"),
});
b.iter(|| black_box(serde_json::to_string(&src).unwrap()));
});
#[cfg(feature = "serde-impl")]
c.bench_function("deserialization of ast node", |b| {
let src = serde_json::to_string(&AstNode::String(Str {
span: DUMMY_SP,
value: String::from("perf-diff"),
}))
.unwrap();
println!("{}", src);
b.iter(|| {
let t: AstNode = serde_json::from_str(&src).unwrap();
black_box(t);
});
});
}
criterion_group!(benches, bench_ast_node, bench_serde);
criterion_main!(benches);

View file

@ -0,0 +1,31 @@
#[cfg(feature = "ahash")]
pub use self::ahash::*;
#[cfg(not(feature = "ahash"))]
pub use self::rustchash::*;
#[cfg(not(feature = "ahash"))]
mod rustchash {
use std::{
collections::{HashMap, HashSet},
hash::BuildHasherDefault,
};
use rustc_hash::FxHasher;
pub type ARandomState = BuildHasherDefault<FxHasher>;
pub type AHashMap<K, V> = HashMap<K, V, ARandomState>;
pub type AHashSet<V> = HashSet<V, ARandomState>;
}
#[cfg(feature = "ahash")]
mod ahash {
use std::collections::{HashMap, HashSet};
pub type ARandomState = ahash::RandomState;
pub type AHashMap<K, V> = HashMap<K, V, ARandomState>;
pub type AHashSet<V> = HashSet<V, ARandomState>;
}

View file

@ -0,0 +1,604 @@
use std::{
cell::{Ref, RefCell, RefMut},
rc::Rc,
sync::Arc,
};
use rustc_hash::FxHashMap;
use swc_atoms::{atom, Atom};
use crate::{
pos::Spanned,
syntax_pos::{BytePos, Span, DUMMY_SP},
};
/// Stores comment.
///
/// ## Implementation notes
///
/// Methods uses `(&self)` instead of `(&mut self)` for some reasons. Firstly,
/// this is similar to the previous api. Secondly, typescript parser requires
/// backtracking, which requires [Clone]. To avoid cloning large vectors, we
/// must use [Rc<RefCell<Comments>>]. We have two option. We may implement it in
/// the parser or in the implementation. If we decide to go with first option,
/// we should pass [Comments] to parser, and as a result we need another method
/// to take comments back. If we decide to go with second way, we can just pass
/// [&Comments] to the parser. Thirdly, `(&self)` allows multi-threaded
/// use-cases such as swc itself.
///
/// We use [Option] instead of no-op Comments implementation to avoid allocation
/// unless required.
pub trait Comments {
fn add_leading(&self, pos: BytePos, cmt: Comment);
fn add_leading_comments(&self, pos: BytePos, comments: Vec<Comment>);
fn has_leading(&self, pos: BytePos) -> bool;
fn move_leading(&self, from: BytePos, to: BytePos);
fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>>;
fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>>;
fn add_trailing(&self, pos: BytePos, cmt: Comment);
fn add_trailing_comments(&self, pos: BytePos, comments: Vec<Comment>);
fn has_trailing(&self, pos: BytePos) -> bool;
fn move_trailing(&self, from: BytePos, to: BytePos);
fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>>;
fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>>;
fn add_pure_comment(&self, pos: BytePos);
fn with_leading<F, Ret>(&self, pos: BytePos, f: F) -> Ret
where
Self: Sized,
F: FnOnce(&[Comment]) -> Ret,
{
let cmts = self.take_leading(pos);
let ret = if let Some(cmts) = &cmts {
f(cmts)
} else {
f(&[])
};
if let Some(cmts) = cmts {
self.add_leading_comments(pos, cmts);
}
ret
}
fn with_trailing<F, Ret>(&self, pos: BytePos, f: F) -> Ret
where
Self: Sized,
F: FnOnce(&[Comment]) -> Ret,
{
let cmts = self.take_trailing(pos);
let ret = if let Some(cmts) = &cmts {
f(cmts)
} else {
f(&[])
};
if let Some(cmts) = cmts {
self.add_trailing_comments(pos, cmts);
}
ret
}
}
macro_rules! delegate {
() => {
fn add_leading(&self, pos: BytePos, cmt: Comment) {
(**self).add_leading(pos, cmt)
}
fn add_leading_comments(&self, pos: BytePos, comments: Vec<Comment>) {
(**self).add_leading_comments(pos, comments)
}
fn has_leading(&self, pos: BytePos) -> bool {
(**self).has_leading(pos)
}
fn move_leading(&self, from: BytePos, to: BytePos) {
(**self).move_leading(from, to)
}
fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
(**self).take_leading(pos)
}
fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
(**self).get_leading(pos)
}
fn add_trailing(&self, pos: BytePos, cmt: Comment) {
(**self).add_trailing(pos, cmt)
}
fn add_trailing_comments(&self, pos: BytePos, comments: Vec<Comment>) {
(**self).add_trailing_comments(pos, comments)
}
fn has_trailing(&self, pos: BytePos) -> bool {
(**self).has_trailing(pos)
}
fn move_trailing(&self, from: BytePos, to: BytePos) {
(**self).move_trailing(from, to)
}
fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
(**self).take_trailing(pos)
}
fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
(**self).get_trailing(pos)
}
fn add_pure_comment(&self, pos: BytePos) {
(**self).add_pure_comment(pos)
}
};
}
impl<T> Comments for &'_ T
where
T: ?Sized + Comments,
{
delegate!();
}
impl<T> Comments for Arc<T>
where
T: ?Sized + Comments,
{
delegate!();
}
impl<T> Comments for Rc<T>
where
T: ?Sized + Comments,
{
delegate!();
}
impl<T> Comments for Box<T>
where
T: ?Sized + Comments,
{
delegate!();
}
/// Implementation of [Comments] which does not store any comments.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
pub struct NoopComments;
impl Comments for NoopComments {
#[cfg_attr(not(debug_assertions), inline(always))]
fn add_leading(&self, _: BytePos, _: Comment) {}
#[cfg_attr(not(debug_assertions), inline(always))]
fn add_leading_comments(&self, _: BytePos, _: Vec<Comment>) {}
#[cfg_attr(not(debug_assertions), inline(always))]
fn has_leading(&self, _: BytePos) -> bool {
false
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn move_leading(&self, _: BytePos, _: BytePos) {}
#[cfg_attr(not(debug_assertions), inline(always))]
fn take_leading(&self, _: BytePos) -> Option<Vec<Comment>> {
None
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn get_leading(&self, _: BytePos) -> Option<Vec<Comment>> {
None
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn add_trailing(&self, _: BytePos, _: Comment) {}
#[cfg_attr(not(debug_assertions), inline(always))]
fn add_trailing_comments(&self, _: BytePos, _: Vec<Comment>) {}
#[cfg_attr(not(debug_assertions), inline(always))]
fn has_trailing(&self, _: BytePos) -> bool {
false
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn move_trailing(&self, _: BytePos, _: BytePos) {}
#[cfg_attr(not(debug_assertions), inline(always))]
fn take_trailing(&self, _: BytePos) -> Option<Vec<Comment>> {
None
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn get_trailing(&self, _: BytePos) -> Option<Vec<Comment>> {
None
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn add_pure_comment(&self, _: BytePos) {}
}
/// This implementation behaves like [NoopComments] if it's [None].
impl<C> Comments for Option<C>
where
C: Comments,
{
fn add_leading(&self, pos: BytePos, cmt: Comment) {
if let Some(c) = self {
c.add_leading(pos, cmt)
}
}
fn add_leading_comments(&self, pos: BytePos, comments: Vec<Comment>) {
if let Some(c) = self {
c.add_leading_comments(pos, comments)
}
}
fn has_leading(&self, pos: BytePos) -> bool {
if let Some(c) = self {
c.has_leading(pos)
} else {
false
}
}
fn move_leading(&self, from: BytePos, to: BytePos) {
if let Some(c) = self {
c.move_leading(from, to)
}
}
fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
if let Some(c) = self {
c.take_leading(pos)
} else {
None
}
}
fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
if let Some(c) = self {
c.get_leading(pos)
} else {
None
}
}
fn add_trailing(&self, pos: BytePos, cmt: Comment) {
if let Some(c) = self {
c.add_trailing(pos, cmt)
}
}
fn add_trailing_comments(&self, pos: BytePos, comments: Vec<Comment>) {
if let Some(c) = self {
c.add_trailing_comments(pos, comments)
}
}
fn has_trailing(&self, pos: BytePos) -> bool {
if let Some(c) = self {
c.has_trailing(pos)
} else {
false
}
}
fn move_trailing(&self, from: BytePos, to: BytePos) {
if let Some(c) = self {
c.move_trailing(from, to)
}
}
fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
if let Some(c) = self {
c.take_trailing(pos)
} else {
None
}
}
fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
if let Some(c) = self {
c.get_trailing(pos)
} else {
None
}
}
fn add_pure_comment(&self, pos: BytePos) {
if let Some(c) = self {
c.add_pure_comment(pos)
}
}
fn with_leading<F, Ret>(&self, pos: BytePos, f: F) -> Ret
where
Self: Sized,
F: FnOnce(&[Comment]) -> Ret,
{
if let Some(c) = self {
c.with_leading(pos, f)
} else {
f(&[])
}
}
fn with_trailing<F, Ret>(&self, pos: BytePos, f: F) -> Ret
where
Self: Sized,
F: FnOnce(&[Comment]) -> Ret,
{
if let Some(c) = self {
c.with_trailing(pos, f)
} else {
f(&[])
}
}
}
pub type SingleThreadedCommentsMapInner = FxHashMap<BytePos, Vec<Comment>>;
pub type SingleThreadedCommentsMap = Rc<RefCell<SingleThreadedCommentsMapInner>>;
/// Single-threaded storage for comments.
#[derive(Debug, Clone, Default)]
pub struct SingleThreadedComments {
leading: SingleThreadedCommentsMap,
trailing: SingleThreadedCommentsMap,
}
impl Comments for SingleThreadedComments {
fn add_leading(&self, pos: BytePos, cmt: Comment) {
self.leading.borrow_mut().entry(pos).or_default().push(cmt);
}
fn add_leading_comments(&self, pos: BytePos, comments: Vec<Comment>) {
self.leading
.borrow_mut()
.entry(pos)
.or_default()
.extend(comments);
}
fn has_leading(&self, pos: BytePos) -> bool {
if let Some(v) = self.leading.borrow().get(&pos) {
!v.is_empty()
} else {
false
}
}
fn move_leading(&self, from: BytePos, to: BytePos) {
let cmt = self.take_leading(from);
if let Some(mut cmt) = cmt {
if from < to && self.has_leading(to) {
cmt.extend(self.take_leading(to).unwrap());
}
self.add_leading_comments(to, cmt);
}
}
fn take_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
self.leading.borrow_mut().remove(&pos)
}
fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> {
self.leading.borrow().get(&pos).map(|c| c.to_owned())
}
fn add_trailing(&self, pos: BytePos, cmt: Comment) {
self.trailing.borrow_mut().entry(pos).or_default().push(cmt);
}
fn add_trailing_comments(&self, pos: BytePos, comments: Vec<Comment>) {
self.trailing
.borrow_mut()
.entry(pos)
.or_default()
.extend(comments);
}
fn has_trailing(&self, pos: BytePos) -> bool {
if let Some(v) = self.trailing.borrow().get(&pos) {
!v.is_empty()
} else {
false
}
}
fn move_trailing(&self, from: BytePos, to: BytePos) {
let cmt = self.take_trailing(from);
if let Some(mut cmt) = cmt {
if from < to && self.has_trailing(to) {
cmt.extend(self.take_trailing(to).unwrap());
}
self.add_trailing_comments(to, cmt);
}
}
fn take_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
self.trailing.borrow_mut().remove(&pos)
}
fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> {
self.trailing.borrow().get(&pos).map(|c| c.to_owned())
}
fn add_pure_comment(&self, pos: BytePos) {
let mut leading_map = self.leading.borrow_mut();
let leading = leading_map.entry(pos).or_default();
let pure_comment = Comment {
kind: CommentKind::Block,
span: DUMMY_SP,
text: atom!("#__PURE__"),
};
if !leading.iter().any(|c| c.text == pure_comment.text) {
leading.push(pure_comment);
}
}
fn with_leading<F, Ret>(&self, pos: BytePos, f: F) -> Ret
where
Self: Sized,
F: FnOnce(&[Comment]) -> Ret,
{
let b = self.leading.borrow();
let cmts = b.get(&pos);
if let Some(cmts) = &cmts {
f(cmts)
} else {
f(&[])
}
}
fn with_trailing<F, Ret>(&self, pos: BytePos, f: F) -> Ret
where
Self: Sized,
F: FnOnce(&[Comment]) -> Ret,
{
let b = self.trailing.borrow();
let cmts = b.get(&pos);
if let Some(cmts) = &cmts {
f(cmts)
} else {
f(&[])
}
}
}
impl SingleThreadedComments {
/// Creates a new `SingleThreadedComments` from the provided leading and
/// trailing.
pub fn from_leading_and_trailing(
leading: SingleThreadedCommentsMap,
trailing: SingleThreadedCommentsMap,
) -> Self {
SingleThreadedComments { leading, trailing }
}
/// Takes all the comments as (leading, trailing).
pub fn take_all(self) -> (SingleThreadedCommentsMap, SingleThreadedCommentsMap) {
(self.leading, self.trailing)
}
/// Borrows all the comments as (leading, trailing).
pub fn borrow_all(
&self,
) -> (
Ref<SingleThreadedCommentsMapInner>,
Ref<SingleThreadedCommentsMapInner>,
) {
(self.leading.borrow(), self.trailing.borrow())
}
/// Borrows all the comments as (leading, trailing).
pub fn borrow_all_mut(
&self,
) -> (
RefMut<SingleThreadedCommentsMapInner>,
RefMut<SingleThreadedCommentsMapInner>,
) {
(self.leading.borrow_mut(), self.trailing.borrow_mut())
}
pub fn with_leading<F, Ret>(&self, pos: BytePos, op: F) -> Ret
where
F: FnOnce(&[Comment]) -> Ret,
{
if let Some(comments) = self.leading.borrow().get(&pos) {
op(comments)
} else {
op(&[])
}
}
pub fn with_trailing<F, Ret>(&self, pos: BytePos, op: F) -> Ret
where
F: FnOnce(&[Comment]) -> Ret,
{
if let Some(comments) = self.trailing.borrow().get(&pos) {
op(comments)
} else {
op(&[])
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
pub struct Comment {
pub kind: CommentKind,
pub span: Span,
/// [`Atom::new_bad`][] is perfectly fine for this value.
pub text: Atom,
}
impl Spanned for Comment {
fn span(&self) -> Span {
self.span
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
pub enum CommentKind {
Line,
Block,
}
#[deprecated(
since = "0.13.5",
note = "helper methods are merged into Comments itself"
)]
pub trait CommentsExt: Comments {
fn with_leading<F, Ret>(&self, pos: BytePos, op: F) -> Ret
where
F: FnOnce(&[Comment]) -> Ret,
{
if let Some(comments) = self.get_leading(pos) {
op(&comments)
} else {
op(&[])
}
}
fn with_trailing<F, Ret>(&self, pos: BytePos, op: F) -> Ret
where
F: FnOnce(&[Comment]) -> Ret,
{
if let Some(comments) = self.get_trailing(pos) {
op(&comments)
} else {
op(&[])
}
}
}
#[allow(deprecated)]
impl<C> CommentsExt for C where C: Comments {}

302
third-party/vendor/swc_common/src/eq.rs vendored Normal file
View file

@ -0,0 +1,302 @@
use std::{cell::RefCell, cmp::PartialEq, rc::Rc, sync::Arc};
use num_bigint::BigInt;
use string_cache::Atom;
use crate::{BytePos, Span, SyntaxContext};
/// Derive with `#[derive(EqIgnoreSpan)]`.
pub trait EqIgnoreSpan {
fn eq_ignore_span(&self, other: &Self) -> bool;
}
impl EqIgnoreSpan for Span {
/// Always returns true
#[inline]
fn eq_ignore_span(&self, _: &Self) -> bool {
true
}
}
impl EqIgnoreSpan for swc_atoms::Atom {
#[inline]
fn eq_ignore_span(&self, r: &Self) -> bool {
self == r
}
}
impl<T> EqIgnoreSpan for [T]
where
T: EqIgnoreSpan,
{
fn eq_ignore_span(&self, other: &Self) -> bool {
self.len() == other.len()
&& self
.iter()
.zip(other.iter())
.all(|(a, b)| a.eq_ignore_span(b))
}
}
impl<T> EqIgnoreSpan for Option<T>
where
T: EqIgnoreSpan,
{
fn eq_ignore_span(&self, other: &Self) -> bool {
match (self, other) {
(Some(l), Some(r)) => l.eq_ignore_span(r),
(None, None) => true,
_ => false,
}
}
}
impl<T> EqIgnoreSpan for Vec<T>
where
T: EqIgnoreSpan,
{
fn eq_ignore_span(&self, other: &Self) -> bool {
self.len() == other.len()
&& self
.iter()
.zip(other.iter())
.all(|(a, b)| a.eq_ignore_span(b))
}
}
/// Derive with `#[derive(TypeEq)]`.
pub trait TypeEq {
/// **Note**: This method should return `true` for non-type values.
fn type_eq(&self, other: &Self) -> bool;
}
impl TypeEq for Span {
/// Always returns true
#[inline]
fn type_eq(&self, _: &Self) -> bool {
true
}
}
impl<T> TypeEq for Option<T>
where
T: TypeEq,
{
fn type_eq(&self, other: &Self) -> bool {
match (self, other) {
(Some(l), Some(r)) => l.type_eq(r),
(None, None) => true,
_ => false,
}
}
}
impl<T> TypeEq for Vec<T>
where
T: TypeEq,
{
fn type_eq(&self, other: &Self) -> bool {
self.len() == other.len() && self.iter().zip(other.iter()).all(|(a, b)| a.type_eq(b))
}
}
/// Implement traits using PartialEq
macro_rules! eq {
($T:ty) => {
impl EqIgnoreSpan for $T {
#[inline]
fn eq_ignore_span(&self, other: &Self) -> bool {
self == other
}
}
impl TypeEq for $T {
#[inline]
fn type_eq(&self, other: &Self) -> bool {
self == other
}
}
};
(
$(
$T:ty
),*
) => {
$(
eq!($T);
)*
};
}
eq!(SyntaxContext, BytePos);
eq!(bool);
eq!(usize, u8, u16, u32, u64, u128);
eq!(isize, i8, i16, i32, i64, i128);
eq!(f32, f64);
eq!(char, str, String);
impl<S: PartialEq> EqIgnoreSpan for Atom<S> {
#[inline]
fn eq_ignore_span(&self, other: &Self) -> bool {
self == other
}
}
impl<S: PartialEq> TypeEq for Atom<S> {
#[inline]
fn type_eq(&self, other: &Self) -> bool {
self == other
}
}
macro_rules! deref {
($T:ident) => {
impl<N> EqIgnoreSpan for $T<N>
where
N: EqIgnoreSpan,
{
#[inline]
fn eq_ignore_span(&self, other: &Self) -> bool {
(**self).eq_ignore_span(&**other)
}
}
impl<N> TypeEq for $T<N>
where
N: TypeEq,
{
#[inline]
fn type_eq(&self, other: &Self) -> bool {
(**self).type_eq(&**other)
}
}
};
(
$(
$T:ident
),*
) => {
$(
deref!($T);
)*
};
}
deref!(Box, Rc, Arc);
impl<'a, N> EqIgnoreSpan for &'a N
where
N: EqIgnoreSpan,
{
#[inline]
fn eq_ignore_span(&self, other: &Self) -> bool {
(**self).eq_ignore_span(&**other)
}
}
impl<'a, N> TypeEq for &'a N
where
N: TypeEq,
{
#[inline]
fn type_eq(&self, other: &Self) -> bool {
(**self).type_eq(&**other)
}
}
impl<N> EqIgnoreSpan for RefCell<N>
where
N: EqIgnoreSpan,
{
fn eq_ignore_span(&self, other: &Self) -> bool {
self.borrow().eq_ignore_span(&*other.borrow())
}
}
impl<N> TypeEq for RefCell<N>
where
N: TypeEq,
{
fn type_eq(&self, other: &Self) -> bool {
self.borrow().type_eq(&*other.borrow())
}
}
impl EqIgnoreSpan for BigInt {
fn eq_ignore_span(&self, other: &Self) -> bool {
self == other
}
}
impl TypeEq for BigInt {
fn type_eq(&self, other: &Self) -> bool {
self == other
}
}
macro_rules! tuple {
(
$num:tt: $F:ident
) => {};
(
$first:tt: $F:ident,
$(
$num:tt: $N:ident
),*
) =>{
tuple!($($num: $N),*);
impl<$F: EqIgnoreSpan, $($N: EqIgnoreSpan),*> EqIgnoreSpan for ($F, $($N,)*) {
fn eq_ignore_span(&self,rhs: &Self) -> bool {
self.$first.eq_ignore_span(&rhs.$first) &&
$(
self.$num.eq_ignore_span(&rhs.$num)
)
&& *
}
}
impl<$F: TypeEq, $($N: TypeEq),*> TypeEq for ($F, $($N,)*) {
fn type_eq(&self,rhs: &Self) -> bool {
self.$first.type_eq(&rhs.$first) &&
$(
self.$num.type_eq(&rhs.$num)
)
&& *
}
}
};
}
tuple!(
25: Z,
24: Y,
23: X,
22: W,
21: V,
20: U,
19: T,
18: S,
17: R,
16: Q,
15: P,
14: O,
13: N,
12: M,
11: L,
10: K,
9: J,
8: I,
7: H,
6: G,
5: F,
4: E,
3: D,
2: C,
1: B,
0: A
);

View file

@ -0,0 +1,510 @@
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::fmt;
use super::{snippet::Style, Applicability, CodeSuggestion, Level, Substitution, SubstitutionPart};
use crate::syntax_pos::{MultiSpan, Span};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
pub struct Message(pub String, pub Style);
#[must_use]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
pub struct Diagnostic {
pub level: Level,
pub message: Vec<Message>,
pub code: Option<DiagnosticId>,
pub span: MultiSpan,
pub children: Vec<SubDiagnostic>,
pub suggestions: Vec<CodeSuggestion>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(u32)))]
pub enum DiagnosticId {
Error(String),
Lint(String),
}
/// For example a note attached to an error.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
pub struct SubDiagnostic {
pub level: Level,
pub message: Vec<Message>,
pub span: MultiSpan,
pub render_span: Option<MultiSpan>,
}
#[derive(PartialEq, Eq, Default)]
pub struct DiagnosticStyledString(pub Vec<StringPart>);
impl DiagnosticStyledString {
pub fn new() -> DiagnosticStyledString {
Default::default()
}
pub fn push_normal<S: Into<String>>(&mut self, t: S) {
self.0.push(StringPart::Normal(t.into()));
}
pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
self.0.push(StringPart::Highlighted(t.into()));
}
pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
DiagnosticStyledString(vec![StringPart::Normal(t.into())])
}
pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
}
pub fn content(&self) -> String {
self.0.iter().map(|x| x.content()).collect::<String>()
}
}
#[derive(PartialEq, Eq)]
pub enum StringPart {
Normal(String),
Highlighted(String),
}
impl StringPart {
pub fn content(&self) -> &str {
match self {
&StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s,
}
}
}
impl Diagnostic {
pub fn new(level: Level, message: &str) -> Self {
Diagnostic::new_with_code(level, None, message)
}
pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self {
Diagnostic {
level,
message: vec![Message(message.to_owned(), Style::NoStyle)],
code,
span: MultiSpan::new(),
children: vec![],
suggestions: vec![],
}
}
pub fn is_error(&self) -> bool {
match self.level {
Level::Bug | Level::Fatal | Level::PhaseFatal | Level::Error | Level::FailureNote => {
true
}
Level::Warning | Level::Note | Level::Help | Level::Cancelled => false,
}
}
/// Cancel the diagnostic (a structured diagnostic must either be emitted or
/// canceled or it will panic when dropped).
pub fn cancel(&mut self) {
self.level = Level::Cancelled;
}
pub fn cancelled(&self) -> bool {
self.level == Level::Cancelled
}
/// Add a span/label to be included in the resulting snippet.
/// This is pushed onto the `MultiSpan` that was created when the
/// diagnostic was first built. If you don't call this function at
/// all, and you just supplied a `Span` to create the diagnostic,
/// then the snippet will just include that `Span`, which is
/// called the primary span.
pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
self.span.push_span_label(span, label.into());
self
}
pub fn replace_span_with(&mut self, after: Span) -> &mut Self {
let before = self.span.clone();
self.set_span(after);
for span_label in before.span_labels() {
if let Some(label) = span_label.label {
self.span_label(after, label);
}
}
self
}
pub fn note_expected_found(
&mut self,
label: &dyn fmt::Display,
expected: DiagnosticStyledString,
found: DiagnosticStyledString,
) -> &mut Self {
self.note_expected_found_extra(label, expected, found, &"", &"")
}
pub fn note_expected_found_extra(
&mut self,
label: &dyn fmt::Display,
expected: DiagnosticStyledString,
found: DiagnosticStyledString,
expected_extra: &dyn fmt::Display,
found_extra: &dyn fmt::Display,
) -> &mut Self {
let mut msg: Vec<_> = vec![Message(format!("expected {} `", label), Style::NoStyle)];
msg.extend(expected.0.iter().map(|x| match *x {
StringPart::Normal(ref s) => Message(s.to_owned(), Style::NoStyle),
StringPart::Highlighted(ref s) => Message(s.to_owned(), Style::Highlight),
}));
msg.push(Message(format!("`{}\n", expected_extra), Style::NoStyle));
msg.push(Message(format!(" found {} `", label), Style::NoStyle));
msg.extend(found.0.iter().map(|x| match *x {
StringPart::Normal(ref s) => Message(s.to_owned(), Style::NoStyle),
StringPart::Highlighted(ref s) => Message(s.to_owned(), Style::Highlight),
}));
msg.push(Message(format!("`{}", found_extra), Style::NoStyle));
// For now, just attach these as notes
self.highlighted_note(msg);
self
}
pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
self.highlighted_note(vec![
Message(format!("`{}` from trait: `", name), Style::NoStyle),
Message(signature, Style::Highlight),
Message("`".to_string(), Style::NoStyle),
]);
self
}
pub fn note(&mut self, msg: &str) -> &mut Self {
self.sub(Level::Note, msg, MultiSpan::new(), None);
self
}
pub fn highlighted_note(&mut self, msg: Vec<Message>) -> &mut Self {
self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
self
}
pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
self.sub(Level::Note, msg, sp.into(), None);
self
}
pub fn warn(&mut self, msg: &str) -> &mut Self {
self.sub(Level::Warning, msg, MultiSpan::new(), None);
self
}
pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
self.sub(Level::Warning, msg, sp.into(), None);
self
}
pub fn help(&mut self, msg: &str) -> &mut Self {
self.sub(Level::Help, msg, MultiSpan::new(), None);
self
}
pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
self.sub(Level::Help, msg, sp.into(), None);
self
}
/// Prints out a message with a suggested edit of the code. If the
/// suggestion is presented inline it will only show the text message
/// and not the text.
///
/// See `CodeSuggestion` for more information.
#[deprecated(note = "Use `span_suggestion_short_with_applicability`")]
pub fn span_suggestion_short(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
self.suggestions.push(CodeSuggestion {
substitutions: vec![Substitution {
parts: vec![SubstitutionPart {
snippet: suggestion,
span: sp,
}],
}],
msg: msg.to_owned(),
show_code_when_inline: false,
applicability: Applicability::Unspecified,
});
self
}
/// Prints out a message with a suggested edit of the code.
///
/// In case of short messages and a simple suggestion,
/// rustc displays it as a label like
///
/// "try adding parentheses: `(tup.0).1`"
///
/// The message
///
/// * should not end in any punctuation (a `:` is added automatically)
/// * should not be a question
/// * should not contain any parts like "the following", "as shown"
/// * may look like "to do xyz, use" or "to do xyz, use abc"
/// * may contain a name of a function, variable or type, but not whole
/// expressions
///
/// See `CodeSuggestion` for more information.
#[deprecated(note = "Use `span_suggestion_with_applicability`")]
pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
self.suggestions.push(CodeSuggestion {
substitutions: vec![Substitution {
parts: vec![SubstitutionPart {
snippet: suggestion,
span: sp,
}],
}],
msg: msg.to_owned(),
show_code_when_inline: true,
applicability: Applicability::Unspecified,
});
self
}
pub fn multipart_suggestion_with_applicability(
&mut self,
msg: &str,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
) -> &mut Self {
self.suggestions.push(CodeSuggestion {
substitutions: vec![Substitution {
parts: suggestion
.into_iter()
.map(|(span, snippet)| SubstitutionPart { snippet, span })
.collect(),
}],
msg: msg.to_owned(),
show_code_when_inline: true,
applicability,
});
self
}
#[deprecated(note = "Use `multipart_suggestion_with_applicability`")]
pub fn multipart_suggestion(
&mut self,
msg: &str,
suggestion: Vec<(Span, String)>,
) -> &mut Self {
self.multipart_suggestion_with_applicability(msg, suggestion, Applicability::Unspecified)
}
/// Prints out a message with multiple suggested edits of the code.
#[deprecated(note = "Use `span_suggestions_with_applicability`")]
pub fn span_suggestions(&mut self, sp: Span, msg: &str, suggestions: Vec<String>) -> &mut Self {
self.suggestions.push(CodeSuggestion {
substitutions: suggestions
.into_iter()
.map(|snippet| Substitution {
parts: vec![SubstitutionPart { snippet, span: sp }],
})
.collect(),
msg: msg.to_owned(),
show_code_when_inline: true,
applicability: Applicability::Unspecified,
});
self
}
/// This is a suggestion that may contain mistakes or fillers and should
/// be read and understood by a human.
pub fn span_suggestion_with_applicability(
&mut self,
sp: Span,
msg: &str,
suggestion: String,
applicability: Applicability,
) -> &mut Self {
self.suggestions.push(CodeSuggestion {
substitutions: vec![Substitution {
parts: vec![SubstitutionPart {
snippet: suggestion,
span: sp,
}],
}],
msg: msg.to_owned(),
show_code_when_inline: true,
applicability,
});
self
}
pub fn span_suggestions_with_applicability(
&mut self,
sp: Span,
msg: &str,
suggestions: impl Iterator<Item = String>,
applicability: Applicability,
) -> &mut Self {
self.suggestions.push(CodeSuggestion {
substitutions: suggestions
.map(|snippet| Substitution {
parts: vec![SubstitutionPart { snippet, span: sp }],
})
.collect(),
msg: msg.to_owned(),
show_code_when_inline: true,
applicability,
});
self
}
pub fn span_suggestion_short_with_applicability(
&mut self,
sp: Span,
msg: &str,
suggestion: String,
applicability: Applicability,
) -> &mut Self {
self.suggestions.push(CodeSuggestion {
substitutions: vec![Substitution {
parts: vec![SubstitutionPart {
snippet: suggestion,
span: sp,
}],
}],
msg: msg.to_owned(),
show_code_when_inline: false,
applicability,
});
self
}
pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
self.span = sp.into();
self
}
pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
self.code = Some(s);
self
}
pub fn get_code(&self) -> Option<DiagnosticId> {
self.code.clone()
}
pub fn message(&self) -> String {
self.message
.iter()
.map(|i| i.0.as_str())
.collect::<String>()
}
pub fn styled_message(&self) -> &Vec<Message> {
&self.message
}
/// Used by a lint. Copies over all details *but* the "main
/// message".
pub fn copy_details_not_message(&mut self, from: &Diagnostic) {
self.span = from.span.clone();
self.code = from.code.clone();
self.children.extend(from.children.iter().cloned())
}
/// Convenience function for internal use, clients should use one of the
/// public methods above.
pub fn sub(
&mut self,
level: Level,
message: &str,
span: MultiSpan,
render_span: Option<MultiSpan>,
) {
let sub = SubDiagnostic {
level,
message: vec![Message(message.to_owned(), Style::NoStyle)],
span,
render_span,
};
self.children.push(sub);
}
/// Convenience function for internal use, clients should use one of the
/// public methods above.
fn sub_with_highlights(
&mut self,
level: Level,
message: Vec<Message>,
span: MultiSpan,
render_span: Option<MultiSpan>,
) {
let sub = SubDiagnostic {
level,
message,
span,
render_span,
};
self.children.push(sub);
}
}
impl SubDiagnostic {
pub fn message(&self) -> String {
self.message
.iter()
.map(|i| i.0.as_str())
.collect::<String>()
}
pub fn styled_message(&self) -> &Vec<Message> {
&self.message
}
}

View file

@ -0,0 +1,348 @@
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::{
fmt::{self, Debug},
ops::{Deref, DerefMut},
thread::panicking,
};
use tracing::debug;
use super::{Applicability, Diagnostic, DiagnosticId, DiagnosticStyledString, Handler, Level};
use crate::syntax_pos::{MultiSpan, Span};
/// Used for emitting structured error messages and other diagnostic
/// information.
///
/// If there is some state in a downstream crate you would like to
/// access in the methods of `DiagnosticBuilder` here, consider
/// extending `HandlerFlags`, accessed via `self.handler.flags`.
#[must_use]
#[derive(Clone)]
pub struct DiagnosticBuilder<'a> {
pub handler: &'a Handler,
#[cfg(not(feature = "__plugin_mode"))]
pub(crate) diagnostic: Box<Diagnostic>,
#[cfg(feature = "__plugin_mode")]
pub diagnostic: Box<Diagnostic>,
allow_suggestions: bool,
}
/// In general, the `DiagnosticBuilder` uses deref to allow access to
/// the fields and methods of the embedded `diagnostic` in a
/// transparent way. *However,* many of the methods are intended to
/// be used in a chained way, and hence ought to return `self`. In
/// that case, we can't just naively forward to the method on the
/// `diagnostic`, because the return type would be a `&Diagnostic`
/// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes
/// it easy to declare such methods on the builder.
#[allow(unused)]
macro_rules! forward {
// Forward pattern for &self -> &Self
(pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)*) -> &Self) => {
pub fn $n(&self, $($name: $ty),*) -> &Self {
#[allow(deprecated)]
self.diagnostic.$n($($name),*);
self
}
};
// Forward pattern for &mut self -> &mut Self
(pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)*) -> &mut Self) => {
pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
#[allow(deprecated)]
self.diagnostic.$n($($name),*);
self
}
};
// Forward pattern for &mut self -> &mut Self, with S: Into<MultiSpan>
// type parameter. No obvious way to make this more generic.
(pub fn $n:ident<S: Into<MultiSpan>>(
&mut self,
$($name:ident: $ty:ty),*
$(,)*) -> &mut Self) => {
pub fn $n<S: Into<MultiSpan>>(&mut self, $($name: $ty),*) -> &mut Self {
#[allow(deprecated)]
self.diagnostic.$n($($name),*);
self
}
};
}
impl<'a> Deref for DiagnosticBuilder<'a> {
type Target = Diagnostic;
fn deref(&self) -> &Diagnostic {
&self.diagnostic
}
}
impl<'a> DerefMut for DiagnosticBuilder<'a> {
fn deref_mut(&mut self) -> &mut Diagnostic {
&mut self.diagnostic
}
}
impl<'a> DiagnosticBuilder<'a> {
forward!(pub fn note_expected_found(&mut self,
label: &dyn fmt::Display,
expected: DiagnosticStyledString,
found: DiagnosticStyledString,
) -> &mut Self);
forward!(pub fn note_expected_found_extra(&mut self,
label: &dyn fmt::Display,
expected: DiagnosticStyledString,
found: DiagnosticStyledString,
expected_extra: &dyn fmt::Display,
found_extra: &dyn fmt::Display,
) -> &mut Self);
forward!(pub fn note(&mut self, msg: &str) -> &mut Self);
forward!(pub fn span_note<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str,
) -> &mut Self);
forward!(pub fn warn(&mut self, msg: &str) -> &mut Self);
forward!(pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self);
forward!(pub fn help(&mut self , msg: &str) -> &mut Self);
forward!(pub fn span_help<S: Into<MultiSpan>>(&mut self,
sp: S,
msg: &str,
) -> &mut Self);
forward!(pub fn span_suggestion_short(
&mut self,
sp: Span,
msg: &str,
suggestion: String,
) -> &mut Self);
forward!(pub fn multipart_suggestion(
&mut self,
msg: &str,
suggestion: Vec<(Span, String)>,
) -> &mut Self);
forward!(pub fn span_suggestion(&mut self,
sp: Span,
msg: &str,
suggestion: String,
) -> &mut Self);
forward!(pub fn span_suggestions(&mut self,
sp: Span,
msg: &str,
suggestions: Vec<String>,
) -> &mut Self);
forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self);
/// Emit the diagnostic.
pub fn emit(&mut self) {
if self.cancelled() {
return;
}
self.handler.emit_db(self);
self.cancel();
}
/// Buffers the diagnostic for later emission, unless handler
/// has disabled such buffering.
pub fn buffer(mut self, buffered_diagnostics: &mut Vec<Diagnostic>) {
if self.handler.flags.dont_buffer_diagnostics || self.handler.flags.treat_err_as_bug {
self.emit();
return;
}
// We need to use `ptr::read` because `DiagnosticBuilder`
// implements `Drop`.
let diagnostic;
unsafe {
diagnostic = ::std::ptr::read(&self.diagnostic);
::std::mem::forget(self);
};
// Logging here is useful to help track down where in logs an error was
// actually emitted.
if cfg!(feature = "debug") {
debug!("buffer: diagnostic={:?}", diagnostic);
}
buffered_diagnostics.push(*diagnostic);
}
/// Convenience function for internal use, clients should use one of the
/// span_* methods instead.
pub fn sub<S: Into<MultiSpan>>(
&mut self,
level: Level,
message: &str,
span: Option<S>,
) -> &mut Self {
let span = span.map(|s| s.into()).unwrap_or_else(MultiSpan::new);
self.diagnostic.sub(level, message, span, None);
self
}
/// Delay emission of this diagnostic as a bug.
///
/// This can be useful in contexts where an error indicates a bug but
/// typically this only happens when other compilation errors have already
/// happened. In those cases this can be used to defer emission of this
/// diagnostic as a bug in the compiler only if no other errors have been
/// emitted.
///
/// In the meantime, though, callsites are required to deal with the "bug"
/// locally in whichever way makes the most sense.
pub fn delay_as_bug(&mut self) {
self.level = Level::Bug;
self.handler.delay_as_bug(*self.diagnostic.clone());
self.cancel();
}
/// Add a span/label to be included in the resulting snippet.
/// This is pushed onto the `MultiSpan` that was created when the
/// diagnostic was first built. If you don't call this function at
/// all, and you just supplied a `Span` to create the diagnostic,
/// then the snippet will just include that `Span`, which is
/// called the primary span.
pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
self.diagnostic.span_label(span, label);
self
}
pub fn multipart_suggestion_with_applicability(
&mut self,
msg: &str,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
) -> &mut Self {
if !self.allow_suggestions {
return self;
}
self.diagnostic
.multipart_suggestion_with_applicability(msg, suggestion, applicability);
self
}
pub fn span_suggestion_with_applicability(
&mut self,
sp: Span,
msg: &str,
suggestion: String,
applicability: Applicability,
) -> &mut Self {
if !self.allow_suggestions {
return self;
}
self.diagnostic
.span_suggestion_with_applicability(sp, msg, suggestion, applicability);
self
}
pub fn span_suggestions_with_applicability(
&mut self,
sp: Span,
msg: &str,
suggestions: impl Iterator<Item = String>,
applicability: Applicability,
) -> &mut Self {
if !self.allow_suggestions {
return self;
}
self.diagnostic
.span_suggestions_with_applicability(sp, msg, suggestions, applicability);
self
}
pub fn span_suggestion_short_with_applicability(
&mut self,
sp: Span,
msg: &str,
suggestion: String,
applicability: Applicability,
) -> &mut Self {
if !self.allow_suggestions {
return self;
}
self.diagnostic.span_suggestion_short_with_applicability(
sp,
msg,
suggestion,
applicability,
);
self
}
pub fn allow_suggestions(&mut self, allow: bool) -> &mut Self {
self.allow_suggestions = allow;
self
}
/// Convenience function for internal use, clients should use one of the
/// struct_* methods on Handler.
pub fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> {
DiagnosticBuilder::new_with_code(handler, level, None, message)
}
/// Convenience function for internal use, clients should use one of the
/// struct_* methods on Handler.
pub fn new_with_code(
handler: &'a Handler,
level: Level,
code: Option<DiagnosticId>,
message: &str,
) -> DiagnosticBuilder<'a> {
let diagnostic = Diagnostic::new_with_code(level, code, message);
DiagnosticBuilder::new_diagnostic(handler, diagnostic)
}
/// Creates a new `DiagnosticBuilder` with an already constructed
/// diagnostic.
#[inline(always)] // box
pub fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> {
DiagnosticBuilder {
handler,
diagnostic: Box::new(diagnostic),
allow_suggestions: true,
}
}
}
impl<'a> Debug for DiagnosticBuilder<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.diagnostic.fmt(f)
}
}
/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or canceled
/// or we emit a bug.
impl<'a> Drop for DiagnosticBuilder<'a> {
fn drop(&mut self) {
if !panicking() && !self.cancelled() {
let mut db = DiagnosticBuilder::new(
self.handler,
Level::Bug,
"Error constructed but not emitted",
);
db.emit();
panic!();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,122 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Bindings to acquire a global named lock.
//!
//! This is intended to be used to synchronize multiple compiler processes to
//! ensure that we can output complete errors without interleaving on Windows.
//! Note that this is currently only needed for allowing only one 32-bit MSVC
//! linker to execute at once on MSVC hosts, so this is only implemented for
//! `cfg(windows)`. Also note that this may not always be used on Windows,
//! only when targeting 32-bit MSVC.
//!
//! For more information about why this is necessary, see where this is called.
use std::any::Any;
#[cfg(windows)]
#[allow(nonstandard_style)]
pub fn acquire_global_lock(name: &str) -> Box<dyn Any> {
use std::{ffi::CString, io};
type LPSECURITY_ATTRIBUTES = *mut u8;
#[allow(clippy::upper_case_acronyms)]
type BOOL = i32;
#[allow(clippy::upper_case_acronyms)]
type LPCSTR = *const u8;
#[allow(clippy::upper_case_acronyms)]
type HANDLE = *mut u8;
#[allow(clippy::upper_case_acronyms)]
type DWORD = u32;
const INFINITE: DWORD = !0;
const WAIT_OBJECT_0: DWORD = 0;
const WAIT_ABANDONED: DWORD = 0x00000080;
extern "system" {
fn CreateMutexA(
lpMutexAttributes: LPSECURITY_ATTRIBUTES,
bInitialOwner: BOOL,
lpName: LPCSTR,
) -> HANDLE;
fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
fn ReleaseMutex(hMutex: HANDLE) -> BOOL;
fn CloseHandle(hObject: HANDLE) -> BOOL;
}
struct Handle(HANDLE);
impl Drop for Handle {
fn drop(&mut self) {
unsafe {
CloseHandle(self.0);
}
}
}
struct Guard(Handle);
impl Drop for Guard {
fn drop(&mut self) {
unsafe {
ReleaseMutex((self.0).0);
}
}
}
let cname = CString::new(name).unwrap();
unsafe {
// Create a named mutex, with no security attributes and also not
// acquired when we create it.
//
// This will silently create one if it doesn't already exist, or it'll
// open up a handle to one if it already exists.
let mutex = CreateMutexA(std::ptr::null_mut(), 0, cname.as_ptr() as *const u8);
if mutex.is_null() {
panic!(
"failed to create global mutex named `{}`: {}",
name,
io::Error::last_os_error()
);
}
let mutex = Handle(mutex);
// Acquire the lock through `WaitForSingleObject`.
//
// A return value of `WAIT_OBJECT_0` means we successfully acquired it.
//
// A return value of `WAIT_ABANDONED` means that the previous holder of
// the thread exited without calling `ReleaseMutex`. This can happen,
// for example, when the compiler crashes or is interrupted via ctrl-c
// or the like. In this case, however, we are still transferred
// ownership of the lock so we continue.
//
// If an error happens.. well... that's surprising!
match WaitForSingleObject(mutex.0, INFINITE) {
WAIT_OBJECT_0 | WAIT_ABANDONED => {}
code => {
panic!(
"WaitForSingleObject failed on global mutex named `{}`: {} (ret={:x})",
name,
io::Error::last_os_error(),
code
);
}
}
// Return a guard which will call `ReleaseMutex` when dropped.
Box::new(Guard(mutex))
}
}
#[cfg(not(windows))]
pub fn acquire_global_lock(_name: &str) -> Box<dyn Any> {
Box::new(())
}

View file

@ -0,0 +1,961 @@
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::{
borrow::Cow,
cell::RefCell,
error, fmt,
io::Write,
panic,
sync::atomic::{AtomicUsize, Ordering::SeqCst},
};
#[cfg(feature = "tty-emitter")]
use termcolor::{Color, ColorSpec};
use self::Level::*;
pub use self::{
diagnostic::{Diagnostic, DiagnosticId, DiagnosticStyledString, SubDiagnostic},
diagnostic_builder::DiagnosticBuilder,
emitter::{ColorConfig, Emitter, EmitterWriter},
};
use crate::{
collections::AHashSet,
rustc_data_structures::stable_hasher::StableHasher,
sync::{Lock, LockCell, Lrc},
syntax_pos::{BytePos, FileLinesResult, FileName, Loc, MultiSpan, Span, NO_EXPANSION},
SpanSnippetError,
};
mod diagnostic;
mod diagnostic_builder;
pub mod emitter;
mod lock;
mod snippet;
mod styled_buffer;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(u32)))]
pub enum Applicability {
MachineApplicable,
HasPlaceholders,
MaybeIncorrect,
Unspecified,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
pub struct CodeSuggestion {
/// Each substitute can have multiple variants due to multiple
/// applicable suggestions
///
/// `foo.bar` might be replaced with `a.b` or `x.y` by replacing
/// `foo` and `bar` on their own:
///
/// ```rust,ignore
/// vec![
/// Substitution {
/// parts: vec![(0..3, "a"), (4..7, "b")],
/// },
/// Substitution {
/// parts: vec![(0..3, "x"), (4..7, "y")],
/// },
/// ]
/// ```
///
/// or by replacing the entire span:
///
/// ```rust,ignore
/// vec![
/// Substitution {
/// parts: vec![(0..7, "a.b")],
/// },
/// Substitution {
/// parts: vec![(0..7, "x.y")],
/// },
/// ]
/// ```
pub substitutions: Vec<Substitution>,
pub msg: String,
pub show_code_when_inline: bool,
/// Whether or not the suggestion is approximate
///
/// Sometimes we may show suggestions with placeholders,
/// which are useful for users but not useful for
/// tools like rustfix
pub applicability: Applicability,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
/// See the docs on `CodeSuggestion::substitutions`
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
pub struct Substitution {
pub parts: Vec<SubstitutionPart>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
pub struct SubstitutionPart {
pub span: Span,
pub snippet: String,
}
pub type SourceMapperDyn = dyn SourceMapper;
pub trait SourceMapper: crate::sync::Send + crate::sync::Sync {
fn lookup_char_pos(&self, pos: BytePos) -> Loc;
fn span_to_lines(&self, sp: Span) -> FileLinesResult;
fn span_to_string(&self, sp: Span) -> String;
fn span_to_filename(&self, sp: Span) -> FileName;
fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>;
fn call_span_if_macro(&self, sp: Span) -> Span;
fn doctest_offset_line(&self, line: usize) -> usize;
fn span_to_snippet(&self, sp: Span) -> Result<String, Box<SpanSnippetError>>;
}
impl CodeSuggestion {
/// Returns the assembled code suggestions and whether they should be shown
/// with an underline.
pub fn splice_lines(&self, cm: &SourceMapperDyn) -> Vec<(String, Vec<SubstitutionPart>)> {
use crate::syntax_pos::{CharPos, Pos};
fn push_trailing(
buf: &mut String,
line_opt: Option<&Cow<'_, str>>,
lo: &Loc,
hi_opt: Option<&Loc>,
) {
let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize()));
if let Some(line) = line_opt {
if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi));
buf.push_str(match hi_opt {
Some(hi) => &line[lo..hi],
None => &line[lo..],
});
}
if hi_opt.is_none() {
buf.push('\n');
}
}
}
assert!(!self.substitutions.is_empty());
self.substitutions
.iter()
.cloned()
.map(|mut substitution| {
// Assumption: all spans are in the same file, and all spans
// are disjoint. Sort in ascending order.
substitution.parts.sort_by_key(|part| part.span.lo());
// Find the bounding span.
let lo = substitution
.parts
.iter()
.map(|part| part.span.lo())
.min()
.unwrap();
let hi = substitution
.parts
.iter()
.map(|part| part.span.hi())
.min()
.unwrap();
let bounding_span = Span::new(lo, hi, NO_EXPANSION);
let lines = cm.span_to_lines(bounding_span).unwrap();
assert!(!lines.lines.is_empty());
// To build up the result, we do this for each span:
// - push the line segment trailing the previous span (at the beginning a
// "phantom" span pointing at the start of the line)
// - push lines between the previous and current span (if any)
// - if the previous and current span are not on the same line push the line
// segment leading up to the current span
// - splice in the span substitution
//
// Finally push the trailing line segment of the last span
let fm = &lines.file;
let mut prev_hi = cm.lookup_char_pos(bounding_span.lo());
prev_hi.col = CharPos::from_usize(0);
let mut prev_line = fm.get_line(lines.lines[0].line_index);
let mut buf = String::new();
for part in &substitution.parts {
let cur_lo = cm.lookup_char_pos(part.span.lo());
if prev_hi.line == cur_lo.line {
push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo));
} else {
push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
// push lines between the previous and current span (if any)
for idx in prev_hi.line..(cur_lo.line - 1) {
if let Some(line) = fm.get_line(idx) {
buf.push_str(line.as_ref());
buf.push('\n');
}
}
if let Some(cur_line) = fm.get_line(cur_lo.line - 1) {
buf.push_str(&cur_line[..cur_lo.col.to_usize()]);
}
}
buf.push_str(&part.snippet);
prev_hi = cm.lookup_char_pos(part.span.hi());
prev_line = fm.get_line(prev_hi.line - 1);
}
// if the replacement already ends with a newline, don't print the next line
if !buf.ends_with('\n') {
push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
}
// remove trailing newlines
while buf.ends_with('\n') {
buf.pop();
}
(buf, substitution.parts)
})
.collect()
}
}
/// Used as a return value to signify a fatal error occurred. (It is also
/// used as the argument to panic at the moment, but that will eventually
/// not be true.)
#[derive(Copy, Clone, Debug)]
#[must_use]
pub struct FatalError;
pub struct FatalErrorMarker;
// // Don't implement Send on FatalError. This makes it impossible to
// // panic!(FatalError). We don't want to invoke the panic handler and print a
// // backtrace for fatal errors.
// impl !Send for FatalError {}
impl FatalError {
pub fn raise(self) -> ! {
panic::resume_unwind(Box::new(FatalErrorMarker))
}
}
impl fmt::Display for FatalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "parser fatal error")
}
}
impl error::Error for FatalError {
fn description(&self) -> &str {
"The parser has encountered a fatal error"
}
}
/// Signifies that the compiler died with an explicit call to `.bug`
/// or `.span_bug` rather than a failed assertion, etc.
#[derive(Copy, Clone, Debug)]
pub struct ExplicitBug;
impl fmt::Display for ExplicitBug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "parser internal bug")
}
}
impl error::Error for ExplicitBug {
fn description(&self) -> &str {
"The parser has encountered an internal bug"
}
}
/// A handler deals with errors; certain errors
/// (fatal, bug, unimpl) may cause immediate exit,
/// others log errors for later reporting.
///
/// # Example
///
/// `swc` provides a global-like variable ([HANDLER]) of type `Handler` that can
/// be used to report errors.
///
/// You can refer to [the lint rules](https://github.com/swc-project/swc/tree/main/crates/swc_ecma_lints/src/rules) for other example usages.
/// All lint rules have code for error reporting.
///
/// ## Error reporting in swc
///
/// ```rust,ignore
/// use swc_common::errors::HANDLER;
///
/// # fn main() {
/// HANDLER.with(|handler| {
/// // You can access the handler for the current file using HANDLER.with.
///
/// // We now report an error
///
/// // `struct_span_err` creates a builder for a diagnostic.
/// // The span passed to `struct_span_err` will used to point the problematic code.
/// //
/// // You may provide additional information, like a previous declaration of parameter.
/// handler
/// .struct_span_err(
/// span,
/// &format!("`{}` used as parameter more than once", js_word),
/// )
/// .span_note(
/// old_span,
/// &format!("previous definition of `{}` here", js_word),
/// )
/// .emit();
/// });
/// # }
/// ```
pub struct Handler {
pub flags: HandlerFlags,
err_count: AtomicUsize,
emitter: Lock<Box<dyn Emitter>>,
continue_after_error: LockCell<bool>,
delayed_span_bugs: Lock<Vec<Diagnostic>>,
// This set contains the `DiagnosticId` of all emitted diagnostics to avoid
// emitting the same diagnostic with extended help (`--teach`) twice, which
// would be unnecessary repetition.
taught_diagnostics: Lock<AHashSet<DiagnosticId>>,
/// Used to suggest rustc --explain <error code>
emitted_diagnostic_codes: Lock<AHashSet<DiagnosticId>>,
// This set contains a hash of every diagnostic that has been emitted by
// this handler. These hashes is used to avoid emitting the same error
// twice.
emitted_diagnostics: Lock<AHashSet<u128>>,
}
fn default_track_diagnostic(_: &Diagnostic) {}
thread_local!(pub static TRACK_DIAGNOSTICS: RefCell<Box<dyn Fn(&Diagnostic)>> =
RefCell::new(Box::new(default_track_diagnostic)));
#[derive(Default)]
pub struct HandlerFlags {
/// If false, warning-level lints are suppressed.
/// (rustc: see `--allow warnings` and `--cap-lints`)
pub can_emit_warnings: bool,
/// If true, error-level diagnostics are upgraded to bug-level.
/// (rustc: see `-Z treat-err-as-bug`)
pub treat_err_as_bug: bool,
/// If true, immediately emit diagnostics that would otherwise be buffered.
/// (rustc: see `-Z dont-buffer-diagnostics` and `-Z treat-err-as-bug`)
pub dont_buffer_diagnostics: bool,
/// If true, immediately print bugs registered with `delay_span_bug`.
/// (rustc: see `-Z report-delayed-bugs`)
pub report_delayed_bugs: bool,
/// show macro backtraces even for non-local macros.
/// (rustc: see `-Z external-macro-backtrace`)
pub external_macro_backtrace: bool,
}
impl Drop for Handler {
fn drop(&mut self) {
if self.err_count() == 0 {
let mut bugs = self.delayed_span_bugs.borrow_mut();
let has_bugs = !bugs.is_empty();
for bug in bugs.drain(..) {
DiagnosticBuilder::new_diagnostic(self, bug).emit();
}
if has_bugs {
panic!("no errors encountered even though `delay_span_bug` issued");
}
}
}
}
impl Handler {
#[cfg(feature = "tty-emitter")]
#[cfg_attr(docsrs, doc(cfg(feature = "tty-emitter")))]
pub fn with_tty_emitter(
color_config: ColorConfig,
can_emit_warnings: bool,
treat_err_as_bug: bool,
cm: Option<Lrc<SourceMapperDyn>>,
) -> Handler {
Handler::with_tty_emitter_and_flags(
color_config,
cm,
HandlerFlags {
can_emit_warnings,
treat_err_as_bug,
..Default::default()
},
)
}
#[cfg(feature = "tty-emitter")]
#[cfg_attr(docsrs, doc(cfg(feature = "tty-emitter")))]
pub fn with_tty_emitter_and_flags(
color_config: ColorConfig,
cm: Option<Lrc<SourceMapperDyn>>,
flags: HandlerFlags,
) -> Handler {
let emitter = Box::new(EmitterWriter::stderr(color_config, cm, false, false));
Handler::with_emitter_and_flags(emitter, flags)
}
/// Example implementation of [Emitter] is [EmitterWriter]
pub fn with_emitter(
can_emit_warnings: bool,
treat_err_as_bug: bool,
emitter: Box<dyn Emitter>,
) -> Handler {
Handler::with_emitter_and_flags(
emitter,
HandlerFlags {
can_emit_warnings,
treat_err_as_bug,
..Default::default()
},
)
}
/// Calls [Self::with_emitter] with [EmitterWriter].
pub fn with_emitter_writer(
dst: Box<dyn Write + Send>,
cm: Option<Lrc<SourceMapperDyn>>,
) -> Handler {
Handler::with_emitter(
true,
false,
Box::new(EmitterWriter::new(dst, cm, false, true)),
)
}
pub fn with_emitter_and_flags(e: Box<dyn Emitter>, flags: HandlerFlags) -> Handler {
Handler {
flags,
err_count: AtomicUsize::new(0),
emitter: Lock::new(e),
continue_after_error: LockCell::new(true),
delayed_span_bugs: Lock::new(Vec::new()),
taught_diagnostics: Default::default(),
emitted_diagnostic_codes: Default::default(),
emitted_diagnostics: Default::default(),
}
}
pub fn set_continue_after_error(&self, continue_after_error: bool) {
self.continue_after_error.set(continue_after_error);
}
/// Resets the diagnostic error count as well as the cached emitted
/// diagnostics.
///
/// NOTE: DO NOT call this function from rustc. It is only meant to be
/// called from external tools that want to reuse a `Parser` cleaning
/// the previously emitted diagnostics as well as the overall count of
/// emitted error diagnostics.
pub fn reset_err_count(&self) {
// actually frees the underlying memory (which `clear` would not do)
*self.emitted_diagnostics.borrow_mut() = Default::default();
self.err_count.store(0, SeqCst);
}
pub fn struct_dummy(&self) -> DiagnosticBuilder<'_> {
DiagnosticBuilder::new(self, Level::Cancelled, "")
}
pub fn struct_span_warn<'a, S: Into<MultiSpan>>(
&'a self,
sp: S,
msg: &str,
) -> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
result.set_span(sp);
if !self.flags.can_emit_warnings {
result.cancel();
}
result
}
pub fn struct_span_warn_with_code<'a, S: Into<MultiSpan>>(
&'a self,
sp: S,
msg: &str,
code: DiagnosticId,
) -> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
result.set_span(sp);
result.code(code);
if !self.flags.can_emit_warnings {
result.cancel();
}
result
}
pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
if !self.flags.can_emit_warnings {
result.cancel();
}
result
}
pub fn struct_span_err<'a, S: Into<MultiSpan>>(
&'a self,
sp: S,
msg: &str,
) -> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
result.set_span(sp);
result
}
pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(
&'a self,
sp: S,
msg: &str,
code: DiagnosticId,
) -> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
result.set_span(sp);
result.code(code);
result
}
// FIXME: This method should be removed (every error should have an associated
// error code).
pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
DiagnosticBuilder::new(self, Level::Error, msg)
}
pub fn struct_err_with_code<'a>(
&'a self,
msg: &str,
code: DiagnosticId,
) -> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
result.code(code);
result
}
pub fn struct_span_fatal<'a, S: Into<MultiSpan>>(
&'a self,
sp: S,
msg: &str,
) -> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
result.set_span(sp);
result
}
pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(
&'a self,
sp: S,
msg: &str,
code: DiagnosticId,
) -> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(self, Level::Fatal, msg);
result.set_span(sp);
result.code(code);
result
}
pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
DiagnosticBuilder::new(self, Level::Fatal, msg)
}
pub fn cancel(&self, err: &mut DiagnosticBuilder<'_>) {
err.cancel();
}
fn panic_if_treat_err_as_bug(&self) {
if self.flags.treat_err_as_bug {
panic!("encountered error with `-Z treat_err_as_bug");
}
}
pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> FatalError {
self.emit(&sp.into(), msg, Fatal);
FatalError
}
pub fn span_fatal_with_code<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
code: DiagnosticId,
) -> FatalError {
self.emit_with_code(&sp.into(), msg, code, Fatal);
FatalError
}
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit(&sp.into(), msg, Error);
}
pub fn mut_span_err<'a, S: Into<MultiSpan>>(
&'a self,
sp: S,
msg: &str,
) -> DiagnosticBuilder<'a> {
let mut result = DiagnosticBuilder::new(self, Level::Error, msg);
result.set_span(sp);
result
}
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
self.emit_with_code(&sp.into(), msg, code, Error);
}
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit(&sp.into(), msg, Warning);
}
pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
self.emit_with_code(&sp.into(), msg, code, Warning);
}
pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
self.emit(&sp.into(), msg, Bug);
panic!("{}", ExplicitBug);
}
pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
if self.flags.treat_err_as_bug {
// FIXME: don't abort here if report_delayed_bugs is off
self.span_bug(sp, msg);
}
let mut diagnostic = Diagnostic::new(Level::Bug, msg);
diagnostic.set_span(sp.into());
self.delay_as_bug(diagnostic);
}
fn delay_as_bug(&self, diagnostic: Diagnostic) {
if self.flags.report_delayed_bugs {
DiagnosticBuilder::new_diagnostic(self, diagnostic.clone()).emit();
}
self.delayed_span_bugs.borrow_mut().push(diagnostic);
}
pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit(&sp.into(), msg, Bug);
}
pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
self.emit(&sp.into(), msg, Note);
}
pub fn span_note_diag<'a>(&'a self, sp: Span, msg: &str) -> DiagnosticBuilder<'a> {
let mut db = DiagnosticBuilder::new(self, Note, msg);
db.set_span(sp);
db
}
pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
self.span_bug(sp, &format!("unimplemented {}", msg));
}
pub fn failure(&self, msg: &str) {
DiagnosticBuilder::new(self, FailureNote, msg).emit()
}
pub fn fatal(&self, msg: &str) -> FatalError {
if self.flags.treat_err_as_bug {
self.bug(msg);
}
DiagnosticBuilder::new(self, Fatal, msg).emit();
FatalError
}
pub fn err(&self, msg: &str) {
if self.flags.treat_err_as_bug {
self.bug(msg);
}
let mut db = DiagnosticBuilder::new(self, Error, msg);
db.emit();
}
pub fn warn(&self, msg: &str) {
let mut db = DiagnosticBuilder::new(self, Warning, msg);
db.emit();
}
pub fn note_without_error(&self, msg: &str) {
let mut db = DiagnosticBuilder::new(self, Note, msg);
db.emit();
}
pub fn bug(&self, msg: &str) -> ! {
let mut db = DiagnosticBuilder::new(self, Bug, msg);
db.emit();
panic!("{}", ExplicitBug);
}
pub fn unimpl(&self, msg: &str) -> ! {
self.bug(&format!("unimplemented {}", msg));
}
fn bump_err_count(&self) {
self.panic_if_treat_err_as_bug();
self.err_count.fetch_add(1, SeqCst);
}
pub fn err_count(&self) -> usize {
self.err_count.load(SeqCst)
}
pub fn has_errors(&self) -> bool {
self.err_count() > 0
}
pub fn print_error_count(&self) {
let s = match self.err_count() {
0 => return,
1 => "aborting due to previous error".to_string(),
_ => format!("aborting due to {} previous errors", self.err_count()),
};
let _ = self.fatal(&s);
let can_show_explain = self.emitter.borrow().should_show_explain();
let are_there_diagnostics = !self.emitted_diagnostic_codes.borrow().is_empty();
if can_show_explain && are_there_diagnostics {
let mut error_codes = self
.emitted_diagnostic_codes
.borrow()
.iter()
.filter_map(|x| match *x {
DiagnosticId::Error(ref s) => Some(s.clone()),
_ => None,
})
.collect::<Vec<_>>();
if !error_codes.is_empty() {
error_codes.sort();
if error_codes.len() > 1 {
let limit = if error_codes.len() > 9 {
9
} else {
error_codes.len()
};
self.failure(&format!(
"Some errors occurred: {}{}",
error_codes[..limit].join(", "),
if error_codes.len() > 9 { "..." } else { "." }
));
self.failure(&format!(
"For more information about an error, try `rustc --explain {}`.",
&error_codes[0]
));
} else {
self.failure(&format!(
"For more information about this error, try `rustc --explain {}`.",
&error_codes[0]
));
}
}
}
}
pub fn abort_if_errors(&self) {
if self.err_count() == 0 {
return;
}
FatalError.raise();
}
pub fn emit(&self, msp: &MultiSpan, msg: &str, lvl: Level) {
if lvl == Warning && !self.flags.can_emit_warnings {
return;
}
let mut db = DiagnosticBuilder::new(self, lvl, msg);
db.set_span(msp.clone());
db.emit();
if !self.continue_after_error.get() {
self.abort_if_errors();
}
}
pub fn emit_with_code(&self, msp: &MultiSpan, msg: &str, code: DiagnosticId, lvl: Level) {
if lvl == Warning && !self.flags.can_emit_warnings {
return;
}
let mut db = DiagnosticBuilder::new_with_code(self, lvl, Some(code), msg);
db.set_span(msp.clone());
db.emit();
if !self.continue_after_error.get() {
self.abort_if_errors();
}
}
/// `true` if we haven't taught a diagnostic with this code already.
/// The caller must then teach the user about such a diagnostic.
///
/// Used to suppress emitting the same error multiple times with extended
/// explanation when calling `-Z teach`.
pub fn must_teach(&self, code: &DiagnosticId) -> bool {
self.taught_diagnostics.borrow_mut().insert(code.clone())
}
pub fn force_print_db(&self, mut db: DiagnosticBuilder<'_>) {
self.emitter.borrow_mut().emit(&db);
db.cancel();
}
fn emit_db(&self, db: &DiagnosticBuilder<'_>) {
let diagnostic = &**db;
TRACK_DIAGNOSTICS.with(|track_diagnostics| {
track_diagnostics.borrow()(diagnostic);
});
if let Some(ref code) = diagnostic.code {
self.emitted_diagnostic_codes
.borrow_mut()
.insert(code.clone());
}
let diagnostic_hash = {
use std::hash::Hash;
let mut hasher = StableHasher::new();
diagnostic.hash(&mut hasher);
hasher.finish()
};
// Only emit the diagnostic if we haven't already emitted an equivalent
// one:
if self
.emitted_diagnostics
.borrow_mut()
.insert(diagnostic_hash)
{
self.emitter.borrow_mut().emit(db);
if db.is_error() {
self.bump_err_count();
}
}
}
}
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)]
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(u32)))]
pub enum Level {
Bug,
Fatal,
// An error which while not immediately fatal, should stop the compiler
// progressing beyond the current phase.
PhaseFatal,
Error,
Warning,
Note,
Help,
Cancelled,
FailureNote,
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.to_str().fmt(f)
}
}
impl Level {
#[cfg(feature = "tty-emitter")]
fn color(self) -> ColorSpec {
let mut spec = ColorSpec::new();
match self {
Bug | Fatal | PhaseFatal | Error => {
spec.set_fg(Some(Color::Red)).set_intense(true);
}
Warning => {
spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
}
Note => {
spec.set_fg(Some(Color::Green)).set_intense(true);
}
Help => {
spec.set_fg(Some(Color::Cyan)).set_intense(true);
}
FailureNote => {}
Cancelled => unreachable!(),
}
spec
}
pub fn to_str(self) -> &'static str {
match self {
Bug => "error: internal compiler error",
Fatal | PhaseFatal | Error => "error",
Warning => "warning",
Note => "note",
Help => "help",
FailureNote => "",
Cancelled => panic!("Shouldn't call on cancelled error"),
}
}
pub fn is_failure_note(self) -> bool {
matches!(self, FailureNote)
}
}
better_scoped_tls::scoped_tls!(
/// Used for error reporting in transform.
///
/// This should be only used for errors from the api which does not returning errors.
///
/// e.g.
/// - `parser` should not use this.
/// - `transforms` should use this to report error, as it does not return [Result].
///
/// See [Handler] for actual usage examples.
pub static HANDLER: Handler
);

View file

@ -0,0 +1,203 @@
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Code for annotating snippets.
use super::Level;
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct Line {
pub line_index: usize,
pub annotations: Vec<Annotation>,
}
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct MultilineAnnotation {
pub depth: usize,
pub line_start: usize,
pub line_end: usize,
pub start_col: usize,
pub end_col: usize,
pub is_primary: bool,
pub label: Option<String>,
}
impl MultilineAnnotation {
pub fn increase_depth(&mut self) {
self.depth += 1;
}
pub fn as_start(&self) -> Annotation {
Annotation {
start_col: self.start_col,
end_col: self.start_col + 1,
is_primary: self.is_primary,
label: None,
annotation_type: AnnotationType::MultilineStart(self.depth),
}
}
pub fn as_end(&self) -> Annotation {
Annotation {
start_col: self.end_col.saturating_sub(1),
end_col: self.end_col,
is_primary: self.is_primary,
label: self.label.clone(),
annotation_type: AnnotationType::MultilineEnd(self.depth),
}
}
pub fn as_line(&self) -> Annotation {
Annotation {
start_col: 0,
end_col: 0,
is_primary: self.is_primary,
label: None,
annotation_type: AnnotationType::MultilineLine(self.depth),
}
}
}
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub enum AnnotationType {
/// Annotation under a single line of code
Singleline,
/// Annotation enclosing the first and last character of a multiline span
Multiline(MultilineAnnotation),
// The Multiline type above is replaced with the following three in order
// to reuse the current label drawing code.
//
// Each of these corresponds to one part of the following diagram:
//
// x | foo(1 + bar(x,
// | _________^ < MultilineStart
// x | | y), < MultilineLine
// | |______________^ label < MultilineEnd
// x | z);
/// Annotation marking the first character of a fully shown multiline span
MultilineStart(usize),
/// Annotation marking the last character of a fully shown multiline span
MultilineEnd(usize),
/// Line at the left enclosing the lines of a fully shown multiline span
// Just a placeholder for the drawing algorithm, to know that it shouldn't skip the first 4
// and last 2 lines of code. The actual line is drawn in `emit_message_default` and not in
// `draw_multiline_line`.
MultilineLine(usize),
}
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct Annotation {
/// Start column, 0-based indexing -- counting *characters*, not
/// utf-8 bytes. Note that it is important that this field goes
/// first, so that when we sort, we sort orderings by start
/// column.
pub start_col: usize,
/// End column within the line (exclusive)
pub end_col: usize,
/// Is this annotation derived from primary span
pub is_primary: bool,
/// Optional label to display adjacent to the annotation.
pub label: Option<String>,
/// Is this a single line, multiline or multiline span minimized down to a
/// smaller span.
pub annotation_type: AnnotationType,
}
impl Annotation {
/// Whether this annotation is a vertical line placeholder.
pub fn is_line(&self) -> bool {
matches!(self.annotation_type, AnnotationType::MultilineLine(_))
}
pub fn is_multiline(&self) -> bool {
matches!(
self.annotation_type,
AnnotationType::Multiline(_)
| AnnotationType::MultilineStart(_)
| AnnotationType::MultilineLine(_)
| AnnotationType::MultilineEnd(_)
)
}
pub fn len(&self) -> usize {
// Account for usize underflows
if self.end_col > self.start_col {
self.end_col - self.start_col
} else {
self.start_col - self.end_col
}
}
pub fn has_label(&self) -> bool {
if let Some(ref label) = self.label {
// Consider labels with no text as effectively not being there
// to avoid weird output with unnecessary vertical lines, like:
//
// X | fn foo(x: u32) {
// | -------^------
// | | |
// | |
// |
//
// Note that this would be the complete output users would see.
!label.is_empty()
} else {
false
}
}
pub fn takes_space(&self) -> bool {
// Multiline annotations always have to keep vertical space.
matches!(
self.annotation_type,
AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_)
)
}
}
#[derive(Debug)]
pub struct StyledString {
pub text: String,
pub style: Style,
}
#[allow(clippy::enum_variant_names)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "diagnostic-serde",
derive(serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(u32)))]
pub enum Style {
MainHeaderMsg,
HeaderMsg,
LineAndColumn,
LineNumber,
Quotation,
UnderlinePrimary,
UnderlineSecondary,
LabelPrimary,
LabelSecondary,
OldSchoolNoteText,
NoStyle,
Level(Level),
Highlight,
}

View file

@ -0,0 +1,170 @@
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Code for creating styled buffers
use super::snippet::{Style, StyledString};
#[derive(Debug)]
pub struct StyledBuffer {
text: Vec<Vec<char>>,
styles: Vec<Vec<Style>>,
}
impl StyledBuffer {
pub fn new() -> StyledBuffer {
StyledBuffer {
text: vec![],
styles: vec![],
}
}
fn replace_tabs(&mut self) {
for (line_pos, line) in self.text.iter_mut().enumerate() {
let mut tab_pos = vec![];
for (pos, c) in line.iter().enumerate() {
if *c == '\t' {
tab_pos.push(pos);
}
}
// start with the tabs at the end of the line to replace them with 4 space chars
for pos in tab_pos.iter().rev() {
assert_eq!(line.remove(*pos), '\t');
// fix the position of the style to match up after replacing the tabs
let s = self.styles[line_pos].remove(*pos);
for _ in 0..4 {
line.insert(*pos, ' ');
self.styles[line_pos].insert(*pos, s);
}
}
}
}
pub fn render(&mut self) -> Vec<Vec<StyledString>> {
let mut output: Vec<Vec<StyledString>> = vec![];
let mut styled_vec: Vec<StyledString> = vec![];
// before we render, replace tabs with spaces
self.replace_tabs();
for (row, row_style) in self.text.iter().zip(&self.styles) {
let mut current_style = Style::NoStyle;
let mut current_text = String::new();
for (&c, &s) in row.iter().zip(row_style) {
if s != current_style {
if !current_text.is_empty() {
styled_vec.push(StyledString {
text: current_text,
style: current_style,
});
}
current_style = s;
current_text = String::new();
}
current_text.push(c);
}
if !current_text.is_empty() {
styled_vec.push(StyledString {
text: current_text,
style: current_style,
});
}
// We're done with the row, push and keep going
output.push(styled_vec);
styled_vec = vec![];
}
output
}
fn ensure_lines(&mut self, line: usize) {
while line >= self.text.len() {
self.text.push(vec![]);
self.styles.push(vec![]);
}
}
pub fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
self.ensure_lines(line);
if col < self.text[line].len() {
self.text[line][col] = chr;
self.styles[line][col] = style;
} else {
let mut i = self.text[line].len();
while i < col {
self.text[line].push(' ');
self.styles[line].push(Style::NoStyle);
i += 1;
}
self.text[line].push(chr);
self.styles[line].push(style);
}
}
pub fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
let mut n = col;
for c in string.chars() {
self.putc(line, n, c, style);
n += 1;
}
}
pub fn prepend(&mut self, line: usize, string: &str, style: Style) {
self.ensure_lines(line);
let string_len = string.len();
// Push the old content over to make room for new content
for _ in 0..string_len {
self.styles[line].insert(0, Style::NoStyle);
self.text[line].insert(0, ' ');
}
self.puts(line, 0, string, style);
}
pub fn append(&mut self, line: usize, string: &str, style: Style) {
if line >= self.text.len() {
self.puts(line, 0, string, style);
} else {
let col = self.text[line].len();
self.puts(line, col, string, style);
}
}
pub fn num_lines(&self) -> usize {
self.text.len()
}
pub fn set_style_range(
&mut self,
line: usize,
col_start: usize,
col_end: usize,
style: Style,
overwrite: bool,
) {
for col in col_start..col_end {
self.set_style(line, col, style, overwrite);
}
}
pub fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) {
if let Some(ref mut line) = self.styles.get_mut(line) {
if let Some(s) = line.get_mut(col) {
if *s == Style::NoStyle || *s == Style::Quotation || overwrite {
*s = style;
}
}
}
}
}

View file

@ -0,0 +1,75 @@
use super::*;
use crate::{FileLoader, FilePathMapping, SourceMap};
use std::{
io,
path::{Path, PathBuf},
};
use sync::Lrc;
use BytePos;
use Span;
struct MyFileLoader;
impl FileLoader for MyFileLoader {
/// Query the existence of a file.
fn file_exists(&self, path: &Path) -> bool {
println!("File exists?: {}", path.display());
true
}
/// Return an absolute path to a file, if possible.
fn abs_path(&self, _path: &Path) -> Option<PathBuf> {
Some("/tmp.js".into())
}
/// Read the contents of an UTF-8 file into memory.
fn read_file(&self, _path: &Path) -> io::Result<String> {
Ok("
function foo() {
with (window) {
}
}"
.into())
}
}
#[test]
fn test() {
let cm = SourceMap::with_file_loader(box MyFileLoader, FilePathMapping::empty());
let file_map = cm
.load_file(Path::new("tmp.js").into())
.expect("failed to load tmp.js");
println!(
"File (start={},end={})",
file_map.start_pos.0, file_map.end_pos.0
);
let start_pos = file_map.start_pos + BytePos(1);
let end_pos = file_map.end_pos - BytePos(1);
let full = Span::new(start_pos, end_pos, Default::default());
let handler = Handler::with_tty_emitter(ColorConfig::Always, false, false, Some(Arc::new(cm)));
::syntax_pos::GLOBALS.set(&::syntax_pos::Globals::new(), || {
DiagnosticBuilder::new_with_code(
&handler,
Error,
Some(DiagnosticId::Error("ABCDE".into())),
"Test span_label",
)
.span(full)
.emit();
DiagnosticBuilder::new_with_code(
&handler,
super::Warning,
Some(DiagnosticId::Lint("WITH_STMT".into())),
"Lint: With statement",
)
.span(Span::new(
start_pos + BytePos(21),
start_pos + BytePos(25),
Default::default(),
))
.emit();
})
}

View file

@ -0,0 +1,434 @@
use std::str;
use debug_unreachable::debug_unreachable;
use crate::syntax_pos::{BytePos, SourceFile};
pub type SourceFileInput<'a> = StringInput<'a>;
/// Implementation of [Input].
#[derive(Clone)]
pub struct StringInput<'a> {
start_pos_of_iter: BytePos,
last_pos: BytePos,
/// Current cursor
iter: str::CharIndices<'a>,
orig: &'a str,
/// Original start position.
orig_start: BytePos,
}
impl<'a> StringInput<'a> {
/// `start` and `end` can be arbitrary value, but start should be less than
/// or equal to end.
///
///
/// `swc` get this value from [SourceMap] because code generator depends on
/// some methods of [SourceMap].
/// If you are not going to use methods from
/// [SourceMap], you may use any value.
pub fn new(src: &'a str, start: BytePos, end: BytePos) -> Self {
assert!(start <= end);
StringInput {
start_pos_of_iter: start,
last_pos: start,
orig: src,
iter: src.char_indices(),
orig_start: start,
}
}
#[inline(always)]
pub fn as_str(&self) -> &str {
self.iter.as_str()
}
#[inline]
pub fn bump_bytes(&mut self, n: usize) {
unsafe {
// Safety: We only proceed, not go back.
self.reset_to(self.last_pos + BytePos(n as u32));
}
}
}
/// Creates an [Input] from [SourceFile]. This is an alias for
///
/// ```ignore
/// StringInput::new(&fm.src, fm.start_pos, fm.end_pos)
/// ```
impl<'a> From<&'a SourceFile> for StringInput<'a> {
fn from(fm: &'a SourceFile) -> Self {
StringInput::new(&fm.src, fm.start_pos, fm.end_pos)
}
}
impl<'a> Input for StringInput<'a> {
#[inline]
fn cur(&mut self) -> Option<char> {
self.iter.clone().next().map(|i| i.1)
}
#[inline]
fn peek(&mut self) -> Option<char> {
self.iter.clone().nth(1).map(|i| i.1)
}
#[inline]
fn peek_ahead(&mut self) -> Option<char> {
self.iter.clone().nth(2).map(|i| i.1)
}
#[inline]
unsafe fn bump(&mut self) {
if let Some((i, c)) = self.iter.next() {
self.last_pos = self.start_pos_of_iter + BytePos((i + c.len_utf8()) as u32);
} else {
unsafe {
debug_unreachable!("bump should not be called when cur() == None");
}
}
}
#[inline]
fn cur_as_ascii(&mut self) -> Option<u8> {
let first_byte = *self.as_str().as_bytes().first()?;
if first_byte <= 0x7f {
Some(first_byte)
} else {
None
}
}
#[inline]
fn is_at_start(&self) -> bool {
self.orig_start == self.last_pos
}
/// TODO(kdy1): Remove this?
#[inline]
fn cur_pos(&mut self) -> BytePos {
self.last_pos
}
#[inline]
fn last_pos(&self) -> BytePos {
self.last_pos
}
#[inline]
unsafe fn slice(&mut self, start: BytePos, end: BytePos) -> &str {
debug_assert!(start <= end, "Cannot slice {:?}..{:?}", start, end);
let s = self.orig;
let start_idx = (start - self.orig_start).0 as usize;
let end_idx = (end - self.orig_start).0 as usize;
debug_assert!(end_idx <= s.len());
let ret = unsafe { s.get_unchecked(start_idx..end_idx) };
self.iter = unsafe { s.get_unchecked(end_idx..) }.char_indices();
self.last_pos = end;
self.start_pos_of_iter = end;
ret
}
fn uncons_while<F>(&mut self, mut pred: F) -> &str
where
F: FnMut(char) -> bool,
{
let s = self.iter.as_str();
let mut last = 0;
for (i, c) in s.char_indices() {
if pred(c) {
last = i + c.len_utf8();
} else {
break;
}
}
debug_assert!(last <= s.len());
let ret = unsafe { s.get_unchecked(..last) };
self.last_pos = self.last_pos + BytePos(last as _);
self.start_pos_of_iter = self.last_pos;
self.iter = unsafe { s.get_unchecked(last..) }.char_indices();
ret
}
fn find<F>(&mut self, mut pred: F) -> Option<BytePos>
where
F: FnMut(char) -> bool,
{
let s = self.iter.as_str();
let mut last = 0;
for (i, c) in s.char_indices() {
if pred(c) {
last = i + c.len_utf8();
break;
}
}
if last == 0 {
return None;
}
debug_assert!(last <= s.len());
self.last_pos = self.last_pos + BytePos(last as _);
self.start_pos_of_iter = self.last_pos;
self.iter = unsafe { s.get_unchecked(last..) }.char_indices();
Some(self.last_pos)
}
#[inline]
unsafe fn reset_to(&mut self, to: BytePos) {
let orig = self.orig;
let idx = (to - self.orig_start).0 as usize;
debug_assert!(idx <= orig.len());
let s = unsafe { orig.get_unchecked(idx..) };
self.iter = s.char_indices();
self.start_pos_of_iter = to;
self.last_pos = to;
}
#[inline]
fn is_byte(&mut self, c: u8) -> bool {
self.iter
.as_str()
.as_bytes()
.first()
.map(|b| *b == c)
.unwrap_or(false)
}
#[inline]
fn is_str(&self, s: &str) -> bool {
self.as_str().starts_with(s)
}
#[inline]
fn eat_byte(&mut self, c: u8) -> bool {
if self.is_byte(c) {
if let Some((i, _)) = self.iter.next() {
self.last_pos = self.start_pos_of_iter + BytePos((i + 1) as u32);
} else {
unsafe {
debug_unreachable!(
"We can't enter here as we already checked the state using `is_byte`"
)
}
}
true
} else {
false
}
}
}
pub trait Input: Clone {
fn cur(&mut self) -> Option<char>;
fn peek(&mut self) -> Option<char>;
fn peek_ahead(&mut self) -> Option<char>;
/// # Safety
///
/// This should be called only when `cur()` returns `Some`. i.e.
/// when the Input is not empty.
unsafe fn bump(&mut self);
/// Returns [None] if it's end of input **or** current character is not an
/// ascii character.
#[inline]
fn cur_as_ascii(&mut self) -> Option<u8> {
self.cur().and_then(|i| {
if i.is_ascii() {
return Some(i as u8);
}
None
})
}
fn is_at_start(&self) -> bool;
fn cur_pos(&mut self) -> BytePos;
fn last_pos(&self) -> BytePos;
/// # Safety
///
/// - start should be less than or equal to end.
/// - start and end should be in the valid range of input.
unsafe fn slice(&mut self, start: BytePos, end: BytePos) -> &str;
/// Takes items from stream, testing each one with predicate. returns the
/// range of items which passed predicate.
fn uncons_while<F>(&mut self, f: F) -> &str
where
F: FnMut(char) -> bool;
/// This method modifies [last_pos()] and [cur_pos()].
fn find<F>(&mut self, f: F) -> Option<BytePos>
where
F: FnMut(char) -> bool;
/// # Safety
///
/// - `to` be in the valid range of input.
unsafe fn reset_to(&mut self, to: BytePos);
/// Implementors can override the method to make it faster.
///
/// `c` must be ASCII.
#[inline]
#[allow(clippy::wrong_self_convention)]
fn is_byte(&mut self, c: u8) -> bool {
match self.cur() {
Some(ch) => ch == c as char,
_ => false,
}
}
/// Implementors can override the method to make it faster.
///
/// `s` must be ASCII only.
fn is_str(&self, s: &str) -> bool;
/// Implementors can override the method to make it faster.
///
/// `c` must be ASCII.
#[inline]
fn eat_byte(&mut self, c: u8) -> bool {
if self.is_byte(c) {
unsafe {
// Safety: We are sure that the input is not empty
self.bump();
}
true
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use crate::{FileName, FilePathMapping, SourceMap};
fn with_test_sess<F>(src: &str, f: F)
where
F: FnOnce(StringInput<'_>),
{
let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let fm = cm.new_source_file(FileName::Real("testing".into()), src.into());
f((&*fm).into())
}
#[test]
fn src_input_slice_1() {
with_test_sess("foo/d", |mut i| {
assert_eq!(unsafe { i.slice(BytePos(1), BytePos(2)) }, "f");
assert_eq!(i.last_pos, BytePos(2));
assert_eq!(i.start_pos_of_iter, BytePos(2));
assert_eq!(i.cur(), Some('o'));
assert_eq!(unsafe { i.slice(BytePos(2), BytePos(4)) }, "oo");
assert_eq!(unsafe { i.slice(BytePos(1), BytePos(4)) }, "foo");
assert_eq!(i.last_pos, BytePos(4));
assert_eq!(i.start_pos_of_iter, BytePos(4));
assert_eq!(i.cur(), Some('/'));
});
}
#[test]
fn src_input_reset_to_1() {
with_test_sess("load", |mut i| {
assert_eq!(unsafe { i.slice(BytePos(1), BytePos(3)) }, "lo");
assert_eq!(i.last_pos, BytePos(3));
assert_eq!(i.start_pos_of_iter, BytePos(3));
assert_eq!(i.cur(), Some('a'));
unsafe { i.reset_to(BytePos(1)) };
assert_eq!(i.cur(), Some('l'));
assert_eq!(i.last_pos, BytePos(1));
assert_eq!(i.start_pos_of_iter, BytePos(1));
});
}
#[test]
fn src_input_smoke_01() {
with_test_sess("foo/d", |mut i| {
assert_eq!(i.cur_pos(), BytePos(1));
assert_eq!(i.last_pos, BytePos(1));
assert_eq!(i.start_pos_of_iter, BytePos(1));
assert_eq!(i.uncons_while(|c| c.is_alphabetic()), "foo");
// assert_eq!(i.cur_pos(), BytePos(4));
assert_eq!(i.last_pos, BytePos(4));
assert_eq!(i.start_pos_of_iter, BytePos(4));
assert_eq!(i.cur(), Some('/'));
unsafe {
i.bump();
}
assert_eq!(i.last_pos, BytePos(5));
assert_eq!(i.cur(), Some('d'));
unsafe {
i.bump();
}
assert_eq!(i.last_pos, BytePos(6));
assert_eq!(i.cur(), None);
});
}
#[test]
fn src_input_find_01() {
with_test_sess("foo/d", |mut i| {
assert_eq!(i.cur_pos(), BytePos(1));
assert_eq!(i.last_pos, BytePos(1));
assert_eq!(i.start_pos_of_iter, BytePos(1));
assert_eq!(i.find(|c| c == '/'), Some(BytePos(5)));
assert_eq!(i.start_pos_of_iter, BytePos(5));
assert_eq!(i.last_pos, BytePos(5));
assert_eq!(i.cur(), Some('d'));
});
}
// #[test]
// fn src_input_smoke_02() {
// let _ = crate::with_test_sess("℘℘/℘℘", | mut i| {
// assert_eq!(i.iter.as_str(), "℘℘/℘℘");
// assert_eq!(i.cur_pos(), BytePos(0));
// assert_eq!(i.last_pos, BytePos(0));
// assert_eq!(i.start_pos, BytePos(0));
// assert_eq!(i.uncons_while(|c| c.is_ident_part()), "℘℘");
//
// assert_eq!(i.iter.as_str(), "/℘℘");
// assert_eq!(i.last_pos, BytePos(6));
// assert_eq!(i.start_pos, BytePos(6));
// assert_eq!(i.cur(), Some('/'));
// i.bump();
// assert_eq!(i.last_pos, BytePos(7));
// assert_eq!(i.start_pos, BytePos(6));
//
// assert_eq!(i.iter.as_str(), "℘℘");
// assert_eq!(i.uncons_while(|c| c.is_ident_part()), "℘℘");
// assert_eq!(i.last_pos, BytePos(13));
// assert_eq!(i.start_pos, BytePos(13));
//
// Ok(())
// });
// }
}

View file

@ -0,0 +1,45 @@
/// Copied from https://users.rust-lang.org/t/iterator-need-to-identify-the-last-element/8836/3
pub trait IdentifyLast: Iterator + Sized {
fn identify_last(self) -> Iter<Self>;
}
impl<It> IdentifyLast for It
where
It: Iterator,
{
fn identify_last(mut self) -> Iter<Self> {
let e = self.next();
Iter {
iter: self,
buffer: e,
}
}
}
pub struct Iter<It>
where
It: Iterator,
{
iter: It,
buffer: Option<It::Item>,
}
impl<It> Iterator for Iter<It>
where
It: Iterator,
{
type Item = (bool, It::Item);
fn next(&mut self) -> Option<Self::Item> {
match self.buffer.take() {
None => None,
Some(e) => match self.iter.next() {
None => Some((true, e)),
Some(f) => {
self.buffer = Some(f);
Some((false, e))
}
},
}
}
}

View file

@ -0,0 +1,92 @@
//! Utilities for the swc project
//!
//!
//! # Cargo features
//!
//! ## `tty-emitter`
//!
//! Adds default implementation of Emitter.
//! Enabling this feature will add tty-related dependencies.
//!
//! ## `sourcemap`
//!
//! Adds methods to generate web sourcemap.
//!
//! ## `plugin-base`
//!
//! Base mode for plugins, which can be enabled by `plugin-mode` or `plugin-rt`.
//!
//! This mode creates a trait which can be used to override `swc_common` itself.
//!
//! ## `plugin-rt`
//!
//! Creates an implementation for the plugin trait. This implements simply
//! invokes thread-locals declared in `swc_common`.
//!
//! ## `plugin-mode`
//!
//! Allows replacing operations related to thread-local variables with a trait.
//!
//!
//! ## `ahash`
//!
//! Use `ahash` instead of `rustc_hash` for `AHashMap` and `AHashSet`.
#![deny(clippy::all)]
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::fmt::Debug;
pub use ast_node::{ast_node, ast_serde, DeserializeEnum, Spanned};
pub use from_variant::FromVariant;
pub use swc_eq_ignore_macros::{EqIgnoreSpan, TypeEq};
pub use swc_visit::chain;
pub use self::{
eq::{EqIgnoreSpan, TypeEq},
errors::{SourceMapper, SourceMapperDyn},
pos::{
hygiene, BytePos, CharPos, FileName, Globals, Loc, LocWithOpt, Mark, MultiSpan, SourceFile,
SourceFileAndBytePos, SourceFileAndLine, Span, SpanLinesError, Spanned, SyntaxContext,
DUMMY_SP, GLOBALS, NO_EXPANSION,
},
source_map::{FileLines, FileLoader, FilePathMapping, SourceMap, SpanSnippetError},
syntax_pos::LineCol,
};
#[doc(hidden)]
pub mod private;
/// A trait for ast nodes.
pub trait AstNode: Debug + PartialEq + Clone + Spanned {
const TYPE: &'static str;
}
pub mod collections;
pub mod comments;
mod eq;
pub mod errors;
pub mod input;
pub mod iter;
pub mod pass;
pub mod plugin;
mod pos;
mod rustc_data_structures;
pub mod serializer;
pub mod source_map;
pub mod sync;
mod syntax_pos;
pub mod util;
#[cfg(all(not(debug_assertions), feature = "plugin-rt", feature = "plugin-mode"))]
compile_error!("You can't enable `plugin-rt` and `plugin-mode` at the same time");
/// Warning: The particular implementation of serialization and deserialization
/// of the ast nodes may change in the future, and so these types would be
/// removed. It's safe to say they will be serializable in some form or another,
/// but not necessarily with these specific types underlying the implementation.
/// As such, *use these types at your own risk*.
#[cfg(feature = "rkyv-impl")]
#[doc(hidden)]
pub use self::syntax_pos::{
ArchivedBytePos, ArchivedCharPos, ArchivedFileName, ArchivedMultiSpan, ArchivedSourceFile,
ArchivedSourceFileAndBytePos, ArchivedSpan, ArchivedSpanLinesError, ArchivedSpanSnippetError,
};

View file

@ -0,0 +1,32 @@
//! This module reexports items from `swc_visit` with some swc-specific traits.
use std::borrow::Cow;
pub use swc_visit::*;
/// A named compiler pass.
pub trait CompilerPass {
///
/// - name should follow hyphen-case.
/// - an implementation should return same name
fn name() -> Cow<'static, str>;
}
impl<V> CompilerPass for Repeat<V>
where
V: CompilerPass + Repeated,
{
fn name() -> Cow<'static, str> {
Cow::Owned(format!("repeat({})", V::name()))
}
}
impl<A, B> CompilerPass for AndThen<A, B>
where
A: CompilerPass,
B: CompilerPass,
{
fn name() -> Cow<'static, str> {
Cow::Owned(format!("{} -> {}", A::name(), B::name()))
}
}

View file

@ -0,0 +1,17 @@
/// A serializable, wrapped struct for the diagnostics information
/// included in plugin binaries.
/// TODO: Must implement bytecheck with forward-compatible schema changes to
/// prevent handshake failure.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
pub struct PluginCorePkgDiagnostics {
pub pkg_version: String,
pub git_sha: String,
pub cargo_features: String,
pub ast_schema_version: u32,
}

View file

@ -0,0 +1,67 @@
use std::env;
use crate::collections::AHashMap;
/// Indexable key to the metadata context for a transform plugin, avoiding
/// serialization & allocation to the host by using incremental number.
/// TransformPluginMetadataContext does not implement Index trait, instead
/// host does manual matching to corresponding value.
#[derive(Copy, Clone)]
pub enum TransformPluginMetadataContextKind {
// This value always should increase, even if some keys are removed in the
// future.
Filename = 1,
Env = 2,
Cwd = 3,
}
impl From<u32> for TransformPluginMetadataContextKind {
fn from(key: u32) -> TransformPluginMetadataContextKind {
match key {
1 => TransformPluginMetadataContextKind::Filename,
2 => TransformPluginMetadataContextKind::Env,
3 => TransformPluginMetadataContextKind::Cwd,
_ => panic!("Invalid TransformPluginMetadataContextKind key"),
}
}
}
/// Host side metadata context plugin may need to access.
/// This is a global context - any plugin in single transform will have same
/// values.
pub struct TransformPluginMetadataContext {
/// The path of the file being processed. This includes all of the path as
/// much as possible.
pub filename: Option<String>,
/// The current environment resolved as process.env.SWC_ENV ||
/// process.env.NODE_ENV || "development"
pub env: String,
/// The current working directory.
pub cwd: Option<String>,
pub experimental: AHashMap<String, String>,
}
impl TransformPluginMetadataContext {
pub fn new(
filename: Option<String>,
env: String,
experimental: Option<AHashMap<String, String>>,
) -> Self {
TransformPluginMetadataContext {
filename,
env,
cwd: env::current_dir()
.map(|cwd| cwd.as_path().to_string_lossy().to_string())
.ok(),
experimental: experimental.unwrap_or_default(),
}
}
pub fn get(&self, key: &TransformPluginMetadataContextKind) -> Option<String> {
match key {
TransformPluginMetadataContextKind::Filename => self.filename.clone(),
TransformPluginMetadataContextKind::Env => Some(self.env.clone()),
TransformPluginMetadataContextKind::Cwd => self.cwd.clone(),
}
}
}

View file

@ -0,0 +1,27 @@
pub mod diagnostics;
pub mod metadata;
#[cfg(feature = "__plugin")]
#[cfg_attr(docsrs, doc(cfg(feature = "__plugin")))]
pub mod serialized;
/**
* Compile-time version constant for the AST struct schema's version.
*
* NOTE: this is for PARTIAL compatibility only, supporting if AST struct
* adds new properties without changing / removing existing properties.
*
* - When adding a new properties to the AST struct:
* 1. Create a new feature flag in cargo.toml
* 2. Create a new schema version with new feature flag.
* 3. Create a new AST struct with compile time feature flag with newly
* added properties. Previous struct should remain with existing feature
* flag, or add previous latest feature flag.
*
* - When removing, or changing existing properties in the AST struct: TBD
*/
#[cfg(feature = "plugin_transform_schema_v1")]
pub const PLUGIN_TRANSFORM_AST_SCHEMA_VERSION: u32 = 1;
// Reserved for the testing purpose.
#[cfg(feature = "plugin_transform_schema_vtest")]
pub const PLUGIN_TRANSFORM_AST_SCHEMA_VERSION: u32 = u32::MAX - 1;

View file

@ -0,0 +1,151 @@
use std::any::type_name;
use anyhow::Error;
#[cfg(feature = "__rkyv")]
use rkyv::Deserialize;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
#[cfg_attr(
feature = "__plugin",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "__plugin", archive(check_bytes))]
#[cfg_attr(feature = "__plugin", archive_attr(repr(u32)))]
/// Enum for possible errors while running transform via plugin.
/// This error indicates internal operation failure either in plugin_runner
/// or plugin_macro. Plugin's transform fn itself does not allow to return
/// error - instead it should use provided `handler` to emit corresponding error
/// to the host.
pub enum PluginError {
/// Occurs when failed to convert size passed from host / guest into usize
/// or similar for the conversion. This is an internal error rasied via
/// plugin_macro, normally plugin author should not raise this manually.
SizeInteropFailure(String),
/// Occurs when failed to reconstruct a struct from `Serialized`.
Deserialize(String),
/// Occurs when failed to serialize a struct into `Serialized`.
/// Unlike deserialize error, this error cannot forward any context for the
/// raw bytes: when serialize failed, there's nothing we can pass between
/// runtime.
Serialize(String),
}
/// Wraps internal representation of serialized data for exchanging data between
/// plugin to the host. Consumers should not rely on specific details of byte
/// format struct contains: it is strict implementation detail which can
/// change anytime.
pub struct PluginSerializedBytes {
pub(crate) field: rkyv::AlignedVec,
}
#[cfg(feature = "__plugin")]
impl PluginSerializedBytes {
/**
* Constructs an instance from already serialized byte
* slices.
*/
#[tracing::instrument(level = "info", skip_all)]
pub fn from_slice(bytes: &[u8]) -> PluginSerializedBytes {
let mut field = rkyv::AlignedVec::new();
field.extend_from_slice(bytes);
PluginSerializedBytes { field }
}
/**
* Constructs an instance from versioned struct by serializing it.
*
* This is sort of mimic TryFrom behavior, since we can't use generic
* to implement TryFrom trait
*/
#[tracing::instrument(level = "info", skip_all)]
pub fn try_serialize<W>(t: &VersionedSerializable<W>) -> Result<Self, Error>
where
W: rkyv::Serialize<rkyv::ser::serializers::AllocSerializer<512>>,
{
rkyv::to_bytes::<_, 512>(t)
.map(|field| PluginSerializedBytes { field })
.map_err(|err| match err {
rkyv::ser::serializers::CompositeSerializerError::SerializerError(e) => e.into(),
rkyv::ser::serializers::CompositeSerializerError::ScratchSpaceError(_e) => {
Error::msg("AllocScratchError")
}
rkyv::ser::serializers::CompositeSerializerError::SharedError(_e) => {
Error::msg("SharedSerializeMapError")
}
})
}
/*
* Internal fn to constructs an instance from raw bytes ptr.
*/
#[tracing::instrument(level = "info", skip_all)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn from_raw_ptr(
raw_allocated_ptr: *const u8,
raw_allocated_ptr_len: usize,
) -> PluginSerializedBytes {
let raw_ptr_bytes =
unsafe { std::slice::from_raw_parts(raw_allocated_ptr, raw_allocated_ptr_len) };
PluginSerializedBytes::from_slice(raw_ptr_bytes)
}
pub fn as_slice(&self) -> &[u8] {
self.field.as_slice()
}
pub fn as_ptr(&self) -> (*const u8, usize) {
(self.field.as_ptr(), self.field.len())
}
#[tracing::instrument(level = "info", skip_all)]
pub fn deserialize<W>(&self) -> Result<VersionedSerializable<W>, Error>
where
W: rkyv::Archive,
W::Archived: rkyv::Deserialize<W, rkyv::de::deserializers::SharedDeserializeMap>,
{
use anyhow::Context;
let archived = unsafe { rkyv::archived_root::<VersionedSerializable<W>>(&self.field[..]) };
archived
.deserialize(&mut rkyv::de::deserializers::SharedDeserializeMap::new())
.with_context(|| format!("failed to deserialize `{}`", type_name::<W>()))
}
}
/// A wrapper type for the structures to be passed into plugins
/// serializes the contained value out-of-line so that newer
/// versions can be viewed as the older version.
///
/// First field indicate version of struct type (schema). Any consumers like
/// swc_plugin_macro can use this to validate compatiblility before attempt to
/// serialize.
#[cfg_attr(
feature = "__plugin",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[repr(transparent)]
#[cfg_attr(feature = "__plugin", archive(check_bytes))]
#[cfg_attr(feature = "__plugin", archive_attr(repr(transparent)))]
#[derive(Debug)]
pub struct VersionedSerializable<T>(
// [NOTE]: https://github.com/rkyv/rkyv/issues/373#issuecomment-1546360897
//#[cfg_attr(feature = "__plugin", with(rkyv::with::AsBox))]
pub T,
);
impl<T> VersionedSerializable<T> {
pub fn new(value: T) -> Self {
Self(value)
}
pub fn inner(&self) -> &T {
&self.0
}
pub fn into_inner(self) -> T {
self.0
}
}

192
third-party/vendor/swc_common/src/pos.rs vendored Normal file
View file

@ -0,0 +1,192 @@
use std::{borrow::Cow, rc::Rc, sync::Arc};
pub use crate::syntax_pos::{
hygiene, BytePos, CharPos, FileName, Globals, Loc, LocWithOpt, Mark, MultiSpan, SourceFile,
SourceFileAndBytePos, SourceFileAndLine, Span, SpanLinesError, SyntaxContext, DUMMY_SP,
GLOBALS, NO_EXPANSION,
};
///
/// # Derive
/// This trait can be derived with `#[derive(Spanned)]`.
pub trait Spanned {
/// Get span of `self`.
fn span(&self) -> Span;
#[inline]
fn span_lo(&self) -> BytePos {
self.span().lo
}
#[inline]
fn span_hi(&self) -> BytePos {
self.span().hi
}
}
impl<'a, T> Spanned for Cow<'a, T>
where
T: Spanned + Clone,
{
#[inline]
fn span(&self) -> Span {
(**self).span()
}
#[inline]
fn span_lo(&self) -> BytePos {
(**self).span_lo()
}
#[inline]
fn span_hi(&self) -> BytePos {
(**self).span_hi()
}
}
impl Spanned for Span {
#[inline(always)]
fn span(&self) -> Span {
*self
}
}
impl Spanned for BytePos {
/// Creates a new single-byte span.
#[inline(always)]
fn span(&self) -> Span {
Span::new(*self, *self, Default::default())
}
}
impl<S> Spanned for Option<S>
where
S: Spanned,
{
#[inline]
fn span(&self) -> Span {
match *self {
Some(ref s) => s.span(),
None => DUMMY_SP,
}
}
#[inline]
fn span_lo(&self) -> BytePos {
match *self {
Some(ref s) => s.span_lo(),
None => BytePos::DUMMY,
}
}
#[inline]
fn span_hi(&self) -> BytePos {
match *self {
Some(ref s) => s.span_hi(),
None => BytePos::DUMMY,
}
}
}
impl<S> Spanned for Rc<S>
where
S: ?Sized + Spanned,
{
fn span(&self) -> Span {
<S as Spanned>::span(self)
}
#[inline]
fn span_lo(&self) -> BytePos {
<S as Spanned>::span_lo(self)
}
#[inline]
fn span_hi(&self) -> BytePos {
<S as Spanned>::span_hi(self)
}
}
impl<S> Spanned for Arc<S>
where
S: ?Sized + Spanned,
{
fn span(&self) -> Span {
<S as Spanned>::span(self)
}
#[inline]
fn span_lo(&self) -> BytePos {
<S as Spanned>::span_lo(self)
}
#[inline]
fn span_hi(&self) -> BytePos {
<S as Spanned>::span_hi(self)
}
}
impl<S> Spanned for Box<S>
where
S: ?Sized + Spanned,
{
fn span(&self) -> Span {
<S as Spanned>::span(self)
}
#[inline]
fn span_lo(&self) -> BytePos {
<S as Spanned>::span_lo(self)
}
#[inline]
fn span_hi(&self) -> BytePos {
<S as Spanned>::span_hi(self)
}
}
impl<'a, S> Spanned for &'a S
where
S: ?Sized + Spanned,
{
fn span(&self) -> Span {
<S as Spanned>::span(self)
}
#[inline]
fn span_lo(&self) -> BytePos {
<S as Spanned>::span_lo(self)
}
#[inline]
fn span_hi(&self) -> BytePos {
<S as Spanned>::span_hi(self)
}
}
impl<A, B> Spanned for ::either::Either<A, B>
where
A: Spanned,
B: Spanned,
{
fn span(&self) -> Span {
match *self {
::either::Either::Left(ref n) => n.span(),
::either::Either::Right(ref n) => n.span(),
}
}
fn span_lo(&self) -> BytePos {
match *self {
::either::Either::Left(ref n) => n.span_lo(),
::either::Either::Right(ref n) => n.span_lo(),
}
}
fn span_hi(&self) -> BytePos {
match *self {
::either::Either::Left(ref n) => n.span_hi(),
::either::Either::Right(ref n) => n.span_hi(),
}
}
}

View file

@ -0,0 +1,3 @@
//! This module is private module and can be changed without notice.
pub use serde::__private as serde;

View file

@ -0,0 +1,2 @@
#![allow(clippy::all)]
pub mod stable_hasher;

View file

@ -0,0 +1,523 @@
use std::{
hash::{BuildHasher, Hash, Hasher},
mem,
};
use siphasher::sip128::{Hash128, Hasher128, SipHasher24};
/// When hashing something that ends up affecting properties like symbol names,
/// we want these symbol names to be calculated independently of other factors
/// like what architecture you're compiling *from*.
///
/// To that end we always convert integers to little-endian format before
/// hashing and the architecture dependent `isize` and `usize` types are
/// extended to 64 bits if needed.
pub struct StableHasher {
state: SipHasher24,
}
impl ::std::fmt::Debug for StableHasher {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "{:?}", self.state)
}
}
pub trait StableHasherResult: Sized {
fn finish(hasher: StableHasher) -> Self;
}
impl StableHasher {
#[inline]
pub fn new() -> Self {
StableHasher {
state: SipHasher24::new_with_keys(0, 0),
}
}
#[inline]
pub fn finish<W: StableHasherResult>(self) -> W {
W::finish(self)
}
}
impl StableHasherResult for u128 {
#[inline]
fn finish(hasher: StableHasher) -> Self {
hasher.finalize().as_u128()
}
}
impl StableHasherResult for u64 {
#[inline]
fn finish(hasher: StableHasher) -> Self {
hasher.finalize().h1
}
}
impl StableHasher {
#[inline]
pub fn finalize(self) -> Hash128 {
self.state.finish128()
}
}
impl Hasher for StableHasher {
fn finish(&self) -> u64 {
panic!("use StableHasher::finalize instead");
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
self.state.write(bytes);
}
#[inline]
fn write_u8(&mut self, i: u8) {
self.state.write_u8(i);
}
#[inline]
fn write_u16(&mut self, i: u16) {
self.state.write_u16(i.to_le());
}
#[inline]
fn write_u32(&mut self, i: u32) {
self.state.write_u32(i.to_le());
}
#[inline]
fn write_u64(&mut self, i: u64) {
self.state.write_u64(i.to_le());
}
#[inline]
fn write_u128(&mut self, i: u128) {
self.state.write_u128(i.to_le());
}
#[inline]
fn write_usize(&mut self, i: usize) {
// Always treat usize as u64 so we get the same results on 32 and 64 bit
// platforms. This is important for symbol hashes when cross compiling,
// for example.
self.state.write_u64((i as u64).to_le());
}
#[inline]
fn write_i8(&mut self, i: i8) {
self.state.write_i8(i);
}
#[inline]
fn write_i16(&mut self, i: i16) {
self.state.write_i16(i.to_le());
}
#[inline]
fn write_i32(&mut self, i: i32) {
self.state.write_i32(i.to_le());
}
#[inline]
fn write_i64(&mut self, i: i64) {
self.state.write_i64(i.to_le());
}
#[inline]
fn write_i128(&mut self, i: i128) {
self.state.write_i128(i.to_le());
}
#[inline]
fn write_isize(&mut self, i: isize) {
// Always treat isize as a 64-bit number so we get the same results on 32 and 64
// bit platforms. This is important for symbol hashes when cross
// compiling, for example. Sign extending here is preferable as it means
// that the same negative number hashes the same on both 32 and 64 bit
// platforms.
let value = i as u64;
// Cold path
#[cold]
#[inline(never)]
fn hash_value(state: &mut SipHasher24, value: u64) {
state.write_u8(0xff);
state.write_u64(value.to_le());
}
// `isize` values often seem to have a small (positive) numeric value in
// practice. To exploit this, if the value is small, we will hash a
// smaller amount of bytes. However, we cannot just skip the leading
// zero bytes, as that would produce the same hash e.g. if you hash two
// values that have the same bit pattern when they are swapped. See https://github.com/rust-lang/rust/pull/93014 for context.
//
// Therefore, we employ the following strategy:
// 1) When we encounter a value that fits within a single byte (the most common
// case), we hash just that byte. This is the most common case that is
// being optimized. However, we do not do this for the value 0xFF, as
// that is a reserved prefix (a bit like in UTF-8). 2) When we encounter
// a larger value, we hash a "marker" 0xFF and then the corresponding
// 8 bytes. Since this prefix cannot occur when we hash a single byte, when we
// hash two `isize`s that fit within a different amount of bytes, they
// should always produce a different byte stream for the hasher.
if value < 0xff {
self.state.write_u8(value as u8);
} else {
hash_value(&mut self.state, value);
}
}
}
/// Something that implements `HashStable<CTX>` can be hashed in a way that is
/// stable across multiple compilation sessions.
pub trait HashStable<CTX> {
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher);
}
/// Implement this for types that can be turned into stable keys like, for
/// example, for DefId that can be converted to a DefPathHash. This is used for
/// bringing maps into a predictable order before hashing them.
pub trait ToStableHashKey<HCX> {
type KeyType: Ord + Clone + Sized + HashStable<HCX>;
fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType;
}
// Implement HashStable by just calling `Hash::hash()`. This works fine for
// self-contained values that don't depend on the hashing context `CTX`.
#[macro_export]
macro_rules! impl_stable_hash_via_hash {
($t:ty) => {
impl<CTX> $crate::rustc_data_structures::stable_hasher::HashStable<CTX> for $t {
#[inline]
fn hash_stable(
&self,
_: &mut CTX,
hasher: &mut $crate::rustc_data_structures::stable_hasher::StableHasher,
) {
::std::hash::Hash::hash(self, hasher);
}
}
};
}
impl_stable_hash_via_hash!(i8);
impl_stable_hash_via_hash!(i16);
impl_stable_hash_via_hash!(i32);
impl_stable_hash_via_hash!(i64);
impl_stable_hash_via_hash!(isize);
impl_stable_hash_via_hash!(u8);
impl_stable_hash_via_hash!(u16);
impl_stable_hash_via_hash!(u32);
impl_stable_hash_via_hash!(u64);
impl_stable_hash_via_hash!(usize);
impl_stable_hash_via_hash!(u128);
impl_stable_hash_via_hash!(i128);
impl_stable_hash_via_hash!(char);
impl_stable_hash_via_hash!(());
impl<CTX> HashStable<CTX> for ::std::num::NonZeroU32 {
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
self.get().hash_stable(ctx, hasher)
}
}
impl<CTX> HashStable<CTX> for f32 {
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
let val: u32 = unsafe { ::std::mem::transmute(*self) };
val.hash_stable(ctx, hasher);
}
}
impl<CTX> HashStable<CTX> for f64 {
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
let val: u64 = unsafe { ::std::mem::transmute(*self) };
val.hash_stable(ctx, hasher);
}
}
impl<CTX> HashStable<CTX> for ::std::cmp::Ordering {
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
(*self as i8).hash_stable(ctx, hasher);
}
}
impl<T1: HashStable<CTX>, CTX> HashStable<CTX> for (T1,) {
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
let (ref _0,) = *self;
_0.hash_stable(ctx, hasher);
}
}
impl<T1: HashStable<CTX>, T2: HashStable<CTX>, CTX> HashStable<CTX> for (T1, T2) {
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
let (ref _0, ref _1) = *self;
_0.hash_stable(ctx, hasher);
_1.hash_stable(ctx, hasher);
}
}
impl<T1, T2, T3, CTX> HashStable<CTX> for (T1, T2, T3)
where
T1: HashStable<CTX>,
T2: HashStable<CTX>,
T3: HashStable<CTX>,
{
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
let (ref _0, ref _1, ref _2) = *self;
_0.hash_stable(ctx, hasher);
_1.hash_stable(ctx, hasher);
_2.hash_stable(ctx, hasher);
}
}
impl<T1, T2, T3, T4, CTX> HashStable<CTX> for (T1, T2, T3, T4)
where
T1: HashStable<CTX>,
T2: HashStable<CTX>,
T3: HashStable<CTX>,
T4: HashStable<CTX>,
{
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
let (ref _0, ref _1, ref _2, ref _3) = *self;
_0.hash_stable(ctx, hasher);
_1.hash_stable(ctx, hasher);
_2.hash_stable(ctx, hasher);
_3.hash_stable(ctx, hasher);
}
}
impl<T: HashStable<CTX>, CTX> HashStable<CTX> for [T] {
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
self.len().hash_stable(ctx, hasher);
for item in self {
item.hash_stable(ctx, hasher);
}
}
}
impl<T: HashStable<CTX>, CTX> HashStable<CTX> for Vec<T> {
#[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
(&self[..]).hash_stable(ctx, hasher);
}
}
impl<T: ?Sized + HashStable<CTX>, CTX> HashStable<CTX> for Box<T> {
#[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
(**self).hash_stable(ctx, hasher);
}
}
impl<T: ?Sized + HashStable<CTX>, CTX> HashStable<CTX> for ::std::rc::Rc<T> {
#[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
(**self).hash_stable(ctx, hasher);
}
}
impl<T: ?Sized + HashStable<CTX>, CTX> HashStable<CTX> for ::std::sync::Arc<T> {
#[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
(**self).hash_stable(ctx, hasher);
}
}
impl<CTX> HashStable<CTX> for str {
#[inline]
fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) {
self.len().hash(hasher);
self.as_bytes().hash(hasher);
}
}
impl<CTX> HashStable<CTX> for String {
#[inline]
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
(&self[..]).hash_stable(hcx, hasher);
}
}
impl<HCX> ToStableHashKey<HCX> for String {
type KeyType = String;
#[inline]
fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
self.clone()
}
}
impl<CTX> HashStable<CTX> for bool {
#[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
(if *self { 1u8 } else { 0u8 }).hash_stable(ctx, hasher);
}
}
impl<T, CTX> HashStable<CTX> for Option<T>
where
T: HashStable<CTX>,
{
#[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
if let Some(ref value) = *self {
1u8.hash_stable(ctx, hasher);
value.hash_stable(ctx, hasher);
} else {
0u8.hash_stable(ctx, hasher);
}
}
}
impl<T1, T2, CTX> HashStable<CTX> for Result<T1, T2>
where
T1: HashStable<CTX>,
T2: HashStable<CTX>,
{
#[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
mem::discriminant(self).hash_stable(ctx, hasher);
match *self {
Ok(ref x) => x.hash_stable(ctx, hasher),
Err(ref x) => x.hash_stable(ctx, hasher),
}
}
}
impl<'a, T, CTX> HashStable<CTX> for &'a T
where
T: HashStable<CTX> + ?Sized,
{
#[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
(**self).hash_stable(ctx, hasher);
}
}
impl<T, CTX> HashStable<CTX> for ::std::mem::Discriminant<T> {
#[inline]
fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) {
::std::hash::Hash::hash(self, hasher);
}
}
// impl<I: ::indexed_vec::Idx, T, CTX> HashStable<CTX> for
// ::indexed_vec::IndexVec<I, T> where
// T: HashStable<CTX>,
// {
// fn hash_stable<W: StableHasherResult>(&self, ctx: &mut CTX, hasher: &mut
// StableHasher<W>) { self.len().hash_stable(ctx, hasher);
// for v in &self.raw {
// v.hash_stable(ctx, hasher);
// }
// }
// }
// impl<I: ::indexed_vec::Idx, CTX> HashStable<CTX> for ::bit_set::BitSet<I> {
// fn hash_stable<W: StableHasherResult>(&self, ctx: &mut CTX, hasher: &mut
// StableHasher<W>) { self.words().hash_stable(ctx, hasher);
// }
// }
impl_stable_hash_via_hash!(::std::path::Path);
impl_stable_hash_via_hash!(::std::path::PathBuf);
impl<K, V, R, HCX> HashStable<HCX> for ::std::collections::HashMap<K, V, R>
where
K: ToStableHashKey<HCX> + Eq,
V: HashStable<HCX>,
R: BuildHasher,
{
#[inline]
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
stable_hash_reduce(
hcx,
hasher,
self.iter(),
self.len(),
|hasher, hcx, (key, value)| {
let key = key.to_stable_hash_key(hcx);
key.hash_stable(hcx, hasher);
value.hash_stable(hcx, hasher);
},
);
}
}
impl<K, R, HCX> HashStable<HCX> for ::std::collections::HashSet<K, R>
where
K: ToStableHashKey<HCX> + Eq,
R: BuildHasher,
{
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
stable_hash_reduce(hcx, hasher, self.iter(), self.len(), |hasher, hcx, key| {
let key = key.to_stable_hash_key(hcx);
key.hash_stable(hcx, hasher);
});
}
}
impl<K, V, HCX> HashStable<HCX> for ::std::collections::BTreeMap<K, V>
where
K: ToStableHashKey<HCX>,
V: HashStable<HCX>,
{
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
stable_hash_reduce(
hcx,
hasher,
self.iter(),
self.len(),
|hasher, hcx, (key, value)| {
let key = key.to_stable_hash_key(hcx);
key.hash_stable(hcx, hasher);
value.hash_stable(hcx, hasher);
},
);
}
}
impl<K, HCX> HashStable<HCX> for ::std::collections::BTreeSet<K>
where
K: ToStableHashKey<HCX>,
{
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
stable_hash_reduce(hcx, hasher, self.iter(), self.len(), |hasher, hcx, key| {
let key = key.to_stable_hash_key(hcx);
key.hash_stable(hcx, hasher);
});
}
}
fn stable_hash_reduce<HCX, I, C, F>(
hcx: &mut HCX,
hasher: &mut StableHasher,
mut collection: C,
length: usize,
hash_function: F,
) where
C: Iterator<Item = I>,
F: Fn(&mut StableHasher, &mut HCX, I),
{
length.hash_stable(hcx, hasher);
match length {
1 => {
hash_function(hasher, hcx, collection.next().unwrap());
}
_ => {
let hash = collection
.map(|value| {
let mut hasher = StableHasher::new();
hash_function(&mut hasher, hcx, value);
hasher.finish::<u128>()
})
.reduce(|accum, value| accum.wrapping_add(value));
hash.hash_stable(hcx, hasher);
}
}
}

View file

@ -0,0 +1,18 @@
#![allow(deprecated)]
#![deprecated = "Not used by swc, and this will be removed with next breaking change"]
use serde::Deserialize;
#[derive(Deserialize)]
#[deprecated = "Not used by swc, and this will be removed with next breaking change"]
pub struct Node<T> {
#[serde(default, rename = "type")]
pub ty: String,
#[serde(flatten)]
pub node: T,
}
#[derive(Deserialize)]
pub struct Type {
#[serde(rename = "type")]
pub ty: String,
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,329 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! This module defines types which are thread safe if `cfg!(feature =
//! "concurrent")` is true.
//!
//! `Lrc` is an alias of either Rc or Arc.
//!
//! `Lock` is a mutex.
//! It internally uses `parking_lot::Mutex` if cfg!(parallel_queries) is true,
//! `RefCell` otherwise.
//!
//! `RwLock` is a read-write lock.
//! It internally uses `parking_lot::RwLock` if cfg!(parallel_queries) is true,
//! `RefCell` otherwise.
//!
//! `LockCell` is a thread safe version of `Cell`, with `set` and `get`
//! operations. It can never deadlock. It uses `Cell` when
//! cfg!(parallel_queries) is false, otherwise it is a `Lock`.
//!
//! `MTLock` is a mutex which disappears if cfg!(parallel_queries) is false.
//!
//! `MTRef` is a immutable reference if cfg!(parallel_queries), and an mutable
//! reference otherwise.
//!
//! `rustc_erase_owner!` erases a OwningRef owner into Erased or Erased + Send +
//! Sync depending on the value of cfg!(parallel_queries).
#[cfg(not(feature = "concurrent"))]
use std::cell::{RefCell as InnerRwLock, RefCell as InnerLock};
use std::{
cmp::Ordering,
collections::HashMap,
fmt,
fmt::{Debug, Formatter},
hash::{BuildHasher, Hash},
};
#[cfg(feature = "concurrent")]
use parking_lot::{Mutex as InnerLock, RwLock as InnerRwLock};
#[cfg(feature = "concurrent")]
pub use self::concurrent::*;
#[cfg(not(feature = "concurrent"))]
pub use self::single::*;
#[cfg(feature = "concurrent")]
mod concurrent {
pub use std::{
marker::{Send, Sync},
sync::Arc as Lrc,
};
pub use once_cell::sync::{Lazy, OnceCell};
pub use parking_lot::{
MappedMutexGuard as MappedLockGuard, MappedRwLockReadGuard as MappedReadGuard,
MappedRwLockWriteGuard as MappedWriteGuard, MutexGuard as LockGuard,
RwLockReadGuard as ReadGuard, RwLockWriteGuard as WriteGuard,
};
}
#[cfg(not(feature = "concurrent"))]
mod single {
pub use once_cell::unsync::{Lazy, OnceCell};
/// Dummy trait because swc_common is in single thread mode.
pub trait Send {}
/// Dummy trait because swc_common is in single thread mode.
pub trait Sync {}
impl<T> Send for T where T: ?Sized {}
impl<T> Sync for T where T: ?Sized {}
pub use std::{
cell::{
Ref as ReadGuard, RefMut as WriteGuard, RefMut as MappedWriteGuard,
RefMut as LockGuard, RefMut as MappedLockGuard,
},
rc::{Rc as Lrc, Weak},
};
}
#[derive(Debug)]
pub struct Lock<T>(InnerLock<T>);
impl<T> Lock<T> {
#[inline(always)]
pub fn new(inner: T) -> Self {
Lock(InnerLock::new(inner))
}
// #[inline(always)]
// pub fn into_inner(self) -> T {
// self.0.into_inner()
// }
//
// #[inline(always)]
// pub fn get_mut(&mut self) -> &mut T {
// self.0.get_mut()
// }
// #[cfg(feature = "concurrent")]
// #[inline(always)]
// pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
// self.0.try_lock()
// }
//
// #[cfg(not(feature = "concurrent"))]
// #[inline(always)]
// pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
// self.0.try_borrow_mut().ok()
// }
#[cfg(feature = "concurrent")]
#[inline(always)]
pub fn lock(&self) -> LockGuard<'_, T> {
self.0.lock()
}
#[cfg(not(feature = "concurrent"))]
#[inline(always)]
pub fn lock(&self) -> LockGuard<'_, T> {
self.0.borrow_mut()
}
// #[inline(always)]
// pub fn with_lock<F: FnOnce(&mut T) -> R, R>(&self, f: F) -> R {
// f(&mut *self.lock())
// }
#[inline(always)]
pub fn borrow(&self) -> LockGuard<'_, T> {
self.lock()
}
#[inline(always)]
pub fn borrow_mut(&self) -> LockGuard<'_, T> {
self.lock()
}
}
impl<T: Default> Default for Lock<T> {
#[inline]
fn default() -> Self {
Lock::new(T::default())
}
}
impl<T> LockCell<T> {
#[inline(always)]
pub fn new(inner: T) -> Self {
LockCell(Lock::new(inner))
}
#[inline(always)]
pub fn set(&self, new_inner: T) {
*self.0.lock() = new_inner;
}
#[inline(always)]
pub fn get(&self) -> T
where
T: Copy,
{
*self.0.lock()
}
}
pub trait HashMapExt<K, V> {
/// Same as HashMap::insert, but it may panic if there's already an
/// entry for `key` with a value not equal to `value`
fn insert_same(&mut self, key: K, value: V);
}
impl<K: Eq + Hash, V: Eq, S: BuildHasher> HashMapExt<K, V> for HashMap<K, V, S> {
fn insert_same(&mut self, key: K, value: V) {
self.entry(key)
.and_modify(|old| assert!(*old == value))
.or_insert(value);
}
}
impl<T: Copy + Debug> Debug for LockCell<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("LockCell")
.field("value", &self.get())
.finish()
}
}
impl<T: Default> Default for LockCell<T> {
/// Creates a `LockCell<T>`, with the `Default` value for T.
#[inline]
fn default() -> LockCell<T> {
LockCell::new(Default::default())
}
}
impl<T: PartialEq + Copy> PartialEq for LockCell<T> {
#[inline]
fn eq(&self, other: &LockCell<T>) -> bool {
self.get() == other.get()
}
}
impl<T: Eq + Copy> Eq for LockCell<T> {}
impl<T: PartialOrd + Copy> PartialOrd for LockCell<T> {
#[inline]
fn partial_cmp(&self, other: &LockCell<T>) -> Option<Ordering> {
self.get().partial_cmp(&other.get())
}
#[inline]
fn lt(&self, other: &LockCell<T>) -> bool {
self.get() < other.get()
}
#[inline]
fn le(&self, other: &LockCell<T>) -> bool {
self.get() <= other.get()
}
#[inline]
fn gt(&self, other: &LockCell<T>) -> bool {
self.get() > other.get()
}
#[inline]
fn ge(&self, other: &LockCell<T>) -> bool {
self.get() >= other.get()
}
}
impl<T: Ord + Copy> Ord for LockCell<T> {
#[inline]
fn cmp(&self, other: &LockCell<T>) -> Ordering {
self.get().cmp(&other.get())
}
}
#[derive(Debug, Default)]
pub struct RwLock<T>(InnerRwLock<T>);
impl<T> RwLock<T> {
#[inline(always)]
pub fn new(inner: T) -> Self {
RwLock(InnerRwLock::new(inner))
}
#[cfg(not(feature = "concurrent"))]
#[inline(always)]
pub fn read(&self) -> ReadGuard<'_, T> {
self.0.borrow()
}
#[cfg(feature = "concurrent")]
#[inline(always)]
pub fn read(&self) -> ReadGuard<'_, T> {
self.0.read()
}
#[inline(always)]
pub fn borrow(&self) -> ReadGuard<'_, T> {
self.read()
}
#[inline(always)]
pub fn get_mut(&mut self) -> &mut T {
self.0.get_mut()
}
#[inline(always)]
pub fn with_read_lock<F: FnOnce(&T) -> R, R>(&self, f: F) -> R {
f(&*self.read())
}
#[allow(clippy::result_unit_err)]
#[cfg(not(feature = "concurrent"))]
#[inline(always)]
pub fn try_write(&self) -> Result<WriteGuard<'_, T>, ()> {
self.0.try_borrow_mut().map_err(|_| ())
}
#[allow(clippy::result_unit_err)]
#[cfg(feature = "concurrent")]
#[inline(always)]
pub fn try_write(&self) -> Result<WriteGuard<'_, T>, ()> {
self.0.try_write().ok_or(())
}
#[cfg(not(feature = "concurrent"))]
#[inline(always)]
pub fn write(&self) -> WriteGuard<'_, T> {
self.0.borrow_mut()
}
#[cfg(feature = "concurrent")]
#[inline(always)]
pub fn write(&self) -> WriteGuard<'_, T> {
self.0.write()
}
#[inline(always)]
pub fn with_write_lock<F: FnOnce(&mut T) -> R, R>(&self, f: F) -> R {
f(&mut *self.write())
}
#[inline(always)]
pub fn borrow_mut(&self) -> WriteGuard<'_, T> {
self.write()
}
}
// FIXME: Probably a bad idea
impl<T: Clone> Clone for RwLock<T> {
#[inline]
fn clone(&self) -> Self {
RwLock::new(self.borrow().clone())
}
}
pub struct LockCell<T>(Lock<T>);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,311 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use unicode_width::UnicodeWidthChar;
use super::*;
/// Finds all newlines, multi-byte characters, and non-narrow characters in a
/// SourceFile.
///
/// This function will use an SSE2 enhanced implementation if hardware support
/// is detected at runtime.
pub fn analyze_source_file(
src: &str,
source_file_start_pos: BytePos,
) -> (Vec<BytePos>, Vec<MultiByteChar>, Vec<NonNarrowChar>) {
let mut lines = vec![source_file_start_pos];
let mut multi_byte_chars = vec![];
let mut non_narrow_chars = vec![];
// Calls the right implementation, depending on hardware support available.
analyze_source_file_generic(
src,
src.len(),
source_file_start_pos,
&mut lines,
&mut multi_byte_chars,
&mut non_narrow_chars,
);
// The code above optimistically registers a new line *after* each \n
// it encounters. If that point is already outside the source_file, remove
// it again.
if let Some(&last_line_start) = lines.last() {
let source_file_end = source_file_start_pos + BytePos::from_usize(src.len());
assert!(source_file_end >= last_line_start);
if last_line_start == source_file_end {
lines.pop();
}
}
(lines, multi_byte_chars, non_narrow_chars)
}
// `scan_len` determines the number of bytes in `src` to scan. Note that the
// function can read past `scan_len` if a multi-byte character start within the
// range but extends past it. The overflow is returned by the function.
fn analyze_source_file_generic(
src: &str,
scan_len: usize,
output_offset: BytePos,
lines: &mut Vec<BytePos>,
multi_byte_chars: &mut Vec<MultiByteChar>,
non_narrow_chars: &mut Vec<NonNarrowChar>,
) -> usize {
assert!(src.len() >= scan_len);
let mut i = 0;
let src_bytes = src.as_bytes();
while i < scan_len {
let byte = unsafe {
// We verified that i < scan_len <= src.len()
*src_bytes.get_unchecked(i)
};
// How much to advance in order to get to the next UTF-8 char in the
// string.
let mut char_len = 1;
if byte < 32 {
// This is an ASCII control character, it could be one of the cases
// that are interesting to us.
let pos = BytePos::from_usize(i) + output_offset;
match byte {
b'\r' => {
if let Some(b'\n') = src_bytes.get(i + 1) {
lines.push(pos + BytePos(2));
i += 2;
continue;
}
lines.push(pos + BytePos(1));
}
b'\n' => {
lines.push(pos + BytePos(1));
}
b'\t' => {
non_narrow_chars.push(NonNarrowChar::Tab(pos));
}
_ => {
non_narrow_chars.push(NonNarrowChar::ZeroWidth(pos));
}
}
} else if byte >= 127 {
// The slow path:
// This is either ASCII control character "DEL" or the beginning of
// a multibyte char. Just decode to `char`.
let c = src[i..].chars().next().unwrap();
char_len = c.len_utf8();
let pos = BytePos::from_usize(i) + output_offset;
if char_len > 1 {
assert!((2..=4).contains(&char_len));
let mbc = MultiByteChar {
pos,
bytes: char_len as u8,
};
multi_byte_chars.push(mbc);
}
// Assume control characters are zero width.
// FIXME: How can we decide between `width` and `width_cjk`?
let char_width = UnicodeWidthChar::width(c).unwrap_or(0);
if char_width != 1 {
non_narrow_chars.push(NonNarrowChar::new(pos, char_width));
}
}
i += char_len;
}
i - scan_len
}
#[cfg(test)]
#[allow(clippy::identity_op)]
mod tests {
use super::*;
macro_rules! test {
(case: $test_name:ident,
text: $text:expr,
source_file_start_pos: $source_file_start_pos:expr,
lines: $lines:expr,
multi_byte_chars: $multi_byte_chars:expr,
non_narrow_chars: $non_narrow_chars:expr,) => {
#[test]
fn $test_name() {
let (lines, multi_byte_chars, non_narrow_chars) =
analyze_source_file($text, BytePos($source_file_start_pos));
let expected_lines: Vec<BytePos> =
$lines.into_iter().map(|pos| BytePos(pos)).collect();
assert_eq!(lines, expected_lines);
let expected_mbcs: Vec<MultiByteChar> = $multi_byte_chars
.into_iter()
.map(|(pos, bytes)| MultiByteChar {
pos: BytePos(pos),
bytes,
})
.collect();
assert_eq!(multi_byte_chars, expected_mbcs);
let expected_nncs: Vec<NonNarrowChar> = $non_narrow_chars
.into_iter()
.map(|(pos, width)| NonNarrowChar::new(BytePos(pos), width))
.collect();
assert_eq!(non_narrow_chars, expected_nncs);
}
};
}
test!(
case: empty_text,
text: "",
source_file_start_pos: 0,
lines: vec![],
multi_byte_chars: vec![],
non_narrow_chars: vec![],
);
test!(
case: newlines_short,
text: "a\nc",
source_file_start_pos: 0,
lines: vec![0, 2],
multi_byte_chars: vec![],
non_narrow_chars: vec![],
);
test!(
case: newlines_long,
text: "012345678\nabcdef012345678\na",
source_file_start_pos: 0,
lines: vec![0, 10, 26],
multi_byte_chars: vec![],
non_narrow_chars: vec![],
);
test!(
case: newline_and_multi_byte_char_in_same_chunk,
text: "01234β789\nbcdef0123456789abcdef",
source_file_start_pos: 0,
lines: vec![0, 11],
multi_byte_chars: vec![(5, 2)],
non_narrow_chars: vec![],
);
test!(
case: newline_and_control_char_in_same_chunk,
text: "01234\u{07}6789\nbcdef0123456789abcdef",
source_file_start_pos: 0,
lines: vec![0, 11],
multi_byte_chars: vec![],
non_narrow_chars: vec![(5, 0)],
);
test!(
case: multi_byte_char_short,
text: "aβc",
source_file_start_pos: 0,
lines: vec![0],
multi_byte_chars: vec![(1, 2)],
non_narrow_chars: vec![],
);
test!(
case: multi_byte_char_long,
text: "0123456789abcΔf012345β",
source_file_start_pos: 0,
lines: vec![0],
multi_byte_chars: vec![(13, 2), (22, 2)],
non_narrow_chars: vec![],
);
test!(
case: multi_byte_char_across_chunk_boundary,
text: "0123456789abcdeΔ123456789abcdef01234",
source_file_start_pos: 0,
lines: vec![0],
multi_byte_chars: vec![(15, 2)],
non_narrow_chars: vec![],
);
test!(
case: multi_byte_char_across_chunk_boundary_tail,
text: "0123456789abcdeΔ....",
source_file_start_pos: 0,
lines: vec![0],
multi_byte_chars: vec![(15, 2)],
non_narrow_chars: vec![],
);
test!(
case: non_narrow_short,
text: "0\t2",
source_file_start_pos: 0,
lines: vec![0],
multi_byte_chars: vec![],
non_narrow_chars: vec![(1, 4)],
);
test!(
case: non_narrow_long,
text: "01\t3456789abcdef01234567\u{07}9",
source_file_start_pos: 0,
lines: vec![0],
multi_byte_chars: vec![],
non_narrow_chars: vec![(2, 4), (24, 0)],
);
test!(
case: output_offset_all,
text: "01\t345\n789abcΔf01234567\u{07}9\nbcΔf",
source_file_start_pos: 1000,
lines: vec![0 + 1000, 7 + 1000, 27 + 1000],
multi_byte_chars: vec![(13 + 1000, 2), (29 + 1000, 2)],
non_narrow_chars: vec![(2 + 1000, 4), (24 + 1000, 0)],
);
test!(
case: unix_lf,
text: "/**\n * foo\n */\n012345678\nabcdef012345678\na",
source_file_start_pos: 0,
lines: vec![0, 4, 11, 15, 25, 41],
multi_byte_chars: vec![],
non_narrow_chars: vec![],
);
test!(
case: windows_cr,
text: "/**\r * foo\r */\r012345678\rabcdef012345678\ra",
source_file_start_pos: 0,
lines: vec![0, 4, 11, 15, 25, 41],
multi_byte_chars: vec![],
non_narrow_chars: vec![],
);
test!(
case: windows_crlf,
text: "/**\r\n * foo\r\n */\r\n012345678\r\nabcdef012345678\r\na",
source_file_start_pos: 0,
lines: vec![0, 5, 13, 18, 29, 46],
multi_byte_chars: vec![],
non_narrow_chars: vec![],
);
}

View file

@ -0,0 +1,606 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Machinery for hygienic macros, inspired by the `MTWT[1]` paper.
//!
//! `[1]` Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler.
//! 2012. *Macros that work together: Compile-time bindings, partial expansion,
//! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
//! DOI=10.1017/S0956796812000093 <https://doi.org/10.1017/S0956796812000093>
#[allow(unused)]
use std::{
collections::{HashMap, HashSet},
fmt,
};
use serde::{Deserialize, Serialize};
use super::GLOBALS;
use crate::collections::AHashMap;
/// A SyntaxContext represents a chain of macro expansions (represented by
/// marks).
#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
pub struct SyntaxContext(#[cfg_attr(feature = "__rkyv", omit_bounds)] u32);
#[cfg(feature = "arbitrary")]
#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
impl<'a> arbitrary::Arbitrary<'a> for SyntaxContext {
fn arbitrary(_: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(SyntaxContext::empty())
}
}
#[allow(unused)]
#[derive(Copy, Clone, Debug)]
struct SyntaxContextData {
outer_mark: Mark,
prev_ctxt: SyntaxContext,
// This context, but with all transparent and semi-transparent marks filtered away.
opaque: SyntaxContext,
// This context, but with all transparent marks filtered away.
opaque_and_semitransparent: SyntaxContext,
}
/// A mark is a unique id associated with a macro expansion.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Mark(u32);
#[allow(unused)]
#[derive(Clone, Debug)]
pub(crate) struct MarkData {
pub(crate) parent: Mark,
pub(crate) is_builtin: bool,
}
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
pub struct MutableMarkContext(pub u32, pub u32, pub u32);
// List of proxy calls injected by the host in the plugin's runtime context.
// When related calls being executed inside of the plugin, it'll call these
// proxies instead which'll call actual host fn.
extern "C" {
// Instead of trying to copy-serialize `Mark`, this fn directly consume
// inner raw value as well as fn and let each context constructs struct
// on their side.
fn __mark_fresh_proxy(mark: u32) -> u32;
fn __mark_parent_proxy(self_mark: u32) -> u32;
fn __mark_is_builtin_proxy(self_mark: u32) -> u32;
fn __mark_set_builtin_proxy(self_mark: u32, is_builtin: u32);
fn __syntax_context_apply_mark_proxy(self_syntax_context: u32, mark: u32) -> u32;
fn __syntax_context_outer_proxy(self_mark: u32) -> u32;
// These are proxy fn uses serializable context to pass forward mutated param
// with return value back to the guest.
fn __mark_is_descendant_of_proxy(self_mark: u32, ancestor: u32, allocated_ptr: i32);
fn __mark_least_ancestor(a: u32, b: u32, allocated_ptr: i32);
fn __syntax_context_remove_mark_proxy(self_mark: u32, allocated_ptr: i32);
}
impl Mark {
/// Shortcut for `Mark::fresh(Mark::root())`
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Mark::fresh(Mark::root())
}
pub fn fresh(parent: Mark) -> Self {
// Note: msvc tries to link against proxied fn for normal build,
// have to limit build target to wasm only to avoid it.
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
return Mark(unsafe { __mark_fresh_proxy(parent.as_u32()) });
// https://github.com/swc-project/swc/pull/3492#discussion_r802224857
// We loosen conditions here for the cases like running plugin's test without
// targeting wasm32-*.
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
return with_marks(|marks| {
marks.push(MarkData {
parent,
is_builtin: false,
});
Mark(marks.len() as u32 - 1)
});
}
/// The mark of the theoretical expansion that generates freshly parsed,
/// unexpanded AST.
#[inline]
pub const fn root() -> Self {
Mark(0)
}
#[inline]
pub fn as_u32(self) -> u32 {
self.0
}
#[inline]
pub fn from_u32(raw: u32) -> Mark {
Mark(raw)
}
#[inline]
pub fn parent(self) -> Mark {
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
return Mark(unsafe { __mark_parent_proxy(self.0) });
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
return with_marks(|marks| marks[self.0 as usize].parent);
}
#[inline]
pub fn is_builtin(self) -> bool {
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
return unsafe { __mark_is_builtin_proxy(self.0) != 0 };
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
{
assert_ne!(self, Mark::root());
with_marks(|marks| marks[self.0 as usize].is_builtin)
}
}
#[inline]
pub fn set_is_builtin(self, is_builtin: bool) {
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
unsafe {
__mark_set_builtin_proxy(self.0, is_builtin as u32)
}
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
{
assert_ne!(self, Mark::root());
with_marks(|marks| marks[self.0 as usize].is_builtin = is_builtin)
}
}
#[allow(unused_assignments)]
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
// This code path executed inside of the guest memory context.
// In here, preallocate memory for the context.
use crate::plugin::serialized::VersionedSerializable;
let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
)
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();
// Calling host proxy fn. Inside of host proxy, host will
// write the result into allocated context in the guest memory space.
unsafe {
__mark_is_descendant_of_proxy(self.0, ancestor.0, ptr as _);
}
// Deserialize result, assign / return values as needed.
let context: MutableMarkContext =
crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
ptr,
len.try_into().expect("Should able to convert ptr length"),
)
.deserialize()
.expect("Should able to deserialize")
.into_inner();
self = Mark::from_u32(context.0);
return context.2 != 0;
}
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
with_marks(|marks| {
while self != ancestor {
if self == Mark::root() {
return false;
}
self = marks[self.0 as usize].parent;
}
true
})
}
#[allow(unused_mut, unused_assignments)]
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
use crate::plugin::serialized::VersionedSerializable;
let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(
&VersionedSerializable::new(MutableMarkContext(0, 0, 0)),
)
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();
unsafe {
__mark_least_ancestor(a.0, b.0, ptr as _);
}
let context: MutableMarkContext =
crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
ptr,
len.try_into().expect("Should able to convert ptr length"),
)
.deserialize()
.expect("Should able to deserialize")
.into_inner();
a = Mark::from_u32(context.0);
b = Mark::from_u32(context.1);
return Mark(context.2);
}
/// Computes a mark such that both input marks are descendants of (or equal
/// to) the returned mark. That is, the following holds:
///
/// ```rust,ignore
/// let la = least_ancestor(a, b);
/// assert!(a.is_descendant_of(la))
/// assert!(b.is_descendant_of(la))
/// ```
#[allow(unused_mut)]
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
pub fn least_ancestor(mut a: Mark, mut b: Mark) -> Mark {
with_marks(|marks| {
// Compute the path from a to the root
let mut a_path = HashSet::<Mark>::default();
while a != Mark::root() {
a_path.insert(a);
a = marks[a.0 as usize].parent;
}
// While the path from b to the root hasn't intersected, move up the tree
while !a_path.contains(&b) {
b = marks[b.0 as usize].parent;
}
b
})
}
}
#[allow(unused)]
#[derive(Debug)]
pub(crate) struct HygieneData {
syntax_contexts: Vec<SyntaxContextData>,
markings: AHashMap<(SyntaxContext, Mark), SyntaxContext>,
}
impl Default for HygieneData {
fn default() -> Self {
Self::new()
}
}
impl HygieneData {
pub(crate) fn new() -> Self {
HygieneData {
syntax_contexts: vec![SyntaxContextData {
outer_mark: Mark::root(),
prev_ctxt: SyntaxContext(0),
opaque: SyntaxContext(0),
opaque_and_semitransparent: SyntaxContext(0),
}],
markings: HashMap::default(),
}
}
fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T {
GLOBALS.with(|globals| {
#[cfg(feature = "parking_lot")]
return f(&mut globals.hygiene_data.lock());
#[cfg(not(feature = "parking_lot"))]
return f(&mut *globals.hygiene_data.lock().unwrap());
})
}
}
#[allow(unused)]
pub(crate) fn with_marks<T, F: FnOnce(&mut Vec<MarkData>) -> T>(f: F) -> T {
GLOBALS.with(|globals| {
#[cfg(feature = "parking_lot")]
return f(&mut globals.marks.lock());
#[cfg(not(feature = "parking_lot"))]
return f(&mut *globals.marks.lock().unwrap());
})
}
// pub fn clear_markings() {
// HygieneData::with(|data| data.markings = HashMap::default());
// }
impl SyntaxContext {
pub const fn empty() -> Self {
SyntaxContext(0)
}
/// Returns `true` if `self` is marked with `mark`.
///
/// Panics if `mark` is not a valid mark.
pub fn has_mark(self, mark: Mark) -> bool {
debug_assert_ne!(
mark,
Mark::root(),
"Cannot check if a span contains a `ROOT` mark"
);
let mut ctxt = self;
loop {
if ctxt == SyntaxContext::empty() {
return false;
}
let m = ctxt.remove_mark();
if m == mark {
return true;
}
if m == Mark::root() {
return false;
}
}
}
#[inline]
pub fn as_u32(self) -> u32 {
self.0
}
#[inline]
pub fn from_u32(raw: u32) -> SyntaxContext {
SyntaxContext(raw)
}
/// Extend a syntax context with a given mark and default transparency for
/// that mark.
pub fn apply_mark(self, mark: Mark) -> SyntaxContext {
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
return unsafe { SyntaxContext(__syntax_context_apply_mark_proxy(self.0, mark.0)) };
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
{
assert_ne!(mark, Mark::root());
self.apply_mark_internal(mark)
}
}
#[allow(unused)]
fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
HygieneData::with(|data| {
let syntax_contexts = &mut data.syntax_contexts;
let mut opaque = syntax_contexts[self.0 as usize].opaque;
let opaque_and_semitransparent =
syntax_contexts[self.0 as usize].opaque_and_semitransparent;
let prev_ctxt = opaque;
opaque = *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
syntax_contexts.push(SyntaxContextData {
outer_mark: mark,
prev_ctxt,
opaque: new_opaque,
opaque_and_semitransparent: new_opaque,
});
new_opaque
});
let prev_ctxt = self;
*data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
let new_opaque_and_semitransparent_and_transparent =
SyntaxContext(syntax_contexts.len() as u32);
syntax_contexts.push(SyntaxContextData {
outer_mark: mark,
prev_ctxt,
opaque,
opaque_and_semitransparent,
});
new_opaque_and_semitransparent_and_transparent
})
})
}
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
pub fn remove_mark(&mut self) -> Mark {
use crate::plugin::serialized::VersionedSerializable;
let context = VersionedSerializable::new(MutableMarkContext(0, 0, 0));
let serialized = crate::plugin::serialized::PluginSerializedBytes::try_serialize(&context)
.expect("Should be serializable");
let (ptr, len) = serialized.as_ptr();
unsafe {
__syntax_context_remove_mark_proxy(self.0, ptr as _);
}
let context: MutableMarkContext =
crate::plugin::serialized::PluginSerializedBytes::from_raw_ptr(
ptr,
len.try_into().expect("Should able to convert ptr length"),
)
.deserialize()
.expect("Should able to deserialize")
.into_inner();
*self = SyntaxContext(context.0);
return Mark::from_u32(context.2);
}
/// Pulls a single mark off of the syntax context. This effectively moves
/// the context up one macro definition level. That is, if we have a
/// nested macro definition as follows:
///
/// ```rust,ignore
/// macro_rules! f {
/// macro_rules! g {
/// ...
/// }
/// }
/// ```
///
/// and we have a SyntaxContext that is referring to something declared by
/// an invocation of g (call it g1), calling remove_mark will result in
/// the SyntaxContext for the invocation of f that created g1.
/// Returns the mark that was removed.
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
pub fn remove_mark(&mut self) -> Mark {
HygieneData::with(|data| {
let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark;
*self = data.syntax_contexts[self.0 as usize].prev_ctxt;
outer_mark
})
}
/// Adjust this context for resolution in a scope created by the given
/// expansion. For example, consider the following three resolutions of
/// `f`:
///
/// ```rust,ignore
/// mod foo {
/// pub fn f() {}
/// } // `f`'s `SyntaxContext` is empty.
/// m!(f);
/// macro m($f:ident) {
/// mod bar {
/// pub fn f() {} // `f`'s `SyntaxContext` has a single `Mark` from `m`.
/// pub fn $f() {} // `$f`'s `SyntaxContext` is empty.
/// }
/// foo::f(); // `f`'s `SyntaxContext` has a single `Mark` from `m`
/// //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`,
/// //| and it resolves to `::foo::f`.
/// bar::f(); // `f`'s `SyntaxContext` has a single `Mark` from `m`
/// //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`,
/// //| and it resolves to `::bar::f`.
/// bar::$f(); // `f`'s `SyntaxContext` is empty.
/// //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`,
/// //| and it resolves to `::bar::$f`.
/// }
/// ```
/// This returns the expansion whose definition scope we use to privacy
/// check the resolution, or `None` if we privacy check as usual (i.e.
/// not w.r.t. a macro definition scope).
pub fn adjust(&mut self, expansion: Mark) -> Option<Mark> {
let mut scope = None;
while !expansion.is_descendant_of(self.outer()) {
scope = Some(self.remove_mark());
}
scope
}
/// Adjust this context for resolution in a scope created by the given
/// expansion via a glob import with the given `SyntaxContext`.
/// For example:
///
/// ```rust,ignore
/// m!(f);
/// macro m($i:ident) {
/// mod foo {
/// pub fn f() {} // `f`'s `SyntaxContext` has a single `Mark` from `m`.
/// pub fn $i() {} // `$i`'s `SyntaxContext` is empty.
/// }
/// n(f);
/// macro n($j:ident) {
/// use foo::*;
/// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n`
/// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`.
/// $i(); // `$i`'s `SyntaxContext` has a mark from `n`
/// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`.
/// $j(); // `$j`'s `SyntaxContext` has a mark from `m`
/// //^ This cannot be glob-adjusted, so this is a resolution error.
/// }
/// }
/// ```
/// This returns `None` if the context cannot be glob-adjusted.
/// Otherwise, it returns the scope to use when privacy checking (see
/// `adjust` for details).
pub fn glob_adjust(
&mut self,
expansion: Mark,
mut glob_ctxt: SyntaxContext,
) -> Option<Option<Mark>> {
let mut scope = None;
while !expansion.is_descendant_of(glob_ctxt.outer()) {
scope = Some(glob_ctxt.remove_mark());
if self.remove_mark() != scope.unwrap() {
return None;
}
}
if self.adjust(expansion).is_some() {
return None;
}
Some(scope)
}
/// Undo `glob_adjust` if possible:
///
/// ```rust,ignore
/// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) {
/// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope));
/// }
/// ```
pub fn reverse_glob_adjust(
&mut self,
expansion: Mark,
mut glob_ctxt: SyntaxContext,
) -> Option<Option<Mark>> {
if self.adjust(expansion).is_some() {
return None;
}
let mut marks = Vec::new();
while !expansion.is_descendant_of(glob_ctxt.outer()) {
marks.push(glob_ctxt.remove_mark());
}
let scope = marks.last().cloned();
while let Some(mark) = marks.pop() {
*self = self.apply_mark(mark);
}
Some(scope)
}
#[inline]
pub fn outer(self) -> Mark {
#[cfg(all(feature = "__plugin_mode", target_arch = "wasm32"))]
return unsafe { Mark(__syntax_context_outer_proxy(self.0)) };
#[cfg(not(all(feature = "__plugin_mode", target_arch = "wasm32")))]
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
}
}
impl fmt::Debug for SyntaxContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "#{}", self.0)
}
}
impl Default for Mark {
fn default() -> Self {
Mark::root()
}
}

View file

@ -0,0 +1,49 @@
pub trait IteratorExt: Iterator {
/// Copied from https://stackoverflow.com/a/49456265/6193633
fn chain_with<F, I>(self, f: F) -> ChainWith<Self, F, I::IntoIter>
where
Self: Sized,
F: FnOnce() -> I,
I: IntoIterator<Item = Self::Item>,
{
ChainWith {
base: self,
factory: Some(f),
iterator: None,
}
}
}
impl<I: Iterator> IteratorExt for I {}
pub struct ChainWith<B, F, I> {
base: B,
factory: Option<F>,
iterator: Option<I>,
}
impl<B, F, I> Iterator for ChainWith<B, F, I::IntoIter>
where
B: Iterator,
F: FnOnce() -> I,
I: IntoIterator<Item = B::Item>,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
if let Some(b) = self.base.next() {
return Some(b);
}
// Exhausted the first, generate the second
if let Some(f) = self.factory.take() {
self.iterator = Some(f().into_iter());
}
self.iterator
.as_mut()
.expect("There must be an iterator")
.next()
}
}

View file

@ -0,0 +1,2 @@
/// Moved
pub use swc_visit::util::map::Map;

View file

@ -0,0 +1,4 @@
pub mod iter;
pub mod map;
pub mod move_map;
pub mod take;

View file

@ -0,0 +1 @@
pub use swc_visit::util::move_map::MoveMap;

View file

@ -0,0 +1,56 @@
use std::mem::replace;
use crate::{Span, DUMMY_SP};
/// Helper for people who are working on `VisitMut`.
///
///
/// This trait is implemented for ast nodes. If not and you need it, please file
/// an issue.
pub trait Take: Sized {
fn take(&mut self) -> Self {
replace(self, Self::dummy())
}
/// Create a dummy value of this type.
fn dummy() -> Self;
/// Mutate `self` using `op`, which accepts owned data.
#[inline]
fn map_with_mut<F>(&mut self, op: F)
where
F: FnOnce(Self) -> Self,
{
let dummy = Self::dummy();
let cur_val = replace(self, dummy);
let new_val = op(cur_val);
let _dummy = replace(self, new_val);
}
}
impl<T> Take for Option<T> {
fn dummy() -> Self {
None
}
}
impl<T> Take for Box<T>
where
T: Take,
{
fn dummy() -> Self {
Box::new(T::dummy())
}
}
impl<T> Take for Vec<T> {
fn dummy() -> Self {
vec![]
}
}
impl Take for Span {
fn dummy() -> Self {
DUMMY_SP
}
}

View file

@ -0,0 +1,24 @@
use serde_json::from_str;
use swc_common::ast_serde;
#[ast_serde]
#[derive(Debug, PartialEq, Eq)]
pub enum Ambiguous {
#[tag("A")]
A(A),
#[tag("B")]
B(B),
}
#[ast_serde("B")]
#[derive(Debug, PartialEq, Eq)]
pub struct A {}
#[ast_serde("B")]
#[derive(Debug, PartialEq, Eq)]
pub struct B {}
#[test]
fn deserialize() {
assert_eq!(A {}, from_str(r#"{"type": "A"}"#).unwrap());
assert_eq!(B {}, from_str(r#"{"type": "B"}"#).unwrap());
}

View file

@ -0,0 +1,38 @@
//! Test that `#[span]` and `#[fold]` can be used at same time.
use serde::{self, Deserialize, Serialize};
use swc_common::{self, ast_node, Span, Spanned};
#[ast_node("Class")]
// See https://github.com/rust-lang/rust/issues/44925
pub struct Class {
#[span]
pub has_span: HasSpan,
pub s: String,
}
#[ast_node("Tuple")]
pub struct Tuple(#[span] HasSpan, usize, usize);
#[derive(Debug, Clone, PartialEq, Eq, Spanned, Serialize, Deserialize)]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(
any(feature = "rkyv-impl"),
archive(bound(serialize = "__S: rkyv::ser::Serializer + rkyv::ser::ScratchSpace"))
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
pub struct HasSpan {
#[cfg_attr(feature = "__rkyv", omit_bounds)]
pub span: Span,
}
#[ast_node]
pub enum Node {
#[tag("Class")]
Class(Class),
#[tag("Tuple")]
Tuple(Tuple),
}

View file

@ -0,0 +1 @@
const foo = 1;

View file

@ -0,0 +1,41 @@
#![cfg(feature = "concurrent")]
use std::{env, path::PathBuf, sync::Arc};
use rayon::prelude::*;
use swc_common::{sync::Lrc, FilePathMapping, SourceFile, SourceMap};
#[test]
fn no_overlap() {
let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let files: Vec<Lrc<SourceFile>> = (0..100000)
.into_par_iter()
.map(|_| {
cm.load_file(
&PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap())
.join("tests")
.join("concurrent.js"),
)
.unwrap()
})
.collect::<Vec<_>>();
// This actually tests if there is overlap
let mut start = files.clone();
start.sort_by_key(|f| f.start_pos);
let mut end = files;
end.sort_by_key(|f| f.end_pos);
start
.into_par_iter()
.zip(end)
.for_each(|(start, end): (Arc<SourceFile>, Arc<SourceFile>)| {
assert_eq!(start.start_pos, end.start_pos);
assert_eq!(start.end_pos, end.end_pos);
assert!(start.start_pos < start.end_pos);
cm.lookup_char_pos(start.start_pos);
});
}

View file

@ -0,0 +1,6 @@
use swc_common::*;
pub struct Struct {}
#[derive(FromVariant)]
pub enum Enum {}

View file

@ -0,0 +1,9 @@
use swc_common::ast_serde;
#[ast_serde]
pub enum Message {
#[tag("Request")]
Request(String),
#[tag("Response")]
Response(u8),
}

View file

@ -0,0 +1,38 @@
#![cfg(feature = "concurrent")]
use rayon::{prelude::*, ThreadPoolBuilder};
use swc_common::{FileName, FilePathMapping, SourceMap};
#[test]
fn stress() {
let _ = ThreadPoolBuilder::new().num_threads(100).build_global();
let fm = SourceMap::new(FilePathMapping::empty());
(0..10000).into_par_iter().for_each(|_| {
fm.new_source_file(
FileName::Anon,
"@Entity()
export class Product extends TimestampedEntity {
@PrimaryGeneratedColumn('uuid')
public id!: string;
@Column()
public price!: number;
@Column({ enum: ProductType })
public type!: ProductType;
@Column()
public productEntityId!: string;
/* ANCHOR: Relations ------------------------------------------------------ */
@OneToMany(() => Order, (order) => order.product)
public orders!: Order[];
@OneToMany(() => Discount, (discount) => discount.product)
public discounts!: Discount[];
}"
.into(),
);
})
}

View file

@ -0,0 +1,19 @@
use swc_common::{BytePos, Span, Spanned};
#[test]
fn lo_hi() {
#[derive(Spanned)]
struct LoHi {
#[span(lo)]
pub lo: BytePos,
#[span(hi)]
pub hi: BytePos,
}
let lo = BytePos(0);
let hi = BytePos(5);
assert_eq!(
LoHi { lo, hi }.span(),
Span::new(lo, hi, Default::default())
);
}