void ValidateExecutableVisitor::visit()

in src/Validation.cpp [483:589]


void ValidateExecutableVisitor::visit(const peg::ast_node& root)
{
	// Visit all of the fragment definitions and check for duplicates.
	peg::for_each_child<peg::fragment_definition>(root,
		[this](const peg::ast_node& fragmentDefinition) {
			const auto& fragmentName = fragmentDefinition.children.front();
			const auto inserted =
				_fragmentDefinitions.emplace(fragmentName->string_view(), fragmentDefinition);

			if (!inserted.second)
			{
				// https://spec.graphql.org/October2021/#sec-Fragment-Name-Uniqueness
				auto position = fragmentDefinition.begin();
				std::ostringstream error;

				error << "Duplicate fragment name: " << inserted.first->first;

				_errors.push_back({ error.str(), { position.line, position.column } });
			}
		});

	// Visit all of the operation definitions and check for duplicates.
	peg::for_each_child<peg::operation_definition>(root,
		[this](const peg::ast_node& operationDefinition) {
			std::string_view operationName;

			peg::on_first_child<peg::operation_name>(operationDefinition,
				[&operationName](const peg::ast_node& child) {
					operationName = child.string_view();
				});

			const auto inserted = _operationDefinitions.emplace(operationName, operationDefinition);

			if (!inserted.second)
			{
				// https://spec.graphql.org/October2021/#sec-Operation-Name-Uniqueness
				auto position = operationDefinition.begin();
				std::ostringstream error;

				error << "Duplicate operation name: " << inserted.first->first;

				_errors.push_back({ error.str(), { position.line, position.column } });
			}
		});

	// Check for lone anonymous operations.
	if (_operationDefinitions.size() > 1)
	{
		auto itr = std::find_if(_operationDefinitions.begin(),
			_operationDefinitions.end(),
			[](const auto& entry) noexcept {
				return entry.first.empty();
			});

		if (itr != _operationDefinitions.end())
		{
			// https://spec.graphql.org/October2021/#sec-Lone-Anonymous-Operation
			auto position = itr->second.get().begin();

			_errors.push_back(
				{ "Anonymous operation not alone", { position.line, position.column } });
		}
	}

	// Visit the executable definitions recursively.
	for (const auto& child : root.children)
	{
		if (child->is_type<peg::fragment_definition>())
		{
			visitFragmentDefinition(*child);
		}
		else if (child->is_type<peg::operation_definition>())
		{
			visitOperationDefinition(*child);
		}
		else
		{
			// https://spec.graphql.org/October2021/#sec-Executable-Definitions
			auto position = child->begin();

			_errors.push_back({ "Unexpected type definition", { position.line, position.column } });
		}
	}

	if (!_fragmentDefinitions.empty())
	{
		// https://spec.graphql.org/October2021/#sec-Fragments-Must-Be-Used
		auto unreferencedFragments = std::move(_fragmentDefinitions);

		for (const auto& name : _referencedFragments)
		{
			unreferencedFragments.erase(name);
		}

		std::transform(unreferencedFragments.begin(),
			unreferencedFragments.end(),
			std::back_inserter(_errors),
			[](const auto& fragmentDefinition) noexcept {
				auto position = fragmentDefinition.second.get().begin();
				std::ostringstream message;

				message << "Unused fragment definition name: " << fragmentDefinition.first;

				return schema_error { message.str(), { position.line, position.column } };
			});
	}
}