THRIFT-5781 implement full deprecation support
Client: netstd
Patch: Jens Geyer
diff --git a/compiler/cpp/src/thrift/generate/t_netstd_generator.cc b/compiler/cpp/src/thrift/generate/t_netstd_generator.cc
index c16a069..e6176a7 100644
--- a/compiler/cpp/src/thrift/generate/t_netstd_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_netstd_generator.cc
@@ -219,6 +219,10 @@
     out << "#pragma warning disable IDE1006  // parts of the code use IDL spelling" << '\n';
     out << "#pragma warning disable CA1822   // empty " << DEEP_COPY_METHOD_NAME << "() methods still non-static" << '\n';
 
+    if( any_deprecations()) {
+      out << "#pragma warning disable CS0618   // silence our own deprecation warnings" << '\n';
+    }
+
     if( target_net_version < 6) {
         out << "#pragma warning disable IDE0083  // pattern matching \"that is not SomeType\" requires net5.0 but we still support earlier versions" << '\n';
     }
@@ -226,6 +230,86 @@
 }
 
 
+bool t_netstd_generator::any_deprecations()
+{
+  // enums
+  vector<t_enum*> enums = program_->get_enums();
+  vector<t_enum*>::iterator en_iter;
+  for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
+    if( is_deprecated((*en_iter)->annotations_)) {
+      return true;
+    }
+
+    // enum values
+    vector<t_enum_value*> evals = (*en_iter)->get_constants();
+    vector<t_enum_value*>::iterator ev_iter;
+    for (ev_iter = evals.begin(); ev_iter != evals.end(); ++ev_iter) {
+      if( is_deprecated((*ev_iter)->annotations_)) {
+        return true;
+      }
+    }
+  }
+
+  // typedefs
+  vector<t_typedef*> typedefs = program_->get_typedefs();
+  vector<t_typedef*>::iterator td_iter;
+  for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
+    if( is_deprecated((*td_iter)->annotations_)) {
+        return true;
+    }
+  }
+
+  // structs, exceptions, unions
+  vector<t_struct*> objects = program_->get_objects();
+  vector<t_struct*>::iterator o_iter;
+  for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
+    if( is_deprecated((*o_iter)->annotations_)) {
+      return true;
+    }
+
+    // struct members
+    const vector<t_field*>& members = (*o_iter)->get_members();
+    vector<t_field*>::const_iterator m_iter;
+    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
+      if( is_deprecated((*m_iter)->annotations_)) {
+        return true;
+      }
+    }
+  }
+
+  /* not yet
+  // constants
+  vector<t_const*> consts = program_->get_consts();
+  vector<t_const*>::iterator c_iter;
+  for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
+    if( is_deprecated((*c_iter)->annotations_)) {
+      return true;
+    }
+  }
+  */
+
+  // services
+  vector<t_service*> services = program_->get_services();
+  vector<t_service*>::iterator sv_iter;
+  for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
+    if( is_deprecated((*sv_iter)->annotations_)) {
+      return true;
+    }
+
+    // service methods
+    vector<t_function*> functions = (*sv_iter)->get_functions();
+    vector<t_function*>::iterator f_iter;
+    for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
+      if( is_deprecated((*f_iter)->annotations_)) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+
 void t_netstd_generator::start_netstd_namespace(ostream& out)
 {
     if (!namespace_name_.empty())
@@ -315,6 +399,7 @@
     start_netstd_namespace(out);
     generate_netstd_doc(out, tenum);
 
+    generate_deprecation_attribute(out, tenum->annotations_);
     out << indent() << "public enum " << type_name(tenum,false) << '\n';
     scope_up(out);
 
@@ -325,6 +410,7 @@
     {
         generate_netstd_doc(out, *c_iter);
         int value = (*c_iter)->get_value();
+        generate_deprecation_attribute(out, (*c_iter)->annotations_);
         out << indent() << normalize_name((*c_iter)->get_name()) << " = " << value << "," << '\n';
     }
 
@@ -850,6 +936,7 @@
 
     string sharp_struct_name = type_name(tstruct, false);
 
+    generate_deprecation_attribute(out, tstruct->annotations_);
     out << indent() << "public " << (is_final ? "sealed " : "") << "partial class " << sharp_struct_name << " : ";
 
     if (is_exception)
@@ -1055,6 +1142,7 @@
 
     bool is_final = tstruct->annotations_.find("final") != tstruct->annotations_.end();
 
+    generate_deprecation_attribute(out, tstruct->annotations_);
     out << indent() << "public " << (is_final ? "sealed " : "") << "partial class " << type_name(tstruct,false) << "Fault" << '\n'
         << indent() << "{" << '\n';
     indent_up();
@@ -1103,7 +1191,7 @@
       out << '\n' << indent() << "{" << '\n';
       indent_up();
     } else {
-      out << '\n';
+      out << ";\n";
     }
 
     for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
@@ -1526,6 +1614,7 @@
     // Let's define the class first
     start_netstd_namespace(out);
 
+    generate_deprecation_attribute(out, tunion->annotations_);
     out << indent() << "public abstract partial class " << normalize_name(tunion->get_name()) << " : TUnionBase" << '\n';
     out << indent() << "{" << '\n';
     indent_up();
@@ -1979,6 +2068,7 @@
         out << indent() << "[ServiceContract(Namespace=\"" << wcf_namespace_ << "\")]" << '\n';
     }
 
+    generate_deprecation_attribute(out, tservice->annotations_);
     prepare_member_name_mapping(tservice);
     out << indent() << "public interface IAsync" << extends_iface << '\n'
         << indent() << "{" << '\n';
@@ -2003,7 +2093,7 @@
             }
         }
 
-        generate_deprecation_attribute(out, *f_iter);
+        generate_deprecation_attribute(out, (*f_iter)->annotations_);
         out << indent() << function_signature_async(*f_iter) << ";" << '\n' << '\n';
     }
     indent_down();
@@ -2011,10 +2101,16 @@
     cleanup_member_name_mapping(tservice);
 }
 
-void t_netstd_generator::generate_deprecation_attribute(ostream& out, t_function* func)
+bool t_netstd_generator::is_deprecated(std::map<std::string, std::vector<std::string>>& annotations)
 {
-  auto iter = func->annotations_.find("deprecated");
-  if( func->annotations_.end() != iter) {
+  auto iter = annotations.find("deprecated");
+  return (annotations.end() != iter);
+}
+
+void t_netstd_generator::generate_deprecation_attribute(ostream& out, std::map<std::string, std::vector<std::string>>& annotations)
+{
+  auto iter = annotations.find("deprecated");
+  if( annotations.end() != iter) {
     out << indent() << "[Obsolete";
     // empty annotation values end up with "1" somewhere, ignore these as well
     if ((iter->second.back().length() > 0) && (iter->second.back() != "1")) {
@@ -2066,6 +2162,7 @@
     out << '\n';
 
     generate_netstd_doc(out, tservice);
+    generate_deprecation_attribute(out, tservice->annotations_);
     prepare_member_name_mapping(tservice);
     out << indent() << "public class Client : " << extends_client << "IAsync" << '\n'
         << indent() << "{" << '\n';
@@ -2089,7 +2186,7 @@
         string function_name = raw_func_name + (add_async_postfix ? "Async" : "");
 
         // async
-        generate_deprecation_attribute(out, *functions_iterator);
+        generate_deprecation_attribute(out, (*functions_iterator)->annotations_);
         out << indent() << "public async " << function_signature_async(*functions_iterator, "") << '\n'
             << indent() << "{" << '\n';
         indent_up();
@@ -2107,7 +2204,7 @@
         out << indent() << "}" << '\n' << '\n';
 
         // async send
-        generate_deprecation_attribute(out, *functions_iterator);
+        generate_deprecation_attribute(out, (*functions_iterator)->annotations_);
         out << indent() << "public async " << function_signature_async(*functions_iterator, "send_", MODE_NO_RETURN) << '\n'
             << indent() << "{" << '\n';
         indent_up();
@@ -2148,7 +2245,7 @@
         if (!(*functions_iterator)->is_oneway())
         {
             // async recv
-            generate_deprecation_attribute(out, *functions_iterator);
+            generate_deprecation_attribute(out, (*functions_iterator)->annotations_);
             out << indent() << "public async " << function_signature_async(*functions_iterator, "recv_", MODE_NO_ARGS) << '\n'
                 << indent() << "{" << '\n';
             indent_up();
@@ -3022,6 +3119,7 @@
     {
         out << indent() << "[DataMember(Order = 0)]" << '\n';
     }
+    generate_deprecation_attribute(out, tfield->annotations_);
 
     out << indent()
         << (isPublic ? "public " : "private ")
diff --git a/compiler/cpp/src/thrift/generate/t_netstd_generator.h b/compiler/cpp/src/thrift/generate/t_netstd_generator.h
index a6e4d90..c65414a 100644
--- a/compiler/cpp/src/thrift/generate/t_netstd_generator.h
+++ b/compiler/cpp/src/thrift/generate/t_netstd_generator.h
@@ -104,7 +104,7 @@
   void generate_netstd_union_reader(ostream& out, t_struct* tunion);
   void generate_function_helpers(ostream& out, t_function* tfunction);
   void generate_service_interface(ostream& out, t_service* tservice);
-  void generate_deprecation_attribute(ostream& out, t_function* func);
+  void generate_deprecation_attribute(ostream& out, std::map<std::string, std::vector<std::string>>& annotations);
   void generate_service_helpers(ostream& out, t_service* tservice);
   void generate_service_client(ostream& out, t_service* tservice);
   void generate_service_server(ostream& out, t_service* tservice);
@@ -218,6 +218,8 @@
   string initialize_field(t_field* tfield);
 
   void pragmas_and_directives(ostream& out);
+  bool any_deprecations();
+  bool is_deprecated(std::map<std::string, std::vector<std::string>>& annotations);
   bool is_nullable_type(t_type* ttype);
   bool force_member_nullable(t_field* tfield);  // see there
   string nullable_suffix();                     // unconditionally