diff --git a/thrift/compiler/compiler.cc b/thrift/compiler/compiler.cc index 493b1e5a866..82d0f45bd29 100644 --- a/thrift/compiler/compiler.cc +++ b/thrift/compiler/compiler.cc @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -773,9 +772,7 @@ std::unique_ptr parse_and_mutate( // C++ codegen inserts an empty const if this is false. Other languages may // dynamically determine whether the schema const exists. if (gparams.inject_schema_const) { - mutator_context mctx; - mctx.bundle = program_bundle.get(); - schema_mutator()(ctx, mctx, *program_bundle->root_program()); + sema::add_schema(ctx, *program_bundle); } program_bundle->root_program()->set_include_prefix( @@ -793,8 +790,8 @@ parse_and_mutate_program( parsing_params params, diagnostic_params dparams) { diagnostic_results results; - sema_context ctx(sm, results, std::move(dparams)); - return {parse_ast(sm, ctx, filename, std::move(params)), results}; + diagnostics_engine diags(sm, results, std::move(dparams)); + return {parse_ast(sm, diags, filename, std::move(params)), results}; } std::unique_ptr parse_and_dump_diagnostics( @@ -803,8 +800,8 @@ std::unique_ptr parse_and_dump_diagnostics( parsing_params pparams, diagnostic_params dparams) { diagnostic_results results; - sema_context ctx(sm, results, std::move(dparams)); - auto programs = parse_ast(sm, ctx, filename, std::move(pparams)); + diagnostics_engine diags(sm, results, std::move(dparams)); + auto programs = parse_ast(sm, diags, filename, std::move(pparams)); for (const auto& diag : results.diagnostics()) { fmt::print(stderr, "{}\n", diag); } diff --git a/thrift/compiler/parse/parse_ast.cc b/thrift/compiler/parse/parse_ast.cc index 70914874659..d9bd8e92da7 100644 --- a/thrift/compiler/parse/parse_ast.cc +++ b/thrift/compiler/parse/parse_ast.cc @@ -36,7 +36,6 @@ #include #include #include -#include #include #include @@ -1095,6 +1094,9 @@ std::unique_ptr parse_ast( } catch (const parsing_terminator&) { return {}; // Return a null program bundle if parsing failed. } + if (diags.has_errors()) { + return programs; + } sema_context ctx( diags.source_mgr(), @@ -1103,21 +1105,7 @@ std::unique_ptr parse_ast( if (sparams) { ctx.sema_parameters() = *sparams; } - - // Resolve types in the root program. - if (!params.use_legacy_type_ref_resolution) { - type_ref_resolver().run(ctx, *programs); - } - std::string program_prefix = root_program.name() + "."; - for (t_placeholder_typedef& t : - root_program.scope()->placeholder_typedefs()) { - if (!t.resolve() && t.name().find(program_prefix) == 0) { - diags.error(t, "Type `{}` not defined.", t.name()); - } - } - if (!diags.has_errors()) { - sema(params.use_legacy_type_ref_resolution).run(ctx, *programs); - } + sema(params.use_legacy_type_ref_resolution).run(ctx, *programs); return programs; } diff --git a/thrift/compiler/sema/ast_mutator.h b/thrift/compiler/sema/ast_mutator.h deleted file mode 100644 index 2df167a2847..00000000000 --- a/thrift/compiler/sema/ast_mutator.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -namespace apache { -namespace thrift { -namespace compiler { - -// Mutators have mutable access to the AST. -struct mutator_context : visitor_context { - t_program_bundle* bundle; -}; - -// An AST mutator is a ast_visitor that collects diagnostics and can -// change the AST. -class ast_mutator - : public basic_ast_visitor { - using base = basic_ast_visitor; - - public: - using base::base; - - void mutate(sema_context& ctx, t_program_bundle& bundle) { - mutator_context mctx; - mctx.bundle = &bundle; - for (auto itr = bundle.programs().rbegin(); itr != bundle.programs().rend(); - ++itr) { - operator()(ctx, mctx, *itr); - } - } -}; - -/// An AST mutator that replaces placeholder_typedefs with resolved types. -struct type_ref_resolver { - void resolve_in_place(t_type_ref& ref) { - unresolved_ = !ref.resolve() || unresolved_; - } - [[nodiscard]] t_type_ref resolve(t_type_ref ref) { - resolve_in_place(ref); - return ref; - } - bool run(sema_context& ctx, t_program_bundle& bundle) { - ast_mutator mutator; - - auto resolve_const_value = [&](t_const_value& node, auto& recurse) -> void { - node.set_ttype(resolve(node.ttype())); - - if (node.kind() == t_const_value::CV_MAP) { - for (auto& map_val : node.get_map()) { - recurse(*map_val.first, recurse); - recurse(*map_val.second, recurse); - } - } else if (node.kind() == t_const_value::CV_LIST) { - for (auto& list_val : node.get_list()) { - recurse(*list_val, recurse); - } - } - }; - - mutator.add_field_visitor( - [&](sema_context&, mutator_context&, t_field& node) { - node.set_type(resolve(node.type())); - - if (auto* dflt = node.get_default_value()) { - resolve_const_value(*dflt, resolve_const_value); - } - }); - - mutator.add_typedef_visitor( - [&](sema_context&, mutator_context&, t_typedef& node) { - node.set_type(resolve(node.type())); - }); - - mutator.add_function_visitor( - [&](sema_context& ctx, mutator_context& mctx, t_function& node) { - resolve_in_place(node.return_type()); - resolve_in_place(node.interaction()); - for (auto& field : node.params().fields()) { - mutator(ctx, mctx, field); - } - }); - mutator.add_throws_visitor( - [&](sema_context& ctx, mutator_context& mctx, t_throws& node) { - for (auto& field : node.fields()) { - mutator(ctx, mctx, field); - } - }); - mutator.add_stream_visitor( - [&](sema_context&, mutator_context&, t_stream& node) { - resolve_in_place(node.elem_type()); - }); - mutator.add_sink_visitor( - [&](sema_context&, mutator_context&, t_sink& node) { - resolve_in_place(node.elem_type()); - resolve_in_place(node.final_response_type()); - }); - - mutator.add_map_visitor([&](sema_context&, mutator_context&, t_map& node) { - resolve_in_place(node.key_type()); - resolve_in_place(node.val_type()); - }); - mutator.add_set_visitor([&](sema_context&, mutator_context&, t_set& node) { - resolve_in_place(node.elem_type()); - }); - mutator.add_list_visitor( - [&](sema_context&, mutator_context&, t_list& node) { - resolve_in_place(node.elem_type()); - }); - - mutator.add_const_visitor( - [&](sema_context&, mutator_context&, t_const& node) { - resolve_in_place(node.type_ref()); - resolve_const_value(*node.value(), resolve_const_value); - }); - - mutator.mutate(ctx, bundle); - return !unresolved_; - } - - private: - bool unresolved_{false}; -}; - -} // namespace compiler -} // namespace thrift -} // namespace apache diff --git a/thrift/compiler/sema/sema.cc b/thrift/compiler/sema/sema.cc index f95e7257fa9..c744bce40e3 100644 --- a/thrift/compiler/sema/sema.cc +++ b/thrift/compiler/sema/sema.cc @@ -21,17 +21,135 @@ #include #include +#include #include #include #include #include -#include #include #include namespace apache::thrift::compiler { namespace { +// Mutators have mutable access to the AST. +struct mutator_context : visitor_context { + t_program_bundle* bundle; +}; + +// An AST mutator is an ast_visitor that collects diagnostics and +// may change the AST. +class ast_mutator + : public basic_ast_visitor { + using base = basic_ast_visitor; + + public: + using base::base; + + void mutate(sema_context& ctx, t_program_bundle& bundle) { + mutator_context mctx; + mctx.bundle = &bundle; + for (auto itr = bundle.programs().rbegin(); itr != bundle.programs().rend(); + ++itr) { + (*this)(ctx, mctx, *itr); + } + } +}; + +/// An AST mutator that replaces placeholder_typedefs with resolved types. +class type_ref_resolver { + private: + bool unresolved_ = false; + + public: + void resolve_in_place(t_type_ref& ref) { + unresolved_ = !ref.resolve() || unresolved_; + } + + [[nodiscard]] t_type_ref resolve(t_type_ref ref) { + resolve_in_place(ref); + return ref; + } + + bool run(sema_context& ctx, t_program_bundle& bundle) { + ast_mutator mutator; + + auto resolve_const_value = [&](t_const_value& node, auto& recurse) -> void { + node.set_ttype(resolve(node.ttype())); + + if (node.kind() == t_const_value::CV_MAP) { + for (auto& map_val : node.get_map()) { + recurse(*map_val.first, recurse); + recurse(*map_val.second, recurse); + } + } else if (node.kind() == t_const_value::CV_LIST) { + for (auto& list_val : node.get_list()) { + recurse(*list_val, recurse); + } + } + }; + + mutator.add_field_visitor( + [&](sema_context&, mutator_context&, t_field& node) { + node.set_type(resolve(node.type())); + + if (auto* dflt = node.get_default_value()) { + resolve_const_value(*dflt, resolve_const_value); + } + }); + + mutator.add_typedef_visitor( + [&](sema_context&, mutator_context&, t_typedef& node) { + node.set_type(resolve(node.type())); + }); + + mutator.add_function_visitor( + [&](sema_context& ctx, mutator_context& mctx, t_function& node) { + resolve_in_place(node.return_type()); + resolve_in_place(node.interaction()); + for (auto& field : node.params().fields()) { + mutator(ctx, mctx, field); + } + }); + mutator.add_throws_visitor( + [&](sema_context& ctx, mutator_context& mctx, t_throws& node) { + for (auto& field : node.fields()) { + mutator(ctx, mctx, field); + } + }); + mutator.add_stream_visitor( + [&](sema_context&, mutator_context&, t_stream& node) { + resolve_in_place(node.elem_type()); + }); + mutator.add_sink_visitor( + [&](sema_context&, mutator_context&, t_sink& node) { + resolve_in_place(node.elem_type()); + resolve_in_place(node.final_response_type()); + }); + + mutator.add_map_visitor([&](sema_context&, mutator_context&, t_map& node) { + resolve_in_place(node.key_type()); + resolve_in_place(node.val_type()); + }); + mutator.add_set_visitor([&](sema_context&, mutator_context&, t_set& node) { + resolve_in_place(node.elem_type()); + }); + mutator.add_list_visitor( + [&](sema_context&, mutator_context&, t_list& node) { + resolve_in_place(node.elem_type()); + }); + + mutator.add_const_visitor( + [&](sema_context&, mutator_context&, t_const& node) { + resolve_in_place(node.type_ref()); + resolve_const_value(*node.value(), resolve_const_value); + }); + + mutator.mutate(ctx, bundle); + return !unresolved_; + } +}; + void match_type_with_const_value( sema_context& ctx, const t_program& program, @@ -478,12 +596,6 @@ std::vector standard_mutators() { } // namespace -ast_mutator schema_mutator() { - ast_mutator mutator; - mutator.add_program_visitor(&inject_schema_const); - return mutator; -} - bool sema::resolve_all_types(sema_context& diags, t_program_bundle& bundle) { bool success = true; if (!use_legacy_type_ref_resolution_) { @@ -507,11 +619,30 @@ bool sema::resolve_all_types(sema_context& diags, t_program_bundle& bundle) { } sema::result sema::run(sema_context& ctx, t_program_bundle& bundle) { + // Resolve types in the root program. + if (!use_legacy_type_ref_resolution_) { + type_ref_resolver().run(ctx, bundle); + } + + t_program& root_program = *bundle.root_program(); + std::string program_prefix = root_program.name() + "."; + + result ret; + for (t_placeholder_typedef& t : + root_program.scope()->placeholder_typedefs()) { + if (!t.resolve() && t.name().find(program_prefix) == 0) { + ctx.error(t, "Type `{}` not defined.", t.name()); + ret.unresolved_types = true; + } + } + if (ctx.has_errors()) { + return ret; + } + for (auto& mutator : standard_mutators()) { mutator.mutate(ctx, bundle); } // We have no more mutators, so all type references **must** resolve. - result ret; ret.unresolved_types = !resolve_all_types(ctx, bundle); if (!ret.unresolved_types) { standard_validator()(ctx, *bundle.root_program()); @@ -519,4 +650,12 @@ sema::result sema::run(sema_context& ctx, t_program_bundle& bundle) { return ret; } +void sema::add_schema(sema_context& ctx, t_program_bundle& bundle) { + mutator_context mctx; + mctx.bundle = &bundle; + ast_mutator mutator; + mutator.add_program_visitor(&inject_schema_const); + mutator(ctx, mctx, *bundle.root_program()); +} + } // namespace apache::thrift::compiler diff --git a/thrift/compiler/sema/sema.h b/thrift/compiler/sema/sema.h index 6f37a6f38ec..0fef2aada39 100644 --- a/thrift/compiler/sema/sema.h +++ b/thrift/compiler/sema/sema.h @@ -16,16 +16,12 @@ #pragma once +#include + namespace apache::thrift::compiler { -class ast_mutator; -class sema_context; class t_program_bundle; -// Extra mutator for schema support. It is separate from the rest of sema -// because it is run optionally. -ast_mutator schema_mutator(); - // Thrift semantic analyzer consisting of a sequence of mutation and validation // stages. struct sema { @@ -45,6 +41,9 @@ struct sema { }; result run(sema_context& ctx, t_program_bundle& bundle); + + // Adds schema to the to the root program. + static void add_schema(sema_context& ctx, t_program_bundle& bundle); }; } // namespace apache::thrift::compiler