1 /*
2 * Copyright (C) 2019 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
17 #include <android-base/logging.h>
18
19 #include <iostream>
20 #include <vector>
21
22 #include "AST.h"
23 #include "Interface.h"
24 #include "Lint.h"
25 #include "LintRegistry.h"
26 #include "Method.h"
27
28 namespace android {
29
30 enum InterfaceMethodType { NONE = 0, ONEWAY = 1, TWOWAY = 2, MIXED = ONEWAY | TWOWAY };
31
32 // To use InterfaceMethodType as a bitfield
operator |(InterfaceMethodType lhs,InterfaceMethodType rhs)33 static inline InterfaceMethodType operator|(InterfaceMethodType lhs, InterfaceMethodType rhs) {
34 using T = std::underlying_type_t<InterfaceMethodType>;
35 return static_cast<InterfaceMethodType>(static_cast<T>(lhs) | static_cast<T>(rhs));
36 }
37
38 // This function returns what kind of methods the interface contains
getInterfaceOnewayType(const Interface & iface,bool includeParentMethods)39 static InterfaceMethodType getInterfaceOnewayType(const Interface& iface,
40 bool includeParentMethods) {
41 InterfaceMethodType onewayType = NONE;
42
43 const std::vector<Method*>& methods = iface.userDefinedMethods();
44 if (methods.empty()) {
45 return (iface.superType() != nullptr && includeParentMethods)
46 ? getInterfaceOnewayType(*iface.superType(), true)
47 : NONE;
48 }
49
50 for (auto method : methods) {
51 if (method->isOneway()) {
52 onewayType = onewayType | ONEWAY;
53 } else {
54 onewayType = onewayType | TWOWAY;
55 }
56
57 if (onewayType == MIXED) {
58 return onewayType;
59 }
60 }
61
62 if (includeParentMethods) {
63 onewayType = onewayType | getInterfaceOnewayType(*iface.superType(), true);
64 }
65
66 CHECK(onewayType != NONE) << "Functions are neither oneway nor non-oneway?: "
67 << iface.location();
68
69 return onewayType;
70 }
71
onewayLint(const AST & ast,std::vector<Lint> * errors)72 static void onewayLint(const AST& ast, std::vector<Lint>* errors) {
73 const Interface* iface = ast.getInterface();
74 if (iface == nullptr) {
75 // No interfaces so no oneway methods.
76 return;
77 }
78
79 InterfaceMethodType ifaceType = getInterfaceOnewayType(*iface, false);
80 if (ifaceType == NONE) {
81 // Can occur for empty interfaces
82 return;
83 }
84
85 std::string lintExplanation =
86 "Since a function being oneway/non-oneway has large implications on the threading "
87 "model and how client code needs to call an interface, it can be confusing/problematic "
88 "when similar looking calls to the same interface result in wildly different "
89 "behavior.\n";
90 if (ifaceType == MIXED) {
91 // This interface in itself is MIXED. Flag to user.
92 errors->push_back(Lint(WARNING, iface->location())
93 << iface->typeName() << " has both oneway and non-oneway methods. "
94 << "It should only contain one of the two.\n"
95 << lintExplanation);
96 return;
97 }
98
99 InterfaceMethodType parentType = getInterfaceOnewayType(*iface->superType(), true);
100 if (parentType == NONE || parentType == MIXED) {
101 // parents are mixed or don't have type, while this interface has only one type of
102 // method. parentType => MIXED would have generated a lint on the parent interface.
103 return;
104 }
105
106 if (parentType != ifaceType) {
107 // type mismatch raise warning.
108 errors->push_back(Lint(WARNING, iface->location())
109 << iface->typeName() << " should only have "
110 << (parentType == ONEWAY ? "oneway" : "non-oneway")
111 << " methods like its parent.\n"
112 << lintExplanation);
113 }
114 }
115
116 REGISTER_LINT(onewayLint);
117
118 } // namespace android
119