Skip to content
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
44 changes: 25 additions & 19 deletions lib/Parse/Lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1379,14 +1379,27 @@ static bool delimiterMatches(unsigned CustomDelimiterLen, const char *&BytesPtr,

/// advanceIfMultilineDelimiter - Centralized check for multiline delimiter.
static bool advanceIfMultilineDelimiter(unsigned CustomDelimiterLen,
const char *&CurPtr,
const char *&CurPtr, const char *EndPtr,
DiagnosticEngine *Diags,
bool IsOpening = false) {
auto scanDelimiter = [&]() -> const char * {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scanDelimiter is used only once. Why not

const char *DelimEnd = nullptr;
{
    // CurPtr here points to the character after `"`.
    const char *TmpPtr = CurPtr;
    if (*(TmpPtr - 1) == '"' &&
        diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags) &&
        diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags)) {
      DelimEnd = TmpPtr;
    } else {
      return false;
    }
}

If it's just a matter of the code style, I'm fine with the lambda.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah personally I prefer the lambda, shame C++ doesn't have guard statements 😄

Copy link
Member

@rintaro rintaro Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or just like swift-syntax counterpart

// CurPtr here points to the character after `"`.
const char *DelimEnd = CurPtr;
if (*(DelimEnd - 1) == '"' &&
    diagnoseZeroWidthMatchAndAdvance('"', DelimEnd, Diags) &&
    diagnoseZeroWidthMatchAndAdvance('"', DelimEnd, Diags)) {
 // Found the delimiter
} else {
  return false;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An empty if body feels weird to me, I don't mind changing it though

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. I'm fine with as-is

// CurPtr here points to the character after `"`.
const char *TmpPtr = CurPtr;
if (*(TmpPtr - 1) == '"' &&
diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags) &&
diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags)) {
Copy link
Member

@rintaro rintaro Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No check for EndPtr because we guarantee *EndPtr is 0?
But maybe we could early check like:

if (CurPtr + 2 >= EndPtr) return false;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah exactly, e.g delimiterMatches does

  while (diagnoseZeroWidthMatchAndAdvance('#', TmpPtr, Diags)) {}

I don't mind adding an extra check though

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Considering delimiterMatches does that, I don't think extra check is needed.

return TmpPtr;
}
return nullptr;
};
auto *DelimEnd = scanDelimiter();
if (!DelimEnd)
return false;

// Test for single-line string literals that resemble multiline delimiter.
const char *TmpPtr = CurPtr + 1;
const char *TmpPtr = DelimEnd - 1;
if (IsOpening && CustomDelimiterLen) {
while (*TmpPtr != '\r' && *TmpPtr != '\n') {
while (TmpPtr != EndPtr && *TmpPtr != '\r' && *TmpPtr != '\n') {
if (*TmpPtr == '"') {
if (delimiterMatches(CustomDelimiterLen, ++TmpPtr, nullptr)) {
return false;
Expand All @@ -1397,15 +1410,8 @@ static bool advanceIfMultilineDelimiter(unsigned CustomDelimiterLen,
}
}

TmpPtr = CurPtr;
if (*(TmpPtr - 1) == '"' &&
diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags) &&
diagnoseZeroWidthMatchAndAdvance('"', TmpPtr, Diags)) {
CurPtr = TmpPtr;
return true;
}

return false;
CurPtr = DelimEnd;
return true;
}

/// lexCharacter - Read a character and return its UTF32 code. If this is the
Expand Down Expand Up @@ -1450,8 +1456,8 @@ unsigned Lexer::lexCharacter(const char *&CurPtr, char StopQuote,

DiagnosticEngine *D = EmitDiagnostics ? getTokenDiags() : nullptr;
auto TmpPtr = CurPtr;
if (IsMultilineString &&
!advanceIfMultilineDelimiter(CustomDelimiterLen, TmpPtr, D))
if (IsMultilineString && !advanceIfMultilineDelimiter(
CustomDelimiterLen, TmpPtr, BufferEnd, D))
return '"';
if (CustomDelimiterLen &&
!delimiterMatches(CustomDelimiterLen, TmpPtr, D, /*IsClosing=*/true))
Expand Down Expand Up @@ -1587,9 +1593,8 @@ static const char *skipToEndOfInterpolatedExpression(const char *CurPtr,
if (!inStringLiteral()) {
// Open string literal.
OpenDelimiters.push_back(CurPtr[-1]);
AllowNewline.push_back(advanceIfMultilineDelimiter(CustomDelimiterLen,
CurPtr, nullptr,
true));
AllowNewline.push_back(advanceIfMultilineDelimiter(
CustomDelimiterLen, CurPtr, EndPtr, nullptr, true));
CustomDelimiter.push_back(CustomDelimiterLen);
continue;
}
Expand All @@ -1602,7 +1607,8 @@ static const char *skipToEndOfInterpolatedExpression(const char *CurPtr,

// Multi-line string can only be closed by '"""'.
if (AllowNewline.back() &&
!advanceIfMultilineDelimiter(CustomDelimiterLen, CurPtr, nullptr))
!advanceIfMultilineDelimiter(CustomDelimiterLen, CurPtr, EndPtr,
nullptr))
continue;

// Check whether we have equivalent number of '#'s.
Expand Down Expand Up @@ -1947,7 +1953,7 @@ void Lexer::lexStringLiteral(unsigned CustomDelimiterLen) {
assert((QuoteChar == '"' || QuoteChar == '\'') && "Unexpected start");

bool IsMultilineString = advanceIfMultilineDelimiter(
CustomDelimiterLen, CurPtr, getTokenDiags(), true);
CustomDelimiterLen, CurPtr, BufferEnd, getTokenDiags(), true);
if (IsMultilineString && *CurPtr != '\n' && *CurPtr != '\r')
diagnose(CurPtr, diag::lex_illegal_multiline_string_start)
.fixItInsert(Lexer::getSourceLoc(CurPtr), "\n");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %empty-directory(%t)

// RUN: echo '#"' > %t/main1.swift
// RUN: echo -n '#"' > %t/main2.swift

// RUN: env DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib not %target-swift-frontend -typecheck %t/main1.swift
// RUN: env DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib not %target-swift-frontend -typecheck %t/main2.swift

// guardmalloc is incompatible with ASAN
// REQUIRES: no_asan