diff --git a/src/it/resources/deprecation.djinni b/src/it/resources/deprecation.djinni new file mode 100644 index 0000000..2a1c98d --- /dev/null +++ b/src/it/resources/deprecation.djinni @@ -0,0 +1,55 @@ + +# enum comment +# +# @deprecated Use something else +my_enum = enum { + # @deprecated Use something else + option1; + # not deprecated + option2; +} + +# flags comment +# +# @deprecated Use someother flags +my_flags = flags { + # @deprecated Use someother flag + flag1; + # not deprecated + flag2; +} + +# record comment +# +# @deprecated Use someother record +my_record = record { + # @deprecated Use someother attribute + attribute: string; + # not deprecated + another: string; + # @deprecated Use someother attribute + again: string; + + # @deprecated Use someother constant + const version: i32 = 1; +} + +# interface comment +# +# @deprecated Use someother interface +my_interface = interface +c { + # @deprecated Use someother method + method_a(value:i32); + # @deprecated Use someother method + const method_b(value:i32); + # @deprecated Use someother method + static method_c(value:i32); + # not deprecated + method_d(); + + # really im not + method_e(); + + # @deprecated Use someother constant + const version: i32 = 1; +} diff --git a/src/it/resources/expected/deprecation/cpp-headers/my_enum.hpp b/src/it/resources/expected/deprecation/cpp-headers/my_enum.hpp new file mode 100644 index 0000000..3215ddc --- /dev/null +++ b/src/it/resources/expected/deprecation/cpp-headers/my_enum.hpp @@ -0,0 +1,29 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +#pragma once + +#include + +/** + * enum comment + * + * @deprecated Use something else + */ +enum class [[deprecated("Use something else")]] MyEnum : int { + /** @deprecated Use something else */ + OPTION1, + /** not deprecated */ + OPTION2, +}; + +namespace std { + +template <> +struct hash<::MyEnum> { + size_t operator()(::MyEnum type) const { + return std::hash()(static_cast(type)); + } +}; + +} // namespace std diff --git a/src/it/resources/expected/deprecation/cpp-headers/my_flags.hpp b/src/it/resources/expected/deprecation/cpp-headers/my_flags.hpp new file mode 100644 index 0000000..4901542 --- /dev/null +++ b/src/it/resources/expected/deprecation/cpp-headers/my_flags.hpp @@ -0,0 +1,50 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +#pragma once + +#include + +/** + * flags comment + * + * @deprecated Use someother flags + */ +enum class [[deprecated("Use someother flags")]] MyFlags : unsigned { + /** @deprecated Use someother flag */ + FLAG1 = 1u << 0, + /** not deprecated */ + FLAG2 = 1u << 1, +}; +constexpr MyFlags operator|(MyFlags lhs, MyFlags rhs) noexcept { + return static_cast(static_cast(lhs) | static_cast(rhs)); +} +inline MyFlags& operator|=(MyFlags& lhs, MyFlags rhs) noexcept { + return lhs = lhs | rhs; +} +constexpr MyFlags operator&(MyFlags lhs, MyFlags rhs) noexcept { + return static_cast(static_cast(lhs) & static_cast(rhs)); +} +inline MyFlags& operator&=(MyFlags& lhs, MyFlags rhs) noexcept { + return lhs = lhs & rhs; +} +constexpr MyFlags operator^(MyFlags lhs, MyFlags rhs) noexcept { + return static_cast(static_cast(lhs) ^ static_cast(rhs)); +} +inline MyFlags& operator^=(MyFlags& lhs, MyFlags rhs) noexcept { + return lhs = lhs ^ rhs; +} +constexpr MyFlags operator~(MyFlags x) noexcept { + return static_cast(~static_cast(x)); +} + +namespace std { + +template <> +struct hash<::MyFlags> { + size_t operator()(::MyFlags type) const { + return std::hash()(static_cast(type)); + } +}; + +} // namespace std diff --git a/src/it/resources/expected/deprecation/cpp-headers/my_interface.hpp b/src/it/resources/expected/deprecation/cpp-headers/my_interface.hpp new file mode 100644 index 0000000..8e8e50a --- /dev/null +++ b/src/it/resources/expected/deprecation/cpp-headers/my_interface.hpp @@ -0,0 +1,38 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +#pragma once + +#include + +/** + * interface comment + * + * @deprecated Use someother interface + */ +class [[deprecated("Use someother interface")]] MyInterface { +public: + virtual ~MyInterface() {} + + /** @deprecated Use someother constant */ + [[deprecated("Use someother constant")]] + static constexpr int32_t VERSION = 1; + + /** @deprecated Use someother method */ + [[deprecated("Use someother method")]] + virtual void method_a(int32_t value) = 0; + + /** @deprecated Use someother method */ + [[deprecated("Use someother method")]] + virtual void method_b(int32_t value) const = 0; + + /** @deprecated Use someother method */ + [[deprecated("Use someother method")]] + static void method_c(int32_t value); + + /** not deprecated */ + virtual void method_d() = 0; + + /** really im not */ + virtual void method_e() = 0; +}; diff --git a/src/it/resources/expected/deprecation/cpp-headers/my_record.hpp b/src/it/resources/expected/deprecation/cpp-headers/my_record.hpp new file mode 100644 index 0000000..d215ec7 --- /dev/null +++ b/src/it/resources/expected/deprecation/cpp-headers/my_record.hpp @@ -0,0 +1,36 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +#pragma once + +#include +#include +#include + +/** + * record comment + * + * @deprecated Use someother record + */ +struct [[deprecated("Use someother record")]] MyRecord final { + + /** @deprecated Use someother constant */ + [[deprecated("Use someother constant")]] + static constexpr int32_t VERSION = 1; + /** @deprecated Use someother attribute */ + [[deprecated("Use someother attribute")]] + std::string attribute; + /** not deprecated */ + std::string another; + /** @deprecated Use someother attribute */ + [[deprecated("Use someother attribute")]] + std::string again; + + MyRecord(std::string attribute_, + std::string another_, + std::string again_) + : attribute(std::move(attribute_)) + , another(std::move(another_)) + , again(std::move(again_)) + {} +}; diff --git a/src/it/resources/expected/deprecation/cpp/my_interface.cpp b/src/it/resources/expected/deprecation/cpp/my_interface.cpp new file mode 100644 index 0000000..2e9a28b --- /dev/null +++ b/src/it/resources/expected/deprecation/cpp/my_interface.cpp @@ -0,0 +1,6 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +#include "my_interface.hpp" // my header + +int32_t constexpr MyInterface::VERSION; diff --git a/src/it/resources/expected/deprecation/generated-files.txt b/src/it/resources/expected/deprecation/generated-files.txt new file mode 100644 index 0000000..f31025c --- /dev/null +++ b/src/it/resources/expected/deprecation/generated-files.txt @@ -0,0 +1,5 @@ +src/it/resources/result/deprecation/cpp-headers/my_enum.hpp +src/it/resources/result/deprecation/cpp-headers/my_flags.hpp +src/it/resources/result/deprecation/cpp-headers/my_record.hpp +src/it/resources/result/deprecation/cpp-headers/my_interface.hpp +src/it/resources/result/deprecation/cpp/my_interface.cpp diff --git a/src/it/resources/expected/deprecation/java/MyEnum.java b/src/it/resources/expected/deprecation/java/MyEnum.java new file mode 100644 index 0000000..c9359d3 --- /dev/null +++ b/src/it/resources/expected/deprecation/java/MyEnum.java @@ -0,0 +1,19 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +package djinni.it; + +/** + * enum comment + * + * @deprecated Use something else + */ +@Deprecated +public enum MyEnum { + /** @deprecated Use something else */ + @Deprecated + OPTION1, + /** not deprecated */ + OPTION2, + ; +} diff --git a/src/it/resources/expected/deprecation/java/MyFlags.java b/src/it/resources/expected/deprecation/java/MyFlags.java new file mode 100644 index 0000000..3e7b161 --- /dev/null +++ b/src/it/resources/expected/deprecation/java/MyFlags.java @@ -0,0 +1,19 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +package djinni.it; + +/** + * flags comment + * + * @deprecated Use someother flags + */ +@Deprecated +public enum MyFlags { + /** @deprecated Use someother flag */ + @Deprecated + FLAG1, + /** not deprecated */ + FLAG2, + ; +} diff --git a/src/it/resources/expected/deprecation/java/MyInterface.java b/src/it/resources/expected/deprecation/java/MyInterface.java new file mode 100644 index 0000000..3d91fed --- /dev/null +++ b/src/it/resources/expected/deprecation/java/MyInterface.java @@ -0,0 +1,98 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +package djinni.it; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * interface comment + * + * @deprecated Use someother interface + */ +@Deprecated +public abstract class MyInterface { + /** @deprecated Use someother constant */ + @Deprecated + public static final int VERSION = 1; + + /** @deprecated Use someother method */ + @Deprecated + public abstract void methodA(int value); + + /** @deprecated Use someother method */ + @Deprecated + public abstract void methodB(int value); + + /** not deprecated */ + public abstract void methodD(); + + /** really im not */ + public abstract void methodE(); + + /** @deprecated Use someother method */ + @Deprecated + public static void methodC(int value) + { + CppProxy.methodC(value); + } + + private static final class CppProxy extends MyInterface + { + private final long nativeRef; + private final AtomicBoolean destroyed = new AtomicBoolean(false); + + private CppProxy(long nativeRef) + { + if (nativeRef == 0) throw new RuntimeException("nativeRef is zero"); + this.nativeRef = nativeRef; + } + + private native void nativeDestroy(long nativeRef); + public void _djinni_private_destroy() + { + boolean destroyed = this.destroyed.getAndSet(true); + if (!destroyed) nativeDestroy(this.nativeRef); + } + @SuppressWarnings("deprecation") + protected void finalize() throws java.lang.Throwable + { + _djinni_private_destroy(); + super.finalize(); + } + + @Override + public void methodA(int value) + { + assert !this.destroyed.get() : "trying to use a destroyed object"; + native_methodA(this.nativeRef, value); + } + private native void native_methodA(long _nativeRef, int value); + + @Override + public void methodB(int value) + { + assert !this.destroyed.get() : "trying to use a destroyed object"; + native_methodB(this.nativeRef, value); + } + private native void native_methodB(long _nativeRef, int value); + + @Override + public void methodD() + { + assert !this.destroyed.get() : "trying to use a destroyed object"; + native_methodD(this.nativeRef); + } + private native void native_methodD(long _nativeRef); + + @Override + public void methodE() + { + assert !this.destroyed.get() : "trying to use a destroyed object"; + native_methodE(this.nativeRef); + } + private native void native_methodE(long _nativeRef); + + public static native void methodC(int value); + } +} diff --git a/src/it/resources/expected/deprecation/java/MyRecord.java b/src/it/resources/expected/deprecation/java/MyRecord.java new file mode 100644 index 0000000..d6f6e34 --- /dev/null +++ b/src/it/resources/expected/deprecation/java/MyRecord.java @@ -0,0 +1,60 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +package djinni.it; + +/** + * record comment + * + * @deprecated Use someother record + */ +@Deprecated +public final class MyRecord { + + /** @deprecated Use someother constant */ + @Deprecated + public static final int VERSION = 1; + + + /*package*/ final String attribute; + + /*package*/ final String another; + + /*package*/ final String again; + + public MyRecord( + String attribute, + String another, + String again) { + this.attribute = attribute; + this.another = another; + this.again = again; + } + + /** @deprecated Use someother attribute */ + @Deprecated + public String getAttribute() { + return attribute; + } + + /** not deprecated */ + public String getAnother() { + return another; + } + + /** @deprecated Use someother attribute */ + @Deprecated + public String getAgain() { + return again; + } + + @Override + public String toString() { + return "MyRecord{" + + "attribute=" + attribute + + "," + "another=" + another + + "," + "again=" + again + + "}"; + } + +} diff --git a/src/it/resources/expected/deprecation/objc-headers/ITMyEnum.h b/src/it/resources/expected/deprecation/objc-headers/ITMyEnum.h new file mode 100644 index 0000000..a251be4 --- /dev/null +++ b/src/it/resources/expected/deprecation/objc-headers/ITMyEnum.h @@ -0,0 +1,18 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +#import + +/** + * enum comment + * + * @deprecated Use something else + */ + __deprecated_msg("Use something else") +typedef NS_ENUM(NSInteger, ITMyEnum) +{ + /** @deprecated Use something else */ + ITMyEnumOption1, + /** not deprecated */ + ITMyEnumOption2, +}; diff --git a/src/it/resources/expected/deprecation/objc-headers/ITMyFlags.h b/src/it/resources/expected/deprecation/objc-headers/ITMyFlags.h new file mode 100644 index 0000000..274c7ec --- /dev/null +++ b/src/it/resources/expected/deprecation/objc-headers/ITMyFlags.h @@ -0,0 +1,18 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +#import + +/** + * flags comment + * + * @deprecated Use someother flags + */ + __deprecated_msg("Use someother flags") +typedef NS_OPTIONS(NSUInteger, ITMyFlags) +{ + /** @deprecated Use someother flag */ + ITMyFlagsFlag1 = 1u << 0, + /** not deprecated */ + ITMyFlagsFlag2 = 1u << 1, +}; diff --git a/src/it/resources/expected/deprecation/objc-headers/ITMyInterface.h b/src/it/resources/expected/deprecation/objc-headers/ITMyInterface.h new file mode 100644 index 0000000..27121a8 --- /dev/null +++ b/src/it/resources/expected/deprecation/objc-headers/ITMyInterface.h @@ -0,0 +1,32 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +#import + +/** @deprecated Use someother constant */ +extern int32_t const ITMyInterfaceVersion __deprecated_msg("Use someother constant"); + +/** + * interface comment + * + * @deprecated Use someother interface + */ + __deprecated_msg("Use someother interface") +@interface ITMyInterface : NSObject + +/** @deprecated Use someother method */ +- (void)methodA:(int32_t)value __deprecated_msg("Use someother method"); + +/** @deprecated Use someother method */ +- (void)methodB:(int32_t)value __deprecated_msg("Use someother method"); + +/** @deprecated Use someother method */ ++ (void)methodC:(int32_t)value __deprecated_msg("Use someother method"); + +/** not deprecated */ +- (void)methodD; + +/** really im not */ +- (void)methodE; + +@end diff --git a/src/it/resources/expected/deprecation/objc-headers/ITMyRecord.h b/src/it/resources/expected/deprecation/objc-headers/ITMyRecord.h new file mode 100644 index 0000000..3fb9f28 --- /dev/null +++ b/src/it/resources/expected/deprecation/objc-headers/ITMyRecord.h @@ -0,0 +1,32 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni from deprecation.djinni + +#import + +/** + * record comment + * + * @deprecated Use someother record + */ + __deprecated_msg("Use someother record") +@interface ITMyRecord : NSObject +- (nonnull instancetype)initWithAttribute:(nonnull NSString *)attribute + another:(nonnull NSString *)another + again:(nonnull NSString *)again; ++ (nonnull instancetype)myRecordWithAttribute:(nonnull NSString *)attribute + another:(nonnull NSString *)another + again:(nonnull NSString *)again; + +/** @deprecated Use someother attribute */ +@property (nonatomic, readonly, nonnull) NSString * attribute __deprecated_msg("Use someother attribute"); + +/** not deprecated */ +@property (nonatomic, readonly, nonnull) NSString * another; + +/** @deprecated Use someother attribute */ +@property (nonatomic, readonly, nonnull) NSString * again __deprecated_msg("Use someother attribute"); + +@end + +/** @deprecated Use someother constant */ +extern int32_t const ITMyRecordVersion __deprecated_msg("Use someother constant"); diff --git a/src/it/resources/expected/deprecation/objc-headers/bridging-header.h b/src/it/resources/expected/deprecation/objc-headers/bridging-header.h new file mode 100644 index 0000000..113b0ee --- /dev/null +++ b/src/it/resources/expected/deprecation/objc-headers/bridging-header.h @@ -0,0 +1,18 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file was generated by Djinni + +// bridging_header.h +// bridging_header + +#import + +//! Project version number for bridgingheader. +FOUNDATION_EXPORT double bridgingheaderVersionNumber; + +//! Project version string for bridgingheader. +FOUNDATION_EXPORT const unsigned char bridgingheaderVersionString[]; + +#import "ITMyEnum.h" +#import "ITMyFlags.h" +#import "ITMyRecord.h" +#import "ITMyInterface.h" diff --git a/src/it/scala/djinni/GeneratorIntegrationTest.scala b/src/it/scala/djinni/GeneratorIntegrationTest.scala index dc6471b..6d6d64c 100644 --- a/src/it/scala/djinni/GeneratorIntegrationTest.scala +++ b/src/it/scala/djinni/GeneratorIntegrationTest.scala @@ -837,4 +837,102 @@ class GeneratorIntegrationTest extends IntegrationTest with GivenWhenThen { cppHeaderFilenames ) } + + it( + "should generate C++14 deprecation attributes from @deprecated notes in comments" + ) { + Given( + "an IDL-file that documents deprecation using @deprecated in comments" + ) + val idlFile = "deprecation" + + When("generating the C++ headers") + val cppHeaderFilenames = CppHeaders( + "my_record.hpp", + "my_enum.hpp", + "my_flags.hpp", + "my_interface.hpp" + ) + val cmd = djinniParams( + idlFile, + cpp = true, + objc = false, + java = false, + python = false, + cWrapper = false, + cppCLI = false, + cppOmitDefaultRecordCtor = true + ) + + djinni(cmd) + + Then( + "the @deprecated comments are generated as C++14 [[deprecated]] attributes" + ) + assertFileContentEquals(idlFile, CPP_HEADERS, cppHeaderFilenames) + } + + it( + "should generate @Deprecated annotations for Java from @deprecated notes in comments" + ) { + Given( + "an IDL-file that documents deprecation using @deprecated in comments" + ) + val idlFile = "deprecation" + + When("generating Java source") + val javaFilenames = + Java("MyRecord.java", "MyEnum.java", "MyFlags.java", "MyInterface.java") + val cmd = djinniParams( + idlFile, + cpp = false, + objc = false, + java = true, + python = false, + cWrapper = false, + cppCLI = false, + cppOmitDefaultRecordCtor = true + ) + + djinni(cmd) + + Then( + "the @deprecated comments are generated as @Deprecated annotations" + ) + assertFileContentEquals(idlFile, JAVA, javaFilenames) + } + + it( + "should generate __deprecated attributes for ObjC from @deprecated notes in comments" + ) { + Given( + "an IDL-file that documents deprecation using @deprecated in comments" + ) + val idlFile = "deprecation" + + When("generating ObjC source") + val objcHeaderFilenames = ObjCHeaders( + "ITMyRecord.h", + "ITMyEnum.h", + "ITMyFlags.h", + "ITMyInterface.h" + ) + val cmd = djinniParams( + idlFile, + cpp = false, + objc = true, + java = false, + python = false, + cWrapper = false, + cppCLI = false, + cppOmitDefaultRecordCtor = true + ) + + djinni(cmd) + + Then( + "the @deprecated comments are generated as __deprecated attributes" + ) + assertFileContentEquals(idlFile, OBJC_HEADERS, objcHeaderFilenames) + } } diff --git a/src/main/scala/djinni/CppGenerator.scala b/src/main/scala/djinni/CppGenerator.scala index 31e5598..aba4743 100644 --- a/src/main/scala/djinni/CppGenerator.scala +++ b/src/main/scala/djinni/CppGenerator.scala @@ -85,6 +85,22 @@ class CppGenerator(spec: Spec) extends Generator(spec) { }) } + def writeDeprecated(w: IndentWriter, doc: Doc) { + deprecatedText(doc) match { + case None => + case Some("") => w.wl("[[deprecated]]") + case Some(s) => w.wl(s"[[deprecated(\"$s\")]]") + } + } + + def deprecatedAttr(doc: Doc) = { + deprecatedText(doc) match { + case None => "" + case Some("") => " [[deprecated]]" + case Some(s) => s" [[deprecated(\"$s\")]]" + } + } + class CppRefs(name: String, extension: String = "") { var hpp = mutable.TreeSet[String]() var hppFwds = mutable.TreeSet[String]() @@ -122,6 +138,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) { val flagsType = "unsigned" val enumType = "int" val underlyingType = if (e.flags) flagsType else enumType + val deprecatedType = deprecatedAttr(doc); writeHppFile( ident, @@ -130,7 +147,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) { refs.hppFwds, w => { writeDoc(w, doc) - w.w(s"enum class $self : $underlyingType").bracedSemi { + w.w(s"enum class${deprecatedType} $self : $underlyingType").bracedSemi { writeEnumOptionNone(w, e, idCpp.enum) writeEnumOptions(w, e, idCpp.enum) writeEnumOptionAll(w, e, idCpp.enum) @@ -317,6 +334,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) { // Write code to the header file w.wl writeDoc(w, c.doc) + writeDeprecated(w, c.doc) w.wl(s"static ${constFieldType} ${idCpp.const(c.ident)}${constValue}") } } @@ -416,6 +434,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) { } val fields = r.fields + val deprecationType = deprecatedAttr(doc) + " " // C++ Header def writeCppPrototype(w: IndentWriter) { @@ -426,11 +445,12 @@ class CppGenerator(spec: Spec) extends Generator(spec) { } writeDoc(w, doc) writeCppTypeParams(w, params) - w.w("struct " + actualSelf + cppFinal).bracedSemi { + w.w("struct" + deprecationType + actualSelf + cppFinal).bracedSemi { generateHppConstants(w, r.consts) // Field definitions. for (f <- r.fields) { writeDoc(w, f.doc) + writeDeprecated(w, f.doc) w.wl(marshal.fieldType(f.ty) + " " + idCpp.field(f.ident) + ";") } @@ -691,7 +711,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) { w => { writeDoc(w, doc) writeCppTypeParams(w, typeParams) - w.w(s"class $self").bracedSemi { + w.w("class" + deprecatedAttr(doc) + s" $self").bracedSemi { w.wlOutdent("public:") // Destructor w.wl(s"virtual ~$self() {}") @@ -701,6 +721,7 @@ class CppGenerator(spec: Spec) extends Generator(spec) { for (m <- i.methods) { w.wl writeMethodDoc(w, m, idCpp.local) + writeDeprecated(w, m.doc) val ret = marshal.returnType(m.ret, methodNamesInScope) val params = m.params.map(p => marshal.paramType(p.ty, methodNamesInScope) + " " + idCpp diff --git a/src/main/scala/djinni/JavaGenerator.scala b/src/main/scala/djinni/JavaGenerator.scala index 28ae52d..90811dd 100755 --- a/src/main/scala/djinni/JavaGenerator.scala +++ b/src/main/scala/djinni/JavaGenerator.scala @@ -76,6 +76,10 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { ) } + def writeDocAnnotations(w: IndentWriter, doc: Doc) { + writeDeprecated(w, doc, "@Deprecated") + } + def generateJavaConstants( w: IndentWriter, consts: Seq[Const], @@ -113,6 +117,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { for (c <- consts) { writeDoc(w, c.doc) + writeDocAnnotations(w, c.doc) javaAnnotationHeader.foreach(w.wl) marshal.nullityAnnotation(c.ty).foreach(w.wl) @@ -138,12 +143,14 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { refs.java, w => { writeDoc(w, doc) + writeDocAnnotations(w, doc) javaAnnotationHeader.foreach(w.wl) w.w( s"${javaClassAccessModifierString}enum ${marshal.typename(ident, e)}" ).braced { for (o <- normalEnumOptions(e)) { writeDoc(w, o.doc) + writeDocAnnotations(w, o.doc) w.wl(idJava.enum(o.ident) + ",") } w.wl(";") @@ -180,6 +187,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { val javaClass = marshal.typename(ident, i) val typeParamList = javaTypeParams(typeParams) writeDoc(w, doc) + writeDocAnnotations(w, doc) javaAnnotationHeader.foreach(w.wl) @@ -202,6 +210,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { for (m <- i.methods if !m.static) { skipFirst { w.wl } writeMethodDoc(w, m, idJava.local) + writeDocAnnotations(w, m.doc) val ret = marshal.returnType(m.ret) val params = m.params.map(p => { val nullityAnnotation = @@ -221,6 +230,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { for (m <- i.methods if m.static) { skipFirst { w.wl } writeMethodDoc(w, m, idJava.local) + writeDocAnnotations(w, m.doc) val ret = marshal.returnType(m.ret) val returnPrefix = if (ret == "void") "" else "return " val params = m.params.map(p => { @@ -390,6 +400,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { refs.java, w => { writeDoc(w, doc) + writeDocAnnotations(w, doc) javaAnnotationHeader.foreach(w.wl) val self = marshal.typename(javaName, r) @@ -441,6 +452,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) { for (f <- r.fields) { w.wl writeDoc(w, f.doc) + writeDocAnnotations(w, f.doc) marshal.nullityAnnotation(f.ty).foreach(w.wl) w.w( "public " + marshal.returnType(Some(f.ty)) + " " + idJava diff --git a/src/main/scala/djinni/ObjcGenerator.scala b/src/main/scala/djinni/ObjcGenerator.scala index ac36776..9e5c8c7 100644 --- a/src/main/scala/djinni/ObjcGenerator.scala +++ b/src/main/scala/djinni/ObjcGenerator.scala @@ -53,6 +53,7 @@ class ObjcGenerator(spec: Spec) extends BaseObjcGenerator(spec) { refs.header, w => { writeDoc(w, doc) + writeDocAttributes(w, doc) w.wl(if (e.flags) { s"typedef NS_OPTIONS(NSUInteger, $self)" } else { @@ -83,6 +84,22 @@ class ObjcGenerator(spec: Spec) extends BaseObjcGenerator(spec) { writeAlignedObjcCall(w, decl, List(), ";", p => ("", "")) } + def deprecatedAttr(doc: Doc): String = { + deprecatedText(doc) match { + case Some("") => " __deprecated" + case Some(message) => s" __deprecated_msg(\"$message\")" + case None => "" + } + } + + def writeDocAttributes(w: IndentWriter, doc: Doc) { + deprecatedText(doc) match { + case Some("") => w.wl("__deprecated") + case Some(message) => w.wl(s" __deprecated_msg(\"$message\")") + case None => + } + } + /** Generate Interface */ override def generateInterface( @@ -133,10 +150,12 @@ class ObjcGenerator(spec: Spec) extends BaseObjcGenerator(spec) { writeDoc(w, c.doc) w.w(s"extern ") writeObjcConstVariableDecl(w, c, self) + w.w(deprecatedAttr(c.doc)) w.wl(s";") } w.wl writeDoc(w, doc) + writeDocAttributes(w, doc) if (i.ext.objc) { if (spec.objcStrictProtocol) { w.wl(s"@protocol $self ") @@ -150,6 +169,7 @@ class ObjcGenerator(spec: Spec) extends BaseObjcGenerator(spec) { w.wl writeMethodDoc(w, m, idObjc.local) writeObjcFuncDecl(m, w) + w.w(deprecatedAttr(m.doc)) w.wl(";") } for (c <- i.consts if !marshal.canBeConstVariable(c)) { @@ -237,6 +257,7 @@ class ObjcGenerator(spec: Spec) extends BaseObjcGenerator(spec) { refs.header, w => { writeDoc(w, doc) + writeDocAttributes(w, doc) w.wl(s"@interface $self : NSObject") def writeInitializer(sign: String, prefix: String) { @@ -269,8 +290,10 @@ class ObjcGenerator(spec: Spec) extends BaseObjcGenerator(spec) { writeDoc(w, f.doc) val nullability = marshal.nullability(f.ty.resolved).fold("")(", " + _) + val deprecated = + deprecatedAttr(f.doc) w.wl(s"@property (nonatomic, readonly${nullability}) ${marshal - .fqFieldType(f.ty)} ${idObjc.field(f.ident)};") + .fqFieldType(f.ty)} ${idObjc.field(f.ident)}${deprecated};") } if (r.derivingTypes.contains(DerivingType.Ord)) { w.wl @@ -285,6 +308,7 @@ class ObjcGenerator(spec: Spec) extends BaseObjcGenerator(spec) { writeDoc(w, c.doc) w.w(s"extern ") writeObjcConstVariableDecl(w, c, noBaseSelf) + w.w(deprecatedAttr(c.doc)) w.wl(s";") } } diff --git a/src/main/scala/djinni/generator.scala b/src/main/scala/djinni/generator.scala index 22adf43..a81ed9f 100644 --- a/src/main/scala/djinni/generator.scala +++ b/src/main/scala/djinni/generator.scala @@ -769,4 +769,23 @@ abstract class Generator(spec: Spec) { w.wl(" */") } } + + def deprecatedText(doc: Doc): Option[String] = { + val pattern = """\s*@deprecated\(?(.*?)\)?$""".r + for (l <- doc.lines) { + l match { + case pattern(message) => + return Some(message.trim()) + case _ => // no match + } + } + return None + } + + def writeDeprecated(w: IndentWriter, doc: Doc, annotation: String) { + deprecatedText(doc) match { + case Some(message) => w.wl(annotation.replace("", message)) + case None => + } + } }