blob: 4f8c6dcad3bce0127fc5a7d21ac237874dd6d31a [file]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "RawPtrPlugin.h"
#include <algorithm>
#include "FindBadRawPtrPatterns.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "llvm/Support/TimeProfiler.h"
#include "tools/clang/plugins/FilteredASTConsumer.h"
using namespace clang;
namespace {
// Name of a cmdline parameter that can be used to specify a file listing fields
// that should not be rewritten to use raw_ptr<T>.
//
// See also:
// - OutputSectionHelper
// - FilterFile
const char kExcludeFieldsArgPrefix[] = "exclude-fields=";
// Name of a cmdline parameter that can be used to add a regular expressions
// that matches paths that should be excluded from the raw pointer usage checks.
const char kRawPtrExcludePathArgPrefix[] = "raw-ptr-exclude-path=";
// Name of a cmdline parameter that can be used to specify a file with
// config options.
//
// See also:
// - RawPtrPluginConfig
const char kRawPtrPluginConfigPathArgPrefix[] = "config-path=";
// Name of a cmdline parameter that can be used to add a regular expressions
// that matches paths that should be excluded from the bad raw_ptr casts checks.
const char kBadRawPtrCastExcludePathArgPrefix[] =
"check-bad-raw-ptr-cast-exclude-path=";
// Name of a cmdline parameter that can be used to add a regular expressions
// that matches function names that should be excluded from the bad raw_ptr cast
// checks. All implicit casts in CallExpr to the specified functions are
// excluded from the check. Use if you know that function does not break a
// reference count.
const char kCheckBadRawPtrCastExcludeFuncArgPrefix[] =
"check-bad-raw-ptr-cast-exclude-func=";
} // namespace
namespace raw_ptr_plugin {
namespace {
class PluginConsumer : public FilteredASTConsumer {
public:
PluginConsumer(CompilerInstance* instance,
const Options& options,
const RawPtrPluginConfig& config)
: options_(options), config_(config), instance_(*instance) {}
void HandleTranslationUnit(clang::ASTContext& context) override {
llvm::TimeTraceScope TimeScope("HandleTranslationUnit for raw-ptr plugin");
ApplyFilter(context);
if (options_.check_bad_raw_ptr_cast || options_.check_raw_ptr_fields ||
options_.check_raw_ref_fields || config_.container_config.enabled ||
(options_.check_raw_ptr_to_stack_allocated &&
!options_.disable_check_raw_ptr_to_stack_allocated_error) ||
options_.check_span_fields) {
FindBadRawPtrPatterns(options_, config_, context, instance_);
}
}
private:
// Options.
const Options options_;
const RawPtrPluginConfig config_;
clang::CompilerInstance& instance_;
};
} // namespace
RawPtrPlugin::RawPtrPlugin() {}
std::unique_ptr<ASTConsumer> RawPtrPlugin::CreateASTConsumer(
CompilerInstance& instance,
llvm::StringRef ref) {
return std::make_unique<PluginConsumer>(&instance, options_, config_);
}
bool LoadPluginConfig(const clang::CompilerInstance& instance,
llvm::StringRef file_path,
ParsedRawPtrPluginConfig& config) {
auto buffer = llvm::MemoryBuffer::getFile(file_path);
if (!buffer) {
unsigned diag_id = instance.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error,
"raw-ptr-plugin: Could not open config file %0: %1");
instance.getDiagnostics().Report(diag_id)
<< file_path << llvm::StringRef(buffer.getError().message()).lower();
return false;
}
llvm::yaml::Input yin(buffer.get()->getBuffer());
yin >> config;
if (yin.error()) {
unsigned diag_id = instance.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error,
"raw-ptr-plugin: YAML error in %0: %1");
instance.getDiagnostics().Report(diag_id)
<< file_path << llvm::StringRef(yin.error().message()).lower();
return false;
}
return true;
}
bool RawPtrPlugin::ParseArgs(const CompilerInstance& instance,
const std::vector<std::string>& args) {
bool config_path_provided = false;
std::string config_path;
bool check_containers = false;
for (llvm::StringRef arg : args) {
if (arg.starts_with(kExcludeFieldsArgPrefix)) {
options_.exclude_fields_file =
arg.substr(strlen(kExcludeFieldsArgPrefix)).str();
} else if (arg.starts_with(kRawPtrExcludePathArgPrefix)) {
options_.raw_ptr_paths_to_exclude_lines.push_back(
arg.substr(strlen(kRawPtrExcludePathArgPrefix)).str());
} else if (arg.starts_with(kRawPtrPluginConfigPathArgPrefix)) {
config_path_provided = true;
config_path = arg.substr(strlen(kRawPtrPluginConfigPathArgPrefix)).str();
} else if (arg.starts_with(kCheckBadRawPtrCastExcludeFuncArgPrefix)) {
options_.check_bad_raw_ptr_cast_exclude_funcs.push_back(
arg.substr(strlen(kCheckBadRawPtrCastExcludeFuncArgPrefix)).str());
} else if (arg.starts_with(kBadRawPtrCastExcludePathArgPrefix)) {
options_.check_bad_raw_ptr_cast_exclude_paths.push_back(
arg.substr(strlen(kBadRawPtrCastExcludePathArgPrefix)).str());
} else if (arg == "check-bad-raw-ptr-cast") {
options_.check_bad_raw_ptr_cast = true;
} else if (arg == "check-raw-ptr-fields") {
options_.check_raw_ptr_fields = true;
} else if (arg == "check-raw-ptr-to-stack-allocated") {
options_.check_raw_ptr_to_stack_allocated = true;
} else if (arg == "disable-check-raw-ptr-to-stack-allocated-error") {
options_.disable_check_raw_ptr_to_stack_allocated_error = true;
} else if (arg == "check-raw-ref-fields") {
options_.check_raw_ref_fields = true;
} else if (arg == "check-ptrs-to-non-string-literals") {
// Rewriting const char pointers was skipped for performance as they are
// likely to point to string literals.
//
// This exclusion mechanism also wrongly excluded some non-string-literals
// like `const uint8_t*` and `const int8*`.
//
// This flag is added to gradually re-include these types in the
// enforcement plugin.
//
// TODO(https://crbug.com/331840473) Remove this flag
// once the necessary members are rewritten and the raw_ptr enforcement
// plugin is up to date.
options_.check_ptrs_to_non_string_literals = true;
} else if (arg == "check-span-fields") {
options_.check_span_fields = true;
} else if (arg == "enable-match-profiling") {
options_.enable_match_profiling = true;
} else if (arg == "no-special-treatment-for-oilpanized-paths") {
options_.explicitly_ignore_oilpanized_paths = false;
} else if (arg == "check-containers") {
check_containers = true;
} else {
llvm::errs() << "Unknown clang plugin argument: " << arg << "\n";
return false;
}
}
if (config_path_provided) {
ParsedRawPtrPluginConfig parsed_config;
if (!LoadPluginConfig(instance, config_path, parsed_config)) {
return false;
}
config_ = RawPtrPluginConfig::Default();
std::string err = config_.Merge(parsed_config);
if (!err.empty()) {
unsigned diag_id = instance.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "raw-ptr-plugin: %0");
instance.getDiagnostics().Report(diag_id) << err;
return false;
}
} else if (check_containers) {
config_ = RawPtrPluginConfig::Default();
}
return true;
}
} // namespace raw_ptr_plugin
static FrontendPluginRegistry::Add<raw_ptr_plugin::RawPtrPlugin> X(
"raw-ptr-plugin",
"Check pointers for safety");