1 //=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*-
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
7 //===----------------------------------------------------------------------===//
9 // This tablegen backend emits Clang Static Analyzer checkers tables.
11 //===----------------------------------------------------------------------===//
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"
23 //===----------------------------------------------------------------------===//
24 // Static Analyzer Checkers Tables generation
25 //===----------------------------------------------------------------------===//
27 static std::string
getPackageFullName(const Record
*R
, StringRef Sep
= ".");
29 static std::string
getParentPackageFullName(const Record
*R
,
30 StringRef Sep
= ".") {
32 if (DefInit
*DI
= dyn_cast
<DefInit
>(R
->getValueInit("ParentPackage")))
33 name
= getPackageFullName(DI
->getDef(), Sep
);
37 static std::string
getPackageFullName(const Record
*R
, StringRef Sep
) {
38 std::string name
= getParentPackageFullName(R
, Sep
);
41 assert(!R
->getValueAsString("PackageName").empty());
42 name
+= R
->getValueAsString("PackageName");
46 static std::string
getCheckerFullName(const Record
*R
, StringRef Sep
= ".") {
47 std::string name
= getParentPackageFullName(R
, Sep
);
50 assert(!R
->getValueAsString("CheckerName").empty());
51 name
+= R
->getValueAsString("CheckerName");
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());
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!");
66 for (unsigned i
= 0, e
= B
->getNumBits(); i
!= e
; ++i
) {
67 const auto *Bit
= dyn_cast
<BitInit
>(B
->getBit(i
));
69 Value
|= uint64_t(Bit
->getValue()) << i
;
71 PrintFatalError(R
.getLoc(),
72 "missing Documentation for " + getCheckerFullName(&R
));
77 static std::string
getCheckerDocs(const Record
&R
) {
78 const BitsInit
*BI
= R
.getValueAsBitsInit("Documentation");
80 PrintFatalError(R
.getLoc(), "missing Documentation<...> member for " +
81 getCheckerFullName(&R
));
83 // Ignore 'Documentation<NotDocumented>' checkers.
84 if (getValueFromBitsInit(BI
, R
) == 0)
87 std::string CheckerFullName
= StringRef(getCheckerFullName(&R
, "-")).lower();
88 return (llvm::Twine("https://clang.llvm.org/docs/analyzer/checkers.html#") +
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
96 static std::string
getCheckerOptionType(const Record
&R
) {
97 if (BitsInit
*BI
= R
.getValueAsBitsInit("Type")) {
98 switch(getValueFromBitsInit(BI
, R
)) {
107 PrintFatalError(R
.getLoc(),
108 "unable to parse command line option type for "
109 + getCheckerFullName(&R
));
113 static std::string
getDevelopmentStage(const Record
&R
) {
114 if (BitsInit
*BI
= R
.getValueAsBitsInit("DevelopmentStage")) {
115 switch(getValueFromBitsInit(BI
, R
)) {
123 PrintFatalError(R
.getLoc(),
124 "unable to parse command line option type for "
125 + getCheckerFullName(&R
));
129 static bool isHidden(const Record
*R
) {
130 if (R
->getValueAsBit("Hidden"))
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());
140 static void printChecker(llvm::raw_ostream
&OS
, const Record
&R
) {
141 OS
<< "CHECKER(" << "\"";
142 OS
.write_escaped(getCheckerFullName(&R
)) << "\", ";
143 OS
<< R
.getName() << ", ";
145 OS
.write_escaped(getStringValue(R
, "HelpText")) << "\", ";
147 OS
.write_escaped(getCheckerDocs(R
));
158 static void printOption(llvm::raw_ostream
&OS
, StringRef FullName
,
161 OS
.write_escaped(getCheckerOptionType(R
)) << "\", \"";
162 OS
.write_escaped(FullName
) << "\", ";
163 OS
<< '\"' << getStringValue(R
, "CmdFlag") << "\", ";
165 OS
.write_escaped(getStringValue(R
, "Desc")) << "\", ";
167 OS
.write_escaped(getStringValue(R
, "DefaultVal")) << "\", ";
169 OS
<< getDevelopmentStage(R
) << "\", ";
171 if (!R
.getValueAsBit("Hidden"))
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");
181 using SortedRecords
= llvm::StringMap
<const Record
*>;
183 OS
<< "// This file is automatically generated. Do not edit this file by "
188 // PACKAGE(PACKAGENAME)
189 // - PACKAGENAME: The name of the package.
191 "#ifdef GET_PACKAGES\n";
193 SortedRecords sortedPackages
;
194 for (unsigned i
= 0, e
= packages
.size(); i
!= e
; ++i
)
195 sortedPackages
[getPackageFullName(packages
[i
])] = packages
[i
];
197 for (SortedRecords::iterator
198 I
= sortedPackages
.begin(), E
= sortedPackages
.end(); I
!= E
; ++I
) {
199 const Record
&R
= *I
->second
;
201 OS
<< "PACKAGE(" << "\"";
202 OS
.write_escaped(getPackageFullName(&R
)) << '\"';
206 OS
<< "#endif // GET_PACKAGES\n"
209 // Emit a package option.
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.
220 // - DEFAULT: The default value for this option.
222 // The full option can be specified in the command like this:
223 // -analyzer-config PACKAGENAME:OPTIONNAME=VALUE
225 "#ifdef GET_PACKAGE_OPTIONS\n";
226 for (const Record
*Package
: packages
) {
228 if (Package
->isValueUnset("PackageOptions"))
231 std::vector
<Record
*> PackageOptions
= Package
232 ->getValueAsListOfDefs("PackageOptions");
233 for (Record
*PackageOpt
: PackageOptions
) {
234 OS
<< "PACKAGE_OPTION(";
235 printOption(OS
, getPackageFullName(Package
), *PackageOpt
);
239 OS
<< "#endif // GET_PACKAGE_OPTIONS\n"
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.
251 "#ifdef GET_CHECKERS\n"
253 for (const Record
*checker
: checkers
) {
254 printChecker(OS
, *checker
);
257 "#endif // GET_CHECKERS\n"
260 // Emit dependencies.
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.
266 "#ifdef GET_CHECKER_DEPENDENCIES\n";
267 for (const Record
*Checker
: checkers
) {
268 if (Checker
->isValueUnset("Dependencies"))
271 for (const Record
*Dependency
:
272 Checker
->getValueAsListOfDefs("Dependencies")) {
273 OS
<< "CHECKER_DEPENDENCY(";
275 OS
.write_escaped(getCheckerFullName(Checker
)) << "\", ";
277 OS
.write_escaped(getCheckerFullName(Dependency
)) << '\"';
282 "#endif // GET_CHECKER_DEPENDENCIES\n";
284 // Emit weak dependencies.
286 // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)
287 // - FULLNAME: The full name of the checker that is supposed to be
289 // - DEPENDENCY: The full name of the checker FULLNAME weak depends on.
291 "#ifdef GET_CHECKER_WEAK_DEPENDENCIES\n";
292 for (const Record
*Checker
: checkers
) {
293 if (Checker
->isValueUnset("WeakDependencies"))
296 for (const Record
*Dependency
:
297 Checker
->getValueAsListOfDefs("WeakDependencies")) {
298 OS
<< "CHECKER_WEAK_DEPENDENCY(";
300 OS
.write_escaped(getCheckerFullName(Checker
)) << "\", ";
302 OS
.write_escaped(getCheckerFullName(Dependency
)) << '\"';
307 "#endif // GET_CHECKER_WEAK_DEPENDENCIES\n";
309 // Emit a package option.
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.
320 // - DEFAULT: The default value for this option.
322 // The full option can be specified in the command like this:
323 // -analyzer-config CHECKERNAME:OPTIONNAME=VALUE
325 "#ifdef GET_CHECKER_OPTIONS\n";
326 for (const Record
*Checker
: checkers
) {
328 if (Checker
->isValueUnset("CheckerOptions"))
331 std::vector
<Record
*> CheckerOptions
= Checker
332 ->getValueAsListOfDefs("CheckerOptions");
333 for (Record
*CheckerOpt
: CheckerOptions
) {
334 OS
<< "CHECKER_OPTION(";
335 printOption(OS
, getCheckerFullName(Checker
), *CheckerOpt
);
339 OS
<< "#endif // GET_CHECKER_OPTIONS\n"