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 } };
});
}
}