mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
344 lines
14 KiB
C++
344 lines
14 KiB
C++
//===--- SourceLocTests.cpp - Tests for source locations of AST nodes -----===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "TestContext.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/Pattern.h"
|
|
#include "swift/AST/Stmt.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace swift;
|
|
using namespace swift::unittest;
|
|
|
|
namespace swift {
|
|
void PrintTo(SourceLoc loc, std::ostream *os) {
|
|
*os << loc.getOpaquePointerValue();
|
|
if (loc.isValid())
|
|
*os << " '" << *static_cast<const char *>(loc.getOpaquePointerValue())
|
|
<< "'";
|
|
}
|
|
|
|
void PrintTo(SourceRange range, std::ostream *os) {
|
|
PrintTo(range.Start, os);
|
|
*os << " - ";
|
|
PrintTo(range.End, os);
|
|
}
|
|
} // end namespace swift
|
|
|
|
TEST(SourceLoc, AssignExpr) {
|
|
TestContext C;
|
|
|
|
// 0123456789012
|
|
auto bufferID = C.Ctx.SourceMgr.addMemBufferCopy("aa.bb = cc.dd");
|
|
SourceLoc start = C.Ctx.SourceMgr.getLocForBufferStart(bufferID);
|
|
|
|
auto destBase = new (C.Ctx) UnresolvedDeclRefExpr(
|
|
DeclNameRef_(C.Ctx.getIdentifier("aa")),
|
|
DeclRefKind::Ordinary,
|
|
DeclNameLoc(start));
|
|
auto dest = new (C.Ctx) UnresolvedDotExpr(
|
|
destBase,
|
|
start.getAdvancedLoc(2),
|
|
DeclNameRef_(C.Ctx.getIdentifier("bb")),
|
|
DeclNameLoc(start.getAdvancedLoc(3)),
|
|
/*implicit*/false);
|
|
auto destImplicit = new (C.Ctx) UnresolvedDotExpr(
|
|
destBase,
|
|
start.getAdvancedLoc(2),
|
|
DeclNameRef_(C.Ctx.getIdentifier("bb")),
|
|
DeclNameLoc(start.getAdvancedLoc(3)),
|
|
/*implicit*/true);
|
|
|
|
auto sourceBase = new (C.Ctx) UnresolvedDeclRefExpr(
|
|
DeclNameRef_(C.Ctx.getIdentifier("cc")),
|
|
DeclRefKind::Ordinary,
|
|
DeclNameLoc(start.getAdvancedLoc(8)));
|
|
auto source = new (C.Ctx) UnresolvedDotExpr(
|
|
sourceBase,
|
|
start.getAdvancedLoc(10),
|
|
DeclNameRef_(C.Ctx.getIdentifier("dd")),
|
|
DeclNameLoc(start.getAdvancedLoc(11)),
|
|
/*implicit*/false);
|
|
auto sourceImplicit = new (C.Ctx) UnresolvedDotExpr(
|
|
sourceBase,
|
|
start.getAdvancedLoc(10),
|
|
DeclNameRef_(C.Ctx.getIdentifier("dd")),
|
|
DeclNameLoc(start.getAdvancedLoc(11)),
|
|
/*implicit*/true);
|
|
|
|
|
|
auto invalid = new (C.Ctx) UnresolvedDeclRefExpr(
|
|
DeclNameRef_(C.Ctx.getIdentifier("invalid")),
|
|
DeclRefKind::Ordinary,
|
|
DeclNameLoc());
|
|
|
|
auto complete = new (C.Ctx) AssignExpr(dest, start.getAdvancedLoc(6), source,
|
|
/*implicit*/false);
|
|
EXPECT_EQ(start, complete->getStartLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(6), complete->getEqualLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(6), complete->getLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(11), complete->getEndLoc());
|
|
EXPECT_EQ(SourceRange(start, start.getAdvancedLoc(11)),
|
|
complete->getSourceRange());
|
|
|
|
// Implicit dest should not change the source range.
|
|
auto completeImplDest = new (C.Ctx) AssignExpr( destImplicit
|
|
, start.getAdvancedLoc(6)
|
|
, source, /*implicit*/false);
|
|
EXPECT_EQ(start, completeImplDest->getStartLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(6), completeImplDest->getEqualLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(6), completeImplDest->getLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(11), completeImplDest->getEndLoc());
|
|
EXPECT_EQ(SourceRange(start, start.getAdvancedLoc(11)),
|
|
completeImplDest->getSourceRange());
|
|
|
|
// Implicit source should not change the source range.
|
|
auto completeImplSrc = new (C.Ctx) AssignExpr( dest, start.getAdvancedLoc(6)
|
|
, sourceImplicit
|
|
, /*implicit*/false);
|
|
EXPECT_EQ(start, completeImplSrc->getStartLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(6), completeImplSrc->getEqualLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(6), completeImplSrc->getLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(11), completeImplSrc->getEndLoc());
|
|
EXPECT_EQ(SourceRange(start, start.getAdvancedLoc(11)),
|
|
completeImplSrc->getSourceRange());
|
|
|
|
auto invalidSource = new (C.Ctx) AssignExpr(dest, SourceLoc(), invalid,
|
|
/*implicit*/false);
|
|
EXPECT_EQ(start, invalidSource->getStartLoc());
|
|
EXPECT_EQ(SourceLoc(), invalidSource->getEqualLoc());
|
|
EXPECT_EQ(start, invalidSource->getLoc()); // If the equal loc is invalid, but start is valid, point at the start
|
|
EXPECT_EQ(start.getAdvancedLoc(3), invalidSource->getEndLoc());
|
|
EXPECT_EQ(SourceRange(start, start.getAdvancedLoc(3)),
|
|
invalidSource->getSourceRange());
|
|
|
|
auto invalidDest = new (C.Ctx) AssignExpr(invalid, SourceLoc(), source,
|
|
/*implicit*/false);
|
|
EXPECT_EQ(start.getAdvancedLoc(8), invalidDest->getStartLoc());
|
|
EXPECT_EQ(SourceLoc(), invalidDest->getEqualLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(8), invalidDest->getLoc()); // If the equal loc is invalid, but start is valid, point at the start
|
|
EXPECT_EQ(start.getAdvancedLoc(11), invalidDest->getEndLoc());
|
|
EXPECT_EQ(SourceRange(start.getAdvancedLoc(8), start.getAdvancedLoc(11)),
|
|
invalidDest->getSourceRange());
|
|
|
|
auto invalidAll = new (C.Ctx) AssignExpr(invalid, SourceLoc(), invalid,
|
|
/*implicit*/false);
|
|
EXPECT_EQ(SourceLoc(), invalidAll->getStartLoc());
|
|
EXPECT_EQ(SourceLoc(), invalidAll->getEqualLoc());
|
|
EXPECT_EQ(SourceLoc(), invalidAll->getLoc());
|
|
EXPECT_EQ(SourceLoc(), invalidAll->getEndLoc());
|
|
EXPECT_EQ(SourceRange(), invalidAll->getSourceRange());
|
|
}
|
|
|
|
TEST(SourceLoc, StmtConditionElement) {
|
|
TestContext C;
|
|
|
|
// In a pattern binding statement condition element the SourceRange is only
|
|
// valid iff the Initializer has a valid end loc and either:
|
|
// a. the IntroducerLoc has a valid start loc
|
|
// b. if the IntroducerLoc is invalid, the pattern has a valid start loc
|
|
// If neither of these hold, source range must be invalid.
|
|
|
|
auto bufferID = C.Ctx.SourceMgr // 0123456789012345678901234567890
|
|
.addMemBufferCopy("if let x = Optional.some(1) { }");
|
|
SourceLoc start = C.Ctx.SourceMgr.getLocForBufferStart(bufferID);
|
|
|
|
auto vardecl = new (C.Ctx) VarDecl(/*IsStatic*/false,
|
|
VarDecl::Introducer::Let,
|
|
/*IsCaptureList*/false,
|
|
start.getAdvancedLoc(7)
|
|
, C.Ctx.getIdentifier("x")
|
|
, nullptr);
|
|
auto pattern = new (C.Ctx) NamedPattern(vardecl);
|
|
auto init = new (C.Ctx) IntegerLiteralExpr( "1", start.getAdvancedLoc(25)
|
|
, false);
|
|
|
|
// Case a, when the IntroducerLoc is valid.
|
|
auto introducer = StmtConditionElement( start.getAdvancedLoc(3)
|
|
, pattern, init);
|
|
|
|
EXPECT_EQ(start.getAdvancedLoc(3), introducer.getStartLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(25), introducer.getEndLoc());
|
|
EXPECT_EQ( SourceRange(start.getAdvancedLoc(3), start.getAdvancedLoc(25))
|
|
, introducer.getSourceRange());
|
|
|
|
// Case b, when the IntroducerLoc is invalid, but the pattern has a valid loc.
|
|
auto patternStmtCond = StmtConditionElement(SourceLoc(), pattern, init);
|
|
|
|
EXPECT_EQ(start.getAdvancedLoc(7), patternStmtCond.getStartLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(25), patternStmtCond.getEndLoc());
|
|
EXPECT_EQ( SourceRange(start.getAdvancedLoc(7), start.getAdvancedLoc(25))
|
|
, patternStmtCond.getSourceRange());
|
|
|
|
// If the IntroducerLoc is valid but the stmt cond init is invalid.
|
|
auto invalidInit = new (C.Ctx) IntegerLiteralExpr("1", SourceLoc(), false);
|
|
auto introducerStmtInvalid = StmtConditionElement( start.getAdvancedLoc(3)
|
|
, pattern, invalidInit);
|
|
|
|
EXPECT_EQ(SourceLoc(), introducerStmtInvalid.getStartLoc());
|
|
EXPECT_EQ(SourceLoc(), introducerStmtInvalid.getEndLoc());
|
|
EXPECT_EQ(SourceRange(), introducerStmtInvalid.getSourceRange());
|
|
|
|
// If the IntroducerLoc is invalid, the pattern is valid, but the stmt cond
|
|
// init is invalid.
|
|
auto patternStmtInvalid = StmtConditionElement( SourceLoc(), pattern
|
|
, invalidInit);
|
|
|
|
EXPECT_EQ(SourceLoc(), patternStmtInvalid.getStartLoc());
|
|
EXPECT_EQ(SourceLoc(), patternStmtInvalid.getEndLoc());
|
|
EXPECT_EQ(SourceRange(), patternStmtInvalid.getSourceRange());
|
|
}
|
|
|
|
TEST(SourceLoc, TupleExpr) {
|
|
TestContext C;
|
|
|
|
// In a TupleExpr, if the parens are both invalid, then you can only have a
|
|
// valid range if there exists at least one expr with a valid source range.
|
|
// The tuple's source range will be the upper bound of the inner source
|
|
// ranges.
|
|
// Source ranges also have the property:
|
|
// Start.isValid() == End.isValid()
|
|
// For example, given the buffer "one", of the form:
|
|
// (tuple_expr
|
|
// (declref_expr range=[test.swift:1:0 - line:1:2] ...)
|
|
// (declref_expr range=invalid ...))
|
|
// the range of this TupleExpr is 1:0 - 1:2.
|
|
// v invalid v invalid
|
|
// ( one, two )
|
|
// valid ^ invalid ^
|
|
// COL: xxxxxx012xxxxxxxxxxxxxxxxx
|
|
// and the SourceRange of 'one' is 1:0 - 1:2.
|
|
|
|
// 01234567
|
|
auto bufferID = C.Ctx.SourceMgr.addMemBufferCopy("one four");
|
|
SourceLoc start = C.Ctx.SourceMgr.getLocForBufferStart(bufferID);
|
|
|
|
auto one = new (C.Ctx) UnresolvedDeclRefExpr(
|
|
DeclNameRef_(C.Ctx.getIdentifier("one")),
|
|
DeclRefKind::Ordinary,
|
|
DeclNameLoc(start));
|
|
|
|
auto two = new (C.Ctx) UnresolvedDeclRefExpr(
|
|
DeclNameRef_(C.Ctx.getIdentifier("two")),
|
|
DeclRefKind::Ordinary,
|
|
DeclNameLoc());
|
|
|
|
auto three = new (C.Ctx) UnresolvedDeclRefExpr(
|
|
DeclNameRef_(C.Ctx.getIdentifier("three")),
|
|
DeclRefKind::Ordinary,
|
|
DeclNameLoc());
|
|
|
|
auto four = new (C.Ctx) UnresolvedDeclRefExpr(
|
|
DeclNameRef_(C.Ctx.getIdentifier("four")),
|
|
DeclRefKind::Ordinary,
|
|
DeclNameLoc(start.getAdvancedLoc(4)));
|
|
|
|
EXPECT_EQ(start, one->getStartLoc());
|
|
EXPECT_EQ(SourceLoc(), two->getStartLoc());
|
|
|
|
// a tuple with only invalid elements
|
|
SmallVector<Expr *, 2> subExprsInvalid({ two, three });
|
|
SmallVector<Identifier, 2> subExprNamesInvalid(2, Identifier());
|
|
auto allInvalid = TupleExpr::createImplicit(C.Ctx, subExprsInvalid, subExprNamesInvalid);
|
|
|
|
EXPECT_EQ(SourceLoc(), allInvalid->getStartLoc());
|
|
EXPECT_EQ(SourceLoc(), allInvalid->getEndLoc());
|
|
EXPECT_EQ(SourceRange(), allInvalid->getSourceRange());
|
|
|
|
// the tuple from the example
|
|
SmallVector<Expr *, 2> subExprsRight({ one, two });
|
|
SmallVector<Identifier, 2> subExprNamesRight(2, Identifier());
|
|
auto rightInvalidTuple = TupleExpr::createImplicit(C.Ctx, subExprsRight, subExprNamesRight);
|
|
|
|
EXPECT_EQ(start, rightInvalidTuple->getStartLoc());
|
|
EXPECT_EQ(start, rightInvalidTuple->getEndLoc());
|
|
EXPECT_EQ(SourceRange(start, start), rightInvalidTuple->getSourceRange());
|
|
|
|
SmallVector<Expr *, 2> subExprsLeft({ two, one });
|
|
SmallVector<Identifier, 2> subExprNamesLeft(2, Identifier());
|
|
auto leftInvalidTuple = TupleExpr::createImplicit(C.Ctx, subExprsLeft, subExprNamesLeft);
|
|
|
|
EXPECT_EQ(start, leftInvalidTuple->getStartLoc());
|
|
EXPECT_EQ(start, leftInvalidTuple->getEndLoc());
|
|
EXPECT_EQ(SourceRange(start, start), leftInvalidTuple->getSourceRange());
|
|
|
|
// Some TupleExprs are triples. If only the middle expr has a valid SourceLoc
|
|
// then the TupleExpr's SourceLoc should point at that.
|
|
SmallVector<Expr *, 3> subExprsTriple({ two, one, two });
|
|
SmallVector<Identifier, 3> subExprNamesTriple(3, Identifier());
|
|
auto tripleValidMid = TupleExpr::createImplicit(C.Ctx, subExprsTriple, subExprNamesTriple);
|
|
EXPECT_EQ(start, tripleValidMid->getStartLoc());
|
|
EXPECT_EQ(start, tripleValidMid->getEndLoc());
|
|
EXPECT_EQ(SourceRange(start, start), tripleValidMid->getSourceRange());
|
|
|
|
// Some TupleExprs are quadruples. Quadruples should point at the range from
|
|
// the first to the last valid exprs.
|
|
SmallVector<Expr *, 4> subExprsQuad({ one, two, four, three });
|
|
SmallVector<Identifier, 4> subExprNamesQuad(4, Identifier());
|
|
auto quadValidMids = TupleExpr::createImplicit(C.Ctx, subExprsQuad, subExprNamesQuad);
|
|
EXPECT_EQ(start, quadValidMids->getStartLoc());
|
|
EXPECT_EQ(start.getAdvancedLoc(4), quadValidMids->getEndLoc());
|
|
EXPECT_EQ(SourceRange(start, start.getAdvancedLoc(4)), quadValidMids->getSourceRange());
|
|
|
|
}
|
|
|
|
TEST(SourceLoc, CharSourceRangeOverlaps) {
|
|
TestContext C;
|
|
auto bufferID = C.Ctx.SourceMgr.addMemBufferCopy("func foo()");
|
|
SourceLoc start = C.Ctx.SourceMgr.getLocForBufferStart(bufferID);
|
|
|
|
// Create exclusive ranges for each of the tokens.
|
|
|
|
CharSourceRange funcRange(start, 4);
|
|
CharSourceRange fooRange(funcRange.getEnd().getAdvancedLoc(1), 3);
|
|
CharSourceRange lParenRange(fooRange.getEnd(), 1);
|
|
CharSourceRange rParenRange(lParenRange.getEnd(), 1);
|
|
CharSourceRange fullRange = C.Ctx.SourceMgr.getRangeForBuffer(bufferID);
|
|
CharSourceRange zeroRange = CharSourceRange(start, 0);
|
|
|
|
// None of the ranges should overlap, and their results should be symmetric.
|
|
|
|
EXPECT_FALSE(funcRange.overlaps(fooRange));
|
|
EXPECT_FALSE(fooRange.overlaps(funcRange));
|
|
|
|
EXPECT_FALSE(fooRange.overlaps(lParenRange));
|
|
EXPECT_FALSE(lParenRange.overlaps(fooRange));
|
|
|
|
EXPECT_FALSE(lParenRange.overlaps(rParenRange));
|
|
EXPECT_FALSE(rParenRange.overlaps(lParenRange));
|
|
|
|
// The 'full range' overlaps all the other tokens and those results should
|
|
// be symmetric.
|
|
|
|
EXPECT_TRUE(fullRange.overlaps(funcRange));
|
|
EXPECT_TRUE(fullRange.overlaps(fooRange));
|
|
EXPECT_TRUE(fullRange.overlaps(lParenRange));
|
|
EXPECT_TRUE(fullRange.overlaps(rParenRange));
|
|
|
|
EXPECT_TRUE(funcRange.overlaps(fullRange));
|
|
EXPECT_TRUE(fooRange.overlaps(fullRange));
|
|
EXPECT_TRUE(lParenRange.overlaps(fullRange));
|
|
EXPECT_TRUE(rParenRange.overlaps(fullRange));
|
|
|
|
// The zero range should not overlap any, and that result should be symmetric.
|
|
|
|
EXPECT_FALSE(zeroRange.overlaps(funcRange));
|
|
EXPECT_FALSE(zeroRange.overlaps(fooRange));
|
|
EXPECT_FALSE(zeroRange.overlaps(lParenRange));
|
|
EXPECT_FALSE(zeroRange.overlaps(rParenRange));
|
|
|
|
EXPECT_FALSE(funcRange.overlaps(zeroRange));
|
|
EXPECT_FALSE(fooRange.overlaps(zeroRange));
|
|
EXPECT_FALSE(lParenRange.overlaps(zeroRange));
|
|
EXPECT_FALSE(rParenRange.overlaps(zeroRange));
|
|
}
|