Skip to content

[SourceKit/CodeFormat] Re-work and improve the indentation implementation #30187

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

Merged
merged 4 commits into from
Mar 13, 2020

Conversation

nathawes
Copy link
Contributor

@nathawes nathawes commented Mar 3, 2020

This restructures the indentation logic around producing a single IndentContext
for the line being indented. An IndentContext has:

  • a ContextLoc, which points to a source location to indent relative to
  • a Kind, indicating whether we should indent relative to that location exactly or to the start of the content on its containing line, and
  • an IndentLevel with the relative number of levels to indent by.

It also improves the handling of:

  • chained and nested parens, braces, square brackets and angle brackets, and how those interact with the exact alignment of parameters, call arguments, and tuple, array and dictionary elements.
  • Indenting to the correct level after an incomplete expression, statement or decl.

Resolves:
rdar://problem/59135010
rdar://problem/25519439
rdar://problem/50137394
rdar://problem/48410444
rdar://problem/48643521
rdar://problem/42171947
rdar://problem/40130724
rdar://problem/41405163
rdar://problem/39367027
rdar://problem/36332430
rdar://problem/34464828
rdar://problem/33113738
rdar://problem/32314354
rdar://problem/30106520
rdar://problem/29773848
rdar://problem/27301544
rdar://problem/27776466
rdar://problem/27230819
rdar://problem/25490868
rdar://problem/23482354
rdar://problem/20193017
rdar://problem/47117735
rdar://problem/55950781
rdar://problem/55939440
rdar://problem/53247352
rdar://problem/54326612
rdar://problem/53131527
rdar://problem/48399673
rdar://problem/51361639
rdar://problem/58285950
rdar://problem/58286076
rdar://problem/53828204
rdar://problem/58286182
rdar://problem/58504167
rdar://problem/58286327
rdar://problem/53828026
rdar://problem/57623821
rdar://problem/56965360
rdar://problem/54470937
rdar://problem/55580761
rdar://problem/46928002
rdar://problem/35807378
rdar://problem/39397252
rdar://problem/26692035
rdar://problem/33760223
rdar://problem/48934744
rdar://problem/43315903
rdar://problem/24630624

@nathawes nathawes requested review from rintaro and benlangmuir March 3, 2020 21:11
@nathawes
Copy link
Contributor Author

nathawes commented Mar 3, 2020

@swift-ci please test

@swift-ci
Copy link
Contributor

swift-ci commented Mar 3, 2020

Build failed
Swift Test Linux Platform
Git Sha - ce60ac6b8268a953933e27886b6eeef5d2fdb342

@theblixguy
Copy link
Collaborator

Wow, that’s a lot of radars!

@nathawes
Copy link
Contributor Author

nathawes commented Mar 3, 2020

@swift-ci please test

@swift-ci
Copy link
Contributor

swift-ci commented Mar 3, 2020

Build failed
Swift Test Linux Platform
Git Sha - ce60ac6b8268a953933e27886b6eeef5d2fdb342

@swift-ci
Copy link
Contributor

swift-ci commented Mar 3, 2020

Build failed
Swift Test OS X Platform
Git Sha - ce60ac6b8268a953933e27886b6eeef5d2fdb342

@nathawes
Copy link
Contributor Author

nathawes commented Mar 3, 2020

@swift-ci please test

@swift-ci
Copy link
Contributor

swift-ci commented Mar 3, 2020

Build failed
Swift Test Linux Platform
Git Sha - e15d8ea59836a166d74d497a783408da86965d7b

@swift-ci
Copy link
Contributor

swift-ci commented Mar 3, 2020

Build failed
Swift Test OS X Platform
Git Sha - e15d8ea59836a166d74d497a783408da86965d7b

@nathawes nathawes force-pushed the indentation branch 2 times, most recently from bf5bafe to 9fce6ac Compare March 5, 2020 00:43
@nathawes
Copy link
Contributor Author

nathawes commented Mar 5, 2020

@swift-ci please test

@swift-ci
Copy link
Contributor

swift-ci commented Mar 5, 2020

Build failed
Swift Test Linux Platform
Git Sha - 923ae09ee80f917937d60cbbe257571c11030bda

@swift-ci
Copy link
Contributor

swift-ci commented Mar 5, 2020

Build failed
Swift Test OS X Platform
Git Sha - 923ae09ee80f917937d60cbbe257571c11030bda

@rintaro
Copy link
Member

rintaro commented Mar 5, 2020

@swift-ci Please test

@@ -568,9 +559,12 @@ ParserResult<Expr> Parser::parseExprKeyPath() {

// FIXME: diagnostics
ParserResult<Expr> rootResult, pathResult;
Optional<ParserStatus> parseStatus;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simply use ParserStatus parseStatus here. The default is success. What I was talking is that ParserResult cannot be success without the value. But ParserStatus can have simple "success" value.

@swift-ci
Copy link
Contributor

swift-ci commented Mar 5, 2020

Build failed
Swift Test Linux Platform
Git Sha - 9fce6ac6953586f4e93e8a24d5c48cbb18c9e9b9

@swift-ci
Copy link
Contributor

swift-ci commented Mar 5, 2020

Build failed
Swift Test OS X Platform
Git Sha - 9fce6ac6953586f4e93e8a24d5c48cbb18c9e9b9

@nathawes
Copy link
Contributor Author

nathawes commented Mar 5, 2020

@swift-ci please test

@swift-ci
Copy link
Contributor

swift-ci commented Mar 5, 2020

Build failed
Swift Test Linux Platform
Git Sha - 9fce6ac6953586f4e93e8a24d5c48cbb18c9e9b9

@swift-ci
Copy link
Contributor

swift-ci commented Mar 5, 2020

Build failed
Swift Test OS X Platform
Git Sha - 9fce6ac6953586f4e93e8a24d5c48cbb18c9e9b9

@nathawes
Copy link
Contributor Author

nathawes commented Mar 5, 2020

Failures seem unrelated.
@swift-ci please smoke test

@nathawes
Copy link
Contributor Author

nathawes commented Mar 5, 2020

@swift-ci please smoke test OS X Platform

Copy link
Contributor

@benlangmuir benlangmuir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this is a big patch. So far I've reviewed everything except the million overloads of FormatWalker:: getIndentContextFrom, of which I've sampled a handful of cases. Overall, I think the approach you've taken is better and more understandable than the previous version, and I'm happy with the overall structure. On the other hand it's not really possible to review every path here.

…tion.

This restructures the indentation logic around producing a single IndentContext
for the line being indented. An IndentContext has:
- a ContextLoc, which points to a source location to indent relative to,
- a Kind, indicating whether we should align with that location exactly, or
  with the start of the content on its containing line, and
- an IndentLevel with the relative number of levels to indent by.

It also improves the handling of:
- chained and nested parens, braces, square brackets and angle brackets, and
  how those interact with the exact alignment of parameters, call arguments,
  and tuple, array and dictionary elements.
- Indenting to the correct level after an incomplete expression, statement or
  decl.

Resolves:
rdar://problem/59135010
rdar://problem/25519439
rdar://problem/50137394
rdar://problem/48410444
rdar://problem/48643521
rdar://problem/42171947
rdar://problem/40130724
rdar://problem/41405163
rdar://problem/39367027
rdar://problem/36332430
rdar://problem/34464828
rdar://problem/33113738
rdar://problem/32314354
rdar://problem/30106520
rdar://problem/29773848
rdar://problem/27301544
rdar://problem/27776466
rdar://problem/27230819
rdar://problem/25490868
rdar://problem/23482354
rdar://problem/20193017
rdar://problem/47117735
rdar://problem/55950781
rdar://problem/55939440
rdar://problem/53247352
rdar://problem/54326612
rdar://problem/53131527
rdar://problem/48399673
rdar://problem/51361639
rdar://problem/58285950
rdar://problem/58286076
rdar://problem/53828204
rdar://problem/58286182
rdar://problem/58504167
rdar://problem/58286327
rdar://problem/53828026
rdar://problem/57623821
rdar://problem/56965360
rdar://problem/54470937
rdar://problem/55580761
rdar://problem/46928002
rdar://problem/35807378
rdar://problem/39397252
rdar://problem/26692035
rdar://problem/33760223
rdar://problem/48934744
rdar://problem/43315903
rdar://problem/24630624
Nathan Hawes added 2 commits March 10, 2020 21:04
…status correctly.

We were always dropping the error status when returning from parseExprImpl. We
were also incorrectly keeping error status after recovering by finding the
right close token in parseList. This change fixes both, and also updates a few
callers of parseList that assumed when they reported a failure parsing an
element the list as a whole would get error status, which isn't true due to
recovery.
…ontextLocs.

- Rename several symbols to make it clearer whether the ranges they deal with
  are open or closed.
- Add comments documenting the implementation of OutdentChecker::hasOutdent
- Fix a bug where code after a doc coment block of the '/**' style was being
  indented 1 space.
- Fix IsInStringLiteral not being set if the indent target was in a string
  segment of an interpolated multiline string.
- Update OutdentChecker::hasOutdent to propagate indent contexts from
  parent parens/brackets/braces to child parens/brackets/braces that start
  later in the same line (like FormatWalker already does). This changes the
  braces in the example below to 'inherit' a ContextLoc from their parent
  square brackets, which have a ContextLoc at 'foo'. This makes the whole
  expression be correctly considered 'outdenting':

  foo(a: "hello"
      b: "hello")[x: {
    print("hello")
  }]
@nathawes
Copy link
Contributor Author

@swift-ci please smoke test

@nathawes
Copy link
Contributor Author

It looks like the swiftpm test failure is related and due to the parser changes. The test parses the code below and checks the reported diagnostic for expected ')' in expression list

import PackageDescription

let package = Package(
    name: "Trivial",
    targets: [
        .target(
            name: "foo",
            dependencies: []),
)

Prior to my parser changes it produced:

/tmp/t.swift:9:1: error: expected expression in container literal
)
^
/tmp/t.swift:9:2: error: expected ')' in expression list
)
 ^
/tmp/t.swift:2:22: note: to match this opening '('
let package = Package(
                     ^

And after:

/tmp/t.swift:9:1: error: expected expression in container literal
)
^

I think this is because when parsing the tuple arg of Package, parseExprList() calls parseExpr() which calls parseExprImpl() which gets an error status back when it tries to parse the collection. Prior to my changesparseExprImpl() was dropping that error status here, which meant we didn't take the true branch here in parseExprList() and still reported the missing right token diagnostics as well. With the error status propagating properly, the true branch is taken and they're suppressed.

Based on that it seems like the new behavior is desirable. @rintaro, do you agree? Should I just update the swiftpm test?

@rintaro
Copy link
Member

rintaro commented Mar 11, 2020

I agree. Let's update the spm test.

nathawes pushed a commit to nathawes/swift-package-manager that referenced this pull request Mar 11, 2020
…a diagnostic it was checking for.

The test parses the code below and checks for a "expected ')' in expression
list" diagnostic:

```
import PackageDescription

let package = Package(
    name: "Trivial",
    targets: [
        .target(
            name: "foo",
            dependencies: []),
)
```
Prior to the parser fix the invalid collection literal failed to parse but the
error status wasn't propagated up to the containing argument list parsing code,
so it produced an additional (and undesirable) missing right token diagnostic
that this test was checking for.

/tmp/t.swift:9:1: error: expected expression in container literal
)
^
/tmp/t.swift:9:2: error: expected ')' in expression list
)
 ^
/tmp/t.swift:2:22: note: to match this opening '('
let package = Package(
                     ^
Afer the parser fix in swiftlang/swift#30187, we only
report the first container literal error and suppress the rest:

/tmp/t.swift:9:1: error: expected expression in container literal
)
^
if (!isInCheckRange(L, R))
return true;

// The CheckRange is made outdenting by any parens/braces/brackets with:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is still complicated, but the examples help a lot to get a feeling for how it works!

}

Optional<Trailing>
checkForTrailingTarget(SourceLoc End) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found the terminology "Trailing" hard to keep straight, since we have trailing closures, trailing where clauses, trailing targets ie. "Trailing". Would it be correct to call this something like PostTokenTarget? Maybe "trailing" is the right word, but given how overloaded it is, it would be nice if we could find the most specific term here.

Copy link
Contributor Author

@nathawes nathawes Mar 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'Token' part isn't quite right, since it can just be an empty line between two tokens. Do you mean "Trailing" specifically or the "TrailingTarget" term in general?

…opagate ContextLocs when necessary.

We don't actually need to set a ContextOverride unless the ContextLoc and L
paren/brace/bracket are on different lines. Combined with the fact that we
only set them if the L and R parens/braces/brackets are on different lines
to, it guarantees there will be at most one override that's applicable on
any given line, which lets us simplify the logic somewhat.
@nathawes
Copy link
Contributor Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - a1b5027a16672c2994a2e00cef5cac86c6fe74f2

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - a1b5027a16672c2994a2e00cef5cac86c6fe74f2

Copy link
Contributor

@benlangmuir benlangmuir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I reviewed the most recent changes, scanned through the many specific indentation context sources, did a few more in-depth looks at specific cases in a debugger, and reviewed the new tests. We can discuss the "Trailing" terminology, but it shouldn't block the PR. Thanks!

@nathawes
Copy link
Contributor Author

This was a huge PR, so thanks a lot for going through it all @benlangmuir.
@rintaro are you ok with the current state of the parser changes?

I'll do another run of the stress tester over the full compat suite (with just the format request enabled) to make sure the last two changes didn't break anything, and do a normal source compat run too just in case the parser changes somehow affect something before I merge.

@nathawes
Copy link
Contributor Author

@swift-ci Please Test Source Compatibility

Copy link
Member

@rintaro rintaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parser changes look good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants