diff options
Diffstat (limited to 'utils/TableGen/ClangSACheckersEmitter.cpp')
-rw-r--r-- | utils/TableGen/ClangSACheckersEmitter.cpp | 223 |
1 files changed, 202 insertions, 21 deletions
diff --git a/utils/TableGen/ClangSACheckersEmitter.cpp b/utils/TableGen/ClangSACheckersEmitter.cpp index 57850a438720..7dd0895b76d4 100644 --- a/utils/TableGen/ClangSACheckersEmitter.cpp +++ b/utils/TableGen/ClangSACheckersEmitter.cpp @@ -1,9 +1,8 @@ //=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*- // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -91,6 +90,90 @@ static std::string getCheckerDocs(const Record &R) { .str(); } +/// Retrieves the type from a CmdOptionTypeEnum typed Record object. Note that +/// the class itself has to be modified for adding a new option type in +/// CheckerBase.td. +static std::string getCheckerOptionType(const Record &R) { + if (BitsInit *BI = R.getValueAsBitsInit("Type")) { + switch(getValueFromBitsInit(BI, R)) { + case 0: + return "int"; + case 1: + return "string"; + case 2: + return "bool"; + } + } + PrintFatalError(R.getLoc(), + "unable to parse command line option type for " + + getCheckerFullName(&R)); + return ""; +} + +static std::string getDevelopmentStage(const Record &R) { + if (BitsInit *BI = R.getValueAsBitsInit("DevelopmentStage")) { + switch(getValueFromBitsInit(BI, R)) { + case 0: + return "alpha"; + case 1: + return "released"; + } + } + + PrintFatalError(R.getLoc(), + "unable to parse command line option type for " + + getCheckerFullName(&R)); + return ""; +} + +static bool isHidden(const Record *R) { + if (R->getValueAsBit("Hidden")) + return true; + + // Not declared as hidden, check the parent package if it is hidden. + if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage"))) + return isHidden(DI->getDef()); + + return false; +} + +static void printChecker(llvm::raw_ostream &OS, const Record &R) { + OS << "CHECKER(" << "\""; + OS.write_escaped(getCheckerFullName(&R)) << "\", "; + OS << R.getName() << ", "; + OS << "\""; + OS.write_escaped(getStringValue(R, "HelpText")) << "\", "; + OS << "\""; + OS.write_escaped(getCheckerDocs(R)); + OS << "\", "; + + if (!isHidden(&R)) + OS << "false"; + else + OS << "true"; + + OS << ")\n"; +} + +static void printOption(llvm::raw_ostream &OS, StringRef FullName, + const Record &R) { + OS << "\""; + OS.write_escaped(getCheckerOptionType(R)) << "\", \""; + OS.write_escaped(FullName) << "\", "; + OS << '\"' << getStringValue(R, "CmdFlag") << "\", "; + OS << '\"'; + OS.write_escaped(getStringValue(R, "Desc")) << "\", "; + OS << '\"'; + OS.write_escaped(getStringValue(R, "DefaultVal")) << "\", "; + OS << '\"'; + OS << getDevelopmentStage(R) << "\", "; + + if (!R.getValueAsBit("Hidden")) + OS << "false"; + else + OS << "true"; +} + namespace clang { void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker"); @@ -101,7 +184,12 @@ void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { OS << "// This file is automatically generated. Do not edit this file by " "hand.\n"; - OS << "\n#ifdef GET_PACKAGES\n"; + // Emit packages. + // + // PACKAGE(PACKAGENAME) + // - PACKAGENAME: The name of the package. + OS << "\n" + "#ifdef GET_PACKAGES\n"; { SortedRecords sortedPackages; for (unsigned i = 0, e = packages.size(); i != e; ++i) @@ -116,22 +204,115 @@ void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { OS << ")\n"; } } - OS << "#endif // GET_PACKAGES\n\n"; - - OS << "\n#ifdef GET_CHECKERS\n"; - for (unsigned i = 0, e = checkers.size(); i != e; ++i) { - const Record &R = *checkers[i]; - - OS << "CHECKER(" << "\""; - OS.write_escaped(getCheckerFullName(&R)) << "\", "; - OS << R.getName() << ", "; - OS << "\""; - OS.write_escaped(getStringValue(R, "HelpText")) << "\", "; - OS << "\""; - OS.write_escaped(getCheckerDocs(R)); - OS << "\""; - OS << ")\n"; + OS << "#endif // GET_PACKAGES\n" + "\n"; + + // Emit a package option. + // + // PACKAGE_OPTION(OPTIONTYPE, PACKAGENAME, OPTIONNAME, DESCRIPTION, DEFAULT) + // - OPTIONTYPE: Type of the option, whether it's integer or boolean etc. + // This is important for validating user input. Note that + // it's a string, rather than an actual type: since we can + // load checkers runtime, we can't use template hackery for + // sorting this out compile-time. + // - PACKAGENAME: Name of the package. + // - OPTIONNAME: Name of the option. + // - DESCRIPTION + // - DEFAULT: The default value for this option. + // + // The full option can be specified in the command like like this: + // -analyzer-config PACKAGENAME:OPTIONNAME=VALUE + OS << "\n" + "#ifdef GET_PACKAGE_OPTIONS\n"; + for (const Record *Package : packages) { + + if (Package->isValueUnset("PackageOptions")) + continue; + + std::vector<Record *> PackageOptions = Package + ->getValueAsListOfDefs("PackageOptions"); + for (Record *PackageOpt : PackageOptions) { + OS << "PACKAGE_OPTION("; + printOption(OS, getPackageFullName(Package), *PackageOpt); + OS << ")\n"; + } + } + OS << "#endif // GET_PACKAGE_OPTIONS\n" + "\n"; + + // Emit checkers. + // + // CHECKER(FULLNAME, CLASS, HELPTEXT) + // - FULLNAME: The full name of the checker, including packages, e.g.: + // alpha.cplusplus.UninitializedObject + // - CLASS: The name of the checker, with "Checker" appended, e.g.: + // UninitializedObjectChecker + // - HELPTEXT: The description of the checker. + OS << "\n" + "#ifdef GET_CHECKERS\n" + "\n"; + for (const Record *checker : checkers) { + printChecker(OS, *checker); + } + OS << "\n" + "#endif // GET_CHECKERS\n" + "\n"; + + // Emit dependencies. + // + // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) + // - FULLNAME: The full name of the checker that depends on another checker. + // - DEPENDENCY: The full name of the checker FULLNAME depends on. + OS << "\n" + "#ifdef GET_CHECKER_DEPENDENCIES\n"; + for (const Record *Checker : checkers) { + if (Checker->isValueUnset("Dependencies")) + continue; + + for (const Record *Dependency : + Checker->getValueAsListOfDefs("Dependencies")) { + OS << "CHECKER_DEPENDENCY("; + OS << '\"'; + OS.write_escaped(getCheckerFullName(Checker)) << "\", "; + OS << '\"'; + OS.write_escaped(getCheckerFullName(Dependency)) << '\"'; + OS << ")\n"; + } + } + OS << "\n" + "#endif // GET_CHECKER_DEPENDENCIES\n"; + + // Emit a package option. + // + // CHECKER_OPTION(OPTIONTYPE, CHECKERNAME, OPTIONNAME, DESCRIPTION, DEFAULT) + // - OPTIONTYPE: Type of the option, whether it's integer or boolean etc. + // This is important for validating user input. Note that + // it's a string, rather than an actual type: since we can + // load checkers runtime, we can't use template hackery for + // sorting this out compile-time. + // - CHECKERNAME: Name of the package. + // - OPTIONNAME: Name of the option. + // - DESCRIPTION + // - DEFAULT: The default value for this option. + // + // The full option can be specified in the command like like this: + // -analyzer-config CHECKERNAME:OPTIONNAME=VALUE + OS << "\n" + "#ifdef GET_CHECKER_OPTIONS\n"; + for (const Record *Checker : checkers) { + + if (Checker->isValueUnset("CheckerOptions")) + continue; + + std::vector<Record *> CheckerOptions = Checker + ->getValueAsListOfDefs("CheckerOptions"); + for (Record *CheckerOpt : CheckerOptions) { + OS << "CHECKER_OPTION("; + printOption(OS, getCheckerFullName(Checker), *CheckerOpt); + OS << ")\n"; + } } - OS << "#endif // GET_CHECKERS\n\n"; + OS << "#endif // GET_CHECKER_OPTIONS\n" + "\n"; } } // end namespace clang |