diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 55819a27def21..3f087fe6988f2 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1296,8 +1296,6 @@ ERROR(swift_native_objc_runtime_base_must_be_identifier,none, ERROR(attr_interpolated_string,none, "'%0' cannot be an interpolated string literal", (StringRef)) -ERROR(attr_multiline_string,none, -"'%0' cannot be a multiline string literal", (StringRef)) ERROR(attr_extended_escaping_string,none, "'%0' cannot be an extended escaping string literal", (StringRef)) diff --git a/include/swift/Parse/Lexer.h b/include/swift/Parse/Lexer.h index 0b388ae4b09c6..d71c0d6e8c9a2 100644 --- a/include/swift/Parse/Lexer.h +++ b/include/swift/Parse/Lexer.h @@ -426,15 +426,19 @@ class Lexer { /// If a copy needs to be made, it will be allocated out of the provided /// \p Buffer. static StringRef getEncodedStringSegment(StringRef Str, - SmallVectorImpl &Buffer) { + SmallVectorImpl &Buffer, + bool IsFirstSegment = false, + bool IsLastSegment = false, + unsigned IndentToStrip = 0, + unsigned CustomDelimiterLen = 0) { SmallString<128> TerminatedStrBuf(Str); TerminatedStrBuf.push_back('\0'); StringRef TerminatedStr = StringRef(TerminatedStrBuf).drop_back(); StringRef Result = getEncodedStringSegmentImpl(TerminatedStr, Buffer, - /*IsFirstSegment*/false, - /*IsLastSegment*/false, - /*IndentToStrip*/0, - /*CustomDelimiterLen*/0); + IsFirstSegment, + IsLastSegment, + IndentToStrip, + CustomDelimiterLen); if (Result == TerminatedStr) return Str; assert(Result.data() == Buffer.data()); diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index eb9bc8aed933d..347c0a4282b3b 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -1560,9 +1560,9 @@ static size_t commonPrefixLength(StringRef shorter, StringRef longer) { /// getMultilineTrailingIndent: /// Determine trailing indent to be used for multiline literal indent stripping. -static std::tuple -getMultilineTrailingIndent(const Token &Str, DiagnosticEngine *Diags) { - StringRef Bytes = getStringLiteralContent(Str); +StringRef +getMultilineTrailingIndent(StringRef Bytes, DiagnosticEngine *Diags = nullptr, + unsigned CustomDelimiterLen = 0) { const char *begin = Bytes.begin(), *end = Bytes.end(), *start = end; bool sawNonWhitespace = false; @@ -1575,11 +1575,9 @@ getMultilineTrailingIndent(const Token &Str, DiagnosticEngine *Diags) { case '\n': case '\r': { ++start; - auto startLoc = Lexer::getSourceLoc(start); - auto string = StringRef(start, end - start); // Disallow escaped newline in the last line. - if (Diags && Str.getCustomDelimiterLen() == 0) { + if (Diags && !CustomDelimiterLen) { auto *Ptr = start - 1; if (*Ptr == '\n') --Ptr; if (*Ptr == '\r') --Ptr; @@ -1595,7 +1593,7 @@ getMultilineTrailingIndent(const Token &Str, DiagnosticEngine *Diags) { } } - return std::make_tuple(string, startLoc); + return StringRef(start, end - start); } default: sawNonWhitespace = true; @@ -1609,7 +1607,7 @@ getMultilineTrailingIndent(const Token &Str, DiagnosticEngine *Diags) { .fixItInsert(loc, "\n"); } - return std::make_tuple("", Lexer::getSourceLoc(end - 1)); + return ""; } /// diagnoseInvalidMultilineIndents: @@ -1673,12 +1671,13 @@ static void diagnoseInvalidMultilineIndents( /// Diagnose contents of string literal that have inconsistent indentation. static void validateMultilineIndents(const Token &Str, DiagnosticEngine *Diags) { - StringRef Indent; - SourceLoc IndentStartLoc; - std::tie(Indent, IndentStartLoc) = getMultilineTrailingIndent(Str, Diags); + StringRef Bytes = getStringLiteralContent(Str); + StringRef Indent = + getMultilineTrailingIndent(Bytes, Diags, Str.getCustomDelimiterLen()); if (Indent.empty()) return; - + SourceLoc IndentStartLoc = Lexer::getSourceLoc(Indent.data()); + // The offset into the previous line where it experienced its first indentation // error, or Indent.size() if every character matched. size_t lastMistakeOffset = std::numeric_limits::max(); @@ -1688,7 +1687,6 @@ static void validateMultilineIndents(const Token &Str, // Prefix of indentation that's present on all lines in linesWithLastMatchLength. StringRef commonIndentation = ""; - StringRef Bytes = getStringLiteralContent(Str); for (size_t pos = Bytes.find('\n'); pos != StringRef::npos; pos = Bytes.find('\n', pos + 1)) { size_t nextpos = pos + 1; auto restOfBytes = Bytes.substr(nextpos); @@ -2109,6 +2107,11 @@ StringRef Lexer::getEncodedStringSegmentImpl(StringRef Bytes, // BytesPtr to avoid a range check subscripting on the StringRef. const char *BytesPtr = Bytes.begin(); + // Special case when being called from EncodedDiagnosticMessage(...) + // This should allow multiline strings to work as attribute messages. + if (IndentToStrip == ~0U) + IndentToStrip = getMultilineTrailingIndent(Bytes).size(); + bool IsEscapedNewline = false; while (BytesPtr < Bytes.end()) { char CurChar = *BytesPtr++; @@ -2203,8 +2206,7 @@ void Lexer::getStringLiteralSegments( bool MultilineString = Str.isMultilineString(), IsFirstSegment = true; unsigned IndentToStrip = 0, CustomDelimiterLen = Str.getCustomDelimiterLen(); if (MultilineString) - IndentToStrip = - std::get<0>(getMultilineTrailingIndent(Str, /*Diags=*/nullptr)).size(); + IndentToStrip = getMultilineTrailingIndent(Bytes).size(); // Note that it is always safe to read one over the end of "Bytes" because // we know that there is a terminating " character. Use BytesPtr to avoid a diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 3c904dcf4efcf..759b95c91db67 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -307,15 +307,11 @@ bool Parser::parseTopLevel() { static Optional getStringLiteralIfNotInterpolated(Parser &P, SourceLoc Loc, const Token &Tok, StringRef DiagText) { - // FIXME: Support extended escaping / multiline string literal. + // FIXME: Support extended escaping string literal. if (Tok.getCustomDelimiterLen()) { P.diagnose(Loc, diag::attr_extended_escaping_string, DiagText); return None; } - if (Tok.isMultilineString()) { - P.diagnose(Loc, diag::attr_multiline_string, DiagText); - return None; - } SmallVector Segments; P.L->getStringLiteralSegments(Tok, Segments); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 06e277b34ca07..6dc3db39a9520 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -2160,7 +2160,7 @@ class EncodedDiagnosticMessage { public: /// \param S A string with an encoded message EncodedDiagnosticMessage(StringRef S) - : Message(Lexer::getEncodedStringSegment(S, Buf)) {} + : Message(Lexer::getEncodedStringSegment(S, Buf, true, true, ~0U)) {} /// The unescaped message to display to the user. const StringRef Message; diff --git a/test/Parse/diagnose_availability.swift b/test/Parse/diagnose_availability.swift index ad41d26644a7e..277ee7b6c9dac 100644 --- a/test/Parse/diagnose_availability.swift +++ b/test/Parse/diagnose_availability.swift @@ -66,11 +66,19 @@ func swiftMessage() {} // expected-error@-1{{'message' cannot be an interpolated string literal}} func interpolatedMessage() {} -// expected-error@+1{{'message' cannot be a multiline string literal}} @available(*, unavailable, message: """ foobar message. """) func multilineMessage() {} +multilineMessage() +// expected-error@-1{{'multilineMessage()' is unavailable: foobar message.}} +// expected-note@-3{{'multilineMessage()' has been explicitly marked unavailable here}} + +@available(*, unavailable, message: " ") +func emptyMessage() {} +emptyMessage() +// expected-error@-1{{'emptyMessage()' is unavailable: }} +// expected-note@-3{{'emptyMessage()' has been explicitly marked unavailable here}} // expected-error@+1{{'message' cannot be an extended escaping string literal}} @available(*, unavailable, message: #"""