Files
swift-mirror/unittests/AST/SourceLocTests.cpp
Rintaro Ishizaki d6556434cd [AST] Make 'StmtConditionElement' a single 'PointerUnion'
Previously, 'IntroducerLoc' and 'ThePattern' were only used for pattern
binidng cases. Create a new 'ConditionalPatternBindingInfo' type to
cover such cases, and make 'StmtConditionElement' a pure 'PointerUnion'
type.

This makes it clear which fields are used in which condition kind. Also,
we can expect overall size reduction of StmtCondition when the
majority of the conditions are simple boolean expressions.
2023-12-13 12:52:53 -08:00

346 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,
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(ConditionalPatternBindingInfo::create(
C.Ctx, 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(
ConditionalPatternBindingInfo::create(C.Ctx, 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(ConditionalPatternBindingInfo::create(
C.Ctx, 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(ConditionalPatternBindingInfo::create(
C.Ctx, 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));
}