Skip to content

Commit

Permalink
[Parse] Allow multiline attribute messages (SE-200) (swiftlang#19219)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
johnno1962 authored and rintaro committed Sep 14, 2018
1 parent 8f5f48f commit e55d425
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 29 deletions.
2 changes: 0 additions & 2 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
14 changes: 9 additions & 5 deletions include/swift/Parse/Lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<char> &Buffer) {
SmallVectorImpl<char> &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());
Expand Down
32 changes: 17 additions & 15 deletions lib/Parse/Lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<StringRef, SourceLoc>
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;

Expand All @@ -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;
Expand All @@ -1595,7 +1593,7 @@ getMultilineTrailingIndent(const Token &Str, DiagnosticEngine *Diags) {
}
}

return std::make_tuple(string, startLoc);
return StringRef(start, end - start);
}
default:
sawNonWhitespace = true;
Expand All @@ -1609,7 +1607,7 @@ getMultilineTrailingIndent(const Token &Str, DiagnosticEngine *Diags) {
.fixItInsert(loc, "\n");
}

return std::make_tuple("", Lexer::getSourceLoc(end - 1));
return "";
}

/// diagnoseInvalidMultilineIndents:
Expand Down Expand Up @@ -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<size_t>::max();
Expand All @@ -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);
Expand Down Expand Up @@ -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++;
Expand Down Expand Up @@ -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
Expand Down
6 changes: 1 addition & 5 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,15 +310,11 @@ bool Parser::parseTopLevel() {
static Optional<StringRef>
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<Lexer::StringSegment, 1> Segments;
P.L->getStringLiteralSegments(Tok, Segments);
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 9 additions & 1 deletion test/Parse/diagnose_availability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: #"""
Expand Down

0 comments on commit e55d425

Please sign in to comment.