mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
Merge #872
872: simplify trait bounds r=matklad a=matklad Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
5cacdfcb3c
@ -4,7 +4,7 @@ use std::{fs, io::Read, path::Path, time::Instant};
|
|||||||
|
|
||||||
use clap::{App, Arg, SubCommand};
|
use clap::{App, Arg, SubCommand};
|
||||||
use join_to_string::join;
|
use join_to_string::join;
|
||||||
use ra_ide_api_light::{extend_selection, file_structure, syntax_tree};
|
use ra_ide_api_light::{extend_selection, file_structure};
|
||||||
use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode};
|
use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode};
|
||||||
use tools::collect_tests;
|
use tools::collect_tests;
|
||||||
use flexi_logger::Logger;
|
use flexi_logger::Logger;
|
||||||
@ -37,7 +37,7 @@ fn main() -> Result<()> {
|
|||||||
let file = file()?;
|
let file = file()?;
|
||||||
let elapsed = start.elapsed();
|
let elapsed = start.elapsed();
|
||||||
if !matches.is_present("no-dump") {
|
if !matches.is_present("no-dump") {
|
||||||
println!("{}", syntax_tree(&file));
|
println!("{}", file.syntax().debug_dump());
|
||||||
}
|
}
|
||||||
eprintln!("parsing: {:?}", elapsed);
|
eprintln!("parsing: {:?}", elapsed);
|
||||||
::std::mem::forget(file);
|
::std::mem::forget(file);
|
||||||
@ -94,7 +94,7 @@ fn render_test(file: &Path, line: usize) -> Result<(String, String)> {
|
|||||||
Some((_start_line, test)) => test,
|
Some((_start_line, test)) => test,
|
||||||
};
|
};
|
||||||
let file = SourceFile::parse(&test.text);
|
let file = SourceFile::parse(&test.text);
|
||||||
let tree = syntax_tree(&file);
|
let tree = file.syntax().debug_dump();
|
||||||
Ok((test.text, tree))
|
Ok((test.text, tree))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ mod marks;
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit};
|
use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit, AstNode};
|
||||||
use ra_text_edit::TextEdit;
|
use ra_text_edit::TextEdit;
|
||||||
use ra_db::{
|
use ra_db::{
|
||||||
SourceDatabase, CheckCanceled,
|
SourceDatabase, CheckCanceled,
|
||||||
@ -244,8 +244,7 @@ impl Analysis {
|
|||||||
/// Returns a syntax tree represented as `String`, for debug purposes.
|
/// Returns a syntax tree represented as `String`, for debug purposes.
|
||||||
// FIXME: use a better name here.
|
// FIXME: use a better name here.
|
||||||
pub fn syntax_tree(&self, file_id: FileId) -> String {
|
pub fn syntax_tree(&self, file_id: FileId) -> String {
|
||||||
let file = self.db.parse(file_id);
|
self.db.parse(file_id).syntax().debug_dump()
|
||||||
ra_ide_api_light::syntax_tree(&file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an edit to remove all newlines in the range, cleaning up minor
|
/// Returns an edit to remove all newlines in the range, cleaning up minor
|
||||||
|
@ -123,10 +123,6 @@ pub fn highlight(root: &SyntaxNode) -> Vec<HighlightedRange> {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn syntax_tree(file: &SourceFile) -> String {
|
|
||||||
::ra_syntax::utils::dump_tree(file.syntax())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ra_syntax::AstNode;
|
use ra_syntax::AstNode;
|
||||||
|
@ -4,6 +4,6 @@ extern crate ra_syntax;
|
|||||||
|
|
||||||
fuzz_target!(|data: &[u8]| {
|
fuzz_target!(|data: &[u8]| {
|
||||||
if let Ok(text) = std::str::from_utf8(data) {
|
if let Ok(text) = std::str::from_utf8(data) {
|
||||||
ra_syntax::utils::check_fuzz_invariants(text)
|
ra_syntax::check_fuzz_invariants(text)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -27,8 +27,6 @@ mod ptr;
|
|||||||
|
|
||||||
pub mod algo;
|
pub mod algo;
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
/// Utilities for simple uses of the parser.
|
|
||||||
pub mod utils;
|
|
||||||
|
|
||||||
pub use rowan::{SmolStr, TextRange, TextUnit};
|
pub use rowan::{SmolStr, TextRange, TextUnit};
|
||||||
pub use ra_parser::SyntaxKind;
|
pub use ra_parser::SyntaxKind;
|
||||||
@ -51,7 +49,7 @@ impl SourceFile {
|
|||||||
fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SourceFile> {
|
fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SourceFile> {
|
||||||
let root = SyntaxNode::new(green, errors);
|
let root = SyntaxNode::new(green, errors);
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
utils::validate_block_structure(&root);
|
validation::validate_block_structure(&root);
|
||||||
}
|
}
|
||||||
assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
|
assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
|
||||||
TreeArc::cast(root)
|
TreeArc::cast(root)
|
||||||
@ -82,3 +80,10 @@ impl SourceFile {
|
|||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_fuzz_invariants(text: &str) {
|
||||||
|
let file = SourceFile::parse(text);
|
||||||
|
let root = file.syntax();
|
||||||
|
validation::validate_block_structure(root);
|
||||||
|
let _ = file.errors();
|
||||||
|
}
|
||||||
|
@ -143,7 +143,7 @@ fn merge_errors(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use test_utils::{extract_range, assert_eq_text};
|
use test_utils::{extract_range, assert_eq_text};
|
||||||
|
|
||||||
use crate::{SourceFile, AstNode, utils::dump_tree};
|
use crate::{SourceFile, AstNode};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn do_check<F>(before: &str, replace_with: &str, reparser: F)
|
fn do_check<F>(before: &str, replace_with: &str, reparser: F)
|
||||||
@ -169,8 +169,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert_eq_text!(
|
assert_eq_text!(
|
||||||
&dump_tree(fully_reparsed.syntax()),
|
&fully_reparsed.syntax().debug_dump(),
|
||||||
&dump_tree(incrementally_reparsed.syntax()),
|
&incrementally_reparsed.syntax().debug_dump(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,12 +6,15 @@
|
|||||||
//! The *real* implementation is in the (language-agnostic) `rowan` crate, this
|
//! The *real* implementation is in the (language-agnostic) `rowan` crate, this
|
||||||
//! modules just wraps its API.
|
//! modules just wraps its API.
|
||||||
|
|
||||||
use std::{fmt, borrow::Borrow};
|
use std::{
|
||||||
|
fmt::{self, Write},
|
||||||
|
borrow::Borrow,
|
||||||
|
};
|
||||||
|
|
||||||
use rowan::{Types, TransparentNewType};
|
use rowan::{Types, TransparentNewType};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
SmolStr, SyntaxKind, TextRange, SyntaxText,
|
SmolStr, SyntaxKind, TextRange, SyntaxText, SourceFile, AstNode,
|
||||||
syntax_error::SyntaxError,
|
syntax_error::SyntaxError,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -24,14 +27,17 @@ impl Types for RaTypes {
|
|||||||
type RootData = Vec<SyntaxError>;
|
type RootData = Vec<SyntaxError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type GreenNode = rowan::GreenNode<RaTypes>;
|
pub(crate) type GreenNode = rowan::GreenNode<RaTypes>;
|
||||||
|
|
||||||
|
/// Marker trait for CST and AST nodes
|
||||||
|
pub trait SyntaxNodeWrapper: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>> {}
|
||||||
|
impl<T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>> SyntaxNodeWrapper for T {}
|
||||||
|
|
||||||
|
/// An owning smart pointer for CST or AST node.
|
||||||
#[derive(PartialEq, Eq, Hash)]
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
pub struct TreeArc<T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>>(
|
pub struct TreeArc<T: SyntaxNodeWrapper>(pub(crate) rowan::TreeArc<RaTypes, T>);
|
||||||
pub(crate) rowan::TreeArc<RaTypes, T>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>> Borrow<T> for TreeArc<T> {
|
impl<T: SyntaxNodeWrapper> Borrow<T> for TreeArc<T> {
|
||||||
fn borrow(&self) -> &T {
|
fn borrow(&self) -> &T {
|
||||||
&*self
|
&*self
|
||||||
}
|
}
|
||||||
@ -39,11 +45,11 @@ impl<T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>> Borrow<T> for Tre
|
|||||||
|
|
||||||
impl<T> TreeArc<T>
|
impl<T> TreeArc<T>
|
||||||
where
|
where
|
||||||
T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>,
|
T: SyntaxNodeWrapper,
|
||||||
{
|
{
|
||||||
pub(crate) fn cast<U>(this: TreeArc<T>) -> TreeArc<U>
|
pub(crate) fn cast<U>(this: TreeArc<T>) -> TreeArc<U>
|
||||||
where
|
where
|
||||||
U: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>,
|
U: SyntaxNodeWrapper,
|
||||||
{
|
{
|
||||||
TreeArc(rowan::TreeArc::cast(this.0))
|
TreeArc(rowan::TreeArc::cast(this.0))
|
||||||
}
|
}
|
||||||
@ -51,7 +57,7 @@ where
|
|||||||
|
|
||||||
impl<T> std::ops::Deref for TreeArc<T>
|
impl<T> std::ops::Deref for TreeArc<T>
|
||||||
where
|
where
|
||||||
T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>,
|
T: SyntaxNodeWrapper,
|
||||||
{
|
{
|
||||||
type Target = T;
|
type Target = T;
|
||||||
fn deref(&self) -> &T {
|
fn deref(&self) -> &T {
|
||||||
@ -61,7 +67,7 @@ where
|
|||||||
|
|
||||||
impl<T> PartialEq<T> for TreeArc<T>
|
impl<T> PartialEq<T> for TreeArc<T>
|
||||||
where
|
where
|
||||||
T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>,
|
T: SyntaxNodeWrapper,
|
||||||
T: PartialEq<T>,
|
T: PartialEq<T>,
|
||||||
{
|
{
|
||||||
fn eq(&self, other: &T) -> bool {
|
fn eq(&self, other: &T) -> bool {
|
||||||
@ -72,7 +78,7 @@ where
|
|||||||
|
|
||||||
impl<T> Clone for TreeArc<T>
|
impl<T> Clone for TreeArc<T>
|
||||||
where
|
where
|
||||||
T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>,
|
T: SyntaxNodeWrapper,
|
||||||
{
|
{
|
||||||
fn clone(&self) -> TreeArc<T> {
|
fn clone(&self) -> TreeArc<T> {
|
||||||
TreeArc(self.0.clone())
|
TreeArc(self.0.clone())
|
||||||
@ -81,7 +87,7 @@ where
|
|||||||
|
|
||||||
impl<T> fmt::Debug for TreeArc<T>
|
impl<T> fmt::Debug for TreeArc<T>
|
||||||
where
|
where
|
||||||
T: TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>>,
|
T: SyntaxNodeWrapper,
|
||||||
T: fmt::Debug,
|
T: fmt::Debug,
|
||||||
{
|
{
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
@ -96,13 +102,24 @@ unsafe impl TransparentNewType for SyntaxNode {
|
|||||||
type Repr = rowan::SyntaxNode<RaTypes>;
|
type Repr = rowan::SyntaxNode<RaTypes>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyntaxNode {
|
impl ToOwned for SyntaxNode {
|
||||||
pub(crate) fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SyntaxNode> {
|
type Owned = TreeArc<SyntaxNode>;
|
||||||
let ptr = TreeArc(rowan::SyntaxNode::new(green, errors));
|
fn to_owned(&self) -> TreeArc<SyntaxNode> {
|
||||||
|
let ptr = TreeArc(self.0.to_owned());
|
||||||
TreeArc::cast(ptr)
|
TreeArc::cast(ptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for SyntaxNode {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(fmt, "{:?}@{:?}", self.kind(), self.range())?;
|
||||||
|
if has_short_text(self.kind()) {
|
||||||
|
write!(fmt, " \"{}\"", self.text())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
Next,
|
Next,
|
||||||
@ -110,48 +127,10 @@ pub enum Direction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SyntaxNode {
|
impl SyntaxNode {
|
||||||
pub fn leaf_text(&self) -> Option<&SmolStr> {
|
pub(crate) fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SyntaxNode> {
|
||||||
self.0.leaf_text()
|
let ptr = TreeArc(rowan::SyntaxNode::new(green, errors));
|
||||||
}
|
|
||||||
pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode> {
|
|
||||||
crate::algo::generate(Some(self), |&node| node.parent())
|
|
||||||
}
|
|
||||||
pub fn descendants(&self) -> impl Iterator<Item = &SyntaxNode> {
|
|
||||||
self.preorder().filter_map(|event| match event {
|
|
||||||
WalkEvent::Enter(node) => Some(node),
|
|
||||||
WalkEvent::Leave(_) => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = &SyntaxNode> {
|
|
||||||
crate::algo::generate(Some(self), move |&node| match direction {
|
|
||||||
Direction::Next => node.next_sibling(),
|
|
||||||
Direction::Prev => node.prev_sibling(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn preorder(&self) -> impl Iterator<Item = WalkEvent<&SyntaxNode>> {
|
|
||||||
self.0.preorder().map(|event| match event {
|
|
||||||
WalkEvent::Enter(n) => WalkEvent::Enter(SyntaxNode::from_repr(n)),
|
|
||||||
WalkEvent::Leave(n) => WalkEvent::Leave(SyntaxNode::from_repr(n)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToOwned for SyntaxNode {
|
|
||||||
type Owned = TreeArc<SyntaxNode>;
|
|
||||||
fn to_owned(&self) -> TreeArc<SyntaxNode> {
|
|
||||||
let ptr = TreeArc(self.0.to_owned());
|
|
||||||
TreeArc::cast(ptr)
|
TreeArc::cast(ptr)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl SyntaxNode {
|
|
||||||
pub(crate) fn root_data(&self) -> &Vec<SyntaxError> {
|
|
||||||
self.0.root_data()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode {
|
|
||||||
self.0.replace_self(replacement)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn kind(&self) -> SyntaxKind {
|
pub fn kind(&self) -> SyntaxKind {
|
||||||
self.0.kind()
|
self.0.kind()
|
||||||
@ -169,6 +148,10 @@ impl SyntaxNode {
|
|||||||
self.0.is_leaf()
|
self.0.is_leaf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn leaf_text(&self) -> Option<&SmolStr> {
|
||||||
|
self.0.leaf_text()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parent(&self) -> Option<&SyntaxNode> {
|
pub fn parent(&self) -> Option<&SyntaxNode> {
|
||||||
self.0.parent().map(SyntaxNode::from_repr)
|
self.0.parent().map(SyntaxNode::from_repr)
|
||||||
}
|
}
|
||||||
@ -193,18 +176,85 @@ impl SyntaxNode {
|
|||||||
SyntaxNodeChildren(self.0.children())
|
SyntaxNodeChildren(self.0.children())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode> {
|
||||||
|
crate::algo::generate(Some(self), |&node| node.parent())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn descendants(&self) -> impl Iterator<Item = &SyntaxNode> {
|
||||||
|
self.preorder().filter_map(|event| match event {
|
||||||
|
WalkEvent::Enter(node) => Some(node),
|
||||||
|
WalkEvent::Leave(_) => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = &SyntaxNode> {
|
||||||
|
crate::algo::generate(Some(self), move |&node| match direction {
|
||||||
|
Direction::Next => node.next_sibling(),
|
||||||
|
Direction::Prev => node.prev_sibling(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn preorder(&self) -> impl Iterator<Item = WalkEvent<&SyntaxNode>> {
|
||||||
|
self.0.preorder().map(|event| match event {
|
||||||
|
WalkEvent::Enter(n) => WalkEvent::Enter(SyntaxNode::from_repr(n)),
|
||||||
|
WalkEvent::Leave(n) => WalkEvent::Leave(SyntaxNode::from_repr(n)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn memory_size_of_subtree(&self) -> usize {
|
pub fn memory_size_of_subtree(&self) -> usize {
|
||||||
self.0.memory_size_of_subtree()
|
self.0.memory_size_of_subtree()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for SyntaxNode {
|
pub fn debug_dump(&self) -> String {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
let mut errors: Vec<_> = match self.ancestors().find_map(SourceFile::cast) {
|
||||||
write!(fmt, "{:?}@{:?}", self.kind(), self.range())?;
|
Some(file) => file.errors(),
|
||||||
if has_short_text(self.kind()) {
|
None => self.root_data().to_vec(),
|
||||||
write!(fmt, " \"{}\"", self.text())?;
|
};
|
||||||
|
errors.sort_by_key(|e| e.offset());
|
||||||
|
let mut err_pos = 0;
|
||||||
|
let mut level = 0;
|
||||||
|
let mut buf = String::new();
|
||||||
|
macro_rules! indent {
|
||||||
|
() => {
|
||||||
|
for _ in 0..level {
|
||||||
|
buf.push_str(" ");
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
|
for event in self.preorder() {
|
||||||
|
match event {
|
||||||
|
WalkEvent::Enter(node) => {
|
||||||
|
indent!();
|
||||||
|
writeln!(buf, "{:?}", node).unwrap();
|
||||||
|
if node.first_child().is_none() {
|
||||||
|
let off = node.range().end();
|
||||||
|
while err_pos < errors.len() && errors[err_pos].offset() <= off {
|
||||||
|
indent!();
|
||||||
|
writeln!(buf, "err: `{}`", errors[err_pos]).unwrap();
|
||||||
|
err_pos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
level += 1;
|
||||||
|
}
|
||||||
|
WalkEvent::Leave(_) => level -= 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(level, 0);
|
||||||
|
for err in errors[err_pos..].iter() {
|
||||||
|
writeln!(buf, "err: `{}`", err).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn root_data(&self) -> &Vec<SyntaxError> {
|
||||||
|
self.0.root_data()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode {
|
||||||
|
self.0.replace_self(replacement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
use std::{str, fmt::Write};
|
|
||||||
|
|
||||||
use crate::{SourceFile, SyntaxKind, WalkEvent, AstNode, SyntaxNode};
|
|
||||||
|
|
||||||
/// Parse a file and create a string representation of the resulting parse tree.
|
|
||||||
pub fn dump_tree(syntax: &SyntaxNode) -> String {
|
|
||||||
let mut errors: Vec<_> = match syntax.ancestors().find_map(SourceFile::cast) {
|
|
||||||
Some(file) => file.errors(),
|
|
||||||
None => syntax.root_data().to_vec(),
|
|
||||||
};
|
|
||||||
errors.sort_by_key(|e| e.offset());
|
|
||||||
let mut err_pos = 0;
|
|
||||||
let mut level = 0;
|
|
||||||
let mut buf = String::new();
|
|
||||||
macro_rules! indent {
|
|
||||||
() => {
|
|
||||||
for _ in 0..level {
|
|
||||||
buf.push_str(" ");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for event in syntax.preorder() {
|
|
||||||
match event {
|
|
||||||
WalkEvent::Enter(node) => {
|
|
||||||
indent!();
|
|
||||||
writeln!(buf, "{:?}", node).unwrap();
|
|
||||||
if node.first_child().is_none() {
|
|
||||||
let off = node.range().end();
|
|
||||||
while err_pos < errors.len() && errors[err_pos].offset() <= off {
|
|
||||||
indent!();
|
|
||||||
writeln!(buf, "err: `{}`", errors[err_pos]).unwrap();
|
|
||||||
err_pos += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
level += 1;
|
|
||||||
}
|
|
||||||
WalkEvent::Leave(_) => level -= 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(level, 0);
|
|
||||||
for err in errors[err_pos..].iter() {
|
|
||||||
writeln!(buf, "err: `{}`", err).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_fuzz_invariants(text: &str) {
|
|
||||||
let file = SourceFile::parse(text);
|
|
||||||
let root = file.syntax();
|
|
||||||
validate_block_structure(root);
|
|
||||||
let _ = file.errors();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn validate_block_structure(root: &SyntaxNode) {
|
|
||||||
let mut stack = Vec::new();
|
|
||||||
for node in root.descendants() {
|
|
||||||
match node.kind() {
|
|
||||||
SyntaxKind::L_CURLY => stack.push(node),
|
|
||||||
SyntaxKind::R_CURLY => {
|
|
||||||
if let Some(pair) = stack.pop() {
|
|
||||||
assert_eq!(
|
|
||||||
node.parent(),
|
|
||||||
pair.parent(),
|
|
||||||
"\nunpaired curleys:\n{}\n{}\n",
|
|
||||||
root.text(),
|
|
||||||
dump_tree(root),
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
node.next_sibling().is_none() && pair.prev_sibling().is_none(),
|
|
||||||
"\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
|
|
||||||
node,
|
|
||||||
root.text(),
|
|
||||||
node.text(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,8 @@ mod string;
|
|||||||
mod block;
|
mod block;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
SourceFile, SyntaxError, AstNode,
|
SourceFile, SyntaxError, AstNode, SyntaxNode,
|
||||||
|
SyntaxKind::{L_CURLY, R_CURLY},
|
||||||
ast,
|
ast,
|
||||||
algo::visit::{visitor_ctx, VisitorCtx},
|
algo::visit::{visitor_ctx, VisitorCtx},
|
||||||
};
|
};
|
||||||
@ -14,12 +15,40 @@ pub(crate) fn validate(file: &SourceFile) -> Vec<SyntaxError> {
|
|||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
for node in file.syntax().descendants() {
|
for node in file.syntax().descendants() {
|
||||||
let _ = visitor_ctx(&mut errors)
|
let _ = visitor_ctx(&mut errors)
|
||||||
.visit::<ast::Byte, _>(self::byte::validate_byte_node)
|
.visit::<ast::Byte, _>(byte::validate_byte_node)
|
||||||
.visit::<ast::ByteString, _>(self::byte_string::validate_byte_string_node)
|
.visit::<ast::ByteString, _>(byte_string::validate_byte_string_node)
|
||||||
.visit::<ast::Char, _>(self::char::validate_char_node)
|
.visit::<ast::Char, _>(char::validate_char_node)
|
||||||
.visit::<ast::String, _>(self::string::validate_string_node)
|
.visit::<ast::String, _>(string::validate_string_node)
|
||||||
.visit::<ast::Block, _>(self::block::validate_block_node)
|
.visit::<ast::Block, _>(block::validate_block_node)
|
||||||
.accept(node);
|
.accept(node);
|
||||||
}
|
}
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn validate_block_structure(root: &SyntaxNode) {
|
||||||
|
let mut stack = Vec::new();
|
||||||
|
for node in root.descendants() {
|
||||||
|
match node.kind() {
|
||||||
|
L_CURLY => stack.push(node),
|
||||||
|
R_CURLY => {
|
||||||
|
if let Some(pair) = stack.pop() {
|
||||||
|
assert_eq!(
|
||||||
|
node.parent(),
|
||||||
|
pair.parent(),
|
||||||
|
"\nunpaired curleys:\n{}\n{}\n",
|
||||||
|
root.text(),
|
||||||
|
root.debug_dump(),
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
node.next_sibling().is_none() && pair.prev_sibling().is_none(),
|
||||||
|
"\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
|
||||||
|
node,
|
||||||
|
root.text(),
|
||||||
|
node.text(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,10 +8,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use test_utils::{project_dir, dir_tests, read_text, collect_tests};
|
use test_utils::{project_dir, dir_tests, read_text, collect_tests};
|
||||||
use ra_syntax::{
|
use ra_syntax::{SourceFile, AstNode, check_fuzz_invariants};
|
||||||
SourceFile, AstNode,
|
|
||||||
utils::{check_fuzz_invariants, dump_tree},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lexer_tests() {
|
fn lexer_tests() {
|
||||||
@ -32,7 +29,7 @@ fn parser_tests() {
|
|||||||
"There should be no errors in the file {:?}",
|
"There should be no errors in the file {:?}",
|
||||||
path.display()
|
path.display()
|
||||||
);
|
);
|
||||||
dump_tree(file.syntax())
|
file.syntax().debug_dump()
|
||||||
});
|
});
|
||||||
dir_tests(&test_data_dir(), &["parser/err", "parser/inline/err"], |text, path| {
|
dir_tests(&test_data_dir(), &["parser/err", "parser/inline/err"], |text, path| {
|
||||||
let file = SourceFile::parse(text);
|
let file = SourceFile::parse(text);
|
||||||
@ -43,7 +40,7 @@ fn parser_tests() {
|
|||||||
"There should be errors in the file {:?}",
|
"There should be errors in the file {:?}",
|
||||||
path.display()
|
path.display()
|
||||||
);
|
);
|
||||||
dump_tree(file.syntax())
|
file.syntax().debug_dump()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user