From c554a5997061e7cb1f89cb122c47fdedc8ae99cd Mon Sep 17 00:00:00 2001 From: Viacheslav Koryagin Date: Thu, 23 Jul 2026 15:17:49 +0300 Subject: [PATCH] THRIFT-6108: implemented exception documentation generation Client: cpp,haxe,java,javame,kotlin,netstd,py --- .../src/thrift/generate/t_cpp_generator.cc | 6 +++ .../src/thrift/generate/t_haxe_generator.cc | 11 ++++++ .../src/thrift/generate/t_javame_generator.cc | 17 +------- .../src/thrift/generate/t_kotlin_generator.cc | 12 +++++- .../src/thrift/generate/t_netstd_generator.cc | 15 +++++++ .../cpp/src/thrift/generate/t_oop_generator.h | 24 ++++++++++++ .../cpp/src/thrift/generate/t_py_generator.cc | 39 +++++++++++++++---- test/ThriftTest.thrift | 6 ++- 8 files changed, 105 insertions(+), 25 deletions(-) diff --git a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc index f468ea88d58..a90f8ee5bee 100644 --- a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc @@ -120,6 +120,7 @@ class t_cpp_generator : public t_oop_generator { void init_generator() override; void close_generator() override; std::string display_name() const override; + std::string get_namespace(t_type* type) override; void generate_consts(std::vector consts) override; @@ -455,6 +456,11 @@ class t_cpp_generator : public t_oop_generator { friend class ProcessorGenerator; }; +std::string t_cpp_generator::get_namespace(t_type *type) { + std::string namespace_str = type->get_program()->get_namespace("cpp"); + return replace_all(namespace_str, ".", "::") + "::"; +} + /** * Prepares for file generation by opening up the necessary file output * streams. diff --git a/compiler/cpp/src/thrift/generate/t_haxe_generator.cc b/compiler/cpp/src/thrift/generate/t_haxe_generator.cc index a05736d2c21..b8dfc64c09a 100644 --- a/compiler/cpp/src/thrift/generate/t_haxe_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_haxe_generator.cc @@ -3148,6 +3148,17 @@ void t_haxe_generator::generate_haxe_doc(ostream& out, t_function* tfunction) { ss << " " << p->get_doc(); } } + + const vector& exceptions = tfunction->get_xceptions()->get_members(); + vector::const_iterator e_iter; + for (e_iter = exceptions.begin(); e_iter != exceptions.end(); ++e_iter) { + t_field* e = *e_iter; + ss << "\n@throws " << type_name(e->get_type()) << " " << e->get_name(); + if (e->has_doc()) { + ss << " " << e->get_doc(); + } + } + generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n"); } } diff --git a/compiler/cpp/src/thrift/generate/t_javame_generator.cc b/compiler/cpp/src/thrift/generate/t_javame_generator.cc index 9e7b459cd8d..8dd455dab79 100644 --- a/compiler/cpp/src/thrift/generate/t_javame_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_javame_generator.cc @@ -188,7 +188,7 @@ class t_javame_generator : public t_oop_generator { void generate_java_doc(std::ostream& out, t_doc* tdoc) override; - void generate_java_doc(std::ostream& out, t_function* tdoc) override; + void generate_java_doc(std::ostream& out, t_function* tfunction) override; void generate_java_docstring_comment(std::ostream& out, string contents) override; @@ -3058,20 +3058,7 @@ void t_javame_generator::generate_java_doc(ostream& out, t_doc* tdoc) { * Emits a JavaDoc comment if the provided function object has a doc in Thrift */ void t_javame_generator::generate_java_doc(ostream& out, t_function* tfunction) { - if (tfunction->has_doc()) { - stringstream ss; - ss << tfunction->get_doc(); - const vector& fields = tfunction->get_arglist()->get_members(); - vector::const_iterator p_iter; - for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { - t_field* p = *p_iter; - ss << "\n@param " << p->get_name(); - if (p->has_doc()) { - ss << " " << p->get_doc(); - } - } - generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n"); - } + t_oop_generator::generate_java_doc(out, tfunction); } void t_javame_generator::generate_deep_copy_container(ostream& out, diff --git a/compiler/cpp/src/thrift/generate/t_kotlin_generator.cc b/compiler/cpp/src/thrift/generate/t_kotlin_generator.cc index 2e81909c48b..59fda48d0dd 100644 --- a/compiler/cpp/src/thrift/generate/t_kotlin_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_kotlin_generator.cc @@ -77,6 +77,7 @@ class t_kotlin_generator : public t_oop_generator { void init_generator() override; void close_generator() override; std::string display_name() const override; + std::string get_namespace(t_type* type) override; void generate_consts(std::vector consts) override; @@ -173,6 +174,15 @@ class t_kotlin_generator : public t_oop_generator { void generate_kotlin_union(t_struct* tstruct); }; +std::string t_kotlin_generator::get_namespace(t_type *type) { + std::string namespace_str = type->get_program()->get_namespace("kotlin"); + if (namespace_str.empty()) { + namespace_str = type->get_program()->get_namespace("java"); + } + + return namespace_str + "."; +} + /** * Prepares for file generation by opening up the necessary file output * streams. @@ -1420,7 +1430,7 @@ void t_kotlin_generator::generate_service_interface(t_service* tservice) { out << "interface " << tservice->get_name() << " {" << '\n'; indent_up(); for (auto tfunc : tservice->get_functions()) { - generate_kdoc_comment(out, tfunc); + generate_java_doc(out, tfunc); indent(out) << function_signature(tfunc) << '\n'; } scope_down(out); diff --git a/compiler/cpp/src/thrift/generate/t_netstd_generator.cc b/compiler/cpp/src/thrift/generate/t_netstd_generator.cc index 61e5bdeb0ff..b6168eb3000 100644 --- a/compiler/cpp/src/thrift/generate/t_netstd_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_netstd_generator.cc @@ -3960,6 +3960,21 @@ void t_netstd_generator::generate_netstd_doc(ostream& out, t_function* tfunction ps << ""; } + const vector& exceptions = tfunction->get_xceptions()->get_members(); + vector::const_iterator e_iter; + for (e_iter = exceptions.begin(); e_iter != exceptions.end(); ++e_iter) + { + t_field* e = *e_iter; + ps << '\n' << "get_type()) << "\">"; + if (e->has_doc()) + { + string str = e->get_doc(); + str.erase(remove(str.begin(), str.end(), '\n'), str.end()); + ps << str; + } + ps << ""; + } + docstring_comment(out, "", "/// ", diff --git a/compiler/cpp/src/thrift/generate/t_oop_generator.h b/compiler/cpp/src/thrift/generate/t_oop_generator.h index bae5613fb0a..32fe42b9621 100644 --- a/compiler/cpp/src/thrift/generate/t_oop_generator.h +++ b/compiler/cpp/src/thrift/generate/t_oop_generator.h @@ -37,6 +37,16 @@ class t_oop_generator : public t_generator { public: t_oop_generator(t_program* program) : t_generator(program) {} + virtual std::string get_namespace(t_type* type) { + std::string package = type->get_program()->get_namespace("java"); + + if (package.empty()) { + return ""; + } + + return package + "."; + } + /** * Scoping, using curly braces! */ @@ -104,6 +114,20 @@ class t_oop_generator : public t_generator { ss << " " << p->get_doc(); } } + + const std::vector& exceptions = tfunction->get_xceptions()->get_members(); + if (!exceptions.empty()) { + std::vector::const_iterator e_iter; + for (e_iter = exceptions.begin(); e_iter != exceptions.end(); ++e_iter) { + t_field* e = *e_iter; + ss << "\n@throws " << get_namespace(get_true_type(e->get_type())) << get_true_type(e->get_type())->get_name(); + if (e->has_doc()) { + std::string doc_string = e->get_doc(); + doc_string.erase(remove(doc_string.begin(), doc_string.end(), '\n'), doc_string.end()); + ss << " " << doc_string; + } + } + } generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n"); } } diff --git a/compiler/cpp/src/thrift/generate/t_py_generator.cc b/compiler/cpp/src/thrift/generate/t_py_generator.cc index 4f82944a11c..a52e9eedfa4 100644 --- a/compiler/cpp/src/thrift/generate/t_py_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_py_generator.cc @@ -250,6 +250,10 @@ class t_py_generator : public t_generator { t_doc* tdoc, t_struct* tstruct, const char* subheader); + void generate_python_params_docstring(ostream& ss, + t_struct* tstruct, + bool* has_doc, + const char* subheader); void generate_python_docstring(std::ostream& out, t_doc* tdoc); @@ -2722,7 +2726,17 @@ void t_py_generator::generate_python_docstring(ostream& out, t_struct* tstruct) * Generates the docstring for a given function. */ void t_py_generator::generate_python_docstring(ostream& out, t_function* tfunction) { - generate_python_docstring(out, tfunction, tfunction->get_arglist(), "Parameters"); + bool has_doc = false; + stringstream ss; + if (tfunction->has_doc()) { + has_doc = true; + ss << tfunction->get_doc(); + } + generate_python_params_docstring(ss, tfunction->get_arglist(), &has_doc, "Parameters"); + generate_python_params_docstring(ss, tfunction->get_xceptions(), &has_doc, "Raises"); + if (has_doc) { + generate_docstring_comment(out, "\"\"\"\n", "", ss.str(), "\"\"\"\n"); + } } /** @@ -2738,18 +2752,31 @@ void t_py_generator::generate_python_docstring(ostream& out, has_doc = true; ss << tdoc->get_doc(); } + generate_python_params_docstring(ss, tstruct, &has_doc, subheader); + if (has_doc) { + generate_docstring_comment(out, "\"\"\"\n", "", ss.str(), "\"\"\"\n"); + } +} +void t_py_generator::generate_python_params_docstring(ostream& ss, + t_struct* tstruct, + bool* has_doc, + const char* subheader) { const vector& fields = tstruct->get_members(); if (fields.size() > 0) { - if (has_doc) { + if (*has_doc) { ss << '\n'; } - has_doc = true; + *has_doc = true; ss << subheader << ":\n"; vector::const_iterator p_iter; for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { t_field* p = *p_iter; - ss << " - " << p->get_name(); + if (tstruct->is_method_xcepts()) { + ss << " - " << type_to_py_type(p->get_type()); + } else { + ss << " - " << p->get_name(); + } if (p->has_doc()) { ss << ": " << p->get_doc(); } else { @@ -2757,10 +2784,6 @@ void t_py_generator::generate_python_docstring(ostream& out, } } } - - if (has_doc) { - generate_docstring_comment(out, "\"\"\"\n", "", ss.str(), "\"\"\"\n"); - } } /** diff --git a/test/ThriftTest.thrift b/test/ThriftTest.thrift index 42607cc3544..4b0cdb998e3 100644 --- a/test/ThriftTest.thrift +++ b/test/ThriftTest.thrift @@ -314,7 +314,11 @@ service ThriftTest * else do not throw anything * @return Xtruct - an Xtruct with string_thing = arg1 */ - Xtruct testMultiException(1: string arg0, 2: string arg1) throws(1: Xception err1, 2: Xception2 err2) + Xtruct testMultiException(1: string arg0, 2: string arg1) throws( + /** Thrown when a bad thing happens */ + 1: Xception err1, + /** Thrown when the input is in incorrect format, for example */ + 2: Xception2 err2) /** * Print 'testOneway(%d): Sleeping...' with secondsToSleep as '%d'