diff --git a/bindgen/ir/IR.cpp b/bindgen/ir/IR.cpp index 24d97f6..1e6ae71 100644 --- a/bindgen/ir/IR.cpp +++ b/bindgen/ir/IR.cpp @@ -101,63 +101,73 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { std::string objectName = handleReservedWords(ir.objectName); - if (!ir.libObjEmpty()) { + bool isLibObjectEmpty = ir.libObjEmpty(); + + if (!isLibObjectEmpty) { if (!ir.linkName.empty()) { s << "@native.link(\"" << ir.linkName << "\")\n"; } s << "@native.extern\n" << "object " << objectName << " {\n"; + } - for (const auto &typeDef : ir.typeDefs) { - if (ir.shouldOutput(typeDef)) { - s << *typeDef; - if (typeDef->getType()) { - auto *structOrUnion = - dynamic_cast(typeDef->getType().get()); - if (structOrUnion && - structOrUnion->hasIllegalUsageOfOpaqueType()) { - llvm::errs() - << "Error: record " << structOrUnion->getName() - << " has field of incomplete type. Declarations " - "that use this type may not work properly.\n"; - llvm::errs().flush(); - } + for (const auto &typeDef : ir.typeDefs) { + if (ir.shouldOutput(typeDef)) { + s << *typeDef; + if (typeDef->getType()) { + auto *structOrUnion = + dynamic_cast(typeDef->getType().get()); + if (structOrUnion && + structOrUnion->hasIllegalUsageOfOpaqueType()) { + llvm::errs() + << "Error: record " << structOrUnion->getName() + << " has field of incomplete type. Declarations " + "that use this type may not work properly.\n"; + llvm::errs().flush(); } } + } else if (isAliasForOpaqueType(typeDef.get()) && + ir.inMainFile(*typeDef)) { + llvm::errs() << "Warning: type alias " + typeDef->getName() + << " is skipped because it is unused alias for " + "incomplete type." + << "\n"; + llvm::errs().flush(); } + } - for (const auto &variable : ir.variables) { - if (!variable->hasIllegalUsageOfOpaqueType()) { - s << *variable; - } else { - llvm::errs() << "Error: Variable " << variable->getName() - << " is skipped because it has incomplete type.\n"; - } + for (const auto &variable : ir.variables) { + if (!variable->hasIllegalUsageOfOpaqueType()) { + s << *variable; + } else { + llvm::errs() << "Error: Variable " << variable->getName() + << " is skipped because it has incomplete type.\n"; } + } - for (const auto &varDefine : ir.varDefines) { - if (!varDefine->hasIllegalUsageOfOpaqueType()) { - s << *varDefine; - } else { - llvm::errs() << "Error: Variable alias " << varDefine->getName() - << " is skipped because it has incomplete type\n"; - llvm::errs().flush(); - } + for (const auto &varDefine : ir.varDefines) { + if (!varDefine->hasIllegalUsageOfOpaqueType()) { + s << *varDefine; + } else { + llvm::errs() << "Error: Variable alias " << varDefine->getName() + << " is skipped because it has incomplete type.\n"; + llvm::errs().flush(); } + } - for (const auto &func : ir.functions) { - if (!func->isLegalScalaNativeFunction()) { - llvm::errs() - << "Warning: Function " << func->getName() - << " is skipped because Scala Native does not support " - "passing structs and arrays by value.\n"; - llvm::errs().flush(); - } else { - s << *func; - } + for (const auto &func : ir.functions) { + if (!func->isLegalScalaNativeFunction()) { + llvm::errs() << "Warning: Function " << func->getName() + << " is skipped because Scala Native does not support " + "passing structs and arrays by value.\n"; + llvm::errs().flush(); + } else { + s << *func; } + } + if (!isLibObjectEmpty) { s << "}\n\n"; } diff --git a/tests/src/test/scala/org/scalanative/bindgen/BindgenReportingSpec.scala b/tests/src/test/scala/org/scalanative/bindgen/BindgenReportingSpec.scala new file mode 100644 index 0000000..1ec7cc1 --- /dev/null +++ b/tests/src/test/scala/org/scalanative/bindgen/BindgenReportingSpec.scala @@ -0,0 +1,83 @@ +package org.scalanative.bindgen + +import java.io.{File, PrintWriter} + +import org.scalatest.FunSpec + +class BindgenReportingSpec extends FunSpec { + describe("Bindgen") { + + val bindgenPath = System.getProperty("bindgen.path") + + def writeToFile(file: File, input: String): Unit = { + new PrintWriter(file) { + try { + write(input) + } finally { + close() + } + } + } + + def bindgen(input: String): Bindings = { + val tempFile = File.createTempFile("scala-native-bindgen-tests", ".h") + try { + writeToFile(tempFile, input) + + Bindgen() + .bindgenExecutable(new File(bindgenPath)) + .header(tempFile) + .name("BindgenTests") + .link("bindgentests") + .packageName("org.scalanative.bindgen.samples") + .excludePrefix("__") + .generate() + + } finally { + tempFile.delete() + } + } + + it("Skips variable with opaque type") { + val bindings = + bindgen(input = """struct undefinedStruct; + |extern struct undefinedStruct removedExtern; + |#define removedExternAlias removedExtern + |""".stripMargin) + assert( + bindings.errs == """Error: Variable removedExtern is skipped because it has incomplete type. + |Error: Variable alias removedExternAlias is skipped because it has incomplete type.""".stripMargin) + + } + + it("Skips function that has parameter of opaque type") { + val bindings = + bindgen(input = """struct undefinedStruct; + |void useUndefinedStruct(struct undefinedStruct); + |""".stripMargin) + assert( + bindings.errs == "Warning: Function useUndefinedStruct is skipped because Scala Native does not " + + "support passing structs and arrays by value.") + } + + it("Skips function that has return value of opaque type") { + val bindings = + bindgen(input = """struct undefinedStruct; + |typedef struct undefinedStruct aliasForUndefinedStruct; + |aliasForUndefinedStruct returnUndefinedStruct(); + |""".stripMargin) + assert( + bindings.errs == "Warning: Function returnUndefinedStruct is skipped because Scala Native does not " + + "support passing structs and arrays by value.") + } + + it("Skips unused alias for opaque type") { + val bindings = + bindgen(input = """union undefinedUnion; + |typedef union undefinedUnion aliasForUndefinedUnion; + |""".stripMargin) + assert( + bindings.errs == "Warning: type alias aliasForUndefinedUnion is skipped because it is unused alias for incomplete type.") + } + } +} diff --git a/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala b/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala index 8f72e23..fab3e2e 100644 --- a/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala +++ b/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala @@ -17,13 +17,8 @@ class BindgenSpec extends FunSpec { assert(new File(bindgenPath).exists, s"$bindgenPath does not exist") } - /** - * @return errors - */ - def bindgen(inputFile: File, - name: String, - outputFile: Option[File] = None): String = { - val bindings = Bindgen() + def bindgen(inputFile: File, name: String, outputFile: File): Unit = { + Bindgen() .bindgenExecutable(new File(bindgenPath)) .header(inputFile) .name(name) @@ -31,12 +26,7 @@ class BindgenSpec extends FunSpec { .packageName("org.scalanative.bindgen.samples") .excludePrefix("__") .generate() - - if (outputFile.isDefined) { - bindings.writeToFile(outputFile.get) - } - - bindings.errs + .writeToFile(outputFile) } def contentOf(file: File) = @@ -48,35 +38,11 @@ class BindgenSpec extends FunSpec { val expected = new File(inputDirectory, testName + ".scala") val output = new File(outputDir, testName + ".scala") - bindgen(input, testName, Option(output)) + bindgen(input, testName, output) assert(output.exists()) assert(contentOf(output) == contentOf(expected)) } - - it(s"should print correct warnings for ${input.getName}") { - val testName = input.getName.replace(".h", "") - - val errs = bindgen(input, testName) - - testName match { - case "LiteralDefine" => - assert( - errs == "Warning: integer value does not fit into 8 bytes: 18446744073709551615\n" + - "Warning: integer value does not fit into 8 bytes: 9223372036854775809") - case "OpaqueTypes" => - assert( - errs == "Error: Variable removedExtern is skipped because it has incomplete type.\n" + - "Error: Variable alias removedExternAlias is skipped because it has incomplete type\n" + - "Warning: Function useUndefinedStruct is skipped because Scala Native does not support passing structs and arrays by value.") - case "Function" => - assert( - errs == "Warning: Function acceptsStructValue is skipped because Scala Native does not support passing structs and arrays by value.\n" + - "Warning: Function returnsStructValue is skipped because Scala Native does not support passing structs and arrays by value.\n" + - "Warning: Function acceptsUnionValue is skipped because Scala Native does not support passing structs and arrays by value.") - case _ => assert(errs == "") - } - } } } }