Keep every Oak node within its parent's range - #3519
Merged
Merged
Conversation
The fantomas-issue skill now starts a case by looking for an ignored one that already pins the bug, and names the trivia script and where the vendored parser lives for when the syntax tree lacks a range. The snapshot README says an issue opened later goes in an ignored case's reason, so a search for its number finds the case, and what becomes of that reason once the case is fixed. The trivia script and report call the slot of a node that is not a token `(whole node)` rather than `(node)`, which read like a placeholder.
FANTOMAS-ANNOTATE-001 only looked at let bindings, so a class built from an untyped primary constructor and untyped `member val`s, as most Oak node types are, passed without a finding. The rule now also reports each untyped parameter of a primary constructor and each `member val` without a type. The unit constructor, `type T() =`, is passed over like a function's unit parameter, and other members stay with the author. The rule stays advisory: `AnalyzeChanged` reports it on the lines a change touches, so existing untyped constructors are not swept up.
Trivia assignment only goes down into a node whose range holds the trivia, so a child that lies outside its parent's range hands its comments to the parent. 352 snapshot cases had such a child. The check that children are in source order now also checks that each lies within its parent, in both test projects, and every hole is closed in the transformer: - a type parameter token no longer starts one column early, on the `<` - a type header covers all its parts, and a signature abbreviation and module span their declarations, past where the parser ends them - an enum case covers its `|`, a binding its `let rec` before attributes and its static optimization clauses, a constructor or get/set binding its accessibility, `inline` and attributes - a type parameter covers its intersection constraints, an exception its `with end` - a getter keeps only the attribute lists after its `with` or `and` The `with` before a type's members moves from TypeNameNode to the type definition, which places it among the members in source order. A `with` that follows the body otherwise stretched the header over the whole body. TypeDefn's static members TypeName, WithKeyword and Members replace ITypeDefn, and the record styles that leave out the `with` now write a comment after it behind the closing brace. Fantomas.Core.Tests parses once in a SetUpFixture before tests run in parallel. A test reading Range.range0 and a first parse could each enter the compiler's range.fs through a different module, and the initialization deadlock the runtime breaks left the parse with a null file index, failing every parse of the run.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Trivia assignment only goes down into a node whose range holds the trivia, so a child that lies outside its parent's range gives its comments to the parent. Fixing #3515 showed one such child, and a check for it found 352 snapshot cases with one. This makes the check part of both test projects and closes every hole it found. No gold changes.
Every child within its parent's range
The source order check is now
assertChildrenInPlace, inFantomas.Core.SnapshotTestsandFantomas.Core.Tests: no child starts before the one in front of it, and every child lies within its parent's range. It runs without exceptions. The holes, all closed inASTTransformer.fs:<or(before it.mkTypeNameNode). A signature abbreviation spans the whole definition, and a module and the Oak widen over their declarations, as the parser endstype T<'a>in a signature before its type parameters.|and xml doc (mkSynEnumCase).let recbefore its attributes and thewhenclauses of a static optimization. An implicit constructor covers its accessibility, and a get/set binding itsinline, attributes and accessibility. A simplifiedwith get () =member starts atmember.with end.withorand.The
withbefore a type's members follows the body intype T = { X: int } with, so it cannot sit inTypeNameNodewithout stretching the header over the body. It moves to the type definitions, placed among the members in source order (membersAroundWith).TypeDefn.TypeName,TypeDefn.WithKeywordandTypeDefn.MembersreplaceITypeDefn. The record styles that leave out thewithnow write a comment after it behind the closing brace, which kept the two cases that cover it passing.New trivia cases put a comment in each closed gap.
A comment after a
withthat formatting leaves out is lost for unions, enums and classes. That is the case onmaintoo, and is left alone here.Fantomas.Core.Testsno longer fails at randomAbout one run in twenty failed 124 tests at once, on
mainas well, with a null file index in the first parse. With--realsig+every module of the compiler'srange.fsruns the one initializer of that file. A test readingRange.range0and a first parse could each enter it through a different module and hold the lock the other waited on. The runtime breaks that cycle by letting one thread go on before the file is initialized, and the parser caches the failure for every parse after it.ParserWarmupFixtureparses once before tests run in parallel. 100 runs in a row passed with it.RangeHelpers.isAbsoluteZerotells the module of a file without code by its start line, as readingabsoluteZeroRangefrom the check made the failure much more frequent.FANTOMAS-ANNOTATE-001covers constructors and auto propertiesThe rule reported let bindings only, so an untyped primary constructor parameter or
member valwent unnoticed. It now reports both. The rule stays advisory, soAnalyzeChangedonly reports it on lines a change touches. The type definition nodes this touches are annotated.Agent guidance
The
fantomas-issueskill now starts by looking for an ignored case that already pins the bug, and points at the trivia script and the parser sources. The snapshot README says an issue opened later goes in an ignored case's reason, and what becomes of that reason once the case passes. The trivia script and report name a node that is not a token(whole node).