xref: /llvm-project/clang/utils/TableGen/ClangSACheckersEmitter.cpp (revision 63aa8cf6becbeb4983e3d1a7fa3cd8a7c7147118)
1 //===-- ClangSACheckersEmitter.cpp - Generate SA checkers tables ----------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This tablegen backend emits Clang Static Analyzer checkers tables.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "TableGenBackends.h"
14 #include "llvm/ADT/StringMap.h"
15 #include "llvm/TableGen/Error.h"
16 #include "llvm/TableGen/Record.h"
17 #include "llvm/TableGen/TableGenBackend.h"
18 #include <string>
19 
20 using namespace llvm;
21 
22 //===----------------------------------------------------------------------===//
23 // Static Analyzer Checkers Tables generation
24 //===----------------------------------------------------------------------===//
25 
26 static std::string getPackageFullName(const Record *R, StringRef Sep = ".");
27 
28 static std::string getParentPackageFullName(const Record *R,
29                                             StringRef Sep = ".") {
30   if (const DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))
31     return getPackageFullName(DI->getDef(), Sep);
32   return "";
33 }
34 
35 static std::string getPackageFullName(const Record *R, StringRef Sep) {
36   std::string name = getParentPackageFullName(R, Sep);
37   if (!name.empty())
38     name += Sep;
39   assert(!R->getValueAsString("PackageName").empty());
40   name += R->getValueAsString("PackageName");
41   return name;
42 }
43 
44 static std::string getCheckerFullName(const Record *R, StringRef Sep = ".") {
45   std::string name = getParentPackageFullName(R, Sep);
46   if (!name.empty())
47     name += Sep;
48   assert(!R->getValueAsString("CheckerName").empty());
49   name += R->getValueAsString("CheckerName");
50   return name;
51 }
52 
53 static StringRef getStringValue(const Record &R, StringRef field) {
54   if (const StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field)))
55     return SI->getValue();
56   return "";
57 }
58 
59 // Calculates the integer value representing the BitsInit object
60 static inline uint64_t getValueFromBitsInit(const BitsInit *B, const Record &R) {
61   assert(B->getNumBits() <= sizeof(uint64_t) * 8 && "BitInits' too long!");
62 
63   uint64_t Value = 0;
64   for (unsigned i = 0, e = B->getNumBits(); i != e; ++i) {
65     const auto *Bit = dyn_cast<BitInit>(B->getBit(i));
66     if (Bit)
67       Value |= uint64_t(Bit->getValue()) << i;
68     else
69       PrintFatalError(R.getLoc(),
70                       "missing Documentation for " + getCheckerFullName(&R));
71   }
72   return Value;
73 }
74 
75 static std::string getCheckerDocs(const Record &R) {
76   const BitsInit *BI = R.getValueAsBitsInit("Documentation");
77   if (!BI)
78     PrintFatalError(R.getLoc(), "missing Documentation<...> member for " +
79                                     getCheckerFullName(&R));
80 
81   // Ignore 'Documentation<NotDocumented>' checkers.
82   if (getValueFromBitsInit(BI, R) == 0)
83     return "";
84 
85   std::string CheckerFullName = StringRef(getCheckerFullName(&R, "-")).lower();
86   return (Twine("https://clang.llvm.org/docs/analyzer/checkers.html#") +
87           CheckerFullName)
88       .str();
89 }
90 
91 /// Retrieves the type from a CmdOptionTypeEnum typed Record object. Note that
92 /// the class itself has to be modified for adding a new option type in
93 /// CheckerBase.td.
94 static StringRef getCheckerOptionType(const Record &R) {
95   if (const BitsInit *BI = R.getValueAsBitsInit("Type")) {
96     switch(getValueFromBitsInit(BI, R)) {
97     case 0:
98       return "int";
99     case 1:
100       return "string";
101     case 2:
102       return "bool";
103     }
104   }
105   PrintFatalError(R.getLoc(),
106                   "unable to parse command line option type for "
107                   + getCheckerFullName(&R));
108   return "";
109 }
110 
111 static StringRef getDevelopmentStage(const Record &R) {
112   if (const BitsInit *BI = R.getValueAsBitsInit("DevelopmentStage")) {
113     switch(getValueFromBitsInit(BI, R)) {
114     case 0:
115       return "alpha";
116     case 1:
117       return "released";
118     }
119   }
120 
121   PrintFatalError(R.getLoc(),
122                   "unable to parse command line option type for "
123                   + getCheckerFullName(&R));
124   return "";
125 }
126 
127 static bool isHidden(const Record *R) {
128   if (R->getValueAsBit("Hidden"))
129     return true;
130 
131   // Not declared as hidden, check the parent package if it is hidden.
132   if (const DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))
133     return isHidden(DI->getDef());
134 
135   return false;
136 }
137 
138 static void printChecker(raw_ostream &OS, const Record &R) {
139   OS << "CHECKER(" << "\"";
140   OS.write_escaped(getCheckerFullName(&R)) << "\", ";
141   OS << R.getName() << ", ";
142   OS << "\"";
143   OS.write_escaped(getStringValue(R, "HelpText")) << "\", ";
144   OS << "\"";
145   OS.write_escaped(getCheckerDocs(R));
146   OS << "\", ";
147 
148   if (!isHidden(&R))
149     OS << "false";
150   else
151     OS << "true";
152 
153   OS << ")\n";
154 }
155 
156 static void printOption(raw_ostream &OS, StringRef FullName, const Record &R) {
157   OS << "\"";
158   OS.write_escaped(getCheckerOptionType(R)) << "\", \"";
159   OS.write_escaped(FullName) << "\", ";
160   OS << '\"' << getStringValue(R, "CmdFlag") << "\", ";
161   OS << '\"';
162   OS.write_escaped(getStringValue(R, "Desc")) << "\", ";
163   OS << '\"';
164   OS.write_escaped(getStringValue(R, "DefaultVal")) << "\", ";
165   OS << '\"';
166   OS << getDevelopmentStage(R) << "\", ";
167 
168   if (!R.getValueAsBit("Hidden"))
169     OS << "false";
170   else
171     OS << "true";
172 }
173 
174 void clang::EmitClangSACheckers(const RecordKeeper &Records, raw_ostream &OS) {
175   ArrayRef<const Record *> checkers =
176       Records.getAllDerivedDefinitions("Checker");
177   ArrayRef<const Record *> packages =
178       Records.getAllDerivedDefinitions("Package");
179 
180   OS << "// This file is automatically generated. Do not edit this file by "
181         "hand.\n";
182 
183   // Emit packages.
184   //
185   // PACKAGE(PACKAGENAME)
186   //   - PACKAGENAME: The name of the package.
187   OS << "\n"
188         "#ifdef GET_PACKAGES\n";
189   {
190     StringMap<const Record *> sortedPackages;
191     for (const Record *Package : packages)
192       sortedPackages[getPackageFullName(Package)] = Package;
193 
194     for (const auto &[_, R] : sortedPackages) {
195       OS << "PACKAGE(" << "\"";
196       OS.write_escaped(getPackageFullName(R)) << '\"';
197       OS << ")\n";
198     }
199   }
200   OS << "#endif // GET_PACKAGES\n"
201         "\n";
202 
203   // Emit a package option.
204   //
205   // PACKAGE_OPTION(OPTIONTYPE, PACKAGENAME, OPTIONNAME, DESCRIPTION, DEFAULT)
206   //   - OPTIONTYPE: Type of the option, whether it's integer or boolean etc.
207   //                 This is important for validating user input. Note that
208   //                 it's a string, rather than an actual type: since we can
209   //                 load checkers runtime, we can't use template hackery for
210   //                 sorting this out compile-time.
211   //   - PACKAGENAME: Name of the package.
212   //   - OPTIONNAME: Name of the option.
213   //   - DESCRIPTION
214   //   - DEFAULT: The default value for this option.
215   //
216   // The full option can be specified in the command like this:
217   //   -analyzer-config PACKAGENAME:OPTIONNAME=VALUE
218   OS << "\n"
219         "#ifdef GET_PACKAGE_OPTIONS\n";
220   for (const Record *Package : packages) {
221     if (Package->isValueUnset("PackageOptions"))
222       continue;
223 
224     for (const Record *PackageOpt :
225          Package->getValueAsListOfDefs("PackageOptions")) {
226       OS << "PACKAGE_OPTION(";
227       printOption(OS, getPackageFullName(Package), *PackageOpt);
228       OS << ")\n";
229     }
230   }
231   OS << "#endif // GET_PACKAGE_OPTIONS\n"
232         "\n";
233 
234   // Emit checkers.
235   //
236   // CHECKER(FULLNAME, CLASS, HELPTEXT)
237   //   - FULLNAME: The full name of the checker, including packages, e.g.:
238   //               alpha.cplusplus.UninitializedObject
239   //   - CLASS: The name of the checker, with "Checker" appended, e.g.:
240   //            UninitializedObjectChecker
241   //   - HELPTEXT: The description of the checker.
242   OS << "\n"
243         "#ifdef GET_CHECKERS\n"
244         "\n";
245   for (const Record *checker : checkers)
246     printChecker(OS, *checker);
247 
248   OS << "\n"
249         "#endif // GET_CHECKERS\n"
250         "\n";
251 
252   // Emit dependencies.
253   //
254   // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)
255   //   - FULLNAME: The full name of the checker that depends on another checker.
256   //   - DEPENDENCY: The full name of the checker FULLNAME depends on.
257   OS << "\n"
258         "#ifdef GET_CHECKER_DEPENDENCIES\n";
259   for (const Record *Checker : checkers) {
260     if (Checker->isValueUnset("Dependencies"))
261       continue;
262 
263     for (const Record *Dependency :
264                             Checker->getValueAsListOfDefs("Dependencies")) {
265       OS << "CHECKER_DEPENDENCY(";
266       OS << '\"';
267       OS.write_escaped(getCheckerFullName(Checker)) << "\", ";
268       OS << '\"';
269       OS.write_escaped(getCheckerFullName(Dependency)) << '\"';
270       OS << ")\n";
271     }
272   }
273   OS << "\n"
274         "#endif // GET_CHECKER_DEPENDENCIES\n";
275 
276   // Emit weak dependencies.
277   //
278   // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)
279   //   - FULLNAME: The full name of the checker that is supposed to be
280   //     registered first.
281   //   - DEPENDENCY: The full name of the checker FULLNAME weak depends on.
282   OS << "\n"
283         "#ifdef GET_CHECKER_WEAK_DEPENDENCIES\n";
284   for (const Record *Checker : checkers) {
285     if (Checker->isValueUnset("WeakDependencies"))
286       continue;
287 
288     for (const Record *Dependency :
289          Checker->getValueAsListOfDefs("WeakDependencies")) {
290       OS << "CHECKER_WEAK_DEPENDENCY(";
291       OS << '\"';
292       OS.write_escaped(getCheckerFullName(Checker)) << "\", ";
293       OS << '\"';
294       OS.write_escaped(getCheckerFullName(Dependency)) << '\"';
295       OS << ")\n";
296     }
297   }
298   OS << "\n"
299         "#endif // GET_CHECKER_WEAK_DEPENDENCIES\n";
300 
301   // Emit a package option.
302   //
303   // CHECKER_OPTION(OPTIONTYPE, CHECKERNAME, OPTIONNAME, DESCRIPTION, DEFAULT)
304   //   - OPTIONTYPE: Type of the option, whether it's integer or boolean etc.
305   //                 This is important for validating user input. Note that
306   //                 it's a string, rather than an actual type: since we can
307   //                 load checkers runtime, we can't use template hackery for
308   //                 sorting this out compile-time.
309   //   - CHECKERNAME: Name of the package.
310   //   - OPTIONNAME: Name of the option.
311   //   - DESCRIPTION
312   //   - DEFAULT: The default value for this option.
313   //
314   // The full option can be specified in the command like this:
315   //   -analyzer-config CHECKERNAME:OPTIONNAME=VALUE
316   OS << "\n"
317         "#ifdef GET_CHECKER_OPTIONS\n";
318   for (const Record *Checker : checkers) {
319     if (Checker->isValueUnset("CheckerOptions"))
320       continue;
321 
322     for (const Record *CheckerOpt :
323          Checker->getValueAsListOfDefs("CheckerOptions")) {
324       OS << "CHECKER_OPTION(";
325       printOption(OS, getCheckerFullName(Checker), *CheckerOpt);
326       OS << ")\n";
327     }
328   }
329   OS << "#endif // GET_CHECKER_OPTIONS\n"
330         "\n";
331 }
332