1f4a2713aSLionel Sambuc //=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*-
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc // The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc //
10f4a2713aSLionel Sambuc // This tablegen backend emits Clang Static Analyzer checkers tables.
11f4a2713aSLionel Sambuc //
12f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
13f4a2713aSLionel Sambuc
14f4a2713aSLionel Sambuc #include "llvm/ADT/DenseSet.h"
15f4a2713aSLionel Sambuc #include "llvm/TableGen/Error.h"
16f4a2713aSLionel Sambuc #include "llvm/TableGen/Record.h"
17f4a2713aSLionel Sambuc #include "llvm/TableGen/TableGenBackend.h"
18f4a2713aSLionel Sambuc #include <map>
19f4a2713aSLionel Sambuc #include <string>
20f4a2713aSLionel Sambuc using namespace llvm;
21f4a2713aSLionel Sambuc
22f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
23f4a2713aSLionel Sambuc // Static Analyzer Checkers Tables generation
24f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
25f4a2713aSLionel Sambuc
26f4a2713aSLionel Sambuc /// \brief True if it is specified hidden or a parent package is specified
27f4a2713aSLionel Sambuc /// as hidden, otherwise false.
isHidden(const Record & R)28f4a2713aSLionel Sambuc static bool isHidden(const Record &R) {
29f4a2713aSLionel Sambuc if (R.getValueAsBit("Hidden"))
30f4a2713aSLionel Sambuc return true;
31f4a2713aSLionel Sambuc // Not declared as hidden, check the parent package if it is hidden.
32f4a2713aSLionel Sambuc if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("ParentPackage")))
33f4a2713aSLionel Sambuc return isHidden(*DI->getDef());
34f4a2713aSLionel Sambuc
35f4a2713aSLionel Sambuc return false;
36f4a2713aSLionel Sambuc }
37f4a2713aSLionel Sambuc
isCheckerNamed(const Record * R)38f4a2713aSLionel Sambuc static bool isCheckerNamed(const Record *R) {
39f4a2713aSLionel Sambuc return !R->getValueAsString("CheckerName").empty();
40f4a2713aSLionel Sambuc }
41f4a2713aSLionel Sambuc
42f4a2713aSLionel Sambuc static std::string getPackageFullName(const Record *R);
43f4a2713aSLionel Sambuc
getParentPackageFullName(const Record * R)44f4a2713aSLionel Sambuc static std::string getParentPackageFullName(const Record *R) {
45f4a2713aSLionel Sambuc std::string name;
46f4a2713aSLionel Sambuc if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))
47f4a2713aSLionel Sambuc name = getPackageFullName(DI->getDef());
48f4a2713aSLionel Sambuc return name;
49f4a2713aSLionel Sambuc }
50f4a2713aSLionel Sambuc
getPackageFullName(const Record * R)51f4a2713aSLionel Sambuc static std::string getPackageFullName(const Record *R) {
52f4a2713aSLionel Sambuc std::string name = getParentPackageFullName(R);
53f4a2713aSLionel Sambuc if (!name.empty()) name += ".";
54f4a2713aSLionel Sambuc return name + R->getValueAsString("PackageName");
55f4a2713aSLionel Sambuc }
56f4a2713aSLionel Sambuc
getCheckerFullName(const Record * R)57f4a2713aSLionel Sambuc static std::string getCheckerFullName(const Record *R) {
58f4a2713aSLionel Sambuc std::string name = getParentPackageFullName(R);
59f4a2713aSLionel Sambuc if (isCheckerNamed(R)) {
60f4a2713aSLionel Sambuc if (!name.empty()) name += ".";
61f4a2713aSLionel Sambuc name += R->getValueAsString("CheckerName");
62f4a2713aSLionel Sambuc }
63f4a2713aSLionel Sambuc return name;
64f4a2713aSLionel Sambuc }
65f4a2713aSLionel Sambuc
getStringValue(const Record & R,StringRef field)66f4a2713aSLionel Sambuc static std::string getStringValue(const Record &R, StringRef field) {
67f4a2713aSLionel Sambuc if (StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field)))
68f4a2713aSLionel Sambuc return SI->getValue();
69f4a2713aSLionel Sambuc return std::string();
70f4a2713aSLionel Sambuc }
71f4a2713aSLionel Sambuc
72f4a2713aSLionel Sambuc namespace {
73f4a2713aSLionel Sambuc struct GroupInfo {
74f4a2713aSLionel Sambuc llvm::DenseSet<const Record*> Checkers;
75f4a2713aSLionel Sambuc llvm::DenseSet<const Record *> SubGroups;
76f4a2713aSLionel Sambuc bool Hidden;
77f4a2713aSLionel Sambuc unsigned Index;
78f4a2713aSLionel Sambuc
GroupInfo__anone56b74130111::GroupInfo79f4a2713aSLionel Sambuc GroupInfo() : Hidden(false) { }
80f4a2713aSLionel Sambuc };
81f4a2713aSLionel Sambuc }
82f4a2713aSLionel Sambuc
addPackageToCheckerGroup(const Record * package,const Record * group,llvm::DenseMap<const Record *,GroupInfo * > & recordGroupMap)83f4a2713aSLionel Sambuc static void addPackageToCheckerGroup(const Record *package, const Record *group,
84f4a2713aSLionel Sambuc llvm::DenseMap<const Record *, GroupInfo *> &recordGroupMap) {
85f4a2713aSLionel Sambuc llvm::DenseSet<const Record *> &checkers = recordGroupMap[package]->Checkers;
86f4a2713aSLionel Sambuc for (llvm::DenseSet<const Record *>::iterator
87f4a2713aSLionel Sambuc I = checkers.begin(), E = checkers.end(); I != E; ++I)
88f4a2713aSLionel Sambuc recordGroupMap[group]->Checkers.insert(*I);
89f4a2713aSLionel Sambuc
90f4a2713aSLionel Sambuc llvm::DenseSet<const Record *> &subGroups = recordGroupMap[package]->SubGroups;
91f4a2713aSLionel Sambuc for (llvm::DenseSet<const Record *>::iterator
92f4a2713aSLionel Sambuc I = subGroups.begin(), E = subGroups.end(); I != E; ++I)
93f4a2713aSLionel Sambuc addPackageToCheckerGroup(*I, group, recordGroupMap);
94f4a2713aSLionel Sambuc }
95f4a2713aSLionel Sambuc
96f4a2713aSLionel Sambuc namespace clang {
EmitClangSACheckers(RecordKeeper & Records,raw_ostream & OS)97f4a2713aSLionel Sambuc void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {
98f4a2713aSLionel Sambuc std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker");
99f4a2713aSLionel Sambuc llvm::DenseMap<const Record *, unsigned> checkerRecIndexMap;
100f4a2713aSLionel Sambuc for (unsigned i = 0, e = checkers.size(); i != e; ++i)
101f4a2713aSLionel Sambuc checkerRecIndexMap[checkers[i]] = i;
102f4a2713aSLionel Sambuc
103f4a2713aSLionel Sambuc // Invert the mapping of checkers to package/group into a one to many
104f4a2713aSLionel Sambuc // mapping of packages/groups to checkers.
105f4a2713aSLionel Sambuc std::map<std::string, GroupInfo> groupInfoByName;
106f4a2713aSLionel Sambuc llvm::DenseMap<const Record *, GroupInfo *> recordGroupMap;
107f4a2713aSLionel Sambuc
108f4a2713aSLionel Sambuc std::vector<Record*> packages = Records.getAllDerivedDefinitions("Package");
109f4a2713aSLionel Sambuc for (unsigned i = 0, e = packages.size(); i != e; ++i) {
110f4a2713aSLionel Sambuc Record *R = packages[i];
111f4a2713aSLionel Sambuc std::string fullName = getPackageFullName(R);
112f4a2713aSLionel Sambuc if (!fullName.empty()) {
113f4a2713aSLionel Sambuc GroupInfo &info = groupInfoByName[fullName];
114f4a2713aSLionel Sambuc info.Hidden = isHidden(*R);
115f4a2713aSLionel Sambuc recordGroupMap[R] = &info;
116f4a2713aSLionel Sambuc }
117f4a2713aSLionel Sambuc }
118f4a2713aSLionel Sambuc
119f4a2713aSLionel Sambuc std::vector<Record*>
120f4a2713aSLionel Sambuc checkerGroups = Records.getAllDerivedDefinitions("CheckerGroup");
121f4a2713aSLionel Sambuc for (unsigned i = 0, e = checkerGroups.size(); i != e; ++i) {
122f4a2713aSLionel Sambuc Record *R = checkerGroups[i];
123f4a2713aSLionel Sambuc std::string name = R->getValueAsString("GroupName");
124f4a2713aSLionel Sambuc if (!name.empty()) {
125f4a2713aSLionel Sambuc GroupInfo &info = groupInfoByName[name];
126f4a2713aSLionel Sambuc recordGroupMap[R] = &info;
127f4a2713aSLionel Sambuc }
128f4a2713aSLionel Sambuc }
129f4a2713aSLionel Sambuc
130f4a2713aSLionel Sambuc for (unsigned i = 0, e = checkers.size(); i != e; ++i) {
131f4a2713aSLionel Sambuc Record *R = checkers[i];
132*0a6a1f1dSLionel Sambuc Record *package = nullptr;
133f4a2713aSLionel Sambuc if (DefInit *
134f4a2713aSLionel Sambuc DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))
135f4a2713aSLionel Sambuc package = DI->getDef();
136f4a2713aSLionel Sambuc if (!isCheckerNamed(R) && !package)
137f4a2713aSLionel Sambuc PrintFatalError(R->getLoc(), "Checker '" + R->getName() +
138f4a2713aSLionel Sambuc "' is neither named, nor in a package!");
139f4a2713aSLionel Sambuc
140f4a2713aSLionel Sambuc if (isCheckerNamed(R)) {
141f4a2713aSLionel Sambuc // Create a pseudo-group to hold this checker.
142f4a2713aSLionel Sambuc std::string fullName = getCheckerFullName(R);
143f4a2713aSLionel Sambuc GroupInfo &info = groupInfoByName[fullName];
144f4a2713aSLionel Sambuc info.Hidden = R->getValueAsBit("Hidden");
145f4a2713aSLionel Sambuc recordGroupMap[R] = &info;
146f4a2713aSLionel Sambuc info.Checkers.insert(R);
147f4a2713aSLionel Sambuc } else {
148f4a2713aSLionel Sambuc recordGroupMap[package]->Checkers.insert(R);
149f4a2713aSLionel Sambuc }
150f4a2713aSLionel Sambuc
151f4a2713aSLionel Sambuc Record *currR = isCheckerNamed(R) ? R : package;
152f4a2713aSLionel Sambuc // Insert the checker and its parent packages into the subgroups set of
153f4a2713aSLionel Sambuc // the corresponding parent package.
154f4a2713aSLionel Sambuc while (DefInit *DI
155f4a2713aSLionel Sambuc = dyn_cast<DefInit>(currR->getValueInit("ParentPackage"))) {
156f4a2713aSLionel Sambuc Record *parentPackage = DI->getDef();
157f4a2713aSLionel Sambuc recordGroupMap[parentPackage]->SubGroups.insert(currR);
158f4a2713aSLionel Sambuc currR = parentPackage;
159f4a2713aSLionel Sambuc }
160f4a2713aSLionel Sambuc // Insert the checker into the set of its group.
161f4a2713aSLionel Sambuc if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("Group")))
162f4a2713aSLionel Sambuc recordGroupMap[DI->getDef()]->Checkers.insert(R);
163f4a2713aSLionel Sambuc }
164f4a2713aSLionel Sambuc
165f4a2713aSLionel Sambuc // If a package is in group, add all its checkers and its sub-packages
166f4a2713aSLionel Sambuc // checkers into the group.
167f4a2713aSLionel Sambuc for (unsigned i = 0, e = packages.size(); i != e; ++i)
168f4a2713aSLionel Sambuc if (DefInit *DI = dyn_cast<DefInit>(packages[i]->getValueInit("Group")))
169f4a2713aSLionel Sambuc addPackageToCheckerGroup(packages[i], DI->getDef(), recordGroupMap);
170f4a2713aSLionel Sambuc
171f4a2713aSLionel Sambuc typedef std::map<std::string, const Record *> SortedRecords;
172f4a2713aSLionel Sambuc typedef llvm::DenseMap<const Record *, unsigned> RecToSortIndex;
173f4a2713aSLionel Sambuc
174f4a2713aSLionel Sambuc SortedRecords sortedGroups;
175f4a2713aSLionel Sambuc RecToSortIndex groupToSortIndex;
176f4a2713aSLionel Sambuc OS << "\n#ifdef GET_GROUPS\n";
177f4a2713aSLionel Sambuc {
178f4a2713aSLionel Sambuc for (unsigned i = 0, e = checkerGroups.size(); i != e; ++i)
179f4a2713aSLionel Sambuc sortedGroups[checkerGroups[i]->getValueAsString("GroupName")]
180f4a2713aSLionel Sambuc = checkerGroups[i];
181f4a2713aSLionel Sambuc
182f4a2713aSLionel Sambuc unsigned sortIndex = 0;
183f4a2713aSLionel Sambuc for (SortedRecords::iterator
184f4a2713aSLionel Sambuc I = sortedGroups.begin(), E = sortedGroups.end(); I != E; ++I) {
185f4a2713aSLionel Sambuc const Record *R = I->second;
186f4a2713aSLionel Sambuc
187f4a2713aSLionel Sambuc OS << "GROUP(" << "\"";
188f4a2713aSLionel Sambuc OS.write_escaped(R->getValueAsString("GroupName")) << "\"";
189f4a2713aSLionel Sambuc OS << ")\n";
190f4a2713aSLionel Sambuc
191f4a2713aSLionel Sambuc groupToSortIndex[R] = sortIndex++;
192f4a2713aSLionel Sambuc }
193f4a2713aSLionel Sambuc }
194f4a2713aSLionel Sambuc OS << "#endif // GET_GROUPS\n\n";
195f4a2713aSLionel Sambuc
196f4a2713aSLionel Sambuc OS << "\n#ifdef GET_PACKAGES\n";
197f4a2713aSLionel Sambuc {
198f4a2713aSLionel Sambuc SortedRecords sortedPackages;
199f4a2713aSLionel Sambuc for (unsigned i = 0, e = packages.size(); i != e; ++i)
200f4a2713aSLionel Sambuc sortedPackages[getPackageFullName(packages[i])] = packages[i];
201f4a2713aSLionel Sambuc
202f4a2713aSLionel Sambuc for (SortedRecords::iterator
203f4a2713aSLionel Sambuc I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) {
204f4a2713aSLionel Sambuc const Record &R = *I->second;
205f4a2713aSLionel Sambuc
206f4a2713aSLionel Sambuc OS << "PACKAGE(" << "\"";
207f4a2713aSLionel Sambuc OS.write_escaped(getPackageFullName(&R)) << "\", ";
208f4a2713aSLionel Sambuc // Group index
209f4a2713aSLionel Sambuc if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group")))
210f4a2713aSLionel Sambuc OS << groupToSortIndex[DI->getDef()] << ", ";
211f4a2713aSLionel Sambuc else
212f4a2713aSLionel Sambuc OS << "-1, ";
213f4a2713aSLionel Sambuc // Hidden bit
214f4a2713aSLionel Sambuc if (isHidden(R))
215f4a2713aSLionel Sambuc OS << "true";
216f4a2713aSLionel Sambuc else
217f4a2713aSLionel Sambuc OS << "false";
218f4a2713aSLionel Sambuc OS << ")\n";
219f4a2713aSLionel Sambuc }
220f4a2713aSLionel Sambuc }
221f4a2713aSLionel Sambuc OS << "#endif // GET_PACKAGES\n\n";
222f4a2713aSLionel Sambuc
223f4a2713aSLionel Sambuc OS << "\n#ifdef GET_CHECKERS\n";
224f4a2713aSLionel Sambuc for (unsigned i = 0, e = checkers.size(); i != e; ++i) {
225f4a2713aSLionel Sambuc const Record &R = *checkers[i];
226f4a2713aSLionel Sambuc
227f4a2713aSLionel Sambuc OS << "CHECKER(" << "\"";
228f4a2713aSLionel Sambuc std::string name;
229f4a2713aSLionel Sambuc if (isCheckerNamed(&R))
230f4a2713aSLionel Sambuc name = getCheckerFullName(&R);
231f4a2713aSLionel Sambuc OS.write_escaped(name) << "\", ";
232f4a2713aSLionel Sambuc OS << R.getName() << ", ";
233f4a2713aSLionel Sambuc OS << getStringValue(R, "DescFile") << ", ";
234f4a2713aSLionel Sambuc OS << "\"";
235f4a2713aSLionel Sambuc OS.write_escaped(getStringValue(R, "HelpText")) << "\", ";
236f4a2713aSLionel Sambuc // Group index
237f4a2713aSLionel Sambuc if (DefInit *DI = dyn_cast<DefInit>(R.getValueInit("Group")))
238f4a2713aSLionel Sambuc OS << groupToSortIndex[DI->getDef()] << ", ";
239f4a2713aSLionel Sambuc else
240f4a2713aSLionel Sambuc OS << "-1, ";
241f4a2713aSLionel Sambuc // Hidden bit
242f4a2713aSLionel Sambuc if (isHidden(R))
243f4a2713aSLionel Sambuc OS << "true";
244f4a2713aSLionel Sambuc else
245f4a2713aSLionel Sambuc OS << "false";
246f4a2713aSLionel Sambuc OS << ")\n";
247f4a2713aSLionel Sambuc }
248f4a2713aSLionel Sambuc OS << "#endif // GET_CHECKERS\n\n";
249f4a2713aSLionel Sambuc
250f4a2713aSLionel Sambuc unsigned index = 0;
251f4a2713aSLionel Sambuc for (std::map<std::string, GroupInfo>::iterator
252f4a2713aSLionel Sambuc I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I)
253f4a2713aSLionel Sambuc I->second.Index = index++;
254f4a2713aSLionel Sambuc
255f4a2713aSLionel Sambuc // Walk through the packages/groups/checkers emitting an array for each
256f4a2713aSLionel Sambuc // set of checkers and an array for each set of subpackages.
257f4a2713aSLionel Sambuc
258f4a2713aSLionel Sambuc OS << "\n#ifdef GET_MEMBER_ARRAYS\n";
259f4a2713aSLionel Sambuc unsigned maxLen = 0;
260f4a2713aSLionel Sambuc for (std::map<std::string, GroupInfo>::iterator
261f4a2713aSLionel Sambuc I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I) {
262f4a2713aSLionel Sambuc maxLen = std::max(maxLen, (unsigned)I->first.size());
263f4a2713aSLionel Sambuc
264f4a2713aSLionel Sambuc llvm::DenseSet<const Record *> &checkers = I->second.Checkers;
265f4a2713aSLionel Sambuc if (!checkers.empty()) {
266f4a2713aSLionel Sambuc OS << "static const short CheckerArray" << I->second.Index << "[] = { ";
267f4a2713aSLionel Sambuc // Make the output order deterministic.
268f4a2713aSLionel Sambuc std::map<int, const Record *> sorted;
269f4a2713aSLionel Sambuc for (llvm::DenseSet<const Record *>::iterator
270f4a2713aSLionel Sambuc I = checkers.begin(), E = checkers.end(); I != E; ++I)
271f4a2713aSLionel Sambuc sorted[(*I)->getID()] = *I;
272f4a2713aSLionel Sambuc
273f4a2713aSLionel Sambuc for (std::map<int, const Record *>::iterator
274f4a2713aSLionel Sambuc I = sorted.begin(), E = sorted.end(); I != E; ++I)
275f4a2713aSLionel Sambuc OS << checkerRecIndexMap[I->second] << ", ";
276f4a2713aSLionel Sambuc OS << "-1 };\n";
277f4a2713aSLionel Sambuc }
278f4a2713aSLionel Sambuc
279f4a2713aSLionel Sambuc llvm::DenseSet<const Record *> &subGroups = I->second.SubGroups;
280f4a2713aSLionel Sambuc if (!subGroups.empty()) {
281f4a2713aSLionel Sambuc OS << "static const short SubPackageArray" << I->second.Index << "[] = { ";
282f4a2713aSLionel Sambuc // Make the output order deterministic.
283f4a2713aSLionel Sambuc std::map<int, const Record *> sorted;
284f4a2713aSLionel Sambuc for (llvm::DenseSet<const Record *>::iterator
285f4a2713aSLionel Sambuc I = subGroups.begin(), E = subGroups.end(); I != E; ++I)
286f4a2713aSLionel Sambuc sorted[(*I)->getID()] = *I;
287f4a2713aSLionel Sambuc
288f4a2713aSLionel Sambuc for (std::map<int, const Record *>::iterator
289f4a2713aSLionel Sambuc I = sorted.begin(), E = sorted.end(); I != E; ++I) {
290f4a2713aSLionel Sambuc OS << recordGroupMap[I->second]->Index << ", ";
291f4a2713aSLionel Sambuc }
292f4a2713aSLionel Sambuc OS << "-1 };\n";
293f4a2713aSLionel Sambuc }
294f4a2713aSLionel Sambuc }
295f4a2713aSLionel Sambuc OS << "#endif // GET_MEMBER_ARRAYS\n\n";
296f4a2713aSLionel Sambuc
297f4a2713aSLionel Sambuc OS << "\n#ifdef GET_CHECKNAME_TABLE\n";
298f4a2713aSLionel Sambuc for (std::map<std::string, GroupInfo>::iterator
299f4a2713aSLionel Sambuc I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I) {
300f4a2713aSLionel Sambuc // Group option string.
301f4a2713aSLionel Sambuc OS << " { \"";
302f4a2713aSLionel Sambuc OS.write_escaped(I->first) << "\","
303f4a2713aSLionel Sambuc << std::string(maxLen-I->first.size()+1, ' ');
304f4a2713aSLionel Sambuc
305f4a2713aSLionel Sambuc if (I->second.Checkers.empty())
306f4a2713aSLionel Sambuc OS << "0, ";
307f4a2713aSLionel Sambuc else
308f4a2713aSLionel Sambuc OS << "CheckerArray" << I->second.Index << ", ";
309f4a2713aSLionel Sambuc
310f4a2713aSLionel Sambuc // Subgroups.
311f4a2713aSLionel Sambuc if (I->second.SubGroups.empty())
312f4a2713aSLionel Sambuc OS << "0, ";
313f4a2713aSLionel Sambuc else
314f4a2713aSLionel Sambuc OS << "SubPackageArray" << I->second.Index << ", ";
315f4a2713aSLionel Sambuc
316f4a2713aSLionel Sambuc OS << (I->second.Hidden ? "true" : "false");
317f4a2713aSLionel Sambuc
318f4a2713aSLionel Sambuc OS << " },\n";
319f4a2713aSLionel Sambuc }
320f4a2713aSLionel Sambuc OS << "#endif // GET_CHECKNAME_TABLE\n\n";
321f4a2713aSLionel Sambuc }
322f4a2713aSLionel Sambuc } // end namespace clang
323