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 <unordered_set>
20 #include <vector>
21
22 #include "AST.h"
23 #include "CompoundType.h"
24 #include "Lint.h"
25 #include "LintRegistry.h"
26 #include "Location.h"
27 #include "Scope.h"
28 #include "Type.h"
29
30 namespace android {
31
32 // if this pattern of separate execution for getDefinedTypes and getReferences is common, it should
33 // be abstacted into a recursivePass type function
lintUnionRecursively(const Scope * scope,std::unordered_set<const Type * > * visited,std::vector<Lint> * errors)34 static void lintUnionRecursively(const Scope* scope, std::unordered_set<const Type*>* visited,
35 std::vector<Lint>* errors) {
36 CHECK(scope->getParseStage() == Type::ParseStage::COMPLETED)
37 << "type parsing is not yet complete";
38
39 if (visited->find(scope) != visited->end()) return;
40 visited->insert(scope);
41
42 std::string lintExplanation =
43 "Union types should not be used since they are not supported in Java and are not type "
44 "safe. Prefer using safe_union instead.";
45 for (const auto* nextType : scope->getDefinedTypes()) {
46 if (!nextType->isCompoundType()) {
47 // if the type is not compound then it cannot be a union type, but can contain one.
48 lintUnionRecursively(static_cast<const Scope*>(nextType), visited, errors);
49 continue;
50 }
51
52 const CompoundType* compoundType = static_cast<const CompoundType*>(nextType);
53 if (compoundType->style() == CompoundType::Style::STYLE_UNION) {
54 errors->push_back(Lint(ERROR, compoundType->location())
55 << compoundType->typeName() << " is defined as a Union type.\n"
56 << lintExplanation << "\n");
57 continue;
58 }
59
60 // Not a union type, so must be struct or safe_union
61 // Definitely still in the same file.
62 lintUnionRecursively(compoundType, visited, errors);
63 }
64
65 for (const auto* nextRef : scope->getReferences()) {
66 if (!nextRef->get()->isCompoundType()) continue;
67
68 const CompoundType* compoundType = static_cast<const CompoundType*>(nextRef->get());
69 if (compoundType->style() == CompoundType::Style::STYLE_UNION) {
70 // The reference was not from this scope
71 if (!Location::inSameFile(scope->location(), nextRef->location())) continue;
72
73 // The type is defined in the same file. Will lint there.
74 if (Location::inSameFile(scope->location(), compoundType->location())) continue;
75
76 errors->push_back(Lint(ERROR, nextRef->location())
77 << "Reference to union type: " << compoundType->typeName()
78 << " located in " << compoundType->location().begin().filename()
79 << "\n"
80 << lintExplanation << "\n");
81 continue;
82 }
83
84 const status_t FOUND_UNION = 1;
85 status_t result = compoundType->recursivePass(
86 Type::ParseStage::COMPLETED,
87 [&](const Type* type) -> status_t {
88 if (!type->isCompoundType()) return OK;
89
90 const CompoundType* compoundType = static_cast<const CompoundType*>(type);
91 if (compoundType->style() == CompoundType::Style::STYLE_UNION) {
92 return FOUND_UNION;
93 }
94
95 // some other type of compound type, like struct/safeunion
96 return OK;
97 },
98 visited);
99
100 if (result == FOUND_UNION) {
101 // The struct contains a reference to a union somewhere
102 errors->push_back(Lint(ERROR, nextRef->location())
103 << "Reference to struct: " << compoundType->typeName()
104 << " located in " << compoundType->location().begin().filename()
105 << " contains a union type.\n"
106 << lintExplanation << "\n");
107 }
108 }
109 }
110
safeunionLint(const AST & ast,std::vector<Lint> * errors)111 static void safeunionLint(const AST& ast, std::vector<Lint>* errors) {
112 // Should lint if it contains a union type at any level
113 std::unordered_set<const Type*> visited;
114
115 lintUnionRecursively(&ast.getRootScope(), &visited, errors);
116 }
117
118 REGISTER_LINT(safeunionLint);
119
120 } // namespace android