#endif
namespace clang {
+class DiagnosticsEngine;
+class AnalyzerOptions;
+
namespace ento {
class CheckerOptInfo;
void initializeManager(CheckerManager &mgr,
SmallVectorImpl<CheckerOptInfo> &opts) const;
+ /// Check if every option corresponds to a specific checker or package.
+ void validateCheckerOptions(const AnalyzerOptions &opts,
+ DiagnosticsEngine &diags) const;
+
/// Prints the name and description of all checkers in this registry.
/// This output is not intended to be machine-parseable.
void printHelp(raw_ostream &out, size_t maxNameChars = 30) const ;
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/StaticAnalyzer/Core/CheckerOptInfo.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Support/raw_ostream.h"
}
}
+void CheckerRegistry::validateCheckerOptions(const AnalyzerOptions &opts,
+ DiagnosticsEngine &diags) const {
+ for (auto &config : opts.Config) {
+ size_t pos = config.getKey().find(':');
+ if (pos == StringRef::npos)
+ continue;
+
+ bool hasChecker = false;
+ StringRef checkerName = config.getKey().substr(0, pos);
+ for (auto &checker : Checkers) {
+ if (checker.FullName.startswith(checkerName) &&
+ (checker.FullName.size() == pos || checker.FullName[pos] == '.')) {
+ hasChecker = true;
+ break;
+ }
+ }
+ if (!hasChecker) {
+ diags.Report(diag::err_unknown_analyzer_checker) << checkerName;
+ }
+ }
+}
+
void CheckerRegistry::printHelp(raw_ostream &out,
size_t maxNameChars) const {
// FIXME: Alphabetical sort puts 'experimental' in the middle.
ClangCheckerRegistry allCheckers(plugins, &diags);
allCheckers.initializeManager(*checkerMgr, checkerOpts);
+ allCheckers.validateCheckerOptions(opts, diags);
checkerMgr->finishedCheckerRegistration();
for (unsigned i = 0, e = checkerOpts.size(); i != e; ++i) {
--- /dev/null
+// RUN: not %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-config unix.mallo:Optimistic=true 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-config uni:Optimistic=true 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-config uni.:Optimistic=true 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-config ..:Optimistic=true 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-config unix.:Optimistic=true 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-config unrelated:Optimistic=true 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-config unix.Malloc:Optimistic=true
+
+// Just to test clang is working.
+# foo
+
+// CHECK: error: