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
21using namespace llvm;
22
23//===----------------------------------------------------------------------===//
24// Static Analyzer Checkers Tables generation
25//===----------------------------------------------------------------------===//
26
27static std::string getPackageFullName(const Record *R, StringRef Sep = ".");
28
29static std::string getParentPackageFullName(const Record *R,
30 StringRef Sep = ".") {
31 std::string name;
32 if (DefInit *DI = dyn_cast<DefInit>(Val: R->getValueInit(FieldName: "ParentPackage")))
33 name = getPackageFullName(R: DI->getDef(), Sep);
34 return name;
35}
36
37static 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(FieldName: "PackageName");
43 return name;
44}
45
46static 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(FieldName: "CheckerName");
52 return name;
53}
54
55static std::string getStringValue(const Record &R, StringRef field) {
56 if (StringInit *SI = dyn_cast<StringInit>(Val: R.getValueInit(FieldName: field)))
57 return std::string(SI->getValue());
58 return std::string();
59}
60
61// Calculates the integer value representing the BitsInit object
62static 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>(Val: B->getBit(Bit: i));
68 if (Bit)
69 Value |= uint64_t(Bit->getValue()) << i;
70 else
71 PrintFatalError(ErrorLoc: R.getLoc(),
72 Msg: "missing Documentation for " + getCheckerFullName(R: &R));
73 }
74 return Value;
75}
76
77static std::string getCheckerDocs(const Record &R) {
78 const BitsInit *BI = R.getValueAsBitsInit(FieldName: "Documentation");
79 if (!BI)
80 PrintFatalError(ErrorLoc: R.getLoc(), Msg: "missing Documentation<...> member for " +
81 getCheckerFullName(R: &R));
82
83 // Ignore 'Documentation<NotDocumented>' checkers.
84 if (getValueFromBitsInit(B: BI, R) == 0)
85 return "";
86
87 std::string CheckerFullName = StringRef(getCheckerFullName(R: &R, Sep: "-")).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.
96static std::string getCheckerOptionType(const Record &R) {
97 if (BitsInit *BI = R.getValueAsBitsInit(FieldName: "Type")) {
98 switch(getValueFromBitsInit(B: BI, R)) {
99 case 0:
100 return "int";
101 case 1:
102 return "string";
103 case 2:
104 return "bool";
105 }
106 }
107 PrintFatalError(ErrorLoc: R.getLoc(),
108 Msg: "unable to parse command line option type for "
109 + getCheckerFullName(R: &R));
110 return "";
111}
112
113static std::string getDevelopmentStage(const Record &R) {
114 if (BitsInit *BI = R.getValueAsBitsInit(FieldName: "DevelopmentStage")) {
115 switch(getValueFromBitsInit(B: BI, R)) {
116 case 0:
117 return "alpha";
118 case 1:
119 return "released";
120 }
121 }
122
123 PrintFatalError(ErrorLoc: R.getLoc(),
124 Msg: "unable to parse command line option type for "
125 + getCheckerFullName(R: &R));
126 return "";
127}
128
129static bool isHidden(const Record *R) {
130 if (R->getValueAsBit(FieldName: "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>(Val: R->getValueInit(FieldName: "ParentPackage")))
135 return isHidden(R: DI->getDef());
136
137 return false;
138}
139
140static void printChecker(llvm::raw_ostream &OS, const Record &R) {
141 OS << "CHECKER(" << "\"";
142 OS.write_escaped(Str: getCheckerFullName(R: &R)) << "\", ";
143 OS << R.getName() << ", ";
144 OS << "\"";
145 OS.write_escaped(Str: getStringValue(R, field: "HelpText")) << "\", ";
146 OS << "\"";
147 OS.write_escaped(Str: getCheckerDocs(R));
148 OS << "\", ";
149
150 if (!isHidden(R: &R))
151 OS << "false";
152 else
153 OS << "true";
154
155 OS << ")\n";
156}
157
158static void printOption(llvm::raw_ostream &OS, StringRef FullName,
159 const Record &R) {
160 OS << "\"";
161 OS.write_escaped(Str: getCheckerOptionType(R)) << "\", \"";
162 OS.write_escaped(Str: FullName) << "\", ";
163 OS << '\"' << getStringValue(R, field: "CmdFlag") << "\", ";
164 OS << '\"';
165 OS.write_escaped(Str: getStringValue(R, field: "Desc")) << "\", ";
166 OS << '\"';
167 OS.write_escaped(Str: getStringValue(R, field: "DefaultVal")) << "\", ";
168 OS << '\"';
169 OS << getDevelopmentStage(R) << "\", ";
170
171 if (!R.getValueAsBit(FieldName: "Hidden"))
172 OS << "false";
173 else
174 OS << "true";
175}
176
177void clang::EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {
178 std::vector<Record*> checkers = Records.getAllDerivedDefinitions(ClassName: "Checker");
179 std::vector<Record*> packages = Records.getAllDerivedDefinitions(ClassName: "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(R: 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(Str: getPackageFullName(R: &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 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(FieldName: "PackageOptions"))
229 continue;
230
231 std::vector<Record *> PackageOptions = Package
232 ->getValueAsListOfDefs(FieldName: "PackageOptions");
233 for (Record *PackageOpt : PackageOptions) {
234 OS << "PACKAGE_OPTION(";
235 printOption(OS, FullName: getPackageFullName(R: Package), R: *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, R: *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(FieldName: "Dependencies"))
269 continue;
270
271 for (const Record *Dependency :
272 Checker->getValueAsListOfDefs(FieldName: "Dependencies")) {
273 OS << "CHECKER_DEPENDENCY(";
274 OS << '\"';
275 OS.write_escaped(Str: getCheckerFullName(R: Checker)) << "\", ";
276 OS << '\"';
277 OS.write_escaped(Str: getCheckerFullName(R: 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(FieldName: "WeakDependencies"))
294 continue;
295
296 for (const Record *Dependency :
297 Checker->getValueAsListOfDefs(FieldName: "WeakDependencies")) {
298 OS << "CHECKER_WEAK_DEPENDENCY(";
299 OS << '\"';
300 OS.write_escaped(Str: getCheckerFullName(R: Checker)) << "\", ";
301 OS << '\"';
302 OS.write_escaped(Str: getCheckerFullName(R: 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 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(FieldName: "CheckerOptions"))
329 continue;
330
331 std::vector<Record *> CheckerOptions = Checker
332 ->getValueAsListOfDefs(FieldName: "CheckerOptions");
333 for (Record *CheckerOpt : CheckerOptions) {
334 OS << "CHECKER_OPTION(";
335 printOption(OS, FullName: getCheckerFullName(R: Checker), R: *CheckerOpt);
336 OS << ")\n";
337 }
338 }
339 OS << "#endif // GET_CHECKER_OPTIONS\n"
340 "\n";
341}
342

source code of clang/utils/TableGen/ClangSACheckersEmitter.cpp