Skip to content

Commit 0048a24

Browse files
committed
Implement BindgenReportingSpec
1 parent ff5491e commit 0048a24

File tree

3 files changed

+137
-78
lines changed

3 files changed

+137
-78
lines changed

bindgen/ir/IR.cpp

+50-40
Original file line numberDiff line numberDiff line change
@@ -101,63 +101,73 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) {
101101

102102
std::string objectName = handleReservedWords(ir.objectName);
103103

104-
if (!ir.libObjEmpty()) {
104+
bool isLibObjectEmpty = ir.libObjEmpty();
105+
106+
if (!isLibObjectEmpty) {
105107
if (!ir.linkName.empty()) {
106108
s << "@native.link(\"" << ir.linkName << "\")\n";
107109
}
108110

109111
s << "@native.extern\n"
110112
<< "object " << objectName << " {\n";
113+
}
111114

112-
for (const auto &typeDef : ir.typeDefs) {
113-
if (ir.shouldOutput(typeDef)) {
114-
s << *typeDef;
115-
if (typeDef->getType()) {
116-
auto *structOrUnion =
117-
dynamic_cast<StructOrUnion *>(typeDef->getType().get());
118-
if (structOrUnion &&
119-
structOrUnion->hasIllegalUsageOfOpaqueType()) {
120-
llvm::errs()
121-
<< "Error: record " << structOrUnion->getName()
122-
<< " has field of incomplete type. Declarations "
123-
"that use this type may not work properly.\n";
124-
llvm::errs().flush();
125-
}
115+
for (const auto &typeDef : ir.typeDefs) {
116+
if (ir.shouldOutput(typeDef)) {
117+
s << *typeDef;
118+
if (typeDef->getType()) {
119+
auto *structOrUnion =
120+
dynamic_cast<StructOrUnion *>(typeDef->getType().get());
121+
if (structOrUnion &&
122+
structOrUnion->hasIllegalUsageOfOpaqueType()) {
123+
llvm::errs()
124+
<< "Error: record " << structOrUnion->getName()
125+
<< " has field of incomplete type. Declarations "
126+
"that use this type may not work properly.\n";
127+
llvm::errs().flush();
126128
}
127129
}
130+
} else if (isAliasForOpaqueType(typeDef.get()) &&
131+
ir.inMainFile(*typeDef)) {
132+
llvm::errs() << "Warning: type alias " + typeDef->getName()
133+
<< " is skipped because it is unused alias for "
134+
"incomplete type."
135+
<< "\n";
136+
llvm::errs().flush();
128137
}
138+
}
129139

130-
for (const auto &variable : ir.variables) {
131-
if (!variable->hasIllegalUsageOfOpaqueType()) {
132-
s << *variable;
133-
} else {
134-
llvm::errs() << "Error: Variable " << variable->getName()
135-
<< " is skipped because it has incomplete type.\n";
136-
}
140+
for (const auto &variable : ir.variables) {
141+
if (!variable->hasIllegalUsageOfOpaqueType()) {
142+
s << *variable;
143+
} else {
144+
llvm::errs() << "Error: Variable " << variable->getName()
145+
<< " is skipped because it has incomplete type.\n";
137146
}
147+
}
138148

139-
for (const auto &varDefine : ir.varDefines) {
140-
if (!varDefine->hasIllegalUsageOfOpaqueType()) {
141-
s << *varDefine;
142-
} else {
143-
llvm::errs() << "Error: Variable alias " << varDefine->getName()
144-
<< " is skipped because it has incomplete type\n";
145-
llvm::errs().flush();
146-
}
149+
for (const auto &varDefine : ir.varDefines) {
150+
if (!varDefine->hasIllegalUsageOfOpaqueType()) {
151+
s << *varDefine;
152+
} else {
153+
llvm::errs() << "Error: Variable alias " << varDefine->getName()
154+
<< " is skipped because it has incomplete type.\n";
155+
llvm::errs().flush();
147156
}
157+
}
148158

149-
for (const auto &func : ir.functions) {
150-
if (!func->isLegalScalaNativeFunction()) {
151-
llvm::errs()
152-
<< "Warning: Function " << func->getName()
153-
<< " is skipped because Scala Native does not support "
154-
"passing structs and arrays by value.\n";
155-
llvm::errs().flush();
156-
} else {
157-
s << *func;
158-
}
159+
for (const auto &func : ir.functions) {
160+
if (!func->isLegalScalaNativeFunction()) {
161+
llvm::errs() << "Warning: Function " << func->getName()
162+
<< " is skipped because Scala Native does not support "
163+
"passing structs and arrays by value.\n";
164+
llvm::errs().flush();
165+
} else {
166+
s << *func;
159167
}
168+
}
160169

170+
if (!isLibObjectEmpty) {
161171
s << "}\n\n";
162172
}
163173

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package org.scalanative.bindgen
2+
3+
import java.io.{File, PrintWriter}
4+
5+
import org.scalatest.FunSpec
6+
7+
class BindgenReportingSpec extends FunSpec {
8+
describe("Bindgen") {
9+
10+
val bindgenPath = System.getProperty("bindgen.path")
11+
12+
def writeToFile(file: File, input: String): Unit = {
13+
new PrintWriter(file) {
14+
try {
15+
write(input)
16+
} finally {
17+
close()
18+
}
19+
}
20+
}
21+
22+
def bindgen(input: String): Bindings = {
23+
val tempFile = File.createTempFile("scala-native-bindgen-tests", ".h")
24+
try {
25+
writeToFile(tempFile, input)
26+
27+
Bindgen()
28+
.bindgenExecutable(new File(bindgenPath))
29+
.header(tempFile)
30+
.name("BindgenTests")
31+
.link("bindgentests")
32+
.packageName("org.scalanative.bindgen.samples")
33+
.excludePrefix("__")
34+
.generate()
35+
36+
} finally {
37+
tempFile.delete()
38+
}
39+
}
40+
41+
it("Skips variable with opaque type") {
42+
val bindings =
43+
bindgen(input = """struct undefinedStruct;
44+
|extern struct undefinedStruct removedExtern;
45+
|#define removedExternAlias removedExtern
46+
|""".stripMargin)
47+
assert(
48+
bindings.errs == """Error: Variable removedExtern is skipped because it has incomplete type.
49+
|Error: Variable alias removedExternAlias is skipped because it has incomplete type.""".stripMargin)
50+
51+
}
52+
53+
it("Skips function that has parameter of opaque type") {
54+
val bindings =
55+
bindgen(input = """struct undefinedStruct;
56+
|void useUndefinedStruct(struct undefinedStruct);
57+
|""".stripMargin)
58+
assert(
59+
bindings.errs == "Warning: Function useUndefinedStruct is skipped because Scala Native does not " +
60+
"support passing structs and arrays by value.")
61+
}
62+
63+
it("Skips function that has return value of opaque type") {
64+
val bindings =
65+
bindgen(input = """struct undefinedStruct;
66+
|typedef struct undefinedStruct aliasForUndefinedStruct;
67+
|aliasForUndefinedStruct returnUndefinedStruct();
68+
|""".stripMargin)
69+
assert(
70+
bindings.errs == "Warning: Function returnUndefinedStruct is skipped because Scala Native does not " +
71+
"support passing structs and arrays by value.")
72+
}
73+
74+
it("Skips unused alias for opaque type") {
75+
val bindings =
76+
bindgen(input = """union undefinedUnion;
77+
|typedef union undefinedUnion aliasForUndefinedUnion;
78+
|""".stripMargin)
79+
assert(
80+
bindings.errs == "Warning: type alias aliasForUndefinedUnion is skipped because it is unused alias for incomplete type.")
81+
}
82+
}
83+
}

tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala

+4-38
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,16 @@ class BindgenSpec extends FunSpec {
1717
assert(new File(bindgenPath).exists, s"$bindgenPath does not exist")
1818
}
1919

20-
/**
21-
* @return errors
22-
*/
23-
def bindgen(inputFile: File,
24-
name: String,
25-
outputFile: Option[File] = None): String = {
26-
val bindings = Bindgen()
20+
def bindgen(inputFile: File, name: String, outputFile: File): Unit = {
21+
Bindgen()
2722
.bindgenExecutable(new File(bindgenPath))
2823
.header(inputFile)
2924
.name(name)
3025
.link("bindgentests")
3126
.packageName("org.scalanative.bindgen.samples")
3227
.excludePrefix("__")
3328
.generate()
34-
35-
if (outputFile.isDefined) {
36-
bindings.writeToFile(outputFile.get)
37-
}
38-
39-
bindings.errs
29+
.writeToFile(outputFile)
4030
}
4131

4232
def contentOf(file: File) =
@@ -48,35 +38,11 @@ class BindgenSpec extends FunSpec {
4838
val expected = new File(inputDirectory, testName + ".scala")
4939
val output = new File(outputDir, testName + ".scala")
5040

51-
bindgen(input, testName, Option(output))
41+
bindgen(input, testName, output)
5242

5343
assert(output.exists())
5444
assert(contentOf(output) == contentOf(expected))
5545
}
56-
57-
it(s"should print correct warnings for ${input.getName}") {
58-
val testName = input.getName.replace(".h", "")
59-
60-
val errs = bindgen(input, testName)
61-
62-
testName match {
63-
case "LiteralDefine" =>
64-
assert(
65-
errs == "Warning: integer value does not fit into 8 bytes: 18446744073709551615\n" +
66-
"Warning: integer value does not fit into 8 bytes: 9223372036854775809")
67-
case "OpaqueTypes" =>
68-
assert(
69-
errs == "Error: Variable removedExtern is skipped because it has incomplete type.\n" +
70-
"Error: Variable alias removedExternAlias is skipped because it has incomplete type\n" +
71-
"Warning: Function useUndefinedStruct is skipped because Scala Native does not support passing structs and arrays by value.")
72-
case "Function" =>
73-
assert(
74-
errs == "Warning: Function acceptsStructValue is skipped because Scala Native does not support passing structs and arrays by value.\n" +
75-
"Warning: Function returnsStructValue is skipped because Scala Native does not support passing structs and arrays by value.\n" +
76-
"Warning: Function acceptsUnionValue is skipped because Scala Native does not support passing structs and arrays by value.")
77-
case _ => assert(errs == "")
78-
}
79-
}
8046
}
8147
}
8248
}

0 commit comments

Comments
 (0)