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