Skip to content

Latest commit

 

History

History
1047 lines (778 loc) · 40.6 KB

CHANGELOG.md

File metadata and controls

1047 lines (778 loc) · 40.6 KB

Changelog

All notable changes to apollo-parser will be documented in this file.

This project adheres to Semantic Versioning.

0.8.3 - 2024-10-07

Fixes

0.8.2 - 2024-09-09

Fixes

  • Allow object type definition syntax without fields - trevor-scheer, pull/901. It is syntactically valid for an object type to have no fields when the {} curly braces are omitted. During validation, this is also valid as long as an extension for the type also exists which does include field definitions.

0.8.1 - 2024-08-20

Features

This makes it easier for pretty-printers, linters and the like to work with the parser API.

0.8.0 - 2024-07-30

BREAKING

This release removes the Error::new constructor. We recommend not creating instances of apollo_parser::Error yourself at all.

Fixes

  • add missing location information for lexer errors - PhoebeSzmucer, pull/886, issue/731 Unexpected characters now raise an error pointing to the character itself, instead of the start of the input document.

0.7.7 - 2024-04-08

Fixes

  • raise an error for empty field sets - tinnou, pull/845 It's not legal to write type Object {} with braces but without declaring any fields. In the past this was accepted by apollo-parser, now it raises an error as required by the spec.

0.7.6 - 2024-02-14

Fixes

  • optimize the most common lexer matches into lookup tables - allancalix, pull/814 Parsing large schema documents can be up to 18% faster, typical documents a few percent.
  • fix infinite loops and crashes found through fuzzing - goto-bus-stop, pull/828 When using a token limit, it was possible to craft a document that would cause an infinite loop, eventually leading to an out of memory crash. This is addressed along with several panics.

Maintenance

0.7.5 - 2023-12-18

Fixes

  • fix parsing \\""" in block string - goto-bus-stop, pull/774 Previously this was parsed as \ followed by the end of the string, now it's correctly parsed as \ followed by an escaped """.
  • emit syntax errors for variables in constant values - SimonSapin, pull/777 default values and type system directive arguments are considered constants and may not use $foo variable values.
  • emit syntax errors for type condition without a type name - rishabh3112, pull/781

0.7.4 - 2023-11-17

Features

  • parse_type parses a selection set with optional outer brackets - lrlna, pull/718 fixing issue/715 This returns a SyntaxTree<Type> which instead of .document() -> cst::Document has .type() -> cst::Type. This is intended to parse the string value of a @field(type:) argument used in some Apollo Federation directives.
    let source = r#"[[NestedList!]]!"#;
    
    let parser = Parser::new(source);
    let cst: SyntaxTree<cst::Type> = parser.parse_type();
    let errors = cst.errors().collect::<Vec<_>>();
    assert_eq!(errors.len(), 0);

Fixes

  • Input object values can be empty - goto-bus-stop, pull/745 fixing issue/744 apollo-parser version 0.7.3 introduced a regression where empty input objects failed to parse. This is now fixed.

    { field(argument: {}) }

0.7.3 - 2023-11-07

Fixes

  • Less recursion in parser implementation - goto-bus-stop, pull/721 fixing issue/666 The parser previously used recursive functions while parsing some repetitive nodes, like members of an enum:

    enum Alphabet { A B C D E F G etc }

    Even though this is a flat list, each member would use a recursive call. Having many members, or fields in a type definition, or arguments in a directive, would all contribute to the recursion limit.

    Those cases are now using iteration instead and no longer contribute to the recursion limit. The default recursion limit is unchanged at 500, but you could reduce it depending on your needs.

  • Fix overly permissive parsing of implements lists and union member types - goto-bus-stop, pull/721 fixing issue/659 Previously these definitions were all accepted, despite missing or excessive & and | separators:
    type Ty implements A B
    type Ty implements A && B
    type Ty implements A & B &
    
    union Ty = A B
    union Ty = A || B
    union Ty = A | B |
    Now they report a syntax error.

0.7.2 - 2023-11-03

Fixes

0.7.1 - 2023-10-10

Features

  • parse_field_set parses a selection set with optional outer brackets - lrlna, pull/685 fixing issue/681 This returns a SyntaxTree<SelectionSet> which instead of .document() -> cst::Document has .field_set() -> cst::SelectionSet. This is intended to parse string value of a FieldSet custom scalar used in some Apollo Federation directives.
    let source = r#"a { a }"#;
    
    let parser = Parser::new(source);
    let cst: SyntaxTree<cst::SelectionSet> = parser.parse_selection_set();
    let errors = cst.errors().collect::<Vec<_>>();
    assert_eq!(errors.len(), 0);

0.7.0 - 2023-10-05

BREAKING

  • rename ast to cst - SimonSapin, [pull/???] The Rowan-based typed syntax tree emitted by the parser used to be called Abstract Syntax Tree (AST) but is in fact not very abstract: it preserves text input losslessly, and all tree leaves are string-based tokens. This renames it to Concrete Syntax Tree (CST) and renames various APIs accordingly. This leaves the name available for a new AST in apollo-compiler 1.0.

0.6.3 - 2023-10-06

Fixes

  • apply recursion limit where needed, reduce its default from 4096 to 500 - SimonSapin, pull/662 The limit was only tracked for nested selection sets, but the parser turns out to use recursion in other cases too. Issue 666 tracks reducing them. Stack overflow was observed with little more than 2000 nesting levels or repetitions in the new test. Defaulting to a quarter of that leaves a comfortable margin.
  • fix various lexer bugs - SimonSapin, pull/646, pull/652 The lexer was too permissive in emitting tokens instead of errors in various cases around numbers, strings, and EOF.
  • fix panic on surrogate code points in unicode escape sequences - SimonSapin, issue/608, pull/658

0.6.2 - 2023-09-08

Fixes

  • fixes to conversions from AST string nodes to Rust Strings - goto-bus-stop, pull/633, issue/609, issue/611 This fix affects the String::from(ast::StringValue) conversion function, which returns the contents of a GraphQL string node. "\"" was previously interpreted as just a backslash, now it is correctly interpreted as a double quote. For block strings, indentation is stripped as required by the spec.

0.6.1 - 2023-08-28

Fixes

  • fix lexing escape-sequence-like text in block strings - goto-bus-stop, pull/638, issue/632 Fixes a regression in 0.6.0 that could cause apollo-parser to reject valid input if a block string contained backslashes. Block strings do not support escape sequences so backslashes are normally literal, but 0.6.0 tried to lex them as escape sequences, which could be invalid (eg. \W is not a supported escape sequence).

    Now block strings are lexed like in 0.5.3. Only the \""" sequence is treated as an escape sequence.

0.6.0 - 2023-08-18

Features

  • zero-alloc lexer - allancalix, pull/322 Rewrites the lexer to avoid allocating for each token. Synthetic benchmarks show about a 25% performance improvement to the parser as a whole.

Fixes

  • fix token limit edge case - goto-bus-stop, pull/619, issue/610 token_limit now includes the EOF token. In the past you could get token_limit + 1 tokens out of the lexer if the token at the limit was the EOF token, but now it really always stops at token_limit.

  • create EOF token with empty data - allancalix, pull/591 Makes consuming the token stream's data produce an identical string to the original input of the lexer.

0.5.3 - 2023-05-12

Fixes

  • variable definition list cannot be empty - lrlna, pull/553 fixing issue/546 We previously allowed an operation with an empty variable definition list, which is incorrect. This change provides a fix.

0.5.2 - 2023-05-10

Features

  • add SyntaxTree::token_limit - SimonSapin, pull/525 This enables finding out how many tokens were present in a succesful parse, which can be useful to choose where to set the limit.

  • add Definition::kind() -> &str and Definition::is_executable_definition() - goto-bus-stop, pull/535 These are new methods on the Definition AST node. kind() returns the kind of definition (eg. "ScalarTypeExtension") and is_executable_definition() returns true for operation definitions and fragment definitions.

Fixes

  • handle escape sequences when reading string contents - goto-bus-stop, pull/541 The String::from(StringValue) implementation now turns escape sequences like \n and \u2764 into their literal characters.

0.5.1 - 2023-04-13

Fixes

  • remove recursion in field parsing - goto-bus-stop, pull/519 The selection::selection_set parser already supports parsing multiple fields. This removes recursion from field parsing, reducing the risk of stack overflow on queries with many fields.

0.5.0 - 2023-02-16

Features

  • new ast::Definition methods - goto-bus-stop, pull/456 When working with Definition nodes, you can use the .name() method to get the name of a definition, regardless of its kind. For schema definitions, it returns None. You can use .is_extension_definition() to check if a definition node is an extension.

Fixes

  • fix token order around type names - goto-bus-stop, issue/362, pull/443

    type Query {
      field: Int # comment
    }

    Previously, the whitespace and comment around the Int type name would end up before the type name in the parse tree. This would mess up the location information for the Int type name. Now this is fixed.

  • fix spans after parsing unexpected tokens - goto-bus-stop, issue/325, pull/446

    Location information for all nodes after an unexpected token was incorrect. It's better now, though still imperfect: lexing errors still have this problem.

  • fix ignored token positioning in the AST - goto-bus-stop, pull/445

    This makes spans for all nodes more specific, not including ignored tokens after the node. When ignored tokens are consumed, they are first stored separately, and then added to the AST just before the next node is started. This way ignored tokens are always inside the outermost possible node, and therefore all individual nodes will have spans that only contain that node and not more.

    The most obvious effect of this is that diagnostics now point to the exact thing they are about, instead of a bunch of whitespace :)

0.4.1 - 2022-12-13

Fixes

  • fix panics when parsing type names with syntax errors - goto-bus-stop, pull/381

    For example, field: [] does not panic anymore. Instead it produces a syntax error and an incomplete List type.

  • continue parsing after a syntax error in an object type field - goto-bus-stop, pull/381

    type A {
       fieldA: [] # ← has error, missing item type
       fieldB: Int
       fieldC: Int
    }

    Previously fieldB and fieldC would not be parsed, now they are.

0.4.0 - 2022-11-28

BREAKING

  • make conversions from GraphQL Values to Rust types fallible - goto-bus-stop, pull/371 fixing issue/358

    In the past you could do:

    let graphql_value: IntValue = get_a_value();
    let x: i32 = graphql_value.into();

    But this .into() implementation could panic if the number was out of range. Now, this conversion is implemented with the TryFrom trait, so you handle out-of-range errors however you want:

    let graphql_value: IntValue = get_a_value();
    let x: i32 = graphql_value.try_into()?;
  • Move with_recursion_limit constructor to a builder method - goto-bus-stop, pull/347

    If you were using the Parser::with_recursion_limit constructor, you now need to use Parser::new().recursion_limit() instead.

Features

  • add API to limit number of tokens to parse - goto-bus-stop, pull/347

    When dealing with untrusted queries, malicious users can submit very large queries to attempt to cause denial-of-service by using lots of memory. To accompany the existing recursion_limit API preventing stack overflows, you can now use token_limit to abort parsing when a large number of tokens is reached.

    You can use the new err.is_limit() API to check if a parse failed because a hard limit was reached.

    let source = format!("query {{ {fields} }}", fields = "a ".repeat(20_000));
    
    let parser = Parser::new(source)
        .recursion_limit(10)
        // You may need an even higher limit if your application actually sends very large queries!
        .token_limit(10_000);
    
    let (ast, errors) = parser.parse();
    if errors.iter().any(|err| err.is_limit()) {
        // there was a limiting error
    }

Maintenance

0.3.2 - 2022-11-15

Fixes

"""unicode in block string 🤷"""
input Filter {
    title: String
}
"""
\""" a/b \"""
"""
input Filter {
    title: String
}

type Query {
    format: String = "Y-m-d\\TH:i:sP"
}

0.3.1 - 2022-11-04

Features

  • streaming lexer - Geal + goto-bus-stop, pull/115

    To help improve performance and memory usage in the lexer, we are now streaming all incoming tokens in the lexer implementation.

  • extend ast::*Value node conversions - SimonSapin, pull/344

    The following node types implement conversion to standard types, extracting their value:

    • StringValueString
    • IntValuei32
    • FloatValuef64
    • BoolValuebool

    These conversions are now also available:

    • Through the From trait, not just the Into trait
    • With borrowed nodes, not just owned

    Example:

    let node: &apollo_parser::ast::StringValue = /* something */;
    let value: String = node.clone().into(); // before
    let value = String::from(node); // now also possible

Documentation

  • example of modifying queries with parser + encoder - lrlna, pull/346 An addition to apollo-parser's example directory encoding various parts of the AST using apollo-encoder's new TryFrom implementation. Examples include:

    • merging two queries
    • omitting certain fields in a query.

0.3.0 - 2022-10-31 💀

BREAKING

  • remove the impl Display for generated nodes - goto-bus-stop, pull/330

    The Display impls for generated nodes returned the source text for that node. That's not a super common operation but it was very easy to access. It's also a very different operation from eg. let content: String = node.string_value().into() which returns the content of a string: node.string_value().to_string() returned the string as it was written in the source code, quotes and escapes and all.

    Now .to_string() is replaced by a .source_string() method. It allocates a new String (just like .to_string() did). A syntax node can represent multiple slices (I think to support different structures like Ropes as input?), so slicing the original source isn't actually possible.

Fixes

  • handle unexpected tokens in top-level document parsing - JrSchild, pull/324 Unexpected tokens directly inside a document would break the loop in the parser, for example:

    @
    {
      name
    }}

    This resulted in the rest of the parsing to be skipped. An error is created here instead.

Maintenance

  • reduce token copying - goto-bus-stop, pull/323

    • Reduce token copying

    Since the original lexer results are not needed anymore after this step, we can take ownership of the tokens and errors vectors and reverse them in-place without making a copy. Big schemas can have 100K+ tokens so it's actually quite a lot of work to copy them.

    • Reduce double-clones of tokens in the parser

    Some of these clones were not necessary. In particular the .expect method cloned the token unconditionally (including the string inside) and then cloned the string again immediately afterwards. This removes the first clone by reordering the current.index() call to satisfy the borrow checker.

    The .data().to_string() clone is only used in the error case, but avoiding that will require more work.

0.2.12 - 2022-09-30

Fixes

  • unterminated string values with line terminators and unicode- lrlna, pull/320 fixes issue/318

    We were missing adding a line terminator character to the errors created by the lexer in case of a unterminated string. This showed up incidentally while dealing with unicode and the fact that it's of a different byte length than most other characters.

0.2.11 - 2022-09-20

Features

  • introduce a getter to parser's green nodes - lrlna, pull/295

    creates a getter to parser's underlying green nodes that make up the resulting, more ergonomic AST. This is handy for our compiler's use case when creating a thread-safe access to the AST.

Fixes

  • selection set is required for named operation definitions- lrlna, pull/301 closes issue/300

    The parser was not creating errors for missing selection sets for named operation definitions such as query namedQuery {. This is now correctly flagged as erroneous graphql.

0.2.10 - 2022-08-16

Fixes

  • unterminated string value in list and object values - bnjjj, pull/267 & [pull/274] closes issue/266

    Create and pop errors with unterminated string values in list and object values. Stops infinite loop when searching for a Value in the parser.

0.2.9 - 2022-07-27

Features

  • Provide APIs for SyntaxNode and SyntaxNodePtr - lrlna, pull/251

    Export a wrapper around SyntaxNodePtr provided by rowan. This allows access to pointers of the AST created by apollo-parser.

0.2.8 - 2022-06-10

Fixes

  • Use recursion limit both for selection set and field parsing - garypen and lrlna, pull/244

    This properly unifies the limits around recursion for both:

    • selection sets
    • fields

    The tests are expanded and properly exercise the various possible outcomes with recursion limits.Fixes a bug with

0.2.7 - 2022-06-08

Features

  • Resource bound parsing execution - garypen, pull/239 closes issue/225

    Introduce recursion limit enforced during SelectionSet parsing.

    There is now a default limit (4_096) applied to parsers during SelectionSet parsing to help prevent stack overflows. This limit can be set manually when creating a parser by using the new fn, Parser::with_recursion_limit(). Details about recursion consumption can be retrieved using the new fn SyntaxTree::recursion_limit(). Recursion limit details are also output as part of the AST debug output when printing a SyntaxTree.

0.2.6 - 2022-05-24

Fixes

  • lex escaped characters in StringValue tokens - bnjjj, pull/228 closes issue/227, issue/229

    StringValues with correctly escaped quotation marks, e.g. { name(id: "\"escaped\"") } would error and not lex correctly. Additionally, invalid escapes in string values, e.g. { name(id: "escaped \a") } should have an error created in the lexer. Both issues are fixed, and correctly bubble up to the parser.

0.2.5 - 2022-04-01

Important: 1 breaking change below, indicated by BREAKING

BREAKING

  • GraphQL Int Values are cast to i32 - bnjjj, pull/197 AST's Int Values have an Into implementation to their Rust type. They were previously converted to i64, which is not compliant with the spec. Int Values are now converted to i32.
    if let ast::Value::IntValue(val) =
        argument.value().expect("Cannot get argument value.")
    {
        let i: i32 = val.into();
    }

Features

  • Adds a .text() method to ast::DirectiveLocation - bnjjj, pull/197 DirectiveLocation can now additionally be accessed with a .text() method.

    let schema = r#"directive @example on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT"#;
    let parser = Parser::new(schema);
    let ast = parser.parse();
    
    assert!(ast.errors.is_empty());
    
    let document = ast.document();
    for definition in document.definitions() {
        if let ast::Definition::DirectiveDefinition(dir_def) = definition {
            let dir_locations: Vec<String> = dir_def
                .directive_locations()
                .unwrap()
                .directive_locations()
                .map(|loc| loc.text().unwrap().to_string())
                .collect();
            assert_eq!(
                dir_locations,
                ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"]
            );
            return;
        }
    }

0.2.4 - 2022-03-07

Fixes

  • correctly parse Arguments Definition - bnjjj, pull/187 closes issue/186

    apollo-parser was creating ARGUMENTS instead of ARGUMENTS_DEFINITION nodes when parsing Arguments Definitions. This change fixes the incorrect parsing and allows to iterate over arguments definitions returned by the AST.

  • Add STRING_VALUE node to DESCRIPTION - bnjjj, pull/188 closes issue/185

    DESCRIPTION nodes are composed of STRING_VALUE nodes. The description string was previously simply added to the DESCRIPTION node which was not spec compliant.

  • Schema Definition has a description - bnjjj, pull/188 closes issue/185

    apollo-parser was parsing descriptions in Schema Definitions, but the graphql ungrammar did not account for a description node. This updates the ungrammar, and provides an accessor method to Schema Definition's description.

  • Add repeatable keyword to GraphQL ungrammar - bnjjj, pull/189

    repeatable keyword was not able to be accessed programmatically from the parsed AST for Directive Definitions, this is now fixed.

0.2.3 - 2022-02-17

Features

  • expose Lexer as a pub struct - bnjjj, pull/168

    The Lexer in apollo-parser is now a publicly available interface.

    use apollo_parser::Lexer;
    
    let query = "
    {
        animal
        ...snackSelection
        ... on Pet {
          playmates {
            count
          }
        }
    }
    ";
    let lexer = Lexer::new(query);
    assert_eq!(lexer.errors().len(), 0);
    
    let tokens = lexer.tokens();

Fixes

  • add a getter for Directives in Variable Definitions - lrlna, pull/172

    While the parser was correctly parsing and accounting for directives in a variable definition, the getter for Directives in VariableDefinition type in the AST was missing. This commit makes an addition to the graphql ungrammar, and by extension the generated AST nodes API.

0.2.2 - 2022-02-11

Fixes

  • create an error when description preceeds operation definition and proceed parsing - MidasLamb, pull/158/ lrlna, pull/160

    According to the spec Operation Definitions don't currently allow for descriptions.

    "this description is not allowed"
    {
      name
      age
    }

    When a description was added before an operation, the parser would continuously try to register the error without removing it from the list of valid tokens. This fix removes the incorrect token, and continuous parsing an OperationDefinition.

  • Correctly parse an Inline Fragment when type condition is absent - bnjjj, pull/164

    The following inline fragment would previously be incorrectly parsed as a FragmentSpread when in reality it's an Inline Fragment:

    query HeroForEpisode {
      ... @tag(name: "team-customers") { # an inline fragment
        primaryFunction
      }
    }

    This has now been fixed.

0.2.1 - 2022-01-26

Fixes

  • fix(apollo-parser): add ignored tokens to TYPE nodes in correct place - lrlna, issue/143 pull/153

    This fixes the location of ignored tokens (COMMA, WHITESPACE) inside a TYPE node.

    Before this commit this sort of query

    mutation MyMutation($custId: Int!, $b: String) {
      myMutation(custId: $custId)
    }

    would result the ast.document.to_string() to have this output:

    mutation MyMutation($custId: , Int!$b:  String) {
        myMutation(custId: $custId)
    }

    which is incorrect. The to_string() now results in the exact same output, as the AST created is correct.

  • fix(apollo-parser): bump BANG token when creating NON_NULL_TYPE - lrlna, issue/142 pull/146

    We are missing BANG token in the AST when a NON_NULL_TYPE gets created. Although the node created is indeed NON_NULL_TYPE, it's also important to keep the original set of tokens. The followin example now works:

    let mutation = r#"
    mutation MyMutation($custId: Int!) {
      myMutation(custId: $custId)
    }"#;
    
    let parser = Parser::new(mutation);
    let ast = parser.parse();
    assert_eq!(ast.errors.len(), 0);
    
    let doc = ast.document();
    assert_eq(&doc, &mutation);

0.2.0 - 2021-12-22

Breaking

  • impl Iterator for ast.errors() - o0Ignition0o, issue/119 pull/120

    ast.errors() now return an Iterator. This makes it a bit easier for users to process any errors returned by the Parser. Below is the new usage example:

    let query = r#"
    type Query {
        "A simple type for getting started!"
        hello: String
        cats(cat: [String]! = ): [String]!
    }"#;
    
    
    let parser = Parser::new(&query);
    let ast = parser.parse();
    
    assert!(ast.errors.len(), 1);
    
    for err in ast.errors() { // no longer need to .iter() on this
        // process errors in a way that's useful for your implementation
        dbg!(&err);
    }

Fixes

  • fix: properly create TYPE's NAMED_TYPE, LIST_TYPE, NON_NULL_TYPE - lrlna, issue/125 pull/127

    Whenever a NAMED_TYPED, LIST_TYPE, NON_NULL_TYPE are created, they are automatically get created as part of the TYPE node, so we do not need to start it manually. This fix makes it possible to once again do:

    if let ast::Type::NamedType(name) = var.ty().unwrap() {
        assert_eq!(name.name().unwrap().text().as_ref(), "Int")
    }
  • fix: create an error when SelectionSet is empty in operation definition - lrlna, pull/134

    An Operation Definition must have a selection set with values, so this query query {} should also come with an error.

  • fix: variable definition can have a LIST_TYPE - lrlna, issue/131 pull/135

    Variable definition was previously not accepting a LIST_TYPE, which is incorrect. This commit fixes this issue.

Maintenance

  • chore: typo in README - lrlna, c598d3

  • fuzzing for apollo-parser - Geal, pull/122

    The fuzz test checks for lexer and parser errors and stops early.

    The following fuzz-encountered errors are fixed:

    • panics on the following input:
    "
    
    • crash on partial block string opening token
    ""
    
    • infinite loop on unfollowed 'extend' ident

    The parser fuzzer catches errors in the lexer and returns early. It will not avoid infinite loops and running out of memory in the lexer.

  • chore: run clippy in CI on benchmark directories - lrlna, pull/123

  • chore: add tests for untermiated strings and invalid type system extensions - lrlna, pull/124

    Follows up on #122 and adds tests for the incorrectly lexed and parsed inputs that fuzzing discovered.

    This commit also changes logic around having an "unexpected end of data" for "" string. This now gets lexed into a StringValue token.

  • chore: allow dead code in xtask's ast_src - lrlna, pull/128

  • chore: add a test for nested SELECTION_SETs - lrlna, pull/137

    This will mostly act as an example in case users are looking for how to work with nested selections and get their FIELD/INLINE_FRAGMENT/FRAGMENT_SPREAD.