1 /*
2  * Copyright (C) 2020, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "diagnostics.h"
17 
18 #include <gmock/gmock.h>
19 #include <gtest/gtest.h>
20 #include <string>
21 #include <vector>
22 
23 #include "aidl.h"
24 #include "parser.h"
25 #include "tests/fake_io_delegate.h"
26 
27 using android::aidl::AidlTypenames;
28 using android::aidl::DiagnosticID;
29 using android::aidl::Options;
30 using android::aidl::internals::load_and_validate_aidl;
31 using android::aidl::test::FakeIoDelegate;
32 using testing::internal::CaptureStderr;
33 using testing::internal::GetCapturedStderr;
34 
35 struct DiagnosticsTest : testing::Test {
ParseFilesDiagnosticsTest36   void ParseFiles(std::vector<std::pair<std::string, std::string>>&& files) {
37     ASSERT_TRUE(files.size() > 0);
38     const std::string main = files.begin()->first;
39     for (const auto& [file, contents] : files) {
40       io.SetFileContents(file, contents);
41     }
42     if (!enable_diagnostic) {
43       ASSERT_TRUE(expect_diagnostic);
44       enable_diagnostic = expect_diagnostic;
45     }
46     // emit diagnostics as warnings.
47     // "java" has no specific meaning here because we're testing CheckValid()
48     const Options options = Options::From("aidl " + optional_args + " -I . --lang java -o out -W" +
49                                           to_string(*enable_diagnostic) + " " + main);
50     CaptureStderr();
51     load_and_validate_aidl(main, options, io, &typenames, nullptr);
52     const std::string err = GetCapturedStderr();
53     if (expect_diagnostic) {
54       EXPECT_THAT(err, testing::HasSubstr("-W" + to_string(*expect_diagnostic)));
55     } else {
56       EXPECT_EQ("", err);
57     }
58   }
59 
60   AidlTypenames typenames;
61   FakeIoDelegate io;
62   std::string optional_args;
63   // The type of diagnostic to enable for the test. If expect_diagnostic is
64   // set, use the same value.
65   std::optional<DiagnosticID> enable_diagnostic;
66   // The expected diagnostic. Must be set.
67   std::optional<DiagnosticID> expect_diagnostic;
68 };
69 
TEST_F(DiagnosticsTest,const_name_ForEnumerator)70 TEST_F(DiagnosticsTest, const_name_ForEnumerator) {
71   expect_diagnostic = DiagnosticID::const_name;
72   ParseFiles({{"Foo.aidl", "enum Foo { foo }"}});
73 }
74 
TEST_F(DiagnosticsTest,const_name_ForConstants)75 TEST_F(DiagnosticsTest, const_name_ForConstants) {
76   expect_diagnostic = DiagnosticID::const_name;
77   ParseFiles({{"IFoo.aidl", "interface IFoo { const int foo = 1; }"}});
78 }
79 
TEST_F(DiagnosticsTest,interface_name)80 TEST_F(DiagnosticsTest, interface_name) {
81   expect_diagnostic = DiagnosticID::interface_name;
82   ParseFiles({{"Foo.aidl", "interface Foo { }"}});
83 }
84 
TEST_F(DiagnosticsTest,enum_explicit_default)85 TEST_F(DiagnosticsTest, enum_explicit_default) {
86   expect_diagnostic = DiagnosticID::enum_explicit_default;
87   ParseFiles({{"Foo.aidl", "parcelable Foo { E e; }"}, {"E.aidl", "enum E { A }"}});
88 }
89 
TEST_F(DiagnosticsTest,inout_parameter)90 TEST_F(DiagnosticsTest, inout_parameter) {
91   expect_diagnostic = DiagnosticID::inout_parameter;
92   ParseFiles({{"IFoo.aidl", "interface IFoo { void foo(inout Bar bar); }"},
93               {"Bar.aidl", "parcelable Bar {}"}});
94 }
95 
TEST_F(DiagnosticsTest,inout_parameter_SuppressAtMethodLevel)96 TEST_F(DiagnosticsTest, inout_parameter_SuppressAtMethodLevel) {
97   enable_diagnostic = DiagnosticID::inout_parameter;
98   expect_diagnostic = {};
99   ParseFiles({
100       {"IFoo.aidl",
101        "interface IFoo { @SuppressWarnings(value={\"inout-parameter\"}) void foo(inout Bar b); }"},
102       {"Bar.aidl", "parcelable Bar {}"},
103   });
104 }
105 
TEST_F(DiagnosticsTest,inout_parameter_SuppressAtDeclLevel)106 TEST_F(DiagnosticsTest, inout_parameter_SuppressAtDeclLevel) {
107   enable_diagnostic = DiagnosticID::inout_parameter;
108   expect_diagnostic = {};
109   ParseFiles({
110       {"IFoo.aidl",
111        "@SuppressWarnings(value={\"inout-parameter\"}) interface IFoo { void foo(inout Bar b); }"},
112       {"Bar.aidl", "parcelable Bar {}"},
113   });
114 }
115 
TEST_F(DiagnosticsTest,UnknownWarning)116 TEST_F(DiagnosticsTest, UnknownWarning) {
117   expect_diagnostic = DiagnosticID::unknown_warning;
118   ParseFiles({
119       {"IFoo.aidl", "@SuppressWarnings(value={\"blahblah\"}) interface IFoo { void foo(); }"},
120   });
121 }
122 
TEST_F(DiagnosticsTest,CantSuppressUnknownWarning)123 TEST_F(DiagnosticsTest, CantSuppressUnknownWarning) {
124   expect_diagnostic = DiagnosticID::unknown_warning;
125   ParseFiles({
126       {"IFoo.aidl",
127        "@SuppressWarnings(value={\"unknown-warning\"})\n"
128        "interface IFoo { @SuppressWarnings(value={\"blah-blah\"}) void foo(); }"},
129   });
130 }
131 
TEST_F(DiagnosticsTest,DontMixOnewayWithTwowayMethods)132 TEST_F(DiagnosticsTest, DontMixOnewayWithTwowayMethods) {
133   expect_diagnostic = DiagnosticID::mixed_oneway;
134   ParseFiles({
135       {"IFoo.aidl", "interface IFoo { void foo(); oneway void bar(); }"},
136   });
137 }
138 
TEST_F(DiagnosticsTest,DontMixOnewayWithTwowayMethodsSuppressedAtMethod)139 TEST_F(DiagnosticsTest, DontMixOnewayWithTwowayMethodsSuppressedAtMethod) {
140   enable_diagnostic = DiagnosticID::mixed_oneway;
141   expect_diagnostic = {};
142   ParseFiles({
143       {"IFoo.aidl",
144        "interface IFoo {\n"
145        "  void foo();\n"
146        "  @SuppressWarnings(value={\"mixed-oneway\"}) oneway void bar();\n"
147        "}"},
148   });
149 }
150 
TEST_F(DiagnosticsTest,OnewayInterfaceIsOkayWithSyntheticMethods)151 TEST_F(DiagnosticsTest, OnewayInterfaceIsOkayWithSyntheticMethods) {
152   optional_args = "--version 2";  // will add getInterfaceVersion() synthetic method
153   enable_diagnostic = DiagnosticID::mixed_oneway;
154   expect_diagnostic = {};
155   ParseFiles({
156       {"IFoo.aidl", "oneway interface IFoo { void foo(); }"},
157   });
158 }
159 
TEST_F(DiagnosticsTest,RedundantOnewayMethodAnnotationInOnewayInterface)160 TEST_F(DiagnosticsTest, RedundantOnewayMethodAnnotationInOnewayInterface) {
161   expect_diagnostic = DiagnosticID::redundant_oneway;
162   ParseFiles({
163       {"IFoo.aidl", "oneway interface IFoo { oneway void foo(int a); }"},
164   });
165 }
166 
TEST_F(DiagnosticsTest,RedundantOnewayMethodSuppressedAtMethod)167 TEST_F(DiagnosticsTest, RedundantOnewayMethodSuppressedAtMethod) {
168   enable_diagnostic = DiagnosticID::redundant_oneway;
169   expect_diagnostic = {};
170   ParseFiles({
171       {"IFoo.aidl",
172        "oneway interface IFoo {\n"
173        "  @SuppressWarnings(value={\"redundant-oneway\"}) oneway void bar();\n"
174        "}"},
175   });
176 }
177 
TEST_F(DiagnosticsTest,ArraysAsOutputParametersConsideredHarmful)178 TEST_F(DiagnosticsTest, ArraysAsOutputParametersConsideredHarmful) {
179   expect_diagnostic = DiagnosticID::out_array;
180   ParseFiles({
181       {"IFoo.aidl", "interface IFoo { void foo(out String[] ret); }"},
182   });
183 }
184 
TEST_F(DiagnosticsTest,file_descriptor)185 TEST_F(DiagnosticsTest, file_descriptor) {
186   expect_diagnostic = DiagnosticID::file_descriptor;
187   ParseFiles({{"IFoo.aidl",
188                "interface IFoo {\n"
189                "  void foo(in FileDescriptor fd);\n"
190                "}"}});
191 }
192 
TEST_F(DiagnosticsTest,out_nullable)193 TEST_F(DiagnosticsTest, out_nullable) {
194   expect_diagnostic = DiagnosticID::out_nullable;
195   ParseFiles({{"IFoo.aidl",
196                "interface IFoo {\n"
197                "  void foo(out @nullable Bar bar);\n"
198                "}"},
199               {"Bar.aidl", "parcelable Bar {}"}});
200 }
201 
TEST_F(DiagnosticsTest,inout_nullable)202 TEST_F(DiagnosticsTest, inout_nullable) {
203   expect_diagnostic = DiagnosticID::out_nullable;
204   ParseFiles({{"IFoo.aidl",
205                "interface IFoo {\n"
206                "  void foo(inout @nullable Bar bar);\n"
207                "}"},
208               {"Bar.aidl", "parcelable Bar {}"}});
209 }
210 
TEST_F(DiagnosticsTest,out_nullable_OkayForArrays)211 TEST_F(DiagnosticsTest, out_nullable_OkayForArrays) {
212   expect_diagnostic = DiagnosticID::out_array;  // not triggering out_nullable
213   ParseFiles({{"IFoo.aidl",
214                "interface IFoo {\n"
215                "  void foo(inout @nullable Bar[] bar1, out @nullable Bar[] bar2);\n"
216                "}"},
217               {"Bar.aidl", "parcelable Bar {}"}});
218 }
219 
TEST_F(DiagnosticsTest,RejectImportsCollisionWithTopLevelDecl)220 TEST_F(DiagnosticsTest, RejectImportsCollisionWithTopLevelDecl) {
221   expect_diagnostic = DiagnosticID::unique_import;
222   ParseFiles({{"p/IFoo.aidl",
223                "package p;\n"
224                "import q.IFoo;\n"  // should collide with previous import
225                "interface IFoo{}"},
226               {"q/IFoo.aidl", "package q; interface IFoo{}"}});
227 }
228 
TEST_F(DiagnosticsTest,RejectImportsCollision)229 TEST_F(DiagnosticsTest, RejectImportsCollision) {
230   expect_diagnostic = DiagnosticID::unique_import;
231   ParseFiles({{"p/IFoo.aidl",
232                "package p;\n"
233                "import q.IBar;\n"
234                "import r.IBar;\n"  // should collide with previous import
235                "interface IFoo{}"},
236               {"q/IBar.aidl", "package q; interface IBar{}"},
237               {"r/IBar.aidl", "package r; interface IBar{}"}});
238 }
239 
TEST_F(DiagnosticsTest,AllowImportingSelf)240 TEST_F(DiagnosticsTest, AllowImportingSelf) {
241   enable_diagnostic = DiagnosticID::unique_import;
242   expect_diagnostic = {};
243   ParseFiles({{"p/IFoo.aidl",
244                "package p;\n"
245                "import p.IFoo;\n"
246                "interface IFoo{}"}});
247 }
248 
TEST_F(DiagnosticsTest,RedundantImports)249 TEST_F(DiagnosticsTest, RedundantImports) {
250   expect_diagnostic = DiagnosticID::unique_import;
251   ParseFiles({{"p/IFoo.aidl",
252                "package p;\n"
253                "import q.IBar;\n"
254                "import q.IBar;\n"
255                "interface IFoo{}"},
256               {"q/IBar.aidl", "package q; interface IBar{}"}});
257 }
258 
TEST_F(DiagnosticsTest,UntypedCollectionInterface)259 TEST_F(DiagnosticsTest, UntypedCollectionInterface) {
260   expect_diagnostic = DiagnosticID::untyped_collection;
261   ParseFiles({{"IFoo.aidl", "interface IFoo { void foo(in Map m); }"}});
262 }
263 
TEST_F(DiagnosticsTest,UntypedCollectionParcelable)264 TEST_F(DiagnosticsTest, UntypedCollectionParcelable) {
265   expect_diagnostic = DiagnosticID::untyped_collection;
266   ParseFiles({{"Foo.aidl", "parcelable Foo { Map m; }"}});
267 }
268 
TEST_F(DiagnosticsTest,UntypedCollectionUnion)269 TEST_F(DiagnosticsTest, UntypedCollectionUnion) {
270   expect_diagnostic = DiagnosticID::untyped_collection;
271   ParseFiles({{"Foo.aidl", "union Foo { List l; }"}});
272 }
273 
TEST_F(DiagnosticsTest,UntypedCollectionInTypeArg)274 TEST_F(DiagnosticsTest, UntypedCollectionInTypeArg) {
275   expect_diagnostic = DiagnosticID::untyped_collection;
276   ParseFiles({{"IFoo.aidl", "interface IFoo { void foo(in Bar<Map> m); }"},
277               {"Bar.aidl", "parcelable Bar<T> {}"}});
278 }
279 
TEST_F(DiagnosticsTest,PermissionMissing)280 TEST_F(DiagnosticsTest, PermissionMissing) {
281   expect_diagnostic = DiagnosticID::missing_permission_annotation;
282   ParseFiles({{"IFoo.aidl", "interface IFoo { void food(); }"}});
283 }
284 
TEST_F(DiagnosticsTest,PermissionMethod)285 TEST_F(DiagnosticsTest, PermissionMethod) {
286   enable_diagnostic = DiagnosticID::missing_permission_annotation;
287   expect_diagnostic = {};
288   ParseFiles({{"IFoo.aidl", "interface IFoo { @EnforcePermission(\"INTERNET\") void food(); }"}});
289 }
290 
TEST_F(DiagnosticsTest,PermissionMethodMissing)291 TEST_F(DiagnosticsTest, PermissionMethodMissing) {
292   expect_diagnostic = DiagnosticID::missing_permission_annotation;
293   ParseFiles({{"IFoo.aidl",
294                "interface IFoo { @EnforcePermission(\"INTERNET\") void food(); void foo2(); }"}});
295 }
296 
TEST_F(DiagnosticsTest,PermissionInterface)297 TEST_F(DiagnosticsTest, PermissionInterface) {
298   enable_diagnostic = DiagnosticID::missing_permission_annotation;
299   expect_diagnostic = {};
300   ParseFiles({{"IFoo.aidl", "@EnforcePermission(\"INTERNET\") interface IFoo { void food(); }"}});
301 }
302 
TEST_F(DiagnosticsTest,RedundantPrefixConstantInterface)303 TEST_F(DiagnosticsTest, RedundantPrefixConstantInterface) {
304   enable_diagnostic = DiagnosticID::redundant_name;
305   expect_diagnostic = DiagnosticID::redundant_name;
306   ParseFiles({{"SomethingStatus.aidl", "interface SomethingStatus { const int STATUS_ONE = 1; }"}});
307 }
308 
TEST_F(DiagnosticsTest,RedundantPrefixConstantParcelable)309 TEST_F(DiagnosticsTest, RedundantPrefixConstantParcelable) {
310   enable_diagnostic = DiagnosticID::redundant_name;
311   expect_diagnostic = DiagnosticID::redundant_name;
312   ParseFiles(
313       {{"SomethingStatus.aidl", "parcelable SomethingStatus { const int STATUS_ONE = 1; }"}});
314 }
315 
TEST_F(DiagnosticsTest,RedundantSuffixConstantParcelable)316 TEST_F(DiagnosticsTest, RedundantSuffixConstantParcelable) {
317   enable_diagnostic = DiagnosticID::redundant_name;
318   expect_diagnostic = DiagnosticID::redundant_name;
319   ParseFiles(
320       {{"SomethingStatus.aidl", "parcelable SomethingStatus { const int ONE_STATUS = 1; }"}});
321 }
322 
TEST_F(DiagnosticsTest,RedundantSuffixConstantParcelable2)323 TEST_F(DiagnosticsTest, RedundantSuffixConstantParcelable2) {
324   enable_diagnostic = DiagnosticID::redundant_name;
325   expect_diagnostic = DiagnosticID::redundant_name;
326   ParseFiles(
327       {{"SomethingStatus.aidl", "parcelable SomethingStatus { const int ONE_SOMETHING = 1; }"}});
328 }
329 
TEST_F(DiagnosticsTest,RedundantConstantUnion)330 TEST_F(DiagnosticsTest, RedundantConstantUnion) {
331   enable_diagnostic = DiagnosticID::redundant_name;
332   expect_diagnostic = DiagnosticID::redundant_name;
333   ParseFiles({{"SomethingStatus.aidl",
334                "union SomethingStatus { const int ONE_SOMETHING = 1; int a; int b;}"}});
335 }
336 
TEST_F(DiagnosticsTest,RedundantPrefixEnum)337 TEST_F(DiagnosticsTest, RedundantPrefixEnum) {
338   enable_diagnostic = DiagnosticID::redundant_name;
339   expect_diagnostic = DiagnosticID::redundant_name;
340   ParseFiles({{"SomethingStatus.aidl", "enum SomethingStatus { STATUS_ONE = 1, }"}});
341 }
342 
TEST_F(DiagnosticsTest,RedundantSuffixEnum)343 TEST_F(DiagnosticsTest, RedundantSuffixEnum) {
344   enable_diagnostic = DiagnosticID::redundant_name;
345   expect_diagnostic = DiagnosticID::redundant_name;
346   ParseFiles({{"SomethingStatus.aidl", "enum SomethingStatus { ONE_STATUS = 1, }"}});
347 }
348 
TEST_F(DiagnosticsTest,RedundantSuffixEnum2)349 TEST_F(DiagnosticsTest, RedundantSuffixEnum2) {
350   enable_diagnostic = DiagnosticID::redundant_name;
351   expect_diagnostic = DiagnosticID::redundant_name;
352   ParseFiles({{"SomethingStatus.aidl", "enum SomethingStatus { ONE_SOMETHING = 1, }"}});
353 }
354