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