Skip to content

[4.0] [SE-0182][Lexer] Diagnose escaped newline at the end of the last line in multiline string #11204

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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ NOTE(lex_multiline_string_indent_should_match_here,none,
"should match %select{space|tab}0 here", (unsigned))
NOTE(lex_multiline_string_indent_change_line,none,
"change indentation of %select{this line|these lines}0 to match closing delimiter", (bool))
ERROR(lex_escaped_newline_at_lastline,none,
"escaped newline at the last line is not allowed", ())

ERROR(lex_invalid_character,none,
"invalid character in source file", ())
Expand Down
29 changes: 22 additions & 7 deletions lib/Parse/Lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1424,13 +1424,28 @@ getMultilineTrailingIndent(const Token &Str, DiagnosticEngine *Diags) {
continue;
case '\n':
case '\r': {
auto bytes = start + 1;
auto length = end-(start+1);

auto bytesLoc = Lexer::getSourceLoc(bytes);
auto string = StringRef(bytes, length);

return std::make_tuple(string, bytesLoc);
start++;
auto startLoc = Lexer::getSourceLoc(start);
auto string = StringRef(start, end - start);

// Disallow escaped newline in the last line.
if (Diags) {
auto *Ptr = start - 1;
if (*Ptr == '\n') --Ptr;
if (*Ptr == '\r') --Ptr;
auto *LineEnd = Ptr + 1;
while (Ptr > begin && (*Ptr == ' ' || *Ptr == '\t')) --Ptr;
if (*Ptr == '\\') {
auto escapeLoc = Lexer::getSourceLoc(Ptr);
bool invalid = true;
while (*--Ptr == '\\') invalid = !invalid;
if (invalid)
Diags->diagnose(escapeLoc, diag::lex_escaped_newline_at_lastline)
.fixItRemoveChars(escapeLoc, Lexer::getSourceLoc(LineEnd));
}
}

return std::make_tuple(string, startLoc);
}
default:
sawNonWhitespace = true;
Expand Down
58 changes: 58 additions & 0 deletions test/Parse/multiline_errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ _ = """
// expected-note@-1{{should match tab here}}
// expected-note@-3{{change indentation of this line to match closing delimiter}} {{1-1= }}

_ = """
\(42
)
""" // expected-error@-1{{insufficient indentation of line in multi-line string literal}}
// expected-note@-1{{should match space here}}
// expected-note@-3{{change indentation of this line to match closing delimiter}} {{1-1= }}

_ = """
Foo
\
Bar
""" // expected-error@-2{{insufficient indentation of line in multi-line string literal}}
// expected-note@-1{{should match space here}}
// expected-note@-4{{change indentation of this line to match closing delimiter}} {{1-1= }}

// a tab is not the same as multiple spaces for de-indentation
_ = """
Thirteen
Expand Down Expand Up @@ -126,3 +141,46 @@ _ = """
line two
"""
// expected-error@-3 {{invalid escape sequence in literal}}

_ = """
line one
line two\
"""
// expected-error@-2 {{escaped newline at the last line is not allowed}} {{11-12=}}

_ = """
\\\
"""
// expected-error@-2 {{escaped newline at the last line is not allowed}} {{5-10=}}

_ = """
\(42)\
"""
// expected-error@-2 {{escaped newline at the last line is not allowed}} {{8-11=}}

_ = """
foo\
"""
// expected-error@-2 {{escaped newline at the last line is not allowed}} {{6-7=}}

_ = """
foo\ """
// expected-error@-1 {{escaped newline at the last line is not allowed}} {{6-7=}}

_ = """
foo\
""" // OK because LF + CR is two new lines.

_ = """
\
"""
// expected-error@-2 {{escaped newline at the last line is not allowed}} {{1-2=}}
// expected-error@-3{{insufficient indentation of line in multi-line string literal}}
// expected-note@-3{{should match space here}}
// expected-note@-5{{change indentation of this line to match closing delimiter}} {{1-1= }}

_ = """\
"""
// FIXME: Bad diagnostics
// expected-error@-3 {{multi-line string literal content must begin on a new line}}
// expected-error@-4 {{escaped newline at the last line is not allowed}} {{8-9=}}
Expand Down
70 changes: 70 additions & 0 deletions test/Parse/multiline_normalize_newline.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// RUN: %target-swift-frontend -dump-parse %s 2>&1 | %FileCheck %s

// CR
_ = """"""
//CHECK: string_literal_expr {{.*}} value=""

_ = """ test """
//CHECK: string_literal_expr {{.*}} value="test"

// CR+LF
_ = """
"""
//CHECK: string_literal_expr {{.*}} value=""

_ = """
test
"""
//CHECK: string_literal_expr {{.*}} value="test"

// CR+LF
_ = """
"""
//CHECK: string_literal_expr {{.*}} value=""
_ = """
test
test
"""
//CHECK: string_literal_expr {{.*}} value="test\ntest"

// LF+CR
_ = """
foo
foo
"""
//CHECK: string_literal_expr {{.*}} value="\nfoo\n\nfoo\n"

// LF+CR+LF
_ = """

foo

foo

"""
//CHECK: string_literal_expr {{.*}} value="\nfoo\n\nfoo\n"

// Mixed no-indent.
_ = """
<LF
<LF<CR
<CR+LF
"""
//CHECK: string_literal_expr {{.*}} value="<LF\n<LF\n<CR\n<CR+LF"

// Mixed indent.
_ = """
<LF
<LF <CR
<CR+LF
"""
//CHECK: string_literal_expr {{.*}} value="<LF\n<LF\n<CR\n<CR+LF"

// Empty line CR, CR+LF, LF.
_ = """
foo


bar
"""
//CHECK: string_literal_expr {{.*}} value="foo\n\n\n\nbar"
Expand Down
7 changes: 6 additions & 1 deletion test/Parse/multiline_string.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ _ = """
"""
// CHECK: "\\"

_ = """
\\
"""
// CHECK: "\\"

_ = """

ABC
Expand Down Expand Up @@ -122,7 +127,7 @@ _ = """
\("""
substring2 \
substring3
""")\
""")
""") \
whitepsace
"""
Expand Down