1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
# Fixing Bugs
Guide to write test cases in the parser’s test suite and how to debug failures.
The general approach to fixing bugs in the parser is to first write an automated test case that reproduces the test case in isolation. This allows you to invoke the parser with minimal dependencies and allows you to set breakpoints inside of it.
Once you’ve written a test case (see below), set a breakpoint in `Parser.parseSourceFile` and navigate the debugger to the place where the parser behaves unexpectedly. While the debugger is stopped at an instance function in <doc:SwiftParser/Parser>, `po self.currentToken` can show you the next token that will be parsed. `po self.lexemes` can show the next tokens that will be parsed.
## Round-Trip Failure or Parser Crash
1. Add a new test case in `SwiftParserTest` that looks like the following
```swift
assertParse(
"""
<#your code that does not round trip#>
"""
)
```
2. Run the test case, read the error message to figure out which part of the source file does not round-trip
## Parse of Valid Source Failed
Diagnostics are produced when the parsed syntax tree contains missing or unexpected nodes. The test case you should start with is identical to the one described in [Round-Trip Failure](#Round-Trip-Failure-or-Parser-Crash). You want to navigate the debugger to the place that incorrectly produces a missing or unexpected node.
## Parse of Valid Source Code Produced an Invalid Syntax Tree
1. Add a test case in `SwiftParserTest` that looks like the following
```swift
assertParse(
"""
<#your code that produces an invalid syntax tree#>
""",
substructure: <#create a syntax node that you expect the tree to have#>
)
```
2. Run the test case and navigate the debugger to the place that produced the invalid syntax node.
## Unhelpful Diagnostic Produced
Unhelpful diagnostics can result from two reasons:
1. The parser does a bad job at recovering from invalid source code and produces a syntax tree that does not match what the developer meant to write
2. The parser recovers reasonably well from the parse failure but complains about missing and unexpected text where a more contextual error would be more helpful.
To distinguish these cases run the following command and look at the dumped syntax tree. Use your own judgment to decide whether this models the intended meaning of the source code reasonably well.
```
swift-parser-cli print-tree /path/to/file/with/unhelpful/diagnostic.swift
```
Fixing the first case where the parser does not recover according to the user’s intent is similar to [Parse of Valid Source Code Produced an Invalid Syntax Tree](#Parse-of-Valid-Source-Code-Produced-an-Invalid-Syntax-Tree). See <doc:SwiftParser/ParserRecovery> for documentation how parser recovery works and determine how to recover better from the invalid source code.
To add a new, more contextual diagnostic, perform the following steps.
1. Add a test case in `SwiftParserTest` that looks like the following
```swift
assertParse(
"""
<#your code that produced the unhelpful diagnostic#>
""",
diagnostics: [
DiagnosticSpec(message: "<#expected diagnostic message#>")
]
)
```
2. Mark the location at which you expect the diagnostic to be produced with `1️⃣`. If you expect multiple diagnostics to be produced, you can use multiple of these markers with different names and use these markers by passing a `locationMarker` to `DiagnosticSpec`. The list of valid marker emojis can be found in `Character.isMarkerEmoji`.
3. Determine which node encompasses all information that is necessary to produce the improved diagnostic – for example `FunctionSignatureSyntax` contains all information to diagnose if the `throws` keyword was written after the `->` instead of in front of it.
4. If the diagnostic message you want to emit does not exist yet, add a case to `StaticParserError` for the new diagnostic. If the diagnostic needs to take parameters, add a new struct to in `ParserDiagnosticMessages.swift`.
5. If the function does not already exist, write a new visit method on `ParseDiagnosticsGenerator`.
6. In that visitation method, detect the pattern for which the improved diagnostic should be emitted and emit it using `diagnostics.append`.
7. Mark the missing or garbage nodes that are covered by the new diagnostic as handled by adding their `SyntaxIdentifier`s to `handledNodes`.
8. If the diagnostic produces Fix-Its assert that they are generated by adding the Fix-It's message to the `DiagnosticSpec` with the `fixIt` parameter and asserting that applying the Fix-Its produces the correct source code by adding the `fixedSource` parameter to `assertParse`.
> 💡 Tip: To make typing the marker emojis more convenient. you can set up code snippets in Xcode. To do this, perform the following steps:
> 1. Type the marker in any Xcode window or find it in some test case
> 2. Select the emoji, right-click it and select “Create Code Snippet…”
> 3. Replace “My Code Snippet” by the emoji marker character and in for “Availability” select “Comment or String”
> 4. Now you can invoke code completion in string literals and get presented with a list of marker emojis
|