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