1fad3a1d3Sopenharmony_ci#![cfg(not(syn_disable_nightly_tests))]
2fad3a1d3Sopenharmony_ci#![cfg(not(miri))]
3fad3a1d3Sopenharmony_ci#![recursion_limit = "1024"]
4fad3a1d3Sopenharmony_ci#![feature(rustc_private)]
5fad3a1d3Sopenharmony_ci#![allow(
6fad3a1d3Sopenharmony_ci    clippy::blocks_in_conditions,
7fad3a1d3Sopenharmony_ci    clippy::manual_assert,
8fad3a1d3Sopenharmony_ci    clippy::manual_let_else,
9fad3a1d3Sopenharmony_ci    clippy::match_like_matches_macro,
10fad3a1d3Sopenharmony_ci    clippy::uninlined_format_args
11fad3a1d3Sopenharmony_ci)]
12fad3a1d3Sopenharmony_ci
13fad3a1d3Sopenharmony_ciextern crate rustc_ast;
14fad3a1d3Sopenharmony_ciextern crate rustc_ast_pretty;
15fad3a1d3Sopenharmony_ciextern crate rustc_data_structures;
16fad3a1d3Sopenharmony_ciextern crate rustc_driver;
17fad3a1d3Sopenharmony_ciextern crate rustc_error_messages;
18fad3a1d3Sopenharmony_ciextern crate rustc_errors;
19fad3a1d3Sopenharmony_ciextern crate rustc_expand;
20fad3a1d3Sopenharmony_ciextern crate rustc_parse as parse;
21fad3a1d3Sopenharmony_ciextern crate rustc_session;
22fad3a1d3Sopenharmony_ciextern crate rustc_span;
23fad3a1d3Sopenharmony_ci
24fad3a1d3Sopenharmony_ciuse crate::common::eq::SpanlessEq;
25fad3a1d3Sopenharmony_ciuse quote::quote;
26fad3a1d3Sopenharmony_ciuse rustc_ast::ast::{
27fad3a1d3Sopenharmony_ci    AngleBracketedArg, AngleBracketedArgs, Crate, GenericArg, GenericParamKind, Generics,
28fad3a1d3Sopenharmony_ci    WhereClause,
29fad3a1d3Sopenharmony_ci};
30fad3a1d3Sopenharmony_ciuse rustc_ast::mut_visit::{self, MutVisitor};
31fad3a1d3Sopenharmony_ciuse rustc_ast_pretty::pprust;
32fad3a1d3Sopenharmony_ciuse rustc_error_messages::{DiagnosticMessage, LazyFallbackBundle};
33fad3a1d3Sopenharmony_ciuse rustc_errors::{translation, Diagnostic, PResult};
34fad3a1d3Sopenharmony_ciuse rustc_session::parse::ParseSess;
35fad3a1d3Sopenharmony_ciuse rustc_span::source_map::FilePathMapping;
36fad3a1d3Sopenharmony_ciuse rustc_span::FileName;
37fad3a1d3Sopenharmony_ciuse std::borrow::Cow;
38fad3a1d3Sopenharmony_ciuse std::fs;
39fad3a1d3Sopenharmony_ciuse std::panic;
40fad3a1d3Sopenharmony_ciuse std::path::Path;
41fad3a1d3Sopenharmony_ciuse std::process;
42fad3a1d3Sopenharmony_ciuse std::sync::atomic::{AtomicUsize, Ordering};
43fad3a1d3Sopenharmony_ciuse std::time::Instant;
44fad3a1d3Sopenharmony_ci
45fad3a1d3Sopenharmony_ci#[macro_use]
46fad3a1d3Sopenharmony_cimod macros;
47fad3a1d3Sopenharmony_ci
48fad3a1d3Sopenharmony_ci#[allow(dead_code)]
49fad3a1d3Sopenharmony_cimod common;
50fad3a1d3Sopenharmony_ci
51fad3a1d3Sopenharmony_cimod repo;
52fad3a1d3Sopenharmony_ci
53fad3a1d3Sopenharmony_ci#[test]
54fad3a1d3Sopenharmony_cifn test_round_trip() {
55fad3a1d3Sopenharmony_ci    common::rayon_init();
56fad3a1d3Sopenharmony_ci    repo::clone_rust();
57fad3a1d3Sopenharmony_ci    let abort_after = common::abort_after();
58fad3a1d3Sopenharmony_ci    if abort_after == 0 {
59fad3a1d3Sopenharmony_ci        panic!("Skipping all round_trip tests");
60fad3a1d3Sopenharmony_ci    }
61fad3a1d3Sopenharmony_ci
62fad3a1d3Sopenharmony_ci    let failed = AtomicUsize::new(0);
63fad3a1d3Sopenharmony_ci
64fad3a1d3Sopenharmony_ci    repo::for_each_rust_file(|path| test(path, &failed, abort_after));
65fad3a1d3Sopenharmony_ci
66fad3a1d3Sopenharmony_ci    let failed = failed.load(Ordering::Relaxed);
67fad3a1d3Sopenharmony_ci    if failed > 0 {
68fad3a1d3Sopenharmony_ci        panic!("{} failures", failed);
69fad3a1d3Sopenharmony_ci    }
70fad3a1d3Sopenharmony_ci}
71fad3a1d3Sopenharmony_ci
72fad3a1d3Sopenharmony_cifn test(path: &Path, failed: &AtomicUsize, abort_after: usize) {
73fad3a1d3Sopenharmony_ci    let content = fs::read_to_string(path).unwrap();
74fad3a1d3Sopenharmony_ci
75fad3a1d3Sopenharmony_ci    let start = Instant::now();
76fad3a1d3Sopenharmony_ci    let (krate, elapsed) = match syn::parse_file(&content) {
77fad3a1d3Sopenharmony_ci        Ok(krate) => (krate, start.elapsed()),
78fad3a1d3Sopenharmony_ci        Err(msg) => {
79fad3a1d3Sopenharmony_ci            errorf!("=== {}: syn failed to parse\n{:?}\n", path.display(), msg);
80fad3a1d3Sopenharmony_ci            let prev_failed = failed.fetch_add(1, Ordering::Relaxed);
81fad3a1d3Sopenharmony_ci            if prev_failed + 1 >= abort_after {
82fad3a1d3Sopenharmony_ci                process::exit(1);
83fad3a1d3Sopenharmony_ci            }
84fad3a1d3Sopenharmony_ci            return;
85fad3a1d3Sopenharmony_ci        }
86fad3a1d3Sopenharmony_ci    };
87fad3a1d3Sopenharmony_ci    let back = quote!(#krate).to_string();
88fad3a1d3Sopenharmony_ci    let edition = repo::edition(path).parse().unwrap();
89fad3a1d3Sopenharmony_ci
90fad3a1d3Sopenharmony_ci    rustc_span::create_session_if_not_set_then(edition, |_| {
91fad3a1d3Sopenharmony_ci        let equal = match panic::catch_unwind(|| {
92fad3a1d3Sopenharmony_ci            let locale_resources = rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec();
93fad3a1d3Sopenharmony_ci            let file_path_mapping = FilePathMapping::empty();
94fad3a1d3Sopenharmony_ci            let sess = ParseSess::new(locale_resources, file_path_mapping);
95fad3a1d3Sopenharmony_ci            let before = match librustc_parse(content, &sess) {
96fad3a1d3Sopenharmony_ci                Ok(before) => before,
97fad3a1d3Sopenharmony_ci                Err(diagnostic) => {
98fad3a1d3Sopenharmony_ci                    errorf!(
99fad3a1d3Sopenharmony_ci                        "=== {}: ignore - librustc failed to parse original content: {}\n",
100fad3a1d3Sopenharmony_ci                        path.display(),
101fad3a1d3Sopenharmony_ci                        translate_message(&diagnostic),
102fad3a1d3Sopenharmony_ci                    );
103fad3a1d3Sopenharmony_ci                    diagnostic.cancel();
104fad3a1d3Sopenharmony_ci                    return Err(true);
105fad3a1d3Sopenharmony_ci                }
106fad3a1d3Sopenharmony_ci            };
107fad3a1d3Sopenharmony_ci            let after = match librustc_parse(back, &sess) {
108fad3a1d3Sopenharmony_ci                Ok(after) => after,
109fad3a1d3Sopenharmony_ci                Err(mut diagnostic) => {
110fad3a1d3Sopenharmony_ci                    errorf!("=== {}: librustc failed to parse", path.display());
111fad3a1d3Sopenharmony_ci                    diagnostic.emit();
112fad3a1d3Sopenharmony_ci                    return Err(false);
113fad3a1d3Sopenharmony_ci                }
114fad3a1d3Sopenharmony_ci            };
115fad3a1d3Sopenharmony_ci            Ok((before, after))
116fad3a1d3Sopenharmony_ci        }) {
117fad3a1d3Sopenharmony_ci            Err(_) => {
118fad3a1d3Sopenharmony_ci                errorf!("=== {}: ignoring librustc panic\n", path.display());
119fad3a1d3Sopenharmony_ci                true
120fad3a1d3Sopenharmony_ci            }
121fad3a1d3Sopenharmony_ci            Ok(Err(equal)) => equal,
122fad3a1d3Sopenharmony_ci            Ok(Ok((mut before, mut after))) => {
123fad3a1d3Sopenharmony_ci                normalize(&mut before);
124fad3a1d3Sopenharmony_ci                normalize(&mut after);
125fad3a1d3Sopenharmony_ci                if SpanlessEq::eq(&before, &after) {
126fad3a1d3Sopenharmony_ci                    errorf!(
127fad3a1d3Sopenharmony_ci                        "=== {}: pass in {}ms\n",
128fad3a1d3Sopenharmony_ci                        path.display(),
129fad3a1d3Sopenharmony_ci                        elapsed.as_secs() * 1000 + u64::from(elapsed.subsec_nanos()) / 1_000_000
130fad3a1d3Sopenharmony_ci                    );
131fad3a1d3Sopenharmony_ci                    true
132fad3a1d3Sopenharmony_ci                } else {
133fad3a1d3Sopenharmony_ci                    errorf!(
134fad3a1d3Sopenharmony_ci                        "=== {}: FAIL\n{}\n!=\n{}\n",
135fad3a1d3Sopenharmony_ci                        path.display(),
136fad3a1d3Sopenharmony_ci                        pprust::crate_to_string_for_macros(&before),
137fad3a1d3Sopenharmony_ci                        pprust::crate_to_string_for_macros(&after),
138fad3a1d3Sopenharmony_ci                    );
139fad3a1d3Sopenharmony_ci                    false
140fad3a1d3Sopenharmony_ci                }
141fad3a1d3Sopenharmony_ci            }
142fad3a1d3Sopenharmony_ci        };
143fad3a1d3Sopenharmony_ci        if !equal {
144fad3a1d3Sopenharmony_ci            let prev_failed = failed.fetch_add(1, Ordering::Relaxed);
145fad3a1d3Sopenharmony_ci            if prev_failed + 1 >= abort_after {
146fad3a1d3Sopenharmony_ci                process::exit(1);
147fad3a1d3Sopenharmony_ci            }
148fad3a1d3Sopenharmony_ci        }
149fad3a1d3Sopenharmony_ci    });
150fad3a1d3Sopenharmony_ci}
151fad3a1d3Sopenharmony_ci
152fad3a1d3Sopenharmony_cifn librustc_parse(content: String, sess: &ParseSess) -> PResult<Crate> {
153fad3a1d3Sopenharmony_ci    static COUNTER: AtomicUsize = AtomicUsize::new(0);
154fad3a1d3Sopenharmony_ci    let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
155fad3a1d3Sopenharmony_ci    let name = FileName::Custom(format!("test_round_trip{}", counter));
156fad3a1d3Sopenharmony_ci    parse::parse_crate_from_source_str(name, content, sess)
157fad3a1d3Sopenharmony_ci}
158fad3a1d3Sopenharmony_ci
159fad3a1d3Sopenharmony_cifn translate_message(diagnostic: &Diagnostic) -> Cow<'static, str> {
160fad3a1d3Sopenharmony_ci    thread_local! {
161fad3a1d3Sopenharmony_ci        static FLUENT_BUNDLE: LazyFallbackBundle = {
162fad3a1d3Sopenharmony_ci            let locale_resources = rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec();
163fad3a1d3Sopenharmony_ci            let with_directionality_markers = false;
164fad3a1d3Sopenharmony_ci            rustc_error_messages::fallback_fluent_bundle(locale_resources, with_directionality_markers)
165fad3a1d3Sopenharmony_ci        };
166fad3a1d3Sopenharmony_ci    }
167fad3a1d3Sopenharmony_ci
168fad3a1d3Sopenharmony_ci    let message = &diagnostic.messages[0].0;
169fad3a1d3Sopenharmony_ci    let args = translation::to_fluent_args(diagnostic.args());
170fad3a1d3Sopenharmony_ci
171fad3a1d3Sopenharmony_ci    let (identifier, attr) = match message {
172fad3a1d3Sopenharmony_ci        DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => return msg.clone(),
173fad3a1d3Sopenharmony_ci        DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
174fad3a1d3Sopenharmony_ci    };
175fad3a1d3Sopenharmony_ci
176fad3a1d3Sopenharmony_ci    FLUENT_BUNDLE.with(|fluent_bundle| {
177fad3a1d3Sopenharmony_ci        let message = fluent_bundle
178fad3a1d3Sopenharmony_ci            .get_message(identifier)
179fad3a1d3Sopenharmony_ci            .expect("missing diagnostic in fluent bundle");
180fad3a1d3Sopenharmony_ci        let value = match attr {
181fad3a1d3Sopenharmony_ci            Some(attr) => message
182fad3a1d3Sopenharmony_ci                .get_attribute(attr)
183fad3a1d3Sopenharmony_ci                .expect("missing attribute in fluent message")
184fad3a1d3Sopenharmony_ci                .value(),
185fad3a1d3Sopenharmony_ci            None => message.value().expect("missing value in fluent message"),
186fad3a1d3Sopenharmony_ci        };
187fad3a1d3Sopenharmony_ci
188fad3a1d3Sopenharmony_ci        let mut err = Vec::new();
189fad3a1d3Sopenharmony_ci        let translated = fluent_bundle.format_pattern(value, Some(&args), &mut err);
190fad3a1d3Sopenharmony_ci        assert!(err.is_empty());
191fad3a1d3Sopenharmony_ci        Cow::Owned(translated.into_owned())
192fad3a1d3Sopenharmony_ci    })
193fad3a1d3Sopenharmony_ci}
194fad3a1d3Sopenharmony_ci
195fad3a1d3Sopenharmony_cifn normalize(krate: &mut Crate) {
196fad3a1d3Sopenharmony_ci    struct NormalizeVisitor;
197fad3a1d3Sopenharmony_ci
198fad3a1d3Sopenharmony_ci    impl MutVisitor for NormalizeVisitor {
199fad3a1d3Sopenharmony_ci        fn visit_angle_bracketed_parameter_data(&mut self, e: &mut AngleBracketedArgs) {
200fad3a1d3Sopenharmony_ci            #[derive(Ord, PartialOrd, Eq, PartialEq)]
201fad3a1d3Sopenharmony_ci            enum Group {
202fad3a1d3Sopenharmony_ci                Lifetimes,
203fad3a1d3Sopenharmony_ci                TypesAndConsts,
204fad3a1d3Sopenharmony_ci                Constraints,
205fad3a1d3Sopenharmony_ci            }
206fad3a1d3Sopenharmony_ci            e.args.sort_by_key(|arg| match arg {
207fad3a1d3Sopenharmony_ci                AngleBracketedArg::Arg(arg) => match arg {
208fad3a1d3Sopenharmony_ci                    GenericArg::Lifetime(_) => Group::Lifetimes,
209fad3a1d3Sopenharmony_ci                    GenericArg::Type(_) | GenericArg::Const(_) => Group::TypesAndConsts,
210fad3a1d3Sopenharmony_ci                },
211fad3a1d3Sopenharmony_ci                AngleBracketedArg::Constraint(_) => Group::Constraints,
212fad3a1d3Sopenharmony_ci            });
213fad3a1d3Sopenharmony_ci            mut_visit::noop_visit_angle_bracketed_parameter_data(e, self);
214fad3a1d3Sopenharmony_ci        }
215fad3a1d3Sopenharmony_ci
216fad3a1d3Sopenharmony_ci        fn visit_generics(&mut self, e: &mut Generics) {
217fad3a1d3Sopenharmony_ci            #[derive(Ord, PartialOrd, Eq, PartialEq)]
218fad3a1d3Sopenharmony_ci            enum Group {
219fad3a1d3Sopenharmony_ci                Lifetimes,
220fad3a1d3Sopenharmony_ci                TypesAndConsts,
221fad3a1d3Sopenharmony_ci            }
222fad3a1d3Sopenharmony_ci            e.params.sort_by_key(|param| match param.kind {
223fad3a1d3Sopenharmony_ci                GenericParamKind::Lifetime => Group::Lifetimes,
224fad3a1d3Sopenharmony_ci                GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
225fad3a1d3Sopenharmony_ci                    Group::TypesAndConsts
226fad3a1d3Sopenharmony_ci                }
227fad3a1d3Sopenharmony_ci            });
228fad3a1d3Sopenharmony_ci            mut_visit::noop_visit_generics(e, self);
229fad3a1d3Sopenharmony_ci        }
230fad3a1d3Sopenharmony_ci
231fad3a1d3Sopenharmony_ci        fn visit_where_clause(&mut self, e: &mut WhereClause) {
232fad3a1d3Sopenharmony_ci            if e.predicates.is_empty() {
233fad3a1d3Sopenharmony_ci                e.has_where_token = false;
234fad3a1d3Sopenharmony_ci            }
235fad3a1d3Sopenharmony_ci        }
236fad3a1d3Sopenharmony_ci    }
237fad3a1d3Sopenharmony_ci
238fad3a1d3Sopenharmony_ci    NormalizeVisitor.visit_crate(krate);
239fad3a1d3Sopenharmony_ci}
240