Merge "Add lint for compound types containing < 2 fields"
diff --git a/lint/lints/emptyStructs.cpp b/lint/lints/emptyStructs.cpp
new file mode 100644
index 0000000..f95d55b
--- /dev/null
+++ b/lint/lints/emptyStructs.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include "AST.h"
+#include "CompoundType.h"
+#include "Lint.h"
+#include "LintRegistry.h"
+#include "Location.h"
+#include "Type.h"
+
+namespace android {
+
+static void checkSmallStructs(const AST& ast, std::vector<Lint>* errors) {
+    // Should lint if it contains a union type at any level
+    std::unordered_set<const Type*> visited;
+    ast.getRootScope().recursivePass(
+            Type::ParseStage::COMPLETED,
+            [&](const Type* type) -> status_t {
+                if (!type->isCompoundType()) return OK;
+
+                const CompoundType* compoundType = static_cast<const CompoundType*>(type);
+
+                // Will lint in the file that contains it
+                if (!Location::inSameFile(compoundType->location(),
+                                          ast.getRootScope().location())) {
+                    return OK;
+                }
+
+                if (compoundType->getReferences().size() == 0) {
+                    errors->push_back(
+                            Lint(ERROR, compoundType->location())
+                            << compoundType->typeName() << " contains no elements.\n"
+                            << "Prefer using android.hidl.safe_union@1.0::Monostate instead.");
+                } else if (compoundType->getReferences().size() == 1) {
+                    errors->push_back(Lint(ERROR, compoundType->location())
+                                      << compoundType->typeName() << " only contains 1 element.\n"
+                                      << "Prefer using the type directly since wrapping it adds "
+                                      << "memory and performance overhead.");
+                }
+
+                return OK;
+            },
+            &visited);
+}
+
+REGISTER_LINT(checkSmallStructs);
+
+}  // namespace android
\ No newline at end of file
diff --git a/lint/test/interfaces/import_types/1.1/types.hal b/lint/test/interfaces/import_types/1.1/types.hal
index 723c35e..ada4f67 100644
--- a/lint/test/interfaces/import_types/1.1/types.hal
+++ b/lint/test/interfaces/import_types/1.1/types.hal
@@ -20,4 +20,5 @@
 
 struct ContainsFoo {
     Foo fooInstance;
+    uint8_t preventLintForSingleElement;
 };
\ No newline at end of file
diff --git a/lint/test/interfaces/safeunion/1.0/IUnionInStruct.hal b/lint/test/interfaces/safeunion/1.0/IUnionInStruct.hal
index f2e3a82..654c280 100644
--- a/lint/test/interfaces/safeunion/1.0/IUnionInStruct.hal
+++ b/lint/test/interfaces/safeunion/1.0/IUnionInStruct.hal
@@ -22,5 +22,6 @@
             uint8_t number;
             int32_t otherNumber;
         } shouldOnlyLintDefinition;
+        uint8_t preventLintForSingleElement;
     };
 };
\ No newline at end of file
diff --git a/lint/test/interfaces/safeunion/1.1/types.hal b/lint/test/interfaces/safeunion/1.1/types.hal
index 6653cfb..f261834 100644
--- a/lint/test/interfaces/safeunion/1.1/types.hal
+++ b/lint/test/interfaces/safeunion/1.1/types.hal
@@ -20,4 +20,5 @@
 
 struct SomeStruct {
     InTypes unionType;
+    uint8_t preventLintForSingleElement;
 };
\ No newline at end of file
diff --git a/lint/test/interfaces/small_structs/1.0/IEmptyStruct.hal b/lint/test/interfaces/small_structs/1.0/IEmptyStruct.hal
new file mode 100644
index 0000000..12255a2
--- /dev/null
+++ b/lint/test/interfaces/small_structs/1.0/IEmptyStruct.hal
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package lint_test.small_structs@1.0;
+
+interface IEmptyStruct {
+    struct EmptyStruct {};
+};
\ No newline at end of file
diff --git a/lint/test/interfaces/small_structs/1.0/IEmptyUnion.hal b/lint/test/interfaces/small_structs/1.0/IEmptyUnion.hal
new file mode 100644
index 0000000..cbcb49f
--- /dev/null
+++ b/lint/test/interfaces/small_structs/1.0/IEmptyUnion.hal
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package lint_test.small_structs@1.0;
+
+interface IEmptyUnion {
+    union EmptyUnion {};
+};
\ No newline at end of file
diff --git a/lint/test/interfaces/small_structs/1.0/IReference.hal b/lint/test/interfaces/small_structs/1.0/IReference.hal
new file mode 100644
index 0000000..2496b30
--- /dev/null
+++ b/lint/test/interfaces/small_structs/1.0/IReference.hal
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package lint_test.small_structs@1.0;
+
+import ISingleStruct.SingleStruct;
+import IEmptyStruct.EmptyStruct;
+
+interface IReference {
+    referenceBadStruct(SingleStruct sStruct) generates (EmptyStruct eStruct);
+};
\ No newline at end of file
diff --git a/lint/test/interfaces/small_structs/1.0/ISingleStruct.hal b/lint/test/interfaces/small_structs/1.0/ISingleStruct.hal
new file mode 100644
index 0000000..61b0663
--- /dev/null
+++ b/lint/test/interfaces/small_structs/1.0/ISingleStruct.hal
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package lint_test.small_structs@1.0;
+
+interface ISingleStruct {
+    struct SingleStruct {
+        uint8_t singleField;
+    };
+};
\ No newline at end of file
diff --git a/lint/test/interfaces/small_structs/1.0/ISingleUnion.hal b/lint/test/interfaces/small_structs/1.0/ISingleUnion.hal
new file mode 100644
index 0000000..4d5874c
--- /dev/null
+++ b/lint/test/interfaces/small_structs/1.0/ISingleUnion.hal
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package lint_test.small_structs@1.0;
+
+interface ISingleUnion {
+    union SingleUnion {
+        uint8_t singleField;
+    };
+};
\ No newline at end of file
diff --git a/lint/test/main.cpp b/lint/test/main.cpp
index e72ff54..0dfb8f1 100644
--- a/lint/test/main.cpp
+++ b/lint/test/main.cpp
@@ -25,7 +25,9 @@
 #include "../LintRegistry.h"
 #include "Coordinator.h"
 
+using ::testing::Contains;
 using ::testing::ContainsRegex;
+using ::testing::Property;
 
 namespace android {
 class HidlLintTest : public ::testing::Test {
@@ -82,6 +84,15 @@
         EXPECT_THAT(errors[0].getMessage(), ContainsRegex(errorMsg)); \
     } while (false)
 
+#define EXPECT_A_LINT(interface, errorMsg)                                                   \
+    do {                                                                                     \
+        std::vector<Lint> errors;                                                            \
+        getLintsForHal(interface, &errors);                                                  \
+        EXPECT_LE(1, errors.size());                                                         \
+        if (errors.size() < 1) break;                                                        \
+        EXPECT_THAT(errors, Contains(Property(&Lint::getMessage, ContainsRegex(errorMsg)))); \
+    } while (false)
+
 TEST_F(HidlLintTest, OnewayLintTest) {
     // Has no errors (empty). Lint size should be 0.
     EXPECT_NO_LINT("lint_test.oneway@1.0::IEmpty");
@@ -152,4 +163,17 @@
     // Imports types.hal from same package with fully qualified name
     EXPECT_LINT("lint_test.import_types@1.1::IImport", "Redundant import");
 }
+
+TEST_F(HidlLintTest, SmallStructsTest) {
+    // Referencing bad structs should not lint
+    EXPECT_NO_LINT("lint_test.small_structs@1.0::IReference");
+
+    // Empty structs/unions should lint
+    EXPECT_LINT("lint_test.small_structs@1.0::IEmptyStruct", "contains no elements");
+    EXPECT_A_LINT("lint_test.small_structs@1.0::IEmptyUnion", "contains no elements");
+
+    // Structs/unions with single field should lint
+    EXPECT_LINT("lint_test.small_structs@1.0::ISingleStruct", "only contains 1 element");
+    EXPECT_A_LINT("lint_test.small_structs@1.0::ISingleUnion", "only contains 1 element");
+}
 }  // namespace android
\ No newline at end of file