Skip to content

Commit b4c60e8

Browse files
authored
Merge pull request #24846 from rintaro/5.1-parse-stringliteral-syntax
[5.1][Parse/Syntax] Changes for string literal parsing
2 parents 437a084 + e14b538 commit b4c60e8

File tree

15 files changed

+418
-207
lines changed

15 files changed

+418
-207
lines changed

include/swift/AST/Expr.h

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2471,14 +2471,30 @@ class UnresolvedDotExpr : public Expr {
24712471
: FunctionRefKind::Unapplied);
24722472
}
24732473

2474-
SourceLoc getLoc() const { return NameLoc.getBaseNameLoc(); }
2474+
SourceLoc getLoc() const {
2475+
if (NameLoc.isValid())
2476+
return NameLoc.getBaseNameLoc();
2477+
else if (DotLoc.isValid())
2478+
return DotLoc;
2479+
else
2480+
return SubExpr->getEndLoc();
2481+
}
24752482

24762483
SourceLoc getStartLoc() const {
2477-
return (DotLoc.isInvalid() ? NameLoc.getSourceRange().End
2478-
: SubExpr->getStartLoc());
2484+
if (SubExpr->getStartLoc().isValid())
2485+
return SubExpr->getStartLoc();
2486+
else if (DotLoc.isValid())
2487+
return DotLoc;
2488+
else
2489+
return NameLoc.getSourceRange().Start;
24792490
}
24802491
SourceLoc getEndLoc() const {
2481-
return NameLoc.getSourceRange().End;
2492+
if (NameLoc.isValid())
2493+
return NameLoc.getSourceRange().End;
2494+
else if (DotLoc.isValid())
2495+
return DotLoc;
2496+
else
2497+
return SubExpr->getEndLoc();
24822498
}
24832499

24842500
SourceLoc getDotLoc() const { return DotLoc; }

lib/Parse/ParseExpr.cpp

Lines changed: 99 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,10 +1862,22 @@ parseStringSegments(SmallVectorImpl<Lexer::StringSegment> &Segments,
18621862
SyntaxParsingContext ExprContext(SyntaxContext,
18631863
SyntaxKind::ExpressionSegment);
18641864

1865+
unsigned DelimiterLen = EntireTok.getCustomDelimiterLen();
1866+
bool HasCustomDelimiter = DelimiterLen > 0;
1867+
18651868
// Backslash is part of an expression segment.
1866-
Token BackSlash(tok::backslash,
1867-
CharSourceRange(Segment.Loc.getAdvancedLoc(-1), 1).str());
1869+
SourceLoc BackSlashLoc = Segment.Loc.getAdvancedLoc(-1 - DelimiterLen);
1870+
Token BackSlash(tok::backslash, CharSourceRange(BackSlashLoc, 1).str());
18681871
ExprContext.addToken(BackSlash, EmptyTrivia, EmptyTrivia);
1872+
1873+
// Custom delimiter may be a part of an expression segment.
1874+
if (HasCustomDelimiter) {
1875+
SourceLoc DelimiterLoc = Segment.Loc.getAdvancedLoc(-DelimiterLen);
1876+
Token Delimiter(tok::raw_string_delimiter,
1877+
CharSourceRange(DelimiterLoc, DelimiterLen).str());
1878+
ExprContext.addToken(Delimiter, EmptyTrivia, EmptyTrivia);
1879+
}
1880+
18691881
// Create a temporary lexer that lexes from the body of the string.
18701882
LexerState BeginState =
18711883
L->getStateForBeginningOfTokenLoc(Segment.Loc);
@@ -1888,30 +1900,12 @@ parseStringSegments(SmallVectorImpl<Lexer::StringSegment> &Segments,
18881900
TokReceiver->registerTokenKindChange(Tok.getLoc(),
18891901
tok::string_interpolation_anchor);
18901902

1891-
SourceLoc lParen, rParen;
1892-
SmallVector<Expr *, 4> args;
1893-
SmallVector<Identifier, 4> argLabels;
1894-
SmallVector<SourceLoc, 4> argLabelLocs;
1895-
Expr *trailingClosureNeverPresent;
1896-
ParserStatus S =
1897-
parseExprList(tok::l_paren, tok::r_paren,
1898-
/*isPostfix=*/false, /*isExprBasic=*/true,
1899-
lParen, args, argLabels, argLabelLocs, rParen,
1900-
trailingClosureNeverPresent,
1901-
SyntaxKind::Unknown);
1902-
assert(!trailingClosureNeverPresent);
1903-
1904-
Status |= S;
1905-
// If there was an error parsing a parameter, add an ErrorExpr to
1906-
// represent it. Prevents spurious errors about a nonexistent
1907-
// appendInterpolation() overload.
1908-
if (S.isError() && args.size() == 0)
1909-
args.push_back(new (Context) ErrorExpr(SourceRange(lParen, rParen)));
1910-
1911-
while (argLabels.size() < args.size())
1912-
argLabels.push_back(Identifier());
1913-
while (argLabelLocs.size() < args.size())
1914-
argLabelLocs.push_back(SourceLoc());
1903+
auto callee = new (Context) UnresolvedDotExpr(InterpolationVarRef,
1904+
/*dotloc=*/BackSlashLoc,
1905+
appendInterpolation,
1906+
/*nameloc=*/DeclNameLoc(),
1907+
/*Implicit=*/true);
1908+
auto S = parseExprCallSuffix(makeParserResult(callee), true);
19151909

19161910
// If we stopped parsing the expression before the expression segment is
19171911
// over, eat the remaining tokens into a token list
@@ -1925,51 +1919,14 @@ parseStringSegments(SmallVectorImpl<Lexer::StringSegment> &Segments,
19251919
L->getLocForEndOfToken(SourceMgr, Tok.getLoc()));
19261920
}
19271921

1922+
Expr *call = S.getPtrOrNull();
1923+
if (!call)
1924+
call = new (Context) ErrorExpr(SourceRange(Segment.Loc,
1925+
Segment.getEndLoc()));
1926+
19281927
InterpolationCount += 1;
1929-
1930-
// In Swift 4.2 and earlier, a single argument with a label would ignore
1931-
// the label (at best), and multiple arguments would form a tuple.
1932-
if (!Context.isSwiftVersionAtLeast(5)) {
1933-
if (args.size() > 1) {
1934-
diagnose(args[1]->getLoc(), diag::string_interpolation_list_changing)
1935-
.highlightChars(args[1]->getLoc(), rParen);
1936-
diagnose(args[1]->getLoc(),
1937-
diag::string_interpolation_list_insert_parens)
1938-
.fixItInsertAfter(lParen, "(")
1939-
.fixItInsert(rParen, ")");
1940-
1941-
args = {
1942-
TupleExpr::create(Context,
1943-
lParen, args, argLabels, argLabelLocs, rParen,
1944-
/*hasTrailingClosure=*/false,
1945-
/*Implicit=*/false)
1946-
};
1947-
argLabels = { Identifier() };
1948-
argLabelLocs = { SourceLoc() };
1949-
}
1950-
else if (args.size() == 1 && argLabels[0] != Identifier()) {
1951-
diagnose(argLabelLocs[0], diag::string_interpolation_label_changing)
1952-
.highlightChars(argLabelLocs[0], args[0]->getStartLoc());
1953-
diagnose(argLabelLocs[0], diag::string_interpolation_remove_label,
1954-
argLabels[0])
1955-
.fixItRemoveChars(argLabelLocs[0], args[0]->getStartLoc());
1956-
1957-
argLabels[0] = Identifier();
1958-
}
1959-
}
1960-
1961-
auto AppendInterpolationRef =
1962-
new (Context) UnresolvedDotExpr(InterpolationVarRef,
1963-
/*dotloc=*/SourceLoc(),
1964-
appendInterpolation,
1965-
/*nameloc=*/DeclNameLoc(),
1966-
/*Implicit=*/true);
1967-
auto AppendInterpolationCall =
1968-
CallExpr::create(Context, AppendInterpolationRef,
1969-
lParen, args, argLabels, argLabelLocs, rParen,
1970-
/*trailingClosure=*/nullptr, /*implicit=*/false);
1971-
1972-
Stmts.push_back(AppendInterpolationCall);
1928+
Stmts.push_back(call);
1929+
Status |= S;
19731930

19741931
if (!Tok.is(tok::eof)) {
19751932
diagnose(Tok, diag::string_interpolation_extra);
@@ -2003,35 +1960,73 @@ ParserResult<Expr> Parser::parseExprStringLiteral() {
20031960
SourceLoc Loc = Tok.getLoc();
20041961
SourceLoc EndLoc = Loc.getAdvancedLoc(Tok.getLength());
20051962

2006-
// The simple case: just a single literal segment.
2007-
if (Segments.size() == 1 &&
2008-
Segments.front().Kind == Lexer::StringSegment::Literal) {
2009-
consumeToken();
2010-
return makeParserResult(
2011-
createStringLiteralExprFromSegment(Context, L, Segments.front(), Loc));
2012-
}
2013-
2014-
// We are now sure this is a string interpolation expression.
2015-
LocalContext.setCreateSyntax(SyntaxKind::StringInterpolationExpr);
2016-
StringRef OpenQuoteStr, CloseQuoteStr;
1963+
StringRef OpenDelimiterStr, OpenQuoteStr, CloseQuoteStr, CloseDelimiterStr;
1964+
unsigned DelimiterLength = Tok.getCustomDelimiterLen();
1965+
bool HasCustomDelimiter = DelimiterLength > 0;
1966+
unsigned QuoteLength;
20171967
tok QuoteKind;
2018-
std::tie(OpenQuoteStr, CloseQuoteStr, QuoteKind) = Tok.isMultilineString() ?
2019-
std::make_tuple(Tok.getRawText().take_front(3),
2020-
Tok.getRawText().take_back(3),
2021-
tok::multiline_string_quote) :
2022-
std::make_tuple(Tok.getRawText().take_front(1),
2023-
Tok.getRawText().take_back(1),
2024-
tok::string_quote);
1968+
std::tie(QuoteLength, QuoteKind) =
1969+
Tok.isMultilineString() ? std::make_tuple(3, tok::multiline_string_quote)
1970+
: std::make_tuple(1, tok::string_quote);
1971+
unsigned CloseQuoteBegin = Tok.getLength() - DelimiterLength - QuoteLength;
1972+
1973+
OpenDelimiterStr = Tok.getRawText().take_front(DelimiterLength);
1974+
OpenQuoteStr = Tok.getRawText().substr(DelimiterLength, QuoteLength);
1975+
CloseQuoteStr = Tok.getRawText().substr(CloseQuoteBegin, QuoteLength);
1976+
CloseDelimiterStr = Tok.getRawText().take_back(DelimiterLength);
20251977

20261978
// Make unknown tokens to represent the open and close quote.
20271979
Token OpenQuote(QuoteKind, OpenQuoteStr);
20281980
Token CloseQuote(QuoteKind, CloseQuoteStr);
20291981
ParsedTrivia EmptyTrivia;
20301982
ParsedTrivia EntireTrailingTrivia = TrailingTrivia;
20311983

2032-
// Add the open quote to the context; the quote should have the leading trivia
2033-
// of the entire string token and a void trailing trivia.
2034-
SyntaxContext->addToken(OpenQuote, LeadingTrivia, EmptyTrivia);
1984+
if (HasCustomDelimiter) {
1985+
Token OpenDelimiter(tok::raw_string_delimiter, OpenDelimiterStr);
1986+
// When a custom delimiter is present, it owns the leading trivia.
1987+
SyntaxContext->addToken(OpenDelimiter, LeadingTrivia, EmptyTrivia);
1988+
1989+
SyntaxContext->addToken(OpenQuote, EmptyTrivia, EmptyTrivia);
1990+
} else {
1991+
// Without custom delimiter the quote owns trailing trivia.
1992+
SyntaxContext->addToken(OpenQuote, LeadingTrivia, EmptyTrivia);
1993+
}
1994+
1995+
// The simple case: just a single literal segment.
1996+
if (Segments.size() == 1 &&
1997+
Segments.front().Kind == Lexer::StringSegment::Literal) {
1998+
{
1999+
consumeExtraToken(Tok);
2000+
consumeTokenWithoutFeedingReceiver();
2001+
2002+
SyntaxParsingContext SegmentsCtx(SyntaxContext,
2003+
SyntaxKind::StringLiteralSegments);
2004+
2005+
SyntaxParsingContext StrSegContext(SyntaxContext,
2006+
SyntaxKind::StringSegment);
2007+
2008+
// Make an unknown token to encapsulate the entire string segment and add
2009+
// such token to the context.
2010+
auto Segment = Segments.front();
2011+
Token content(tok::string_segment,
2012+
CharSourceRange(Segment.Loc, Segment.Length).str());
2013+
SyntaxContext->addToken(content, EmptyTrivia, EmptyTrivia);
2014+
}
2015+
2016+
if (HasCustomDelimiter) {
2017+
SyntaxContext->addToken(CloseQuote, EmptyTrivia, EmptyTrivia);
2018+
2019+
Token CloseDelimiter(tok::raw_string_delimiter, CloseDelimiterStr);
2020+
// When a custom delimiter is present it owns the trailing trivia.
2021+
SyntaxContext->addToken(CloseDelimiter, EmptyTrivia, EntireTrailingTrivia);
2022+
} else {
2023+
// Without custom delimiter the quote owns trailing trivia.
2024+
SyntaxContext->addToken(CloseQuote, EmptyTrivia, EntireTrailingTrivia);
2025+
}
2026+
2027+
return makeParserResult(
2028+
createStringLiteralExprFromSegment(Context, L, Segments.front(), Loc));
2029+
}
20352030

20362031
// We don't expose the entire interpolated string as one token. Instead, we
20372032
// should expose the tokens in each segment.
@@ -2044,8 +2039,8 @@ ParserResult<Expr> Parser::parseExprStringLiteral() {
20442039

20452040
// We're not in a place where an interpolation would be valid.
20462041
if (!CurLocalContext) {
2047-
// Return an error, but include an empty InterpolatedStringLiteralExpr
2048-
// so that parseDeclPoundDiagnostic() can figure out why this string
2042+
// Return an error, but include an empty InterpolatedStringLiteralExpr
2043+
// so that parseDeclPoundDiagnostic() can figure out why this string
20492044
// literal was bad.
20502045
return makeParserErrorResult(
20512046
new (Context) InterpolatedStringLiteralExpr(Loc, 0, 0, nullptr));
@@ -2075,7 +2070,7 @@ ParserResult<Expr> Parser::parseExprStringLiteral() {
20752070

20762071
// Collect all string segments.
20772072
SyntaxParsingContext SegmentsCtx(SyntaxContext,
2078-
SyntaxKind::StringInterpolationSegments);
2073+
SyntaxKind::StringLiteralSegments);
20792074
Status = parseStringSegments(Segments, EntireTok, InterpolationVar,
20802075
Stmts, LiteralCapacity, InterpolationCount);
20812076

@@ -2084,9 +2079,16 @@ ParserResult<Expr> Parser::parseExprStringLiteral() {
20842079
AppendingExpr = new (Context) TapExpr(nullptr, Body);
20852080
}
20862081

2087-
// Add the close quote the context; the quote should have a void leading trivia
2088-
// and the trailing trivia of the entire string token.
2089-
SyntaxContext->addToken(CloseQuote, EmptyTrivia, EntireTrailingTrivia);
2082+
if (HasCustomDelimiter) {
2083+
SyntaxContext->addToken(CloseQuote, EmptyTrivia, EmptyTrivia);
2084+
2085+
Token CloseDelimiter(tok::raw_string_delimiter, CloseDelimiterStr);
2086+
// When a custom delimiter is present it owns the trailing trivia.
2087+
SyntaxContext->addToken(CloseDelimiter, EmptyTrivia, EntireTrailingTrivia);
2088+
} else {
2089+
// Without custom delimiter the quote owns trailing trivia.
2090+
SyntaxContext->addToken(CloseQuote, EmptyTrivia, EntireTrailingTrivia);
2091+
}
20902092

20912093
if (AppendingExpr->getBody()->getNumElements() == 1) {
20922094
Status.setIsParseError();

0 commit comments

Comments
 (0)