Annotate getters with @Pure when --java-checkerframework is specified. (#5510)
authorjaceksur <39417826+jaceksur@users.noreply.github.com>
Tue, 10 Sep 2019 17:01:27 +0000 (10:01 -0700)
committerWouter van Oortmerssen <aardappel@gmail.com>
Tue, 10 Sep 2019 17:01:27 +0000 (10:01 -0700)
Together with @Nullable, this allows users to use static analysis tools
like CheckerFramework to catch NPEs caused by unset fields.

include/flatbuffers/idl.h
src/flatc.cpp
src/idl_gen_general.cpp

index c3cec06..506a717 100644 (file)
@@ -514,6 +514,7 @@ struct IDLOptions {
   std::string cpp_object_api_string_type;
   bool cpp_object_api_string_flexible_constructor;
   bool gen_nullable;
+  bool java_checkerframework;
   bool gen_generated;
   std::string object_prefix;
   std::string object_suffix;
@@ -594,6 +595,7 @@ struct IDLOptions {
         cpp_object_api_pointer_type("std::unique_ptr"),
         cpp_object_api_string_flexible_constructor(false),
         gen_nullable(false),
+        java_checkerframework(false),
         gen_generated(false),
         object_suffix("T"),
         union_value_namespacing(true),
index 99a442d..2f48c85 100644 (file)
@@ -101,6 +101,7 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const {
     "  --gen-object-api   Generate an additional object-based API.\n"
     "  --gen-compare      Generate operator== for object-based API types.\n"
     "  --gen-nullable     Add Clang _Nullable for C++ pointer. or @Nullable for Java\n"
+    "  --java-checkerframework Add @Pure for Java.\n"
     "  --gen-generated    Add @Generated annotation for Java\n"
     "  --gen-all          Generate not just code for the current schema files,\n"
     "                     but for all files it includes as well.\n"
@@ -261,6 +262,8 @@ int FlatCompiler::Compile(int argc, const char **argv) {
         opts.cpp_object_api_string_flexible_constructor = true;
       } else if (arg == "--gen-nullable") {
         opts.gen_nullable = true;
+      } else if (arg == "--java-checkerframework") {
+        opts.java_checkerframework = true;
       } else if (arg == "--gen-generated") {
         opts.gen_generated = true;
       } else if (arg == "--object-prefix") {
index a5b8942..6919351 100644 (file)
@@ -228,6 +228,9 @@ class GeneralGenerator : public BaseGenerator {
       if (parser_.opts.gen_nullable) {
         code += "\nimport javax.annotation.Nullable;\n";
       }
+      if (parser_.opts.java_checkerframework) {
+        code += "\nimport org.checkerframework.dataflow.qual.Pure;\n";
+      }
       code += lang_.class_annotation;
     }
     if (parser_.opts.gen_generated) {
@@ -254,6 +257,14 @@ class GeneralGenerator : public BaseGenerator {
                : "";
   }
 
+  std::string GenPureAnnotation(const Type &t) const {
+    return lang_.language == IDLOptions::kJava &&
+                   parser_.opts.java_checkerframework &&
+                   !IsScalar(DestinationType(t, true).base_type)
+               ? " @Pure "
+               : "";
+  }
+
   std::string GenTypeBasic(const Type &type, bool enableLangOverrides) const {
     // clang-format off
   static const char * const java_typename[] = {
@@ -969,10 +980,11 @@ class GeneralGenerator : public BaseGenerator {
       std::string dest_mask = DestinationMask(field.value.type, true);
       std::string dest_cast = DestinationCast(field.value.type);
       std::string src_cast = SourceCast(field.value.type);
-      std::string method_start = "  public " +
-                                 (field.required ? "" : GenNullableAnnotation(field.value.type)) +
-                                 type_name_dest + optional + " " +
-                                 MakeCamel(field.name, lang_.first_camel_upper);
+      std::string method_start =
+          "  public " +
+          (field.required ? "" : GenNullableAnnotation(field.value.type)) +
+          GenPureAnnotation(field.value.type) + type_name_dest + optional +
+          " " + MakeCamel(field.name, lang_.first_camel_upper);
       std::string obj = lang_.language == IDLOptions::kCSharp
                             ? "(new " + type_name + "())"
                             : "obj";