1 /*
2  * Copyright (C) 2016 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 #pragma once
18 
19 #include <stdio.h>
20 
21 #include <map>
22 #include <mutex>
23 #include <set>
24 #include <string>
25 #include <vector>
26 
27 #include <llvm/ADT/StringRef.h>
28 
29 #include "Arch.h"
30 #include "CompilationType.h"
31 #include "Utils.h"
32 
33 namespace clang {
34 class ASTContext;
35 class Decl;
36 }
37 
38 enum class DeclarationType {
39   function,
40   variable,
41   inconsistent,
42 };
43 
44 struct AvailabilityValues {
45   bool future = false;
46   int introduced = 0;
47   int deprecated = 0;
48   int obsoleted = 0;
49 
emptyAvailabilityValues50   bool empty() const {
51     return !(future || introduced || deprecated || obsoleted);
52   }
53 
54   bool operator==(const AvailabilityValues& rhs) const {
55     return std::tie(introduced, deprecated, obsoleted) ==
56            std::tie(rhs.introduced, rhs.deprecated, rhs.obsoleted);
57   }
58 
59   bool operator!=(const AvailabilityValues& rhs) const {
60     return !(*this == rhs);
61   }
62 };
63 
64 std::string to_string(const AvailabilityValues& av);
65 
66 struct DeclarationAvailability {
67   AvailabilityValues global_availability;
68   ArchMap<AvailabilityValues> arch_availability;
69 
emptyDeclarationAvailability70   bool empty() const {
71     if (!global_availability.empty()) {
72       return false;
73     }
74 
75     for (const auto& it : arch_availability) {
76       if (!it.second.empty()) {
77         return false;
78       }
79     }
80 
81     return true;
82   }
83 
84   bool operator==(const DeclarationAvailability& rhs) const {
85     return std::tie(global_availability, arch_availability) ==
86            std::tie(rhs.global_availability, rhs.arch_availability);
87   }
88 
89   bool operator!=(const DeclarationAvailability& rhs) const {
90     return !(*this == rhs);
91   }
92 
93   // Returns false if the availability declarations conflict.
94   bool merge(const DeclarationAvailability& other);
95 };
96 
97 std::string to_string(const DeclarationAvailability& decl_av);
98 
99 struct FileLocation {
100   unsigned line;
101   unsigned column;
102 
103   bool operator<(const FileLocation& rhs) const {
104     return std::tie(line, column) < std::tie(rhs.line, rhs.column);
105   }
106 
107   bool operator==(const FileLocation& rhs) const {
108     return std::tie(line, column) == std::tie(rhs.line, rhs.column);
109   }
110 };
111 
112 struct Location {
113   std::string filename;
114   FileLocation start;
115   FileLocation end;
116 
117   bool operator<(const Location& rhs) const {
118     return std::tie(filename, start, end) < std::tie(rhs.filename, rhs.start, rhs.end);
119   }
120 };
121 
122 std::string to_string(const Location& loc);
123 
124 struct Declaration {
125   std::string name;
126   Location location;
127 
128   bool is_extern;
129   bool is_definition;
130   bool no_guard;
131   std::map<CompilationType, DeclarationAvailability> availability;
132 
133   bool calculateAvailability(DeclarationAvailability* output) const;
134   bool operator<(const Declaration& rhs) const {
135     return location < rhs.location;
136   }
137 
138   void dump(const std::string& base_path = "", FILE* out = stdout, unsigned indent = 0) const {
139     std::string indent_str(indent, ' ');
140     fprintf(out, "%s", indent_str.c_str());
141 
142     fprintf(out, "%s ", is_extern ? "extern" : "static");
143     fprintf(out, "%s ", is_definition ? "definition" : "declaration");
144     if (no_guard) {
145       fprintf(out, "no_guard ");
146     }
147     fprintf(out, "@ %s:%u:%u", StripPrefix(location.filename, base_path).str().c_str(),
148             location.start.line, location.start.column);
149 
150     if (!availability.empty()) {
151       DeclarationAvailability avail;
152 
153       fprintf(out, "\n%s  ", indent_str.c_str());
154       if (!calculateAvailability(&avail)) {
155         fprintf(out, "invalid availability\n");
156       } else {
157         fprintf(out, "%s\n", to_string(avail).c_str());
158       }
159     }
160   }
161 };
162 
163 struct Symbol {
164   std::string name;
165   std::map<Location, Declaration> declarations;
166 
167   bool calculateAvailability(DeclarationAvailability* output) const;
168   bool hasDeclaration(const CompilationType& type) const;
169 
170   bool operator<(const Symbol& rhs) const {
171     return name < rhs.name;
172   }
173 
174   bool operator==(const Symbol& rhs) const {
175     return name == rhs.name;
176   }
177 
178   void dump(const std::string& base_path = "", FILE* out = stdout) const {
179     DeclarationAvailability availability;
180     bool valid_availability = calculateAvailability(&availability);
181     fprintf(out, "  %s: ", name.c_str());
182 
183     if (valid_availability) {
184       fprintf(out, "%s\n", to_string(availability).c_str());
185     } else {
186       fprintf(out, "invalid\n");
187     }
188 
189     for (auto& it : declarations) {
190       it.second.dump(base_path, out, 4);
191     }
192   }
193 };
194 
195 class HeaderDatabase {
196   std::mutex mutex;
197 
198  public:
199   std::map<std::string, Symbol> symbols;
200 
201   void parseAST(CompilationType type, clang::ASTContext& ast);
202 
203   void dump(const std::string& base_path = "", FILE* out = stdout) const {
204     fprintf(out, "HeaderDatabase contains %zu symbols:\n", symbols.size());
205     for (const auto& pair : symbols) {
206       pair.second.dump(base_path, out);
207     }
208   }
209 };
210