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