@@ -289,9 +289,9 @@ struct DiagnosticSpec {
289
289
}
290
290
291
291
/// Assert that `location` is the same as that of `locationMarker` in `tree`.
292
- func assertLocation< T : SyntaxProtocol > (
292
+ func assertLocation(
293
293
_ location: SourceLocation ,
294
- in tree: T ,
294
+ in tree: some SyntaxProtocol ,
295
295
markerLocations: [ String : Int ] ,
296
296
expectedLocationMarker locationMarker: String ,
297
297
file: StaticString = #filePath,
@@ -315,9 +315,9 @@ func assertLocation<T: SyntaxProtocol>(
315
315
316
316
/// Assert that the diagnostic `note` produced in `tree` matches `spec`,
317
317
/// using `markerLocations` to translate markers to actual source locations.
318
- func assertNote< T : SyntaxProtocol > (
318
+ func assertNote(
319
319
_ note: Note ,
320
- in tree: T ,
320
+ in tree: some SyntaxProtocol ,
321
321
markerLocations: [ String : Int ] ,
322
322
expected spec: NoteSpec
323
323
) {
@@ -335,9 +335,9 @@ func assertNote<T: SyntaxProtocol>(
335
335
336
336
/// Assert that the diagnostic `diag` produced in `tree` matches `spec`,
337
337
/// using `markerLocations` to translate markers to actual source locations.
338
- func assertDiagnostic< T : SyntaxProtocol > (
338
+ func assertDiagnostic(
339
339
_ diag: Diagnostic ,
340
- in tree: T ,
340
+ in tree: some SyntaxProtocol ,
341
341
markerLocations: [ String : Int ] ,
342
342
expected spec: DiagnosticSpec
343
343
) {
@@ -491,9 +491,9 @@ public struct AssertParseOptions: OptionSet, Sendable {
491
491
extension ParserTestCase {
492
492
/// After a test case has been mutated, assert that the mutated source
493
493
/// round-trips and doesn’t hit any assertion failures in the parser.
494
- fileprivate static func assertMutationRoundTrip< S : SyntaxProtocol > (
494
+ fileprivate static func assertMutationRoundTrip(
495
495
source: [ UInt8 ] ,
496
- _ parse: ( inout Parser ) -> S ,
496
+ _ parse: ( inout Parser ) -> some SyntaxProtocol ,
497
497
swiftVersion: Parser . SwiftVersion ? ,
498
498
experimentalFeatures: Parser . ExperimentalFeatures ,
499
499
file: StaticString ,
@@ -524,14 +524,84 @@ extension ParserTestCase {
524
524
}
525
525
}
526
526
527
+ enum FixItsApplication {
528
+ /// Apply only the fix-its whose messages are included in `applyFixIts` to generate `fixedSource`.
529
+ case optIn( applyFixIts: [ String ] , fixedSource: String )
530
+ /// Apply all fix-its to generate `fixedSource`.
531
+ case all( fixedSource: String )
532
+
533
+ init ? ( applyFixIts: [ String ] ? , expectedFixedSource: String ? ) {
534
+ if let applyFixIts {
535
+ self = . optIn( applyFixIts: applyFixIts, fixedSource: expectedFixedSource ?? " " )
536
+ } else if let expectedFixedSource {
537
+ self = . all( fixedSource: expectedFixedSource)
538
+ } else {
539
+ return nil
540
+ }
541
+ }
542
+
543
+ var applyFixIts : [ String ] ? {
544
+ switch self {
545
+ case . optIn( let applyFixIts, _) :
546
+ return applyFixIts
547
+ case . all:
548
+ return nil
549
+ }
550
+ }
551
+
552
+ var expectedFixedSource : String {
553
+ switch self {
554
+ case . optIn( _, let fixedSource) , . all( let fixedSource) :
555
+ return fixedSource
556
+ }
557
+ }
558
+ }
559
+
560
+ /// Convenient version of `assertParse` that allows checking a single configuration of applied Fix-Its.
561
+ ///
562
+ /// - Parameters:
563
+ /// - applyFixIts: Applies only the fix-its with these messages. Nil means applying all fix-its.
564
+ /// - expectedFixedSource: Asserts that the source after applying fix-its matches
565
+ /// this string.
566
+ func assertParse(
567
+ _ markedSource: String ,
568
+ _ parse: @Sendable ( inout Parser ) -> some SyntaxProtocol = { SourceFileSyntax . parse ( from: & $0) } ,
569
+ substructure expectedSubstructure: ( some SyntaxProtocol ) ? = Optional< Syntax> . none,
570
+ substructureAfterMarker: String = " START " ,
571
+ diagnostics expectedDiagnostics: [ DiagnosticSpec ] = [ ] ,
572
+ applyFixIts: [ String ] ? = nil ,
573
+ fixedSource expectedFixedSource: String ? = nil ,
574
+ options: AssertParseOptions = [ ] ,
575
+ swiftVersion: Parser . SwiftVersion ? = nil ,
576
+ experimentalFeatures: Parser . ExperimentalFeatures ? = nil ,
577
+ file: StaticString = #filePath,
578
+ line: UInt = #line
579
+ ) {
580
+ assertParse (
581
+ markedSource,
582
+ parse,
583
+ substructure: expectedSubstructure,
584
+ substructureAfterMarker: substructureAfterMarker,
585
+ diagnostics: expectedDiagnostics,
586
+ fixItsApplications: FixItsApplication ( applyFixIts: applyFixIts, expectedFixedSource: expectedFixedSource) . map {
587
+ [ $0]
588
+ } ?? [ ] ,
589
+ options: options,
590
+ swiftVersion: swiftVersion,
591
+ experimentalFeatures: experimentalFeatures,
592
+ file: file,
593
+ line: line
594
+ )
595
+ }
596
+
527
597
/// Verifies that parsing of `markedSource` produces expected results using a
528
598
/// combination of various testing techniques:
529
599
///
530
600
/// 1. Asserts that parsing of `markedSource` round-trips, ie. that the printed
531
601
/// parsed tree is the same as the input.
532
602
/// 2. Checks that parsing produces the expected list of diagnostics. If no
533
603
/// diagnostics are passed, asserts that the input parses without any errors.
534
- /// 3. Checks that applying all Fix-Its of the source code results in the
604
+ /// 3. Checks that applying fix-its of the source code results in the
535
605
/// expected fixed source, effectively testing the Fix-Its.
536
606
/// 4. If a substructure is passed, asserts that the parsed tree contains a
537
607
/// subtree of that structure.
@@ -558,21 +628,19 @@ extension ParserTestCase {
558
628
/// - expectedDiagnostics: Asserts the given diagnostics were output, by default it
559
629
/// asserts the parse was successful (ie. it has no diagnostics). Note
560
630
/// that `DiagnosticsSpec` uses the location marked by `1️⃣` by default.
561
- /// - applyFixIts: Applies only the fix-its with these messages.
562
- /// - expectedFixedSource: Asserts that the source after applying fix-its matches
563
- /// this string.
631
+ /// - fixItsApplications: A list of `FixItsApplication` to check for whether applying certain fix-its to the source
632
+ /// will generate a certain expected fixed source. An empty list means no fix-its are expected.
564
633
/// - swiftVersion: The version of Swift using which the file should be parsed.
565
634
/// Defaults to the latest version.
566
635
/// - experimentalFeatures: A list of experimental features to enable, or
567
636
/// `nil` to enable the default set of features provided by the test case.
568
- func assertParse< S : SyntaxProtocol > (
637
+ func assertParse(
569
638
_ markedSource: String ,
570
- _ parse: @Sendable ( inout Parser ) -> S = { SourceFileSyntax . parse ( from: & $0) } ,
639
+ _ parse: @Sendable ( inout Parser ) -> some SyntaxProtocol = { SourceFileSyntax . parse ( from: & $0) } ,
571
640
substructure expectedSubstructure: ( some SyntaxProtocol ) ? = Optional< Syntax> . none,
572
641
substructureAfterMarker: String = " START " ,
573
642
diagnostics expectedDiagnostics: [ DiagnosticSpec ] = [ ] ,
574
- applyFixIts: [ String ] ? = nil ,
575
- fixedSource expectedFixedSource: String ? = nil ,
643
+ fixItsApplications: [ FixItsApplication ] = [ ] ,
576
644
options: AssertParseOptions = [ ] ,
577
645
swiftVersion: Parser . SwiftVersion ? = nil ,
578
646
experimentalFeatures: Parser . ExperimentalFeatures ? = nil ,
@@ -591,7 +659,7 @@ extension ParserTestCase {
591
659
parser. enableAlternativeTokenChoices ( )
592
660
}
593
661
#endif
594
- let tree : S = parse ( & parser)
662
+ let tree = parse ( & parser)
595
663
596
664
// Round-trip
597
665
assertStringsEqualWithDiff (
@@ -640,27 +708,30 @@ extension ParserTestCase {
640
708
}
641
709
642
710
// Applying Fix-Its
643
- if expectedDiagnostics. contains ( where: { !$0. fixIts. isEmpty } ) && expectedFixedSource == nil {
711
+ if expectedDiagnostics. contains ( where: { !$0. fixIts. isEmpty } ) && fixItsApplications . isEmpty {
644
712
XCTFail ( " Expected a fixed source if the test case produces diagnostics with Fix-Its " , file: file, line: line)
645
- } else if let expectedFixedSource = expectedFixedSource {
646
- let fixedTree = FixItApplier . applyFixes ( from: diags, filterByMessages: applyFixIts, to: tree)
647
- var fixedTreeDescription = fixedTree. description
648
- if options. contains ( . normalizeNewlinesInFixedSource) {
649
- fixedTreeDescription =
650
- fixedTreeDescription
651
- . replacingOccurrences ( of: " \r \n " , with: " \n " )
652
- . replacingOccurrences ( of: " \r " , with: " \n " )
713
+ } else {
714
+ for fixItsApplication in fixItsApplications {
715
+ let applyFixIts = fixItsApplication. applyFixIts
716
+ let fixedTree = FixItApplier . applyFixes ( from: diags, filterByMessages: applyFixIts, to: tree)
717
+ var fixedTreeDescription = fixedTree. description
718
+ if options. contains ( . normalizeNewlinesInFixedSource) {
719
+ fixedTreeDescription =
720
+ fixedTreeDescription
721
+ . replacingOccurrences ( of: " \r \n " , with: " \n " )
722
+ . replacingOccurrences ( of: " \r " , with: " \n " )
723
+ }
724
+ assertStringsEqualWithDiff (
725
+ fixedTreeDescription. trimmingTrailingWhitespace ( ) ,
726
+ fixItsApplication. expectedFixedSource. trimmingTrailingWhitespace ( ) ,
727
+ " Applying \( applyFixIts? . description ?? " all Fix-Its " ) didn’t produce the expected fixed source " ,
728
+ file: file,
729
+ line: line
730
+ )
653
731
}
654
- assertStringsEqualWithDiff (
655
- fixedTreeDescription. trimmingTrailingWhitespace ( ) ,
656
- expectedFixedSource. trimmingTrailingWhitespace ( ) ,
657
- " Applying all Fix-Its didn’t produce the expected fixed source " ,
658
- file: file,
659
- line: line
660
- )
661
732
}
662
733
663
- if expectedDiagnostics. allSatisfy ( { $0. fixIts. isEmpty } ) && expectedFixedSource != nil {
734
+ if expectedDiagnostics. allSatisfy ( { $0. fixIts. isEmpty } ) && !fixItsApplications . isEmpty {
664
735
XCTFail (
665
736
" Fixed source was provided but the test case produces no diagnostics with Fix-Its " ,
666
737
file: file,
@@ -747,9 +818,9 @@ class TriviaRemover: SyntaxRewriter {
747
818
}
748
819
}
749
820
750
- func assertBasicFormat< S : SyntaxProtocol > (
821
+ func assertBasicFormat(
751
822
source: String ,
752
- parse: ( inout Parser ) -> S ,
823
+ parse: ( inout Parser ) -> some SyntaxProtocol ,
753
824
swiftVersion: Parser . SwiftVersion ? ,
754
825
experimentalFeatures: Parser . ExperimentalFeatures ,
755
826
file: StaticString = #filePath,
0 commit comments