//===--- 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(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 subExprsInvalid({ two, three }); SmallVector 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 subExprsRight({ one, two }); SmallVector 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 subExprsLeft({ two, one }); SmallVector 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 subExprsTriple({ two, one, two }); SmallVector 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 subExprsQuad({ one, two, four, three }); SmallVector 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)); }