#include "StackAllocatedChecker.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Frontend/CompilerInstance.h"
namespace raw_ptr_plugin {
namespace {
const char kStackAllocatedFieldError[] =
"Non-stack-allocated type '%0' has a field '%1' which is a stack-allocated "
"type, pointer/reference to a stack-allocated type, or template "
"instantiation with a stack-allocated type as template parameter.";
const clang::Type* StripReferences(const clang::Type* type) {
while (type) {
if (type->isArrayType()) {
type = type->getPointeeOrArrayElementType();
} else if (type->isPointerType() || type->isReferenceType()) {
type = type->getPointeeType().getTypePtrOrNull();
} else {
break;
}
}
return type;
}
}
bool StackAllocatedPredicate::IsStackAllocated(
const clang::CXXRecordDecl* record) const {
if (!record) {
return false;
}
auto iter = cache_.find(record);
if (iter != cache_.end()) {
return iter->second;
}
bool stack_allocated = false;
for (clang::Decl* decl : record->decls()) {
clang::TypeAliasDecl* alias = clang::dyn_cast<clang::TypeAliasDecl>(decl);
if (!alias) {
continue;
}
if (alias->getName() == "IsStackAllocatedTypeMarker") {
stack_allocated = true;
break;
}
}
if (record->hasDefinition()) {
for (clang::CXXRecordDecl::base_class_const_iterator it =
record->bases_begin();
!stack_allocated && it != record->bases_end(); ++it) {
clang::CXXRecordDecl* parent_record =
it->getType().getTypePtr()->getAsCXXRecordDecl();
stack_allocated = IsStackAllocated(parent_record);
}
}
iter = cache_.insert({record, stack_allocated}).first;
if (auto* field_record_template =
clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(record)) {
const auto& template_args = field_record_template->getTemplateArgs();
for (unsigned i = 0; i < template_args.size(); i++) {
if (template_args[i].getKind() == clang::TemplateArgument::Type) {
const auto* type =
StripReferences(template_args[i].getAsType().getTypePtrOrNull());
if (type && IsStackAllocated(type->getAsCXXRecordDecl())) {
stack_allocated = true;
}
}
}
}
iter->second = stack_allocated;
return stack_allocated;
}
StackAllocatedChecker::StackAllocatedChecker(clang::CompilerInstance& compiler)
: compiler_(compiler),
stack_allocated_field_error_signature_(
compiler.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error,
kStackAllocatedFieldError)) {}
void StackAllocatedChecker::Check(clang::CXXRecordDecl* record) {
if (!record->isCompleteDefinition()) {
return;
}
if (predicate_.IsStackAllocated(record)) {
return;
}
for (clang::RecordDecl::field_iterator it = record->field_begin();
it != record->field_end(); ++it) {
clang::FieldDecl* field = *it;
bool ignore = false;
for (auto annotation : field->specific_attrs<clang::AnnotateAttr>()) {
if (annotation->getAnnotation() == "stack_allocated_ignore") {
ignore = true;
break;
}
}
if (ignore) {
continue;
}
const clang::Type* type =
StripReferences(field->getType().getTypePtrOrNull());
if (!type) {
continue;
}
auto* field_record = type->getAsCXXRecordDecl();
if (!field_record) {
continue;
}
if (predicate_.IsStackAllocated(field_record)) {
compiler_.getDiagnostics().Report(field->getLocation(),
stack_allocated_field_error_signature_)
<< record->getName() << field->getNameAsString();
}
}
}
}