Skip to content

Commit

Permalink
red-knot: Use parse_unchecked to get all parse errors
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser committed Jun 4, 2024
1 parent 0c75548 commit 4af0673
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 98 deletions.
13 changes: 7 additions & 6 deletions crates/red_knot/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use std::time::Duration;

use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::{ModModule, StringLiteral};
use ruff_python_parser::Parsed;

use crate::cache::KeyValueCache;
use crate::db::{LintDb, LintJar, QueryResult};
use crate::files::FileId;
use crate::module::ModuleName;
use crate::parse::{parse, Parsed};
use crate::parse::parse;
use crate::source::{source_text, Source};
use crate::symbols::{
resolve_global_symbol, symbol_table, Definition, GlobalSymbolId, SymbolId, SymbolTable,
Expand Down Expand Up @@ -40,7 +41,7 @@ pub(crate) fn lint_syntax(db: &dyn LintDb, file_id: FileId) -> QueryResult<Diagn
let parsed = parse(db.upcast(), *file_id)?;

if parsed.errors().is_empty() {
let ast = parsed.ast();
let ast = parsed.syntax();

let mut visitor = SyntaxLintVisitor {
diagnostics,
Expand Down Expand Up @@ -86,7 +87,7 @@ pub(crate) fn lint_semantic(db: &dyn LintDb, file_id: FileId) -> QueryResult<Dia
let context = SemanticLintContext {
file_id: *file_id,
source,
parsed,
parsed: &*parsed,
symbols,
db,
diagnostics: RefCell::new(Vec::new()),
Expand Down Expand Up @@ -194,7 +195,7 @@ fn lint_bad_overrides(context: &SemanticLintContext) -> QueryResult<()> {
pub struct SemanticLintContext<'a> {
file_id: FileId,
source: Source,
parsed: Parsed,
parsed: &'a Parsed<ModModule>,
symbols: Arc<SymbolTable>,
db: &'a dyn LintDb,
diagnostics: RefCell<Vec<String>>,
Expand All @@ -209,8 +210,8 @@ impl<'a> SemanticLintContext<'a> {
self.file_id
}

pub fn ast(&self) -> &ModModule {
self.parsed.ast()
pub fn ast(&self) -> &'a ModModule {
self.parsed.syntax()
}

pub fn symbols(&self) -> &SymbolTable {
Expand Down
73 changes: 10 additions & 63 deletions crates/red_knot/src/parse.rs
Original file line number Diff line number Diff line change
@@ -1,87 +1,34 @@
use std::ops::{Deref, DerefMut};
use std::sync::Arc;

use ruff_python_ast as ast;
use ruff_python_parser::{Mode, ParseError};
use ruff_text_size::{Ranged, TextRange};
use ruff_python_ast::ModModule;
use ruff_python_parser::{Mode, Parsed};

use crate::cache::KeyValueCache;
use crate::db::{QueryResult, SourceDb};
use crate::files::FileId;
use crate::source::source_text;

#[derive(Debug, Clone, PartialEq)]
pub struct Parsed {
inner: Arc<ParsedInner>,
}

#[derive(Debug, PartialEq)]
struct ParsedInner {
ast: ast::ModModule,
errors: Vec<ParseError>,
}

impl Parsed {
fn new(ast: ast::ModModule, errors: Vec<ParseError>) -> Self {
Self {
inner: Arc::new(ParsedInner { ast, errors }),
}
}

pub(crate) fn from_text(text: &str) -> Self {
let result = ruff_python_parser::parse(text, Mode::Module);

let (module, errors) = match result {
Ok(parsed) => match parsed.into_syntax() {
ast::Mod::Module(module) => (module, vec![]),
ast::Mod::Expression(expression) => (
ast::ModModule {
range: expression.range(),
body: vec![ast::Stmt::Expr(ast::StmtExpr {
range: expression.range(),
value: expression.body,
})],
},
vec![],
),
},
Err(errors) => (
ast::ModModule {
range: TextRange::default(),
body: Vec::new(),
},
vec![errors],
),
};

Parsed::new(module, errors)
}

pub fn ast(&self) -> &ast::ModModule {
&self.inner.ast
}

pub fn errors(&self) -> &[ParseError] {
&self.inner.errors
}
}

#[tracing::instrument(level = "debug", skip(db))]
pub(crate) fn parse(db: &dyn SourceDb, file_id: FileId) -> QueryResult<Parsed> {
pub(crate) fn parse(db: &dyn SourceDb, file_id: FileId) -> QueryResult<Arc<Parsed<ModModule>>> {
let jar = db.jar()?;

jar.parsed.get(&file_id, |file_id| {
let source = source_text(db, *file_id)?;

Ok(Parsed::from_text(source.text()))
Ok(Arc::new(
ruff_python_parser::parse_unchecked(source.text(), Mode::Module)
.try_into_module()
.unwrap(),
))
})
}

#[derive(Debug, Default)]
pub struct ParsedStorage(KeyValueCache<FileId, Parsed>);
pub struct ParsedStorage(KeyValueCache<FileId, Arc<Parsed<ModModule>>>);

impl Deref for ParsedStorage {
type Target = KeyValueCache<FileId, Parsed>;
type Target = KeyValueCache<FileId, Arc<Parsed<ModModule>>>;

fn deref(&self) -> &Self::Target {
&self.0
Expand Down
43 changes: 23 additions & 20 deletions crates/red_knot/src/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub fn symbol_table(db: &dyn SemanticDb, file_id: FileId) -> QueryResult<Arc<Sym

jar.symbol_tables.get(&file_id, |_| {
let parsed = parse(db.upcast(), file_id)?;
Ok(Arc::from(SymbolTable::from_ast(parsed.ast())))
Ok(Arc::from(SymbolTable::from_ast(parsed.syntax())))
})
}

Expand Down Expand Up @@ -903,13 +903,16 @@ mod tests {
use crate::symbols::{ScopeKind, SymbolFlags, SymbolTable};

mod from_ast {
use crate::parse::Parsed;
use crate::symbols::{Definition, ScopeKind, SymbolId, SymbolIterator, SymbolTable};
use ruff_python_ast as ast;
use textwrap::dedent;
use ruff_python_ast::ModModule;
use ruff_python_parser::{Mode, Parsed};

use crate::symbols::{Definition, ScopeKind, SymbolId, SymbolIterator, SymbolTable};

fn parse(code: &str) -> Parsed {
Parsed::from_text(&dedent(code))
fn parse(code: &str) -> Parsed<ModModule> {
ruff_python_parser::parse_unchecked(code, Mode::Module)
.try_into_module()
.unwrap()
}

fn names<I>(it: SymbolIterator<I>) -> Vec<&str>
Expand All @@ -924,14 +927,14 @@ mod tests {
#[test]
fn empty() {
let parsed = parse("");
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()).len(), 0);
}

#[test]
fn simple() {
let parsed = parse("x");
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["x"]);
assert_eq!(
table
Expand All @@ -944,15 +947,15 @@ mod tests {
#[test]
fn annotation_only() {
let parsed = parse("x: int");
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["int", "x"]);
// TODO record definition
}

#[test]
fn import() {
let parsed = parse("import foo");
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["foo"]);
assert_eq!(
table
Expand All @@ -965,21 +968,21 @@ mod tests {
#[test]
fn import_sub() {
let parsed = parse("import foo.bar");
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["foo"]);
}

#[test]
fn import_as() {
let parsed = parse("import foo.bar as baz");
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["baz"]);
}

#[test]
fn import_from() {
let parsed = parse("from bar import foo");
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["foo"]);
assert_eq!(
table
Expand All @@ -999,7 +1002,7 @@ mod tests {
#[test]
fn assign() {
let parsed = parse("x = foo");
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["foo", "x"]);
assert_eq!(
table
Expand All @@ -1025,7 +1028,7 @@ mod tests {
y = 2
",
);
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["C", "y"]);
let scopes = table.root_child_scope_ids();
assert_eq!(scopes.len(), 1);
Expand All @@ -1050,7 +1053,7 @@ mod tests {
y = 2
",
);
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["func", "y"]);
let scopes = table.root_child_scope_ids();
assert_eq!(scopes.len(), 1);
Expand All @@ -1076,7 +1079,7 @@ mod tests {
y = 2
",
);
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["func"]);
let scopes = table.root_child_scope_ids();
assert_eq!(scopes.len(), 2);
Expand Down Expand Up @@ -1104,7 +1107,7 @@ mod tests {
x = 1
",
);
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["func"]);
let scopes = table.root_child_scope_ids();
assert_eq!(scopes.len(), 1);
Expand All @@ -1130,7 +1133,7 @@ mod tests {
x = 1
",
);
let table = SymbolTable::from_ast(parsed.ast());
let table = SymbolTable::from_ast(parsed.syntax());
assert_eq!(names(table.root_symbols()), vec!["C"]);
let scopes = table.root_child_scope_ids();
assert_eq!(scopes.len(), 1);
Expand All @@ -1157,7 +1160,7 @@ mod tests {
#[test]
fn reachability_trivial() {
let parsed = parse("x = 1; x");
let ast = parsed.ast();
let ast = parsed.syntax();
let table = SymbolTable::from_ast(ast);
let x_sym = table
.root_symbol_id_by_name("x")
Expand Down
8 changes: 4 additions & 4 deletions crates/red_knot/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ pub fn infer_definition_type(
Ok(ty)
} else {
let parsed = parse(db.upcast(), file_id)?;
let ast = parsed.ast();
let ast = parsed.syntax();
let table = symbol_table(db, file_id)?;
let node = node_key.resolve_unwrap(ast.as_any_node_ref());

Expand All @@ -127,7 +127,7 @@ pub fn infer_definition_type(
Ok(ty)
} else {
let parsed = parse(db.upcast(), file_id)?;
let ast = parsed.ast();
let ast = parsed.syntax();
let table = symbol_table(db, file_id)?;
let node = node_key
.resolve(ast.as_any_node_ref())
Expand All @@ -154,14 +154,14 @@ pub fn infer_definition_type(
}
Definition::Assignment(node_key) => {
let parsed = parse(db.upcast(), file_id)?;
let ast = parsed.ast();
let ast = parsed.syntax();
let node = node_key.resolve_unwrap(ast.as_any_node_ref());
// TODO handle unpacking assignment correctly (here and for AnnotatedAssignment case, below)
infer_expr_type(db, file_id, &node.value)
}
Definition::AnnotatedAssignment(node_key) => {
let parsed = parse(db.upcast(), file_id)?;
let ast = parsed.ast();
let ast = parsed.syntax();
let node = node_key.resolve_unwrap(ast.as_any_node_ref());
// TODO actually look at the annotation
let Some(value) = &node.value else {
Expand Down
9 changes: 4 additions & 5 deletions crates/ruff_python_parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
//! [parsing]: https://en.wikipedia.org/wiki/Parsing
//! [lexer]: crate::lexer

use std::cell::OnceCell;
use std::ops::Deref;

pub use crate::error::{FStringErrorType, ParseError, ParseErrorType};
Expand Down Expand Up @@ -308,7 +307,7 @@ impl Parsed<Mod> {
/// returns [`None`].
///
/// [`Some(Parsed<ModModule>)`]: Some
fn try_into_module(self) -> Option<Parsed<ModModule>> {
pub fn try_into_module(self) -> Option<Parsed<ModModule>> {
match self.syntax {
Mod::Module(module) => Some(Parsed {
syntax: module,
Expand All @@ -327,7 +326,7 @@ impl Parsed<Mod> {
/// Otherwise, it returns [`None`].
///
/// [`Some(Parsed<ModExpression>)`]: Some
fn try_into_expression(self) -> Option<Parsed<ModExpression>> {
pub fn try_into_expression(self) -> Option<Parsed<ModExpression>> {
match self.syntax {
Mod::Module(_) => None,
Mod::Expression(expression) => Some(Parsed {
Expand Down Expand Up @@ -370,14 +369,14 @@ pub struct Tokens {
raw: Vec<Token>,

/// Index of the first [`TokenKind::Unknown`] token or the length of the token vector.
first_unknown_or_len: OnceCell<usize>,
first_unknown_or_len: std::sync::OnceLock<usize>,
}

impl Tokens {
pub(crate) fn new(tokens: Vec<Token>) -> Tokens {
Tokens {
raw: tokens,
first_unknown_or_len: OnceCell::new(),
first_unknown_or_len: std::sync::OnceLock::new(),
}
}

Expand Down

0 comments on commit 4af0673

Please sign in to comment.