Files
swift-mirror/unittests/AST/SourceLocTests.cpp
Brian Gesiak 4108e1d9af [Sema] Mark VarDecl in capture lists
Fixes SR-2757.

Variables in capture lists are treated as 'let' constants, which can
result in misleading, incorrect diagnostics. Mark them as such in order
to produce better diagnostics, by adding an extra parameter to the
VarDecl initializer.

Alternatively, these variables could be marked as implicit, but that
results in other diagnostic problems: capture list variables that are
never used produce warnings, but these warnings aren't normally emitted for
implicit variables. Other assertions in the compiler also misfire when
these variables are treated as implicit.

Another alternative would be to walk up the AST and determine whether
the `VarDecl`, but there doesn't appear to be a way to do so.
2017-01-01 12:41:06 -05:00

292 lines
12 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 - 2016 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 << " '" << *(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(
C.Ctx.getIdentifier("aa"),
DeclRefKind::Ordinary,
DeclNameLoc(start));
auto dest = new (C.Ctx) UnresolvedDotExpr(
destBase,
start.getAdvancedLoc(2),
C.Ctx.getIdentifier("bb"),
DeclNameLoc(start.getAdvancedLoc(3)),
/*implicit*/false);
auto destImplicit = new (C.Ctx) UnresolvedDotExpr(
destBase,
start.getAdvancedLoc(2),
C.Ctx.getIdentifier("bb"),
DeclNameLoc(start.getAdvancedLoc(3)),
/*implicit*/true);
auto sourceBase = new (C.Ctx) UnresolvedDeclRefExpr(
C.Ctx.getIdentifier("cc"),
DeclRefKind::Ordinary,
DeclNameLoc(start.getAdvancedLoc(8)));
auto source = new (C.Ctx) UnresolvedDotExpr(
sourceBase,
start.getAdvancedLoc(10),
C.Ctx.getIdentifier("dd"),
DeclNameLoc(start.getAdvancedLoc(11)),
/*implicit*/false);
auto sourceImplicit = new (C.Ctx) UnresolvedDotExpr(
sourceBase,
start.getAdvancedLoc(10),
C.Ctx.getIdentifier("dd"),
DeclNameLoc(start.getAdvancedLoc(11)),
/*implicit*/true);
auto invalid = new (C.Ctx) UnresolvedDeclRefExpr(
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(SourceLoc(), invalidSource->getLoc());
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(SourceLoc(), invalidDest->getLoc());
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, /*IsLet*/true,
/*IsCaptureList*/false,
start.getAdvancedLoc(7)
, C.Ctx.getIdentifier("x")
, Type()
, 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(
C.Ctx.getIdentifier("one"),
DeclRefKind::Ordinary,
DeclNameLoc(start));
auto two = new (C.Ctx) UnresolvedDeclRefExpr(
C.Ctx.getIdentifier("two"),
DeclRefKind::Ordinary,
DeclNameLoc());
auto three = new (C.Ctx) UnresolvedDeclRefExpr(
C.Ctx.getIdentifier("three"),
DeclRefKind::Ordinary,
DeclNameLoc());
auto four = new (C.Ctx) UnresolvedDeclRefExpr(
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());
}