Skip to content

Commit e55d425

Browse files
johnno1962rintaro
authored andcommitted
[Parse] Allow multiline attribute messages (SE-200) (#19219)
Multiline string literal at attribute message position was disallowed in 59778f8. Reworked to try to at least get multiline strings working which might be useful as messages for attributes (for example a detailed “unavailable” annotation) minus the code which read off the start of the StringRef buffer.
1 parent 8f5f48f commit e55d425

File tree

6 files changed

+37
-29
lines changed

6 files changed

+37
-29
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,8 +1296,6 @@ ERROR(swift_native_objc_runtime_base_must_be_identifier,none,
12961296

12971297
ERROR(attr_interpolated_string,none,
12981298
"'%0' cannot be an interpolated string literal", (StringRef))
1299-
ERROR(attr_multiline_string,none,
1300-
"'%0' cannot be a multiline string literal", (StringRef))
13011299
ERROR(attr_extended_escaping_string,none,
13021300
"'%0' cannot be an extended escaping string literal", (StringRef))
13031301

include/swift/Parse/Lexer.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -426,15 +426,19 @@ class Lexer {
426426
/// If a copy needs to be made, it will be allocated out of the provided
427427
/// \p Buffer.
428428
static StringRef getEncodedStringSegment(StringRef Str,
429-
SmallVectorImpl<char> &Buffer) {
429+
SmallVectorImpl<char> &Buffer,
430+
bool IsFirstSegment = false,
431+
bool IsLastSegment = false,
432+
unsigned IndentToStrip = 0,
433+
unsigned CustomDelimiterLen = 0) {
430434
SmallString<128> TerminatedStrBuf(Str);
431435
TerminatedStrBuf.push_back('\0');
432436
StringRef TerminatedStr = StringRef(TerminatedStrBuf).drop_back();
433437
StringRef Result = getEncodedStringSegmentImpl(TerminatedStr, Buffer,
434-
/*IsFirstSegment*/false,
435-
/*IsLastSegment*/false,
436-
/*IndentToStrip*/0,
437-
/*CustomDelimiterLen*/0);
438+
IsFirstSegment,
439+
IsLastSegment,
440+
IndentToStrip,
441+
CustomDelimiterLen);
438442
if (Result == TerminatedStr)
439443
return Str;
440444
assert(Result.data() == Buffer.data());

lib/Parse/Lexer.cpp

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,9 +1560,9 @@ static size_t commonPrefixLength(StringRef shorter, StringRef longer) {
15601560

15611561
/// getMultilineTrailingIndent:
15621562
/// Determine trailing indent to be used for multiline literal indent stripping.
1563-
static std::tuple<StringRef, SourceLoc>
1564-
getMultilineTrailingIndent(const Token &Str, DiagnosticEngine *Diags) {
1565-
StringRef Bytes = getStringLiteralContent(Str);
1563+
StringRef
1564+
getMultilineTrailingIndent(StringRef Bytes, DiagnosticEngine *Diags = nullptr,
1565+
unsigned CustomDelimiterLen = 0) {
15661566
const char *begin = Bytes.begin(), *end = Bytes.end(), *start = end;
15671567
bool sawNonWhitespace = false;
15681568

@@ -1575,11 +1575,9 @@ getMultilineTrailingIndent(const Token &Str, DiagnosticEngine *Diags) {
15751575
case '\n':
15761576
case '\r': {
15771577
++start;
1578-
auto startLoc = Lexer::getSourceLoc(start);
1579-
auto string = StringRef(start, end - start);
15801578

15811579
// Disallow escaped newline in the last line.
1582-
if (Diags && Str.getCustomDelimiterLen() == 0) {
1580+
if (Diags && !CustomDelimiterLen) {
15831581
auto *Ptr = start - 1;
15841582
if (*Ptr == '\n') --Ptr;
15851583
if (*Ptr == '\r') --Ptr;
@@ -1595,7 +1593,7 @@ getMultilineTrailingIndent(const Token &Str, DiagnosticEngine *Diags) {
15951593
}
15961594
}
15971595

1598-
return std::make_tuple(string, startLoc);
1596+
return StringRef(start, end - start);
15991597
}
16001598
default:
16011599
sawNonWhitespace = true;
@@ -1609,7 +1607,7 @@ getMultilineTrailingIndent(const Token &Str, DiagnosticEngine *Diags) {
16091607
.fixItInsert(loc, "\n");
16101608
}
16111609

1612-
return std::make_tuple("", Lexer::getSourceLoc(end - 1));
1610+
return "";
16131611
}
16141612

16151613
/// diagnoseInvalidMultilineIndents:
@@ -1673,12 +1671,13 @@ static void diagnoseInvalidMultilineIndents(
16731671
/// Diagnose contents of string literal that have inconsistent indentation.
16741672
static void validateMultilineIndents(const Token &Str,
16751673
DiagnosticEngine *Diags) {
1676-
StringRef Indent;
1677-
SourceLoc IndentStartLoc;
1678-
std::tie(Indent, IndentStartLoc) = getMultilineTrailingIndent(Str, Diags);
1674+
StringRef Bytes = getStringLiteralContent(Str);
1675+
StringRef Indent =
1676+
getMultilineTrailingIndent(Bytes, Diags, Str.getCustomDelimiterLen());
16791677
if (Indent.empty())
16801678
return;
1681-
1679+
SourceLoc IndentStartLoc = Lexer::getSourceLoc(Indent.data());
1680+
16821681
// The offset into the previous line where it experienced its first indentation
16831682
// error, or Indent.size() if every character matched.
16841683
size_t lastMistakeOffset = std::numeric_limits<size_t>::max();
@@ -1688,7 +1687,6 @@ static void validateMultilineIndents(const Token &Str,
16881687
// Prefix of indentation that's present on all lines in linesWithLastMatchLength.
16891688
StringRef commonIndentation = "";
16901689

1691-
StringRef Bytes = getStringLiteralContent(Str);
16921690
for (size_t pos = Bytes.find('\n'); pos != StringRef::npos; pos = Bytes.find('\n', pos + 1)) {
16931691
size_t nextpos = pos + 1;
16941692
auto restOfBytes = Bytes.substr(nextpos);
@@ -2109,6 +2107,11 @@ StringRef Lexer::getEncodedStringSegmentImpl(StringRef Bytes,
21092107
// BytesPtr to avoid a range check subscripting on the StringRef.
21102108
const char *BytesPtr = Bytes.begin();
21112109

2110+
// Special case when being called from EncodedDiagnosticMessage(...)
2111+
// This should allow multiline strings to work as attribute messages.
2112+
if (IndentToStrip == ~0U)
2113+
IndentToStrip = getMultilineTrailingIndent(Bytes).size();
2114+
21122115
bool IsEscapedNewline = false;
21132116
while (BytesPtr < Bytes.end()) {
21142117
char CurChar = *BytesPtr++;
@@ -2203,8 +2206,7 @@ void Lexer::getStringLiteralSegments(
22032206
bool MultilineString = Str.isMultilineString(), IsFirstSegment = true;
22042207
unsigned IndentToStrip = 0, CustomDelimiterLen = Str.getCustomDelimiterLen();
22052208
if (MultilineString)
2206-
IndentToStrip =
2207-
std::get<0>(getMultilineTrailingIndent(Str, /*Diags=*/nullptr)).size();
2209+
IndentToStrip = getMultilineTrailingIndent(Bytes).size();
22082210

22092211
// Note that it is always safe to read one over the end of "Bytes" because
22102212
// we know that there is a terminating " character. Use BytesPtr to avoid a

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,15 +310,11 @@ bool Parser::parseTopLevel() {
310310
static Optional<StringRef>
311311
getStringLiteralIfNotInterpolated(Parser &P, SourceLoc Loc, const Token &Tok,
312312
StringRef DiagText) {
313-
// FIXME: Support extended escaping / multiline string literal.
313+
// FIXME: Support extended escaping string literal.
314314
if (Tok.getCustomDelimiterLen()) {
315315
P.diagnose(Loc, diag::attr_extended_escaping_string, DiagText);
316316
return None;
317317
}
318-
if (Tok.isMultilineString()) {
319-
P.diagnose(Loc, diag::attr_multiline_string, DiagText);
320-
return None;
321-
}
322318

323319
SmallVector<Lexer::StringSegment, 1> Segments;
324320
P.L->getStringLiteralSegments(Tok, Segments);

lib/Sema/TypeChecker.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2160,7 +2160,7 @@ class EncodedDiagnosticMessage {
21602160
public:
21612161
/// \param S A string with an encoded message
21622162
EncodedDiagnosticMessage(StringRef S)
2163-
: Message(Lexer::getEncodedStringSegment(S, Buf)) {}
2163+
: Message(Lexer::getEncodedStringSegment(S, Buf, true, true, ~0U)) {}
21642164

21652165
/// The unescaped message to display to the user.
21662166
const StringRef Message;

test/Parse/diagnose_availability.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,19 @@ func swiftMessage() {}
6666
// expected-error@-1{{'message' cannot be an interpolated string literal}}
6767
func interpolatedMessage() {}
6868

69-
// expected-error@+1{{'message' cannot be a multiline string literal}}
7069
@available(*, unavailable, message: """
7170
foobar message.
7271
""")
7372
func multilineMessage() {}
73+
multilineMessage()
74+
// expected-error@-1{{'multilineMessage()' is unavailable: foobar message.}}
75+
// expected-note@-3{{'multilineMessage()' has been explicitly marked unavailable here}}
76+
77+
@available(*, unavailable, message: " ")
78+
func emptyMessage() {}
79+
emptyMessage()
80+
// expected-error@-1{{'emptyMessage()' is unavailable: }}
81+
// expected-note@-3{{'emptyMessage()' has been explicitly marked unavailable here}}
7482

7583
// expected-error@+1{{'message' cannot be an extended escaping string literal}}
7684
@available(*, unavailable, message: #"""

0 commit comments

Comments
 (0)