|  | // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "tools/gn/parse_tree.h" | 
|  |  | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "test/test.h" | 
|  | #include "tools/gn/input_file.h" | 
|  | #include "tools/gn/scope.h" | 
|  | #include "tools/gn/test_with_scope.h" | 
|  |  | 
|  | TEST(ParseTree, Accessor) { | 
|  | TestWithScope setup; | 
|  |  | 
|  | // Make a pretend parse node with proper tracking that we can blame for the | 
|  | // given value. | 
|  | InputFile input_file(SourceFile("//foo")); | 
|  | Token base_token(Location(&input_file, 1, 1, 1), Token::IDENTIFIER, "a"); | 
|  | Token member_token(Location(&input_file, 1, 1, 1), Token::IDENTIFIER, "b"); | 
|  |  | 
|  | AccessorNode accessor; | 
|  | accessor.set_base(base_token); | 
|  |  | 
|  | std::unique_ptr<IdentifierNode> member_identifier = | 
|  | std::make_unique<IdentifierNode>(member_token); | 
|  | accessor.set_member(std::move(member_identifier)); | 
|  |  | 
|  | // The access should fail because a is not defined. | 
|  | Err err; | 
|  | Value result = accessor.Execute(setup.scope(), &err); | 
|  | EXPECT_TRUE(err.has_error()); | 
|  | EXPECT_EQ(Value::NONE, result.type()); | 
|  |  | 
|  | // Define a as a Scope. It should still fail because b isn't defined. | 
|  | err = Err(); | 
|  | setup.scope()->SetValue( | 
|  | "a", Value(nullptr, std::make_unique<Scope>(setup.scope())), nullptr); | 
|  | result = accessor.Execute(setup.scope(), &err); | 
|  | EXPECT_TRUE(err.has_error()); | 
|  | EXPECT_EQ(Value::NONE, result.type()); | 
|  |  | 
|  | // Define b, accessor should succeed now. | 
|  | const int64_t kBValue = 42; | 
|  | err = Err(); | 
|  | setup.scope() | 
|  | ->GetMutableValue("a", Scope::SEARCH_NESTED, false) | 
|  | ->scope_value() | 
|  | ->SetValue("b", Value(nullptr, kBValue), nullptr); | 
|  | result = accessor.Execute(setup.scope(), &err); | 
|  | EXPECT_FALSE(err.has_error()); | 
|  | ASSERT_EQ(Value::INTEGER, result.type()); | 
|  | EXPECT_EQ(kBValue, result.int_value()); | 
|  | } | 
|  |  | 
|  | TEST(ParseTree, BlockUnusedVars) { | 
|  | TestWithScope setup; | 
|  |  | 
|  | // Printing both values should be OK. | 
|  | // | 
|  | // The crazy template definition here is a way to execute a block without | 
|  | // defining a target. Templates require that both the target_name and the | 
|  | // invoker be used, which is what the assertion statement inside the template | 
|  | // does. | 
|  | TestParseInput input_all_used( | 
|  | "template(\"foo\") { assert(target_name != 0 && invoker != 0) }\n" | 
|  | "foo(\"a\") {\n" | 
|  | "  a = 12\n" | 
|  | "  b = 13\n" | 
|  | "  print(\"$a $b\")\n" | 
|  | "}"); | 
|  | EXPECT_FALSE(input_all_used.has_error()); | 
|  |  | 
|  | Err err; | 
|  | input_all_used.parsed()->Execute(setup.scope(), &err); | 
|  | EXPECT_FALSE(err.has_error()); | 
|  |  | 
|  | // Skipping one should throw an unused var error. | 
|  | TestParseInput input_unused( | 
|  | "foo(\"a\") {\n" | 
|  | "  a = 12\n" | 
|  | "  b = 13\n" | 
|  | "  print(\"$a\")\n" | 
|  | "}"); | 
|  | EXPECT_FALSE(input_unused.has_error()); | 
|  |  | 
|  | input_unused.parsed()->Execute(setup.scope(), &err); | 
|  | EXPECT_TRUE(err.has_error()); | 
|  |  | 
|  | // Also verify that the unused variable has the correct origin set. The | 
|  | // origin will point to the value assigned to the variable (in this case, the | 
|  | // "13" assigned to "b". | 
|  | EXPECT_EQ(3, err.location().line_number()); | 
|  | EXPECT_EQ(7, err.location().column_number()); | 
|  | } | 
|  |  | 
|  | TEST(ParseTree, OriginForDereference) { | 
|  | TestWithScope setup; | 
|  | TestParseInput input( | 
|  | "a = 6\n" | 
|  | "get_target_outputs(a)"); | 
|  | EXPECT_FALSE(input.has_error()); | 
|  |  | 
|  | Err err; | 
|  | input.parsed()->Execute(setup.scope(), &err); | 
|  | EXPECT_TRUE(err.has_error()); | 
|  |  | 
|  | // The origin for the "not a string" error message should be where the value | 
|  | // was dereferenced (the "a" on the second line). | 
|  | EXPECT_EQ(2, err.location().line_number()); | 
|  | EXPECT_EQ(20, err.location().column_number()); | 
|  | } | 
|  |  | 
|  | TEST(ParseTree, SortRangeExtraction) { | 
|  | TestWithScope setup; | 
|  |  | 
|  | // Ranges are [begin, end). | 
|  |  | 
|  | { | 
|  | TestParseInput input( | 
|  | "sources = [\n" | 
|  | "  \"a\",\n" | 
|  | "  \"b\",\n" | 
|  | "  \n" | 
|  | "  #\n" | 
|  | "  # Block\n" | 
|  | "  #\n" | 
|  | "  \n" | 
|  | "  \"c\"," | 
|  | "  \"d\"," | 
|  | "]\n"); | 
|  | EXPECT_FALSE(input.has_error()); | 
|  | ASSERT_TRUE(input.parsed()->AsBlock()); | 
|  | ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp()); | 
|  | const BinaryOpNode* binop = | 
|  | input.parsed()->AsBlock()->statements()[0]->AsBinaryOp(); | 
|  | ASSERT_TRUE(binop->right()->AsList()); | 
|  | const ListNode* list = binop->right()->AsList(); | 
|  | EXPECT_EQ(5u, list->contents().size()); | 
|  | auto ranges = list->GetSortRanges(); | 
|  | ASSERT_EQ(2u, ranges.size()); | 
|  | EXPECT_EQ(0u, ranges[0].begin); | 
|  | EXPECT_EQ(2u, ranges[0].end); | 
|  | EXPECT_EQ(3u, ranges[1].begin); | 
|  | EXPECT_EQ(5u, ranges[1].end); | 
|  | } | 
|  |  | 
|  | { | 
|  | TestParseInput input( | 
|  | "sources = [\n" | 
|  | "  \"a\",\n" | 
|  | "  \"b\",\n" | 
|  | "  \n" | 
|  | "  # Attached comment.\n" | 
|  | "  \"c\"," | 
|  | "  \"d\"," | 
|  | "]\n"); | 
|  | EXPECT_FALSE(input.has_error()); | 
|  | ASSERT_TRUE(input.parsed()->AsBlock()); | 
|  | ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp()); | 
|  | const BinaryOpNode* binop = | 
|  | input.parsed()->AsBlock()->statements()[0]->AsBinaryOp(); | 
|  | ASSERT_TRUE(binop->right()->AsList()); | 
|  | const ListNode* list = binop->right()->AsList(); | 
|  | EXPECT_EQ(4u, list->contents().size()); | 
|  | auto ranges = list->GetSortRanges(); | 
|  | ASSERT_EQ(2u, ranges.size()); | 
|  | EXPECT_EQ(0u, ranges[0].begin); | 
|  | EXPECT_EQ(2u, ranges[0].end); | 
|  | EXPECT_EQ(2u, ranges[1].begin); | 
|  | EXPECT_EQ(4u, ranges[1].end); | 
|  | } | 
|  |  | 
|  | { | 
|  | TestParseInput input( | 
|  | "sources = [\n" | 
|  | "  # At end of list.\n" | 
|  | "  \"zzzzzzzzzzz.cc\"," | 
|  | "]\n"); | 
|  | EXPECT_FALSE(input.has_error()); | 
|  | ASSERT_TRUE(input.parsed()->AsBlock()); | 
|  | ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp()); | 
|  | const BinaryOpNode* binop = | 
|  | input.parsed()->AsBlock()->statements()[0]->AsBinaryOp(); | 
|  | ASSERT_TRUE(binop->right()->AsList()); | 
|  | const ListNode* list = binop->right()->AsList(); | 
|  | EXPECT_EQ(1u, list->contents().size()); | 
|  | auto ranges = list->GetSortRanges(); | 
|  | ASSERT_EQ(1u, ranges.size()); | 
|  | EXPECT_EQ(0u, ranges[0].begin); | 
|  | EXPECT_EQ(1u, ranges[0].end); | 
|  | } | 
|  |  | 
|  | { | 
|  | TestParseInput input( | 
|  | "sources = [\n" | 
|  | "  # Block at start.\n" | 
|  | "  \n" | 
|  | "  \"z.cc\"," | 
|  | "  \"y.cc\"," | 
|  | "]\n"); | 
|  | EXPECT_FALSE(input.has_error()); | 
|  | ASSERT_TRUE(input.parsed()->AsBlock()); | 
|  | ASSERT_TRUE(input.parsed()->AsBlock()->statements()[0]->AsBinaryOp()); | 
|  | const BinaryOpNode* binop = | 
|  | input.parsed()->AsBlock()->statements()[0]->AsBinaryOp(); | 
|  | ASSERT_TRUE(binop->right()->AsList()); | 
|  | const ListNode* list = binop->right()->AsList(); | 
|  | EXPECT_EQ(3u, list->contents().size()); | 
|  | auto ranges = list->GetSortRanges(); | 
|  | ASSERT_EQ(1u, ranges.size()); | 
|  | EXPECT_EQ(1u, ranges[0].begin); | 
|  | EXPECT_EQ(3u, ranges[0].end); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(ParseTree, Integers) { | 
|  | static const char* const kGood[] = { | 
|  | "0", | 
|  | "10", | 
|  | "-54321", | 
|  | "9223372036854775807",   // INT64_MAX | 
|  | "-9223372036854775808",  // INT64_MIN | 
|  | }; | 
|  | for (auto* s : kGood) { | 
|  | TestParseInput input(std::string("x = ") + s); | 
|  | EXPECT_FALSE(input.has_error()); | 
|  |  | 
|  | TestWithScope setup; | 
|  | Err err; | 
|  | input.parsed()->Execute(setup.scope(), &err); | 
|  | EXPECT_FALSE(err.has_error()); | 
|  | } | 
|  |  | 
|  | static const char* const kBad[] = { | 
|  | "-0", | 
|  | "010", | 
|  | "-010", | 
|  | "9223372036854775808",   // INT64_MAX + 1 | 
|  | "-9223372036854775809",  // INT64_MIN - 1 | 
|  | }; | 
|  | for (auto* s : kBad) { | 
|  | TestParseInput input(std::string("x = ") + s); | 
|  | EXPECT_FALSE(input.has_error()); | 
|  |  | 
|  | TestWithScope setup; | 
|  | Err err; | 
|  | input.parsed()->Execute(setup.scope(), &err); | 
|  | EXPECT_TRUE(err.has_error()); | 
|  | } | 
|  | } |