-
Notifications
You must be signed in to change notification settings - Fork 602
Update comments / docs for Spanned
#1549
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
ec24d26
018afff
19a9278
dd9cfee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,25 +19,73 @@ use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; | |
use core::fmt::{self, Debug, Formatter}; | ||
use core::hash::{Hash, Hasher}; | ||
|
||
use crate::tokenizer::{Token, TokenWithLocation}; | ||
use crate::tokenizer::TokenWithLocation; | ||
|
||
#[cfg(feature = "serde")] | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[cfg(feature = "visitor")] | ||
use sqlparser_derive::{Visit, VisitMut}; | ||
|
||
/// A wrapper type for attaching tokens to AST nodes that should be ignored in comparisons and hashing. | ||
/// This should be used when a token is not relevant for semantics, but is still needed for | ||
/// accurate source location tracking. | ||
/// A wrapper over [`TokenWithLocation`]s that ignores the token and source | ||
/// location in comparisons and hashing. | ||
/// | ||
/// This type is used when the token and location is not relevant for semantics, | ||
/// but is still needed for accurate source location tracking, for example, in | ||
/// the nodes in the [ast](crate::ast) module. | ||
/// | ||
/// Note: **All** `AttachedTokens` are equal. | ||
/// | ||
/// # Examples | ||
/// | ||
/// Same token, different location are equal | ||
/// ``` | ||
/// # use sqlparser::ast::helpers::attached_token::AttachedToken; | ||
/// # use sqlparser::tokenizer::{Location, Span, Token, TokenWithLocation}; | ||
/// // commas @ line 1, column 10 | ||
/// let tok1 = TokenWithLocation::new( | ||
/// Token::Comma, | ||
/// Span::new(Location::new(1, 10), Location::new(1, 11)), | ||
/// ); | ||
/// // commas @ line 2, column 20 | ||
/// let tok2 = TokenWithLocation::new( | ||
/// Token::Comma, | ||
/// Span::new(Location::new(2, 20), Location::new(2, 21)), | ||
/// ); | ||
/// | ||
/// assert_ne!(tok1, tok2); // token with locations are *not* equal | ||
/// assert_eq!(AttachedToken(tok1), AttachedToken(tok2)); // attached tokens are | ||
/// ``` | ||
/// | ||
/// Different token, different location are equal 🤯 | ||
/// | ||
/// ``` | ||
/// # use sqlparser::ast::helpers::attached_token::AttachedToken; | ||
/// # use sqlparser::tokenizer::{Location, Span, Token, TokenWithLocation}; | ||
/// // commas @ line 1, column 10 | ||
/// let tok1 = TokenWithLocation::new( | ||
/// Token::Comma, | ||
/// Span::new(Location::new(1, 10), Location::new(1, 11)), | ||
/// ); | ||
/// // period @ line 2, column 20 | ||
/// let tok2 = TokenWithLocation::new( | ||
/// Token::Period, | ||
/// Span::new(Location::new(2, 10), Location::new(2, 21)), | ||
/// ); | ||
/// | ||
/// assert_ne!(tok1, tok2); // token with locations are *not* equal | ||
/// assert_eq!(AttachedToken(tok1), AttachedToken(tok2)); // attached tokens are | ||
/// ``` | ||
/// // period @ line 2, column 20 | ||
#[derive(Clone)] | ||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] | ||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] | ||
pub struct AttachedToken(pub TokenWithLocation); | ||
|
||
impl AttachedToken { | ||
/// Return a new Empty AttachedToken | ||
pub fn empty() -> Self { | ||
AttachedToken(TokenWithLocation::wrap(Token::EOF)) | ||
AttachedToken(TokenWithLocation::new_eof()) | ||
} | ||
} | ||
|
||
|
@@ -80,3 +128,9 @@ impl From<TokenWithLocation> for AttachedToken { | |
AttachedToken(value) | ||
} | ||
} | ||
|
||
impl From<AttachedToken> for TokenWithLocation { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this seemed to be an obvious gap so I just added it to avoid a papercut |
||
fn from(value: AttachedToken) -> Self { | ||
value.0 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,21 +21,51 @@ use super::{ | |
|
||
/// Given an iterator of spans, return the [Span::union] of all spans. | ||
fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span { | ||
iter.reduce(|acc, item| acc.union(&item)) | ||
.unwrap_or(Span::empty()) | ||
Span::union_iter(iter) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this was useful enough I felt making it a method in |
||
} | ||
|
||
/// A trait for AST nodes that have a source span for use in diagnostics. | ||
/// Trait for AST nodes that have a source location information. | ||
/// | ||
/// Source spans are not guaranteed to be entirely accurate. They may | ||
/// be missing keywords or other tokens. Some nodes may not have a computable | ||
/// span at all, in which case they return [`Span::empty()`]. | ||
/// # Notes: | ||
/// | ||
/// Source [`Span`] are not yet complete. They may be missing: | ||
/// | ||
/// 1. keywords or other tokens | ||
/// 2. span information entirely, in which case they return [`Span::empty()`]. | ||
/// | ||
/// Note Some impl blocks (rendered below) are annotated with which nodes are | ||
/// missing spans. See [this ticket] for additional information and status. | ||
/// | ||
/// [this ticket]: https://github.com/apache/datafusion-sqlparser-rs/issues/1548 | ||
/// | ||
/// # Example | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I felt an example will help people understand how to use the Spans |
||
/// ``` | ||
/// # use sqlparser::parser::{Parser, ParserError}; | ||
/// # use sqlparser::ast::Spanned; | ||
/// # use sqlparser::dialect::GenericDialect; | ||
/// # use sqlparser::tokenizer::Location; | ||
/// # fn main() -> Result<(), ParserError> { | ||
/// let dialect = GenericDialect {}; | ||
/// let sql = r#"SELECT * | ||
/// FROM table_1"#; | ||
/// let statements = Parser::new(&dialect) | ||
/// .try_with_sql(sql)? | ||
/// .parse_statements()?; | ||
/// // Get the span of the first statement (SELECT) | ||
/// let span = statements[0].span(); | ||
/// // statement starts at line 1, column 1 (1 based, not 0 based) | ||
/// assert_eq!(span.start, Location::new(1, 1)); | ||
/// // statement ends on line 2, column 15 | ||
/// assert_eq!(span.end, Location::new(2, 15)); | ||
/// # Ok(()) | ||
/// # } | ||
/// ``` | ||
/// | ||
/// Some impl blocks may contain doc comments with information | ||
/// on which nodes are missing spans. | ||
pub trait Spanned { | ||
/// Compute the source span for this AST node, by recursively | ||
/// combining the spans of its children. | ||
/// Return the [`Span`] (the minimum and maximum [`Location`]) for this AST | ||
/// node, by recursively combining the spans of its children. | ||
/// | ||
/// [`Location`]: crate::tokenizer::Location | ||
fn span(&self) -> Span; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,9 @@ | |
//! 1. [`Parser::parse_sql`] and [`Parser::new`] for the Parsing API | ||
//! 2. [`ast`] for the AST structure | ||
//! 3. [`Dialect`] for supported SQL dialects | ||
//! 4. [`Spanned`] for source text locations (see "Source Spans" below for details) | ||
//! | ||
//! [`Spanned`]: ast::Spanned | ||
//! | ||
//! # Example parsing SQL text | ||
//! | ||
|
@@ -61,13 +64,67 @@ | |
//! // The original SQL text can be generated from the AST | ||
//! assert_eq!(ast[0].to_string(), sql); | ||
//! ``` | ||
//! | ||
//! [sqlparser crates.io page]: https://crates.io/crates/sqlparser | ||
//! [`Parser::parse_sql`]: crate::parser::Parser::parse_sql | ||
//! [`Parser::new`]: crate::parser::Parser::new | ||
//! [`AST`]: crate::ast | ||
//! [`ast`]: crate::ast | ||
//! [`Dialect`]: crate::dialect::Dialect | ||
//! | ||
//! # Source Spans | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This content is largely migrated from |
||
//! | ||
//! Starting with version `0.53.0` sqlparser introduced source spans to the | ||
//! AST. This feature provides source information for syntax errors enables | ||
alamb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
//! better error messages. See [issue #1548] for more information and the | ||
//! [`Spanned`] traite to access the spans. | ||
alamb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
//! | ||
//! [issue #1548]: https://github.com/apache/datafusion-sqlparser-rs/issues/1548 | ||
//! [`Spanned`]: ast::Spanned | ||
//! | ||
//! ## Migration Guide | ||
//! | ||
//! For the next few releases, we will be incrementally adding source spans to the | ||
//! AST odes, trying to minimize the impact on existing users. Some breaking | ||
alamb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
//! changes are inevitable, and the following is a summary of the changes: | ||
//! | ||
//! #### New fields for spans (must be added to any existing pattern matches) | ||
//! | ||
//! The primary change is that new fields will be added to AST nodes to store the source `Span` or `TokenWithLocation`. | ||
//! | ||
//! This will require | ||
//! 1. Adding new fields to existing pattern matches. | ||
//! 2. Filling in the proper span information when constructing AST nodes. | ||
//! | ||
//! For example, since `Ident` now stores a `Span`, to construct an `Ident` you | ||
//! must provide now provide one: | ||
//! | ||
//! Previously: | ||
//! ```text | ||
//! # use sqlparser::ast::Ident; | ||
//! Ident { | ||
//! value: "name".into(), | ||
//! quote_style: None, | ||
//! } | ||
//! ``` | ||
//! Now | ||
//! ```rust | ||
//! # use sqlparser::ast::Ident; | ||
//! # use sqlparser::tokenizer::Span; | ||
//! Ident { | ||
//! value: "name".into(), | ||
//! quote_style: None, | ||
//! span: Span::empty(), | ||
//! }; | ||
//! ``` | ||
//! | ||
//! Similarly, when pattern matching on `Ident`, you must now account for the | ||
//! `span` field. | ||
//! | ||
//! #### Misc. | ||
//! - [`TokenWithLocation`] stores a full `Span`, rather than just a source location. | ||
//! Users relying on `token.location` should use `token.location.start` instead. | ||
//! | ||
//![`TokenWithLocation`]: tokenizer::TokenWithLocation | ||
|
||
#![cfg_attr(not(feature = "std"), no_std)] | ||
#![allow(clippy::upper_case_acronyms)] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drive by clarification