From 782b865c5533ad020a61ae79ec8a658acfa96861 Mon Sep 17 00:00:00 2001 From: jaceksur <39417826+jaceksur@users.noreply.github.com> Date: Tue, 10 Sep 2019 10:01:27 -0700 Subject: [PATCH] Annotate getters with @Pure when --java-checkerframework is specified. (#5510) Together with @Nullable, this allows users to use static analysis tools like CheckerFramework to catch NPEs caused by unset fields. --- include/flatbuffers/idl.h | 2 ++ src/flatc.cpp | 3 +++ src/idl_gen_general.cpp | 20 ++++++++++++++++---- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index c3cec0664..506a717de 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -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), diff --git a/src/flatc.cpp b/src/flatc.cpp index 99a442d98..2f48c85fd 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -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") { diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index a5b8942e8..6919351bb 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -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";