Skip to content

Commit 3c543dd

Browse files
author
Nathan Hawes
authored
Merge pull request #10382 from nathawes/rdar32148117-multiline-syntax-coloring-issues-4.0
[4.0] Fix multi-line string syntax coloring issues
2 parents 6eeffb2 + b6e9d62 commit 3c543dd

21 files changed

+1352
-229
lines changed

lib/IDE/SyntaxModel.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,15 @@ SyntaxModelContext::SyntaxModelContext(SourceFile &SrcFile)
206206
break;
207207
}
208208

209+
case tok::unknown: {
210+
if (Tok.getRawText().startswith("\"")) {
211+
// This is an invalid string literal
212+
Kind = SyntaxNodeKind::String;
213+
break;
214+
}
215+
continue;
216+
}
217+
209218
default:
210219
continue;
211220
}

test/IDE/coloring.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,15 @@ func f(x: Int) -> Int {
227227
// CHECK: <str>"This is string </str>\<anchor>(</anchor>genFn({(a:<type>Int</type> -> <type>Int</type>) <kw>in</kw> a})<anchor>)</anchor><str> interpolation"</str>
228228
"This is string \(genFn({(a:Int -> Int) in a})) interpolation"
229229

230+
// CHECK: <str>"This is unterminated</str>
231+
"This is unterminated
232+
233+
// CHECK: <str>"This is unterminated with ignored \(interpolation) in it</str>
234+
"This is unterminated with ignored \(interpolation) in it
235+
236+
// CHECK: <str>"This is terminated with invalid \(interpolation" + "in it"</str>
237+
"This is terminated with invalid \(interpolation" + "in it"
238+
230239
// CHECK: <str>"""
231240
// CHECK-NEXT: This is a multiline string.
232241
// CHECK-NEXT: """</str>
@@ -236,9 +245,19 @@ func f(x: Int) -> Int {
236245

237246
// CHECK: <str>"""
238247
// CHECK-NEXT: This is a multiline</str>\<anchor>(</anchor> <str>"interpolated"</str> <anchor>)</anchor><str>string
248+
// CHECK-NEXT: </str>\<anchor>(</anchor>
249+
// CHECK-NEXT: <str>"""
250+
// CHECK-NEXT: inner
251+
// CHECK-NEXT: """</str>
252+
// CHECK-NEXT: <anchor>)</anchor><str>
239253
// CHECK-NEXT: """</str>
240254
"""
241255
This is a multiline\( "interpolated" )string
256+
\(
257+
"""
258+
inner
259+
"""
260+
)
242261
"""
243262
}
244263

test/IDE/unterminated_multiline.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-swift-ide-test -syntax-coloring -source-filename %s | %FileCheck %s
2+
// RUN: %target-swift-ide-test -syntax-coloring -typecheck -source-filename %s | %FileCheck %s
3+
4+
// CHECK: <kw>let</kw> x = <str>"""
5+
// CHECK-NEXT: This is an unterminated
6+
// CHECK-NEXT: \( "multiline" )
7+
// CHECK-NEXT: string followed by code
8+
// CHECK-NEXT: ""
9+
// CHECK-NEXT: func foo() {}
10+
// CHECK-NEXT: </str>
11+
let x = """
12+
This is an unterminated
13+
\( "multiline" )
14+
string followed by code
15+
""
16+
func foo() {}

test/SourceKit/DocumentStructure/mark_edit.swift.response

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
]
1414
}
1515
{
16-
key.offset: 0,
17-
key.length: 3,
1816
key.diagnostic_stage: source.diagnostic.stage.swift.parse,
1917
key.substructure: [
2018
{
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
let x = /*
3+
4+
*/ 2
5+
func foo() {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
let y = /*
2+
3+
*// /*
4+
5+
*/ 2
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
let a = "value"
2+
let x = """
3+
4+
\(
5+
a
6+
)
7+
8+
9+
10+
func foo () -> String {
11+
return "foo"
12+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/// This function does stuff
2+
///
3+
/// - parameter first: The first parameter
4+
///
5+
/// - returns: The return value
6+
func foo(first: Int) -> String {
7+
return ""
8+
}
9+
10+
let x = "Changing this string should only affect this line"
11+
12+
/// This function does other stuff
13+
///
14+
/// - parameter first: The first parameter
15+
///
16+
/// - returns: The return value
17+
func bar(first: Int) -> String {
18+
return ""
19+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
let l = 2
2+
l
3+
\( 56
4+
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
/// A comment
3+
/// - Note: important things
4+
let x = 42
5+
6+
struct Point {
7+
let x: Int = x
8+
let y: Int = x
9+
10+
func mag2() {
11+
return x*x + y*y;
12+
}
13+
}
14+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
let x = 421
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// RUN: %sourcekitd-test -req=open -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift -pos=4:2 -replace=" " -length=1 == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift -pos=4:2 -replace="/" -length=1 == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift -pos=1:1 -replace="//" -length=2 | %sed_clean > %t.response
2+
// RUN: %FileCheck -input-file=%t.response %s
3+
4+
// Initial state
5+
6+
// CHECK: {{^}}{
7+
// CHECK-NEXT: key.offset: 0,
8+
// CHECK-NEXT: key.length: 34,
9+
// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
10+
// CHECK-NEXT: key.syntaxmap: [
11+
// CHECK-NEXT: {
12+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
13+
// CHECK-NEXT: key.offset: 3,
14+
// CHECK-NEXT: key.length: 3
15+
// CHECK-NEXT: },
16+
// CHECK-NEXT: {
17+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
18+
// CHECK-NEXT: key.offset: 7,
19+
// CHECK-NEXT: key.length: 1
20+
// CHECK-NEXT: },
21+
// CHECK-NEXT: {
22+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
23+
// CHECK-NEXT: key.offset: 11,
24+
// CHECK-NEXT: key.length: 6
25+
// CHECK-NEXT: },
26+
// CHECK-NEXT: {
27+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.number,
28+
// CHECK-NEXT: key.offset: 18,
29+
// CHECK-NEXT: key.length: 1
30+
// CHECK-NEXT: },
31+
// CHECK-NEXT: {
32+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
33+
// CHECK-NEXT: key.offset: 20,
34+
// CHECK-NEXT: key.length: 4
35+
// CHECK-NEXT: },
36+
// CHECK-NEXT: {
37+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
38+
// CHECK-NEXT: key.offset: 25,
39+
// CHECK-NEXT: key.length: 3
40+
// CHECK-NEXT: }
41+
// CHECK-NEXT: ],
42+
43+
// After removing the '/' from the comment close
44+
45+
// CHECK: {{^}}{
46+
// CHECK-NEXT: key.offset: 3,
47+
// CHECK-NEXT: key.length: 31,
48+
// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
49+
// CHECK-NEXT: key.syntaxmap: [
50+
// CHECK-NEXT: {
51+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
52+
// CHECK-NEXT: key.offset: 3,
53+
// CHECK-NEXT: key.length: 3
54+
// CHECK-NEXT: },
55+
// CHECK-NEXT: {
56+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
57+
// CHECK-NEXT: key.offset: 7,
58+
// CHECK-NEXT: key.length: 1
59+
// CHECK-NEXT: },
60+
// CHECK-NEXT: {
61+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
62+
// CHECK-NEXT: key.offset: 11,
63+
// CHECK-NEXT: key.length: 23
64+
// CHECK-NEXT: }
65+
// CHECK-NEXT: ],
66+
67+
// After adding the '/' back to the comment close
68+
69+
// CHECK: {{^}}{
70+
// CHECK-NEXT: key.offset: 3,
71+
// CHECK-NEXT: key.length: 31,
72+
// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
73+
// CHECK-NEXT: key.syntaxmap: [
74+
// CHECK-NEXT: {
75+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
76+
// CHECK-NEXT: key.offset: 3,
77+
// CHECK-NEXT: key.length: 3
78+
// CHECK-NEXT: },
79+
// CHECK-NEXT: {
80+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
81+
// CHECK-NEXT: key.offset: 7,
82+
// CHECK-NEXT: key.length: 1
83+
// CHECK-NEXT: },
84+
// CHECK-NEXT: {
85+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
86+
// CHECK-NEXT: key.offset: 11,
87+
// CHECK-NEXT: key.length: 6
88+
// CHECK-NEXT: },
89+
// CHECK-NEXT: {
90+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.number,
91+
// CHECK-NEXT: key.offset: 18,
92+
// CHECK-NEXT: key.length: 1
93+
// CHECK-NEXT: },
94+
// CHECK-NEXT: {
95+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
96+
// CHECK-NEXT: key.offset: 20,
97+
// CHECK-NEXT: key.length: 4
98+
// CHECK-NEXT: },
99+
// CHECK-NEXT: {
100+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
101+
// CHECK-NEXT: key.offset: 25,
102+
// CHECK-NEXT: key.length: 3
103+
// CHECK-NEXT: }
104+
// CHECK-NEXT: ],
105+
106+
// after adding a single-line comment on the line above
107+
108+
// CHECK: {{^}}{
109+
// CHECK-NEXT: key.offset: 0,
110+
// CHECK-NEXT: key.length: 3,
111+
// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
112+
// CHECK-NEXT: key.syntaxmap: [
113+
// CHECK-NEXT: {
114+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
115+
// CHECK-NEXT: key.offset: 0,
116+
// CHECK-NEXT: key.length: 3
117+
// CHECK-NEXT: }
118+
// CHECK-NEXT: ],
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// RUN: %sourcekitd-test -req=open -print-raw-response %S/Inputs/syntaxmap-edit-chained-comment.swift == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-chained-comment.swift -pos=1:9 -replace=" " -length=1 == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-chained-comment.swift -pos=1:9 -replace="/" -length=1 | %sed_clean > %t.response
2+
// RUN: %FileCheck -input-file=%t.response %s
3+
4+
// Initial state
5+
6+
// CHECK: {{^}}{
7+
// CHECK-NEXT: key.offset: 0,
8+
// CHECK-NEXT: key.length: 25,
9+
// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
10+
// CHECK-NEXT: key.syntaxmap: [
11+
// CHECK-NEXT: {
12+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
13+
// CHECK-NEXT: key.offset: 0,
14+
// CHECK-NEXT: key.length: 3
15+
// CHECK-NEXT: },
16+
// CHECK-NEXT: {
17+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
18+
// CHECK-NEXT: key.offset: 4,
19+
// CHECK-NEXT: key.length: 1
20+
// CHECK-NEXT: },
21+
// CHECK-NEXT: {
22+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
23+
// CHECK-NEXT: key.offset: 8,
24+
// CHECK-NEXT: key.length: 6
25+
// CHECK-NEXT: },
26+
// CHECK-NEXT: {
27+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
28+
// CHECK-NEXT: key.offset: 16,
29+
// CHECK-NEXT: key.length: 6
30+
// CHECK-NEXT: },
31+
// CHECK-NEXT: {
32+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.number,
33+
// CHECK-NEXT: key.offset: 23,
34+
// CHECK-NEXT: key.length: 1
35+
// CHECK-NEXT: }
36+
// CHECK-NEXT: ],
37+
38+
39+
// After replacing the '/' from the comment open with ' '
40+
41+
// CHECK: {{^}}{
42+
// CHECK-NEXT: key.offset: 0,
43+
// CHECK-NEXT: key.length: 25,
44+
// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
45+
// CHECK-NEXT: key.syntaxmap: [
46+
// CHECK-NEXT: {
47+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
48+
// CHECK-NEXT: key.offset: 0,
49+
// CHECK-NEXT: key.length: 3
50+
// CHECK-NEXT: },
51+
// CHECK-NEXT: {
52+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
53+
// CHECK-NEXT: key.offset: 4,
54+
// CHECK-NEXT: key.length: 1
55+
// CHECK-NEXT: },
56+
// CHECK-NEXT: {
57+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
58+
// CHECK-NEXT: key.offset: 13,
59+
// CHECK-NEXT: key.length: 6
60+
// CHECK-NEXT: },
61+
// CHECK-NEXT: {
62+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.number,
63+
// CHECK-NEXT: key.offset: 23,
64+
// CHECK-NEXT: key.length: 1
65+
// CHECK-NEXT: }
66+
// CHECK-NEXT: ],
67+
68+
// After adding the '/' back to the comment open
69+
70+
// CHECK: {{^}}{
71+
// CHECK-NEXT: key.offset: 0,
72+
// CHECK-NEXT: key.length: 25,
73+
// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
74+
// CHECK-NEXT: key.syntaxmap: [
75+
// CHECK-NEXT: {
76+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
77+
// CHECK-NEXT: key.offset: 0,
78+
// CHECK-NEXT: key.length: 3
79+
// CHECK-NEXT: },
80+
// CHECK-NEXT: {
81+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
82+
// CHECK-NEXT: key.offset: 4,
83+
// CHECK-NEXT: key.length: 1
84+
// CHECK-NEXT: },
85+
// CHECK-NEXT: {
86+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
87+
// CHECK-NEXT: key.offset: 8,
88+
// CHECK-NEXT: key.length: 6
89+
// CHECK-NEXT: },
90+
// CHECK-NEXT: {
91+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
92+
// CHECK-NEXT: key.offset: 16,
93+
// CHECK-NEXT: key.length: 6
94+
// CHECK-NEXT: },
95+
// CHECK-NEXT: {
96+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.number,
97+
// CHECK-NEXT: key.offset: 23,
98+
// CHECK-NEXT: key.length: 1
99+
// CHECK-NEXT: }
100+
// CHECK-NEXT: ],

test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
}
3030
{
3131
key.offset: 1,
32-
key.length: 36,
32+
key.length: 34,
3333
key.diagnostic_stage: source.diagnostic.stage.swift.parse,
3434
key.syntaxmap: [
3535
{

test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
]
2929
}
3030
{
31-
key.offset: 36,
32-
key.length: 0,
3331
key.diagnostic_stage: source.diagnostic.stage.swift.parse,
3432
key.syntaxmap: [
3533
]

0 commit comments

Comments
 (0)