Skip to content

[Parse] 2nd attempt at multiline attribute messages (SE-200) #19219

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
merged 2 commits into from
Sep 14, 2018
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: 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 @@ -307,15 +307,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