diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt index c6496167d3828b9089c2419cd4725090a3f4d94e..16a932448fe13273d057b87b1b33ad4200efe4ed 100644 --- a/clang/CMakeLists.txt +++ b/clang/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.20.0) set(LLVM_SUBPROJECT_TITLE "Clang") +enable_testing() if(NOT DEFINED LLVM_COMMON_CMAKE_UTILS) set(LLVM_COMMON_CMAKE_UTILS ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) @@ -925,10 +926,21 @@ if (CLANG_BOLT AND NOT LLVM_BUILD_INSTRUMENTED) ) endif() +add_custom_target(cr-check-all COMMAND ${CMAKE_CTEST_COMMAND} -V) + +function(cr_add_test name testprog) + add_custom_target( + ${name} COMMAND ${testprog} ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + add_dependencies(cr-check-all ${name}) +endfunction(cr_add_test) + if (LLVM_ADD_NATIVE_VISUALIZERS_TO_SOLUTION) add_subdirectory(utils/ClangVisualizers) endif() add_subdirectory(utils/hmaptool) +add_subdirectory(blink_gc_plugin) +add_subdirectory(raw_ptr_plugin) if(CLANG_BUILT_STANDALONE) llvm_distribution_add_targets() diff --git a/clang/blink_gc_plugin/BadPatternFinder.cpp b/clang/blink_gc_plugin/BadPatternFinder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a3a4da607de97774f1492cdb5df6a1e71c9a5ec --- /dev/null +++ b/clang/blink_gc_plugin/BadPatternFinder.cpp @@ -0,0 +1,671 @@ +// Copyright 2017 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "BadPatternFinder.h" + +#include +#include + +#include + +#include "BlinkGCPluginOptions.h" +#include "Config.h" +#include "DiagnosticsReporter.h" +#include "RecordInfo.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersMacros.h" + +using namespace clang::ast_matchers; + +namespace { + +TypeMatcher GarbageCollectedType() { + auto has_gc_base = hasCanonicalType(hasDeclaration( + cxxRecordDecl(isDerivedFrom(hasAnyName("::cppgc::GarbageCollected", + "::cppgc::GarbageCollectedMixin"))) + .bind("gctype"))); + return anyOf(has_gc_base, + hasCanonicalType(arrayType(hasElementType(has_gc_base)))); +} + +TypeMatcher MemberType() { + auto has_member_base = hasCanonicalType(hasDeclaration( + classTemplateSpecializationDecl( + hasName("::cppgc::internal::BasicMember"), + hasAnyTemplateArgument( + refersToType(hasCanonicalType(hasDeclaration(anyOf( + cxxRecordDecl(hasName("::cppgc::internal::StrongMemberTag")), + cxxRecordDecl( + hasName("::cppgc::internal::WeakMemberTag")))))))) + .bind("member"))); + return anyOf(has_member_base, + hasCanonicalType(arrayType(hasElementType(has_member_base)))); +} + +TypeMatcher TraceableType() { + auto has_gc_base = hasCanonicalType(hasDeclaration( + cxxRecordDecl( + hasMethod(cxxMethodDecl( + hasName("Trace"), isConst(), parameterCountIs(1), + hasParameter( + 0, parmVarDecl(hasType(pointerType(pointee(hasCanonicalType( + hasDeclaration(cxxRecordDecl(isSameOrDerivedFrom( + hasName("cppgc::Visitor"))))))))))))) + .bind("traceable"))); + return anyOf(has_gc_base, + hasCanonicalType(arrayType(hasElementType(has_gc_base)))); +} + +class UniquePtrGarbageCollectedMatcher : public MatchFinder::MatchCallback { + public: + explicit UniquePtrGarbageCollectedMatcher(DiagnosticsReporter& diagnostics) + : diagnostics_(diagnostics) {} + + void Register(MatchFinder& match_finder) { + // Matches any application of make_unique where the template argument is + // known to refer to a garbage-collected type. + auto make_unique_matcher = + callExpr( + callee(functionDecl( + hasAnyName("::std::make_unique", "::base::WrapUnique"), + hasTemplateArgument( + 0, refersToType(GarbageCollectedType()))) + .bind("badfunc"))) + .bind("bad"); + match_finder.addDynamicMatcher(make_unique_matcher, this); + } + + void run(const MatchFinder::MatchResult& result) override { + auto* bad_use = result.Nodes.getNodeAs("bad"); + auto* bad_function = result.Nodes.getNodeAs("badfunc"); + auto* gc_type = result.Nodes.getNodeAs("gctype"); + diagnostics_.UniquePtrUsedWithGC(bad_use, bad_function, gc_type); + } + + private: + DiagnosticsReporter& diagnostics_; +}; + +bool IsOnStack(const clang::Decl* decl, RecordCache& record_cache) { + if (dyn_cast(decl)) { + return true; + } + const clang::FieldDecl* field_decl = dyn_cast(decl); + assert(field_decl); + const clang::CXXRecordDecl* parent_decl = + dyn_cast(field_decl->getParent()); + assert(parent_decl); + return record_cache.Lookup(parent_decl)->IsStackAllocated(); +} + +class OptionalOrRawPtrToGCedMatcher : public MatchFinder::MatchCallback { + public: + OptionalOrRawPtrToGCedMatcher(DiagnosticsReporter& diagnostics, + RecordCache& record_cache) + : diagnostics_(diagnostics), record_cache_(record_cache) {} + + void Register(MatchFinder& match_finder) { + // Matches fields and new-expressions of type std::optional or + // absl::optional where the template argument is known to refer to a + // garbage-collected type. + auto optional_gced_type = hasType( + classTemplateSpecializationDecl( + hasAnyName("::absl::optional", "::std::optional", "::base::raw_ptr", + "::base::raw_ref"), + hasTemplateArgument(0, refersToType(anyOf(GarbageCollectedType(), + TraceableType())))) + .bind("type")); + auto optional_field = fieldDecl(optional_gced_type).bind("bad_decl"); + auto optional_var = varDecl(optional_gced_type).bind("bad_decl"); + auto optional_new_expression = + cxxNewExpr(has(cxxConstructExpr(optional_gced_type))).bind("bad_new"); + match_finder.addDynamicMatcher(optional_field, this); + match_finder.addDynamicMatcher(optional_var, this); + match_finder.addDynamicMatcher(optional_new_expression, this); + } + + void run(const MatchFinder::MatchResult& result) override { + auto* type = result.Nodes.getNodeAs("type"); + bool is_optional = (type->getName() == "optional"); + auto* arg_type = result.Nodes.getNodeAs("gctype"); + bool is_gced = arg_type; + if (!arg_type) { + arg_type = result.Nodes.getNodeAs("traceable"); + } + assert(arg_type); + if (auto* bad_decl = result.Nodes.getNodeAs("bad_decl")) { + if (Config::IsIgnoreAnnotated(bad_decl)) { + return; + } + // Optionals of non-GCed traceable or GCed collections are allowed on + // stack. + if (is_optional && + (!is_gced || Config::IsGCCollection(arg_type->getName())) && + IsOnStack(bad_decl, record_cache_)) { + return; + } + if (is_optional) { + diagnostics_.OptionalDeclUsedWithGC(bad_decl, type, arg_type); + } else { + diagnostics_.RawPtrOrRefDeclUsedWithGC(bad_decl, type, arg_type); + } + } else { + auto* bad_new = result.Nodes.getNodeAs("bad_new"); + assert(bad_new); + if (is_optional) { + diagnostics_.OptionalNewExprUsedWithGC(bad_new, type, arg_type); + } else { + diagnostics_.RawPtrOrRefNewExprUsedWithGC(bad_new, type, arg_type); + } + } + } + + private: + DiagnosticsReporter& diagnostics_; + RecordCache& record_cache_; +}; + +class OptionalMemberMatcher : public MatchFinder::MatchCallback { + public: + OptionalMemberMatcher(DiagnosticsReporter& diagnostics, + RecordCache& record_cache) + : diagnostics_(diagnostics), record_cache_(record_cache) {} + + void Register(MatchFinder& match_finder) { + // Matches fields and new-expressions of type std::optional or + // absl::optional where the template argument is known to refer to a + // garbage-collected type. + auto optional_gced_type = + hasType(classTemplateSpecializationDecl( + hasAnyName("::absl::optional", "::std::optional"), + hasTemplateArgument(0, refersToType(MemberType()))) + .bind("type")); + // On stack optional is safe, so we don't need to find variables + // here.Matching fields should suffice. + auto optional_field = fieldDecl(optional_gced_type).bind("bad_decl"); + auto optional_new_expression = + cxxNewExpr(has(cxxConstructExpr(optional_gced_type))).bind("bad_new"); + match_finder.addDynamicMatcher(optional_field, this); + match_finder.addDynamicMatcher(optional_new_expression, this); + } + + void run(const MatchFinder::MatchResult& result) override { + auto* type = result.Nodes.getNodeAs("type"); + auto* member = result.Nodes.getNodeAs("member"); + if (auto* bad_decl = result.Nodes.getNodeAs("bad_decl")) { + if (Config::IsIgnoreAnnotated(bad_decl) || + IsOnStack(bad_decl, record_cache_)) { + return; + } + diagnostics_.OptionalDeclUsedWithMember(bad_decl, type, member); + } else { + auto* bad_new = result.Nodes.getNodeAs("bad_new"); + assert(bad_new); + diagnostics_.OptionalNewExprUsedWithMember(bad_new, type, member); + } + } + + private: + DiagnosticsReporter& diagnostics_; + RecordCache& record_cache_; +}; + +class CollectionOfGarbageCollectedMatcher : public MatchFinder::MatchCallback { + public: + explicit CollectionOfGarbageCollectedMatcher(DiagnosticsReporter& diagnostics, + RecordCache& record_cache) + : diagnostics_(diagnostics), record_cache_(record_cache) {} + + void Register(MatchFinder& match_finder) { + auto gced_ptr_or_ref = + anyOf(GarbageCollectedType(), + pointerType(pointee(GarbageCollectedType())).bind("ptr"), + referenceType(pointee(GarbageCollectedType())).bind("ptr")); + auto gced_ptr_ref_or_pair = + anyOf(gced_ptr_or_ref, + hasCanonicalType(hasDeclaration((classTemplateSpecializationDecl( + hasName("::std::pair"), + hasAnyTemplateArgument(refersToType(gced_ptr_or_ref))))))); + auto member_ptr_or_ref = + anyOf(MemberType(), pointerType(pointee(MemberType())), + referenceType(pointee(MemberType()))); + auto member_ptr_ref_or_pair = + anyOf(member_ptr_or_ref, + hasCanonicalType(hasDeclaration((classTemplateSpecializationDecl( + hasName("::std::pair"), + hasAnyTemplateArgument(refersToType(member_ptr_or_ref))))))); + auto gced_or_member = anyOf(gced_ptr_ref_or_pair, member_ptr_ref_or_pair); + auto has_wtf_collection_name = hasAnyName( + "::WTF::Vector", "::WTF::Deque", "::WTF::HashSet", + "::WTF::LinkedHashSet", "::WTF::HashCountedSet", "::WTF::HashMap"); + auto has_std_collection_name = + hasAnyName("::std::vector", "::std::map", "::std::unordered_map", + "::std::set", "::std::unordered_set", "::std::array"); + auto partition_allocator = hasCanonicalType( + hasDeclaration(cxxRecordDecl(hasName("::WTF::PartitionAllocator")))); + auto wtf_collection_decl = + classTemplateSpecializationDecl( + has_wtf_collection_name, + hasAnyTemplateArgument(refersToType(gced_or_member)), + hasAnyTemplateArgument(refersToType(partition_allocator))) + .bind("collection"); + auto std_collection_decl = + classTemplateSpecializationDecl( + has_std_collection_name, + hasAnyTemplateArgument(refersToType(gced_or_member))) + .bind("collection"); + auto any_collection = hasType(hasCanonicalType( + hasDeclaration(anyOf(wtf_collection_decl, std_collection_decl)))); + auto collection_field = fieldDecl(any_collection).bind("bad_decl"); + auto collection_var = varDecl(any_collection).bind("bad_decl"); + auto collection_new_expression = + cxxNewExpr(has(cxxConstructExpr(any_collection))).bind("bad_new"); + match_finder.addDynamicMatcher(collection_field, this); + match_finder.addDynamicMatcher(collection_var, this); + match_finder.addDynamicMatcher(collection_new_expression, this); + } + + void run(const MatchFinder::MatchResult& result) override { + auto* collection = + result.Nodes.getNodeAs("collection"); + auto* gc_type = result.Nodes.getNodeAs("gctype"); + auto* member = result.Nodes.getNodeAs("member"); + assert(gc_type || member); + if (auto* bad_decl = result.Nodes.getNodeAs("bad_decl")) { + if (Config::IsIgnoreAnnotated(bad_decl)) { + return; + } + if (collection->getNameAsString() == "array") { + if (member || Config::IsGCCollection(gc_type->getName())) { + // std::array of Members is fine as long as it is traced (which is + // enforced by another checker). + return; + } + if (result.Nodes.getNodeAs("ptr") && + IsOnStack(bad_decl, record_cache_)) { + // On stack std::array of raw pointers to GCed type is allowed. + // Note: this may miss cases of std::array>, + // but such cases don't currently exist in the codebase. + return; + } + } + if (gc_type) { + diagnostics_.CollectionOfGCed(bad_decl, collection, gc_type); + } else { + assert(member); + diagnostics_.CollectionOfMembers(bad_decl, collection, member); + } + } else { + auto* bad_new = result.Nodes.getNodeAs("bad_new"); + assert(bad_new); + if (gc_type) { + diagnostics_.CollectionOfGCed(bad_new, collection, gc_type); + } else { + assert(member); + diagnostics_.CollectionOfMembers(bad_new, collection, member); + } + } + } + + private: + DiagnosticsReporter& diagnostics_; + RecordCache& record_cache_; +}; + +// For the absl::variant checker, we need to match the inside of a variadic +// template class, which doesn't seem easy with the built-in matchers: define +// a custom matcher to go through the template parameter list. +AST_MATCHER_P(clang::TemplateArgument, + parameterPackHasAnyElement, + // Clang exports other instantiations of Matcher via + // using-declarations in public headers, e.g. `using TypeMatcher + // = Matcher`. + // + // Once https://reviews.llvm.org/D89920, a Clang patch adding a + // similar alias for template arguments, lands, this can be + // changed to TemplateArgumentMatcher and won't need to use the + // internal namespace any longer. + clang::ast_matchers::internal::Matcher, + InnerMatcher) { + if (Node.getKind() != clang::TemplateArgument::Pack) { + return false; + } + return llvm::any_of(Node.pack_elements(), + [&](const clang::TemplateArgument& Arg) { + return InnerMatcher.matches(Arg, Finder, Builder); + }); +} + +// Prevents the use of garbage collected objects in `absl::variant`. +// That's because `absl::variant` doesn't work well with concurrent marking. +// Oilpan uses an object's type to know how to trace it. If the type stored in +// an `absl::variant` changes while the object is concurrently being marked, +// Oilpan might fail to find a matching pair of element type and reference. +// This in turn can lead to UAFs and other memory corruptions. +class VariantGarbageCollectedMatcher : public MatchFinder::MatchCallback { + public: + explicit VariantGarbageCollectedMatcher(DiagnosticsReporter& diagnostics) + : diagnostics_(diagnostics) {} + + void Register(MatchFinder& match_finder) { + // Matches any constructed absl::variant where a template argument is + // known to refer to a garbage-collected type. + auto variant_construction = + cxxConstructExpr( + hasDeclaration(cxxConstructorDecl( + ofClass(classTemplateSpecializationDecl( + hasAnyName("::absl::variant", "::std::variant"), + hasAnyTemplateArgument(parameterPackHasAnyElement( + refersToType(GarbageCollectedType())))) + .bind("variant"))))) + .bind("bad"); + match_finder.addDynamicMatcher(variant_construction, this); + } + + void run(const MatchFinder::MatchResult& result) override { + auto* bad_use = result.Nodes.getNodeAs("bad"); + auto* variant = result.Nodes.getNodeAs("variant"); + auto* gc_type = result.Nodes.getNodeAs("gctype"); + diagnostics_.VariantUsedWithGC(bad_use, variant, gc_type); + } + + private: + DiagnosticsReporter& diagnostics_; +}; + +class MemberOnStackMatcher : public MatchFinder::MatchCallback { + public: + explicit MemberOnStackMatcher(DiagnosticsReporter& diagnostics) + : diagnostics_(diagnostics) {} + + void Register(MatchFinder& match_finder) { + auto class_member_variable_matcher = + varDecl(hasType(MemberType())).bind("var"); + match_finder.addDynamicMatcher(class_member_variable_matcher, this); + } + + void run(const MatchFinder::MatchResult& result) override { + auto* member = result.Nodes.getNodeAs("var"); + if (Config::IsIgnoreAnnotated(member)) { + return; + } + diagnostics_.MemberOnStack(member); + } + + private: + DiagnosticsReporter& diagnostics_; +}; + +class WeakPtrToGCedMatcher : public MatchFinder::MatchCallback { + public: + explicit WeakPtrToGCedMatcher(DiagnosticsReporter& diagnostics) + : diagnostics_(diagnostics) {} + + void Register(MatchFinder& match_finder) { + // Matches declarations of type base::WeakPtr and base::WeakPtrFactory + // where the template argument is known to refer to a garbage-collected + // type. + auto weak_ptr_type = hasType( + classTemplateSpecializationDecl( + hasAnyName("::base::WeakPtr", "::base::WeakPtrFactory"), + hasTemplateArgument(0, refersToType(GarbageCollectedType()))) + .bind("weak_ptr")); + auto weak_ptr_field = fieldDecl(weak_ptr_type).bind("bad_decl"); + auto weak_ptr_var = varDecl(weak_ptr_type).bind("bad_decl"); + auto weak_ptr_new_expression = + cxxNewExpr(has(cxxConstructExpr(weak_ptr_type))).bind("bad_decl"); + match_finder.addDynamicMatcher(weak_ptr_field, this); + match_finder.addDynamicMatcher(weak_ptr_var, this); + match_finder.addDynamicMatcher(weak_ptr_new_expression, this); + } + + void run(const MatchFinder::MatchResult& result) override { + auto* decl = result.Nodes.getNodeAs("bad_decl"); + if (Config::IsIgnoreAnnotated(decl)) { + return; + } + auto* weak_ptr = result.Nodes.getNodeAs("weak_ptr"); + auto* gc_type = result.Nodes.getNodeAs("gctype"); + diagnostics_.WeakPtrToGCed(decl, weak_ptr, gc_type); + } + + private: + DiagnosticsReporter& diagnostics_; +}; + +AST_MATCHER(clang::CXXRecordDecl, isDisallowedNewClass) { + auto& context = Finder->getASTContext(); + + auto gc_matcher = GarbageCollectedType(); + if (gc_matcher.matches(context.getTypeDeclType(&Node), Finder, Builder)) { + // This is a normal GCed class, bail out. + return false; + } + + // First, look for methods in this class. + auto method = std::find_if( + Node.method_begin(), Node.method_end(), [](const auto& method) { + return method->getNameAsString() == kNewOperatorName && + method->getNumParams() == 1; + }); + if (method != Node.method_end()) { + // We found the 'operator new'. Check if it's deleted. + return method->isDeleted(); + } + + // Otherwise, lookup in the base classes. + for (auto& base_spec : Node.bases()) { + if (auto* base = base_spec.getType()->getAsCXXRecordDecl()) { + if (matches(*base, Finder, Builder)) { + return true; + } + } + } + + return false; +} + +size_t RoundUp(size_t value, size_t align) { + assert((align & (align - 1)) == 0); + return (value + align - 1) & ~(align - 1); +} + +// Very approximate way of calculating size of a record based on fields. +// Doesn't take into account alignment of base subobjects, but only its own +// fields. +size_t RequiredSizeForFields(const clang::ASTContext& context, + size_t current_size, + const std::vector& field_types) { + size_t largest_field_alignment = 0; + + for (clang::QualType type : field_types) { + assert(!type->isDependentType()); + const size_t current_field_alignment = context.getTypeAlign(type); + current_size = RoundUp(current_size, current_field_alignment); + current_size += context.getTypeSize(type); + largest_field_alignment = + std::max(largest_field_alignment, current_field_alignment); + } + + current_size = RoundUp(current_size, largest_field_alignment); + return current_size; +} + +class PaddingInGCedMatcher : public MatchFinder::MatchCallback { + public: + PaddingInGCedMatcher(clang::ASTContext& context, + DiagnosticsReporter& diagnostics) + : context_(context), diagnostics_(diagnostics) {} + + void Register(MatchFinder& match_finder) { + auto member_field_matcher = + cxxRecordDecl(has(fieldDecl(hasType(MemberType())).bind("field")), + isDisallowedNewClass()) + .bind("record"); + match_finder.addMatcher(member_field_matcher, this); + } + + void run(const MatchFinder::MatchResult& result) override { + auto* class_decl = result.Nodes.getNodeAs("record"); + if (class_decl->isDependentType() || class_decl->isUnion()) { + return; + } + + if (auto* member_decl = result.Nodes.getNodeAs("field"); + member_decl && Config::IsIgnoreAnnotated(member_decl)) { + return; + } + + if (auto* cxx_record_decl = + clang::dyn_cast(class_decl)) { + if (cxx_record_decl->getNumVBases()) { + // Don't process class with virtual bases. + return; + } + } + + std::vector fields; + for (auto* field : class_decl->fields()) { + if (field->isBitField()) { + // Don't process types with bitfields yet. + return; + } + if (field->isZeroSize(context_)) { + // Don't process types with [[no_unique_address]] on the fields. + return; + } + if (field->hasAttr()) { + // Ignore classes containing alignas on the fields. + return; + } + + fields.push_back(field->getType()); + } + assert(fields.size() > 0); + + const clang::ASTRecordLayout& layout = + context_.getASTRecordLayout(class_decl); + const size_t base_size = layout.getFieldOffset(0); + + const size_t size_before = + RequiredSizeForFields(context_, base_size, fields); + + std::sort(fields.begin(), fields.end(), + [this](clang::QualType t1, clang::QualType t2) { + // Try simply sort by sizes, ignoring alignment. + return context_.getTypeSize(t1) > context_.getTypeSize(t2); + }); + + const size_t size_after = + RequiredSizeForFields(context_, base_size, fields); + + if (size_after < size_before) { + diagnostics_.AdditionalPadding( + class_decl, (size_before - size_after) / context_.getCharWidth()); + } + } + + private: + clang::ASTContext& context_; + DiagnosticsReporter& diagnostics_; +}; + +class GCedVarOrField : public MatchFinder::MatchCallback { + public: + explicit GCedVarOrField(DiagnosticsReporter& diagnostics) + : diagnostics_(diagnostics) {} + + void Register(MatchFinder& match_finder) { + auto gced_field = + fieldDecl(hasType(GarbageCollectedType())).bind("bad_field"); + // As opposed to definitions, declarations of function templates with + // unfulfilled requires-clauses get instantiated and as such are + // observable in the clang AST (as functions without a body). If such a + // method takes a GCed type as a parameter, we should not alert on it + // since the method is never actually used. This is common in Blink due to + // the implementation of the variant CHECK_* and DCHECK_* macros + // (specifically the DEFINE_CHECK_OP_IMPL macro). + auto unimplemented_template_instance_parameter = + parmVarDecl(hasAncestor(functionDecl(hasParent(functionTemplateDecl()), + unless(hasBody(stmt()))))); + auto gced_var = varDecl(hasType(GarbageCollectedType()), + unless(unimplemented_template_instance_parameter)) + .bind("bad_var"); + match_finder.addDynamicMatcher(gced_field, this); + match_finder.addDynamicMatcher(gced_var, this); + } + + void run(const MatchFinder::MatchResult& result) override { + const auto* gctype = result.Nodes.getNodeAs("gctype"); + assert(gctype); + if (Config::IsGCCollection(gctype->getName())) { + return; + } + const auto* field = result.Nodes.getNodeAs("bad_field"); + if (field) { + if (Config::IsIgnoreAnnotated(field)) { + return; + } + diagnostics_.GCedField(field, gctype); + } else { + const auto* var = result.Nodes.getNodeAs("bad_var"); + assert(var); + if (Config::IsIgnoreAnnotated(var)) { + return; + } + diagnostics_.GCedVar(var, gctype); + } + } + + private: + DiagnosticsReporter& diagnostics_; +}; + +} // namespace + +void FindBadPatterns(clang::ASTContext& ast_context, + DiagnosticsReporter& diagnostics, + RecordCache& record_cache, + const BlinkGCPluginOptions& options) { + MatchFinder match_finder; + + UniquePtrGarbageCollectedMatcher unique_ptr_gc(diagnostics); + unique_ptr_gc.Register(match_finder); + + OptionalOrRawPtrToGCedMatcher optional_or_rawptr_gc(diagnostics, + record_cache); + optional_or_rawptr_gc.Register(match_finder); + + CollectionOfGarbageCollectedMatcher collection_of_gc(diagnostics, + record_cache); + if (options.enable_off_heap_collections_of_gced_check) { + collection_of_gc.Register(match_finder); + } + + VariantGarbageCollectedMatcher variant_gc(diagnostics); + variant_gc.Register(match_finder); + + MemberOnStackMatcher member_on_stack(diagnostics); + if (options.enable_members_on_stack_check) { + member_on_stack.Register(match_finder); + } + + PaddingInGCedMatcher padding_in_gced(ast_context, diagnostics); + if (options.enable_extra_padding_check) { + padding_in_gced.Register(match_finder); + } + + WeakPtrToGCedMatcher weak_ptr_to_gced(diagnostics); + weak_ptr_to_gced.Register(match_finder); + + GCedVarOrField gced_var_or_field(diagnostics); + gced_var_or_field.Register(match_finder); + + OptionalMemberMatcher optional_member(diagnostics, record_cache); + optional_member.Register(match_finder); + + match_finder.matchAST(ast_context); +} diff --git a/clang/blink_gc_plugin/BadPatternFinder.h b/clang/blink_gc_plugin/BadPatternFinder.h new file mode 100644 index 0000000000000000000000000000000000000000..093305d880c95003e8fd9ca1ddf39a8143ad4ec0 --- /dev/null +++ b/clang/blink_gc_plugin/BadPatternFinder.h @@ -0,0 +1,18 @@ +// Copyright 2017 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +struct BlinkGCPluginOptions; +class DiagnosticsReporter; +class RecordCache; + +namespace clang { +class ASTContext; +} // namespace clang + +// Detects and reports use of banned patterns, such as applying +// std::make_unique to a garbage-collected type. +void FindBadPatterns(clang::ASTContext& ast_context, + DiagnosticsReporter&, + RecordCache& record_cache, + const BlinkGCPluginOptions&); diff --git a/clang/blink_gc_plugin/BlinkGCPlugin.cpp b/clang/blink_gc_plugin/BlinkGCPlugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..42a33444ebb929dfc69912460726193de3ce7e71 --- /dev/null +++ b/clang/blink_gc_plugin/BlinkGCPlugin.cpp @@ -0,0 +1,64 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This clang plugin checks various invariants of the Blink garbage +// collection infrastructure. +// +// Errors are described at: +// http://www.chromium.org/developers/blink-gc-plugin-errors + +#include "BlinkGCPluginConsumer.h" +#include "BlinkGCPluginOptions.h" +#include "Config.h" + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" + +using namespace clang; + +class BlinkGCPluginAction : public PluginASTAction { + public: + BlinkGCPluginAction() {} + + protected: + // Overridden from PluginASTAction: + std::unique_ptr CreateASTConsumer(CompilerInstance& instance, + llvm::StringRef ref) override { + return std::make_unique(instance, options_); + } + + PluginASTAction::ActionType getActionType() override { + return CmdlineBeforeMainAction; + } + + bool ParseArgs(const CompilerInstance&, + const std::vector& args) override { + for (const auto& arg : args) { + if (arg == "dump-graph") { + options_.dump_graph = true; + } else if (arg == "enable-persistent-in-unique-ptr-check") { + options_.enable_persistent_in_unique_ptr_check = true; + } else if (arg == "enable-members-on-stack-check") { + options_.enable_members_on_stack_check = true; + } else if (arg == "enable-extra-padding-check") { + options_.enable_extra_padding_check = true; + } else if (arg == "disable-off-heap-collections-of-gced-check") { + options_.enable_off_heap_collections_of_gced_check = false; + } else if (arg == "enable-ptrs-to-traceable-check") { + options_.enable_ptrs_to_traceable_check = true; + } else { + llvm::errs() << "Unknown blink-gc-plugin argument: " << arg << "\n"; + return false; + } + } + return true; + } + + private: + BlinkGCPluginOptions options_; +}; + +static FrontendPluginRegistry::Add X( + "blink-gc-plugin", + "Check Blink GC invariants"); diff --git a/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp b/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f9ef368acf3bf00e31c3d84fa4363f37779413c1 --- /dev/null +++ b/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp @@ -0,0 +1,697 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "BlinkGCPluginConsumer.h" + +#include +#include + +#include "BadPatternFinder.h" +#include "CheckDispatchVisitor.h" +#include "CheckFieldsVisitor.h" +#include "CheckFinalizerVisitor.h" +#include "CheckForbiddenFieldsVisitor.h" +#include "CheckGCRootsVisitor.h" +#include "CheckTraceVisitor.h" +#include "CollectVisitor.h" +#include "JsonWriter.h" +#include "RecordInfo.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Sema/Sema.h" +#include "llvm/Support/TimeProfiler.h" + +using namespace clang; + +namespace { + +// Use a local RAV implementation to simply collect all FunctionDecls marked for +// late template parsing. This happens with the flag -fdelayed-template-parsing, +// which is on by default in MSVC-compatible mode. +std::set GetLateParsedFunctionDecls(TranslationUnitDecl* decl) { + struct Visitor : public RecursiveASTVisitor { + bool VisitFunctionDecl(FunctionDecl* function_decl) { + if (function_decl->isLateTemplateParsed()) + late_parsed_decls.insert(function_decl); + return true; + } + + std::set late_parsed_decls; + } v; + v.TraverseDecl(decl); + return v.late_parsed_decls; +} + +class EmptyStmtVisitor : public RecursiveASTVisitor { + public: + static bool isEmpty(Stmt* stmt) { + EmptyStmtVisitor visitor; + visitor.TraverseStmt(stmt); + return visitor.empty_; + } + + bool WalkUpFromCompoundStmt(CompoundStmt* stmt) { + empty_ = stmt->body_empty(); + return false; + } + bool VisitStmt(Stmt*) { + empty_ = false; + return false; + } + private: + EmptyStmtVisitor() : empty_(true) {} + bool empty_; +}; + +const CXXRecordDecl* GetFirstTemplateArgAsCXXRecordDecl( + const CXXRecordDecl* gc_base) { + if (const auto* gc_base_template_id = + dyn_cast(gc_base)) { + const TemplateArgumentList& gc_args = + gc_base_template_id->getTemplateArgs(); + if (!gc_args.size() || gc_args[0].getKind() != TemplateArgument::Type) + return nullptr; + return gc_args[0].getAsType()->getAsCXXRecordDecl(); + } + return nullptr; +} + +} // namespace + +BlinkGCPluginConsumer::BlinkGCPluginConsumer( + clang::CompilerInstance& instance, + const BlinkGCPluginOptions& options) + : instance_(instance), + reporter_(instance), + options_(options), + cache_(instance), + json_(0) { + // Only check structures in blink, cppgc and pdfium. + options_.checked_namespaces.insert("blink"); + options_.checked_namespaces.insert("cppgc"); + + // Add Pdfium subfolders containing GCed classes. + options_.checked_directories.push_back("fpdfsdk/"); + options_.checked_directories.push_back("fxjs/"); + options_.checked_directories.push_back("xfa/"); + + // Ignore GC implementation files. + options_.ignored_directories.push_back( + "third_party/blink/renderer/platform/heap/collection_support/"); + options_.ignored_directories.push_back("v8/src/heap/cppgc/"); + options_.ignored_directories.push_back("v8/src/heap/cppgc-js/"); +} + +void BlinkGCPluginConsumer::HandleTranslationUnit(ASTContext& context) { + llvm::TimeTraceScope TimeScope( + "BlinkGCPluginConsumer::HandleTranslationUnit"); + // Don't run the plugin if the compilation unit is already invalid. + if (reporter_.hasErrorOccurred()) + return; + + ParseFunctionTemplates(context.getTranslationUnitDecl()); + + CollectVisitor visitor; + visitor.TraverseDecl(context.getTranslationUnitDecl()); + + if (options_.dump_graph) { + std::error_code err; + SmallString<128> OutputFile(instance_.getFrontendOpts().OutputFile); + llvm::sys::path::replace_extension(OutputFile, "graph.json"); + json_ = JsonWriter::from(instance_.createOutputFile( + OutputFile, // OutputPath + true, // Binary + true, // RemoveFileOnSignal + false, // UseTemporary + false)); // CreateMissingDirectories + if (!err && json_) { + json_->OpenList(); + } else { + json_ = 0; + llvm::errs() + << "[blink-gc] " + << "Failed to create an output file for the object graph.\n"; + } + } + + for (const auto& record : visitor.record_decls()) + CheckRecord(cache_.Lookup(record)); + + for (const auto& method : visitor.trace_decls()) + CheckTracingMethod(method); + + if (json_) { + json_->CloseList(); + delete json_; + json_ = 0; + } + + FindBadPatterns(context, reporter_, cache_, options_); +} + +void BlinkGCPluginConsumer::ParseFunctionTemplates(TranslationUnitDecl* decl) { + if (!instance_.getLangOpts().DelayedTemplateParsing) + return; // Nothing to do. + + std::set late_parsed_decls = GetLateParsedFunctionDecls(decl); + clang::Sema& sema = instance_.getSema(); + + for (const FunctionDecl* fd : late_parsed_decls) { + assert(fd->isLateTemplateParsed()); + + if (!Config::IsTraceMethod(fd)) + continue; + + if (instance_.getSourceManager().isInSystemHeader( + instance_.getSourceManager().getSpellingLoc(fd->getLocation()))) + continue; + + // Force parsing and AST building of the yet-uninstantiated function + // template trace method bodies. + clang::LateParsedTemplate* lpt = sema.LateParsedTemplateMap[fd].get(); + sema.LateTemplateParser(sema.OpaqueParser, *lpt); + } +} + +void BlinkGCPluginConsumer::CheckRecord(RecordInfo* info) { + if (IsIgnored(info)) + return; + + CXXRecordDecl* record = info->record(); + + // TODO: what should we do to check unions? + if (record->isUnion()) + return; + + // If this is the primary template declaration, check its specializations. + if (record->isThisDeclarationADefinition() && + record->getDescribedClassTemplate()) { + ClassTemplateDecl* tmpl = record->getDescribedClassTemplate(); + for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin(); + it != tmpl->spec_end(); + ++it) { + CheckClass(cache_.Lookup(*it)); + } + return; + } + + CheckClass(info); +} + +void BlinkGCPluginConsumer::CheckClass(RecordInfo* info) { + if (!info) + return; + + if (CXXMethodDecl* trace = info->GetTraceMethod()) { + if (info->IsStackAllocated()) + reporter_.TraceMethodForStackAllocatedClass(info, trace); + if (trace->isPureVirtual()) + reporter_.ClassDeclaresPureVirtualTrace(info, trace); + } else if (info->RequiresTraceMethod()) { + reporter_.ClassRequiresTraceMethod(info); + } + + // Check polymorphic classes that are GC-derived or have a trace method. + if (info->record()->hasDefinition() && info->record()->isPolymorphic()) { + // TODO: Check classes that inherit a trace method. + CXXMethodDecl* trace = info->GetTraceMethod(); + if (trace || info->IsGCDerived()) + CheckPolymorphicClass(info, trace); + } + + { + CheckFieldsVisitor visitor(options_); + if (visitor.ContainsInvalidFields(info)) + reporter_.ClassContainsInvalidFields(info, visitor.invalid_fields()); + } + + if (info->IsGCDerived()) { + // Check that CRTP pattern for GCed classes is correctly used. + if (auto* base_spec = info->GetDirectGCBase()) { + // Skip the check if base_spec name is dependent. The check will occur + // later for actual specializations. + if (!base_spec->getType()->isDependentType()) { + const CXXRecordDecl* base_decl = + base_spec->getType()->getAsCXXRecordDecl(); + const CXXRecordDecl* first_arg = + GetFirstTemplateArgAsCXXRecordDecl(base_decl); + // The last check is for redeclaratation cases, for example, when + // explicit instantiation declaration is followed by the corresponding + // explicit instantiation definition. + if (!first_arg || + first_arg->getFirstDecl() != info->record()->getFirstDecl()) { + reporter_.ClassMustCRTPItself(info, base_decl, base_spec); + } + } + } + + // It is illegal for a class to be both stack allocated and garbage + // collected. + if (info->IsStackAllocated()) { + for (auto& base : info->GetBases()) { + RecordInfo* base_info = base.second.info(); + if (Config::IsGCBase(base_info->name()) || base_info->IsGCDerived()) { + reporter_.StackAllocatedDerivesGarbageCollected(info, &base.second); + } + } + } + + if (!info->IsGCMixin()) { + CheckLeftMostDerived(info); + CheckDispatch(info); + if (CXXMethodDecl* newop = info->DeclaresNewOperator()) { + if (!info->IsStackAllocated() && + !Config::IsGCBase(newop->getParent()->getName()) && + !Config::IsIgnoreAnnotated(newop)) { + reporter_.ClassOverridesNew(info, newop); + } + } + } + + { + CheckGCRootsVisitor visitor(options_); + if (visitor.ContainsGCRoots(info)) + reporter_.ClassContainsGCRoots(info, visitor.gc_roots()); + reporter_.ClassContainsGCRootRefs(info, visitor.gc_root_refs()); + } + + CheckForbiddenFieldsVisitor visitor; + if (visitor.ContainsForbiddenFields(info)) { + reporter_.ClassContainsForbiddenFields(info, visitor.forbidden_fields()); + } + + if (info->NeedsFinalization()) + CheckFinalization(info); + } + + DumpClass(info); +} + +CXXRecordDecl* BlinkGCPluginConsumer::GetDependentTemplatedDecl( + const Type& type) { + const TemplateSpecializationType* tmpl_type = + type.getAs(); + if (!tmpl_type) + return 0; + + TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl(); + if (!tmpl_decl) + return 0; + + return dyn_cast(tmpl_decl->getTemplatedDecl()); +} + +// The GC infrastructure assumes that if the vtable of a polymorphic +// base-class is not initialized for a given object (ie, it is partially +// initialized) then the object does not need to be traced. Thus, we must +// ensure that any polymorphic class with a trace method does not have any +// tractable fields that are initialized before we are sure that the vtable +// and the trace method are both defined. There are two cases that need to +// hold to satisfy that assumption: +// +// 1. If trace is virtual, then it must be defined in the left-most base. +// This ensures that if the vtable is initialized then it contains a pointer +// to the trace method. +// +// 2. If trace is non-virtual, then the trace method is defined and we must +// ensure that the left-most base defines a vtable. This ensures that the +// first thing to be initialized when constructing the object is the vtable +// itself. +void BlinkGCPluginConsumer::CheckPolymorphicClass( + RecordInfo* info, + CXXMethodDecl* trace) { + CXXRecordDecl* left_most = info->record(); + CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); + CXXRecordDecl* left_most_base = 0; + while (it != left_most->bases_end()) { + left_most_base = it->getType()->getAsCXXRecordDecl(); + if (!left_most_base && it->getType()->isDependentType()) + left_most_base = RecordInfo::GetDependentTemplatedDecl(*it->getType()); + + // TODO: Find a way to correctly check actual instantiations + // for dependent types. The escape below will be hit, eg, when + // we have a primary template with no definition and + // specializations for each case (such as SupplementBase) in + // which case we don't succeed in checking the required + // properties. + if (!left_most_base || !left_most_base->hasDefinition()) + return; + + StringRef name = left_most_base->getName(); + // We know GCMixin base defines virtual trace. + if (Config::IsGCMixinBase(name)) + return; + + // Stop with the left-most prior to a safe polymorphic base (a safe base + // is non-polymorphic and contains no fields). + if (Config::IsSafePolymorphicBase(name)) + break; + + left_most = left_most_base; + it = left_most->bases_begin(); + } + + if (RecordInfo* left_most_info = cache_.Lookup(left_most)) { + // Check condition (1): + if (trace && trace->isVirtual()) { + if (CXXMethodDecl* trace = left_most_info->GetTraceMethod()) { + if (trace->isVirtual()) + return; + } + reporter_.BaseClassMustDeclareVirtualTrace(info, left_most); + return; + } + + // Check condition (2): + if (DeclaresVirtualMethods(left_most)) + return; + if (left_most_base) { + // Get the base next to the "safe polymorphic base" + if (it != left_most->bases_end()) + ++it; + if (it != left_most->bases_end()) { + if (CXXRecordDecl* next_base = it->getType()->getAsCXXRecordDecl()) { + if (CXXRecordDecl* next_left_most = GetLeftMostBase(next_base)) { + if (DeclaresVirtualMethods(next_left_most)) + return; + reporter_.LeftMostBaseMustBePolymorphic(info, next_left_most); + return; + } + } + } + } + reporter_.LeftMostBaseMustBePolymorphic(info, left_most); + } +} + +CXXRecordDecl* BlinkGCPluginConsumer::GetLeftMostBase( + CXXRecordDecl* left_most) { + CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); + while (it != left_most->bases_end()) { + if (it->getType()->isDependentType()) + left_most = RecordInfo::GetDependentTemplatedDecl(*it->getType()); + else + left_most = it->getType()->getAsCXXRecordDecl(); + if (!left_most || !left_most->hasDefinition()) + return 0; + it = left_most->bases_begin(); + } + return left_most; +} + +bool BlinkGCPluginConsumer::DeclaresVirtualMethods(CXXRecordDecl* decl) { + CXXRecordDecl::method_iterator it = decl->method_begin(); + for (; it != decl->method_end(); ++it) + if (it->isVirtual() && !it->isPureVirtual()) + return true; + return false; +} + +void BlinkGCPluginConsumer::CheckLeftMostDerived(RecordInfo* info) { + CXXRecordDecl* left_most = GetLeftMostBase(info->record()); + if (!left_most) + return; + if (!Config::IsGCBase(left_most->getName()) || Config::IsGCMixinBase(left_most->getName())) + reporter_.ClassMustLeftMostlyDeriveGC(info); +} + +void BlinkGCPluginConsumer::CheckDispatch(RecordInfo* info) { + CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod(); + CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod(); + if (!trace_dispatch && !finalize_dispatch) + return; + + CXXRecordDecl* base = trace_dispatch ? trace_dispatch->getParent() + : finalize_dispatch->getParent(); + + // Check that dispatch methods are defined at the base. + if (base == info->record()) { + if (!trace_dispatch) + reporter_.MissingTraceDispatchMethod(info); + } + + // Check that classes implementing manual dispatch do not have vtables. + if (info->record()->isPolymorphic()) { + reporter_.VirtualAndManualDispatch( + info, trace_dispatch ? trace_dispatch : finalize_dispatch); + } + + // If this is a non-abstract class check that it is dispatched to. + // TODO: Create a global variant of this local check. We can only check if + // the dispatch body is known in this compilation unit. + if (info->IsConsideredAbstract()) + return; + + const FunctionDecl* defn; + + if (trace_dispatch && trace_dispatch->isDefined(defn)) { + CheckDispatchVisitor visitor(info); + visitor.TraverseStmt(defn->getBody()); + if (!visitor.dispatched_to_receiver()) + reporter_.MissingTraceDispatch(defn, info); + } + + if (finalize_dispatch && finalize_dispatch->isDefined(defn)) { + CheckDispatchVisitor visitor(info); + visitor.TraverseStmt(defn->getBody()); + if (!visitor.dispatched_to_receiver()) + reporter_.MissingFinalizeDispatch(defn, info); + } +} + +// TODO: Should we collect destructors similar to trace methods? +void BlinkGCPluginConsumer::CheckFinalization(RecordInfo* info) { + CXXDestructorDecl* dtor = info->record()->getDestructor(); + if (!dtor || !dtor->hasBody()) + return; + + CheckFinalizerVisitor visitor(&cache_); + visitor.TraverseCXXMethodDecl(dtor); + if (!visitor.finalized_fields().empty()) { + reporter_.FinalizerAccessesFinalizedFields(dtor, + visitor.finalized_fields()); + } +} + +void BlinkGCPluginConsumer::CheckTracingMethod(CXXMethodDecl* method) { + RecordInfo* parent = cache_.Lookup(method->getParent()); + if (IsIgnored(parent)) + return; + + // Check templated tracing methods by checking the template instantiations. + // Specialized templates are handled as ordinary classes. + if (ClassTemplateDecl* tmpl = + parent->record()->getDescribedClassTemplate()) { + for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin(); + it != tmpl->spec_end(); + ++it) { + // Check trace using each template instantiation as the holder. + if (Config::IsTemplateInstantiation(*it)) + CheckTraceOrDispatchMethod(cache_.Lookup(*it), method); + } + return; + } + + CheckTraceOrDispatchMethod(parent, method); +} + +void BlinkGCPluginConsumer::CheckTraceOrDispatchMethod( + RecordInfo* parent, + CXXMethodDecl* method) { + Config::TraceMethodType trace_type = Config::GetTraceMethodType(method); + if (trace_type == Config::TRACE_AFTER_DISPATCH_METHOD || + !parent->GetTraceDispatchMethod()) { + CheckTraceMethod(parent, method, trace_type); + } + // Dispatch methods are checked when we identify subclasses. +} + +void BlinkGCPluginConsumer::CheckTraceMethod( + RecordInfo* parent, + CXXMethodDecl* trace, + Config::TraceMethodType trace_type) { + // A trace method must not override any non-virtual trace methods. + if (trace_type == Config::TRACE_METHOD) { + for (auto& base : parent->GetBases()) + if (CXXMethodDecl* other = base.second.info()->InheritsNonVirtualTrace()) + reporter_.OverriddenNonVirtualTrace(parent, trace, other); + } + + CheckTraceVisitor visitor(trace, parent, &cache_); + visitor.TraverseCXXMethodDecl(trace); + + for (auto& base : parent->GetBases()) + if (!base.second.IsProperlyTraced()) + reporter_.BaseRequiresTracing(parent, trace, base.first); + + for (auto& field : parent->GetFields()) { + if (!field.second.IsProperlyTraced() || + field.second.IsInproperlyTraced()) { + // Report one or more tracing-related field errors. + reporter_.FieldsImproperlyTraced(parent, trace); + break; + } + } +} + +void BlinkGCPluginConsumer::DumpClass(RecordInfo* info) { + if (!json_) + return; + + json_->OpenObject(); + json_->Write("name", info->record()->getQualifiedNameAsString()); + json_->Write("loc", GetLocString(info->record()->getBeginLoc())); + json_->CloseObject(); + + class DumpEdgeVisitor : public RecursiveEdgeVisitor { + public: + DumpEdgeVisitor(JsonWriter* json) : json_(json) {} + void DumpEdge(RecordInfo* src, + RecordInfo* dst, + const std::string& lbl, + const Edge::LivenessKind& kind, + const std::string& loc) { + json_->OpenObject(); + json_->Write("src", src->record()->getQualifiedNameAsString()); + json_->Write("dst", dst->record()->getQualifiedNameAsString()); + json_->Write("lbl", lbl); + json_->Write("kind", kind); + json_->Write("loc", loc); + json_->Write("ptr", + !Parent() ? "val" : + Parent()->IsRawPtr() ? + (static_cast(Parent())->HasReferenceType() ? + "reference" : "raw") : + Parent()->IsRefPtr() ? "ref" : + Parent()->IsUniquePtr() ? "unique" : + (Parent()->IsMember() || Parent()->IsWeakMember()) ? "mem" : + "val"); + json_->CloseObject(); + } + + void DumpField(RecordInfo* src, FieldPoint* point, const std::string& loc) { + src_ = src; + point_ = point; + loc_ = loc; + point_->edge()->Accept(this); + } + + void AtValue(Value* e) override { + // The liveness kind of a path from the point to this value + // is given by the innermost place that is non-strong. + Edge::LivenessKind kind = Edge::kStrong; + for (Context::iterator it = context().begin(); it != context().end(); + ++it) { + Edge::LivenessKind pointer_kind = (*it)->Kind(); + if (pointer_kind != Edge::kStrong) { + kind = pointer_kind; + break; + } + } + DumpEdge( + src_, e->value(), point_->field()->getNameAsString(), kind, loc_); + } + + private: + JsonWriter* json_; + RecordInfo* src_; + FieldPoint* point_; + std::string loc_; + }; + + DumpEdgeVisitor visitor(json_); + + for (auto& base : info->GetBases()) + visitor.DumpEdge(info, base.second.info(), "", Edge::kStrong, + GetLocString(base.second.spec().getBeginLoc())); + + for (auto& field : info->GetFields()) + visitor.DumpField(info, &field.second, + GetLocString(field.second.field()->getBeginLoc())); +} + +std::string BlinkGCPluginConsumer::GetLocString(SourceLocation loc) { + const SourceManager& source_manager = instance_.getSourceManager(); + PresumedLoc ploc = source_manager.getPresumedLoc(loc); + if (ploc.isInvalid()) + return ""; + std::string loc_str; + llvm::raw_string_ostream os(loc_str); + os << ploc.getFilename() + << ":" << ploc.getLine() + << ":" << ploc.getColumn(); + return os.str(); +} + +bool BlinkGCPluginConsumer::IsIgnored(RecordInfo* record) { + return (!record || !InCheckedNamespaceOrDirectory(record) || + IsIgnoredClass(record) || InIgnoredDirectory(record)); +} + +bool BlinkGCPluginConsumer::IsIgnoredClass(RecordInfo* info) { + // Ignore any class prefixed by SameSizeAs. These are used in + // Blink to verify class sizes and don't need checking. + const std::string SameSizeAs = "SameSizeAs"; + if (info->name().compare(0, SameSizeAs.size(), SameSizeAs) == 0) + return true; + return (options_.ignored_classes.find(info->name()) != + options_.ignored_classes.end()); +} + +bool BlinkGCPluginConsumer::InIgnoredDirectory(RecordInfo* info) { + std::string filename; + if (!GetFilename(info->record()->getBeginLoc(), &filename)) + return false; // TODO: should we ignore non-existing file locations? +#if defined(_WIN32) + std::replace(filename.begin(), filename.end(), '\\', '/'); +#endif + for (const auto& ignored_dir : options_.ignored_directories) + if (filename.find(ignored_dir) != std::string::npos) { + return true; + } + return false; +} + +bool BlinkGCPluginConsumer::InCheckedNamespaceOrDirectory(RecordInfo* info) { + if (!info) + return false; + for (DeclContext* context = info->record()->getDeclContext(); + !context->isTranslationUnit(); + context = context->getParent()) { + if (NamespaceDecl* decl = dyn_cast(context)) { + if (decl->isAnonymousNamespace()) + return true; + if (options_.checked_namespaces.find(decl->getNameAsString()) != + options_.checked_namespaces.end()) { + return true; + } + } + } + std::string filename; + if (!GetFilename(info->record()->getBeginLoc(), &filename)) { + return false; + } +#if defined(_WIN32) + std::replace(filename.begin(), filename.end(), '\\', '/'); +#endif + for (const auto& checked_dir : options_.checked_directories) { + if (filename.find(checked_dir) != std::string::npos) { + return true; + } + } + return false; +} + +bool BlinkGCPluginConsumer::GetFilename(SourceLocation loc, + std::string* filename) { + const SourceManager& source_manager = instance_.getSourceManager(); + SourceLocation spelling_location = source_manager.getSpellingLoc(loc); + PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location); + if (ploc.isInvalid()) { + // If we're in an invalid location, we're looking at things that aren't + // actually stated in the source. + return false; + } + *filename = ploc.getFilename(); + return true; +} diff --git a/clang/blink_gc_plugin/BlinkGCPluginConsumer.h b/clang/blink_gc_plugin/BlinkGCPluginConsumer.h new file mode 100644 index 0000000000000000000000000000000000000000..781f60bcf47467a9d01d335d1eace4e37db86ad8 --- /dev/null +++ b/clang/blink_gc_plugin/BlinkGCPluginConsumer.h @@ -0,0 +1,89 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_BLINK_GC_PLUGIN_BLINK_GC_PLUGIN_CONSUMER_H_ +#define TOOLS_BLINK_GC_PLUGIN_BLINK_GC_PLUGIN_CONSUMER_H_ + +#include + +#include "BlinkGCPluginOptions.h" +#include "Config.h" +#include "DiagnosticsReporter.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/CompilerInstance.h" + +class JsonWriter; +class RecordInfo; + +// Main class containing checks for various invariants of the Blink +// garbage collection infrastructure. +class BlinkGCPluginConsumer : public clang::ASTConsumer { + public: + BlinkGCPluginConsumer(clang::CompilerInstance& instance, + const BlinkGCPluginOptions& options); + + void HandleTranslationUnit(clang::ASTContext& context) override; + + private: + void ParseFunctionTemplates(clang::TranslationUnitDecl* decl); + + // Main entry for checking a record declaration. + void CheckRecord(RecordInfo* info); + + // Check a class-like object (eg, class, specialization, instantiation). + void CheckClass(RecordInfo* info); + + clang::CXXRecordDecl* GetDependentTemplatedDecl(const clang::Type& type); + + void CheckPolymorphicClass(RecordInfo* info, clang::CXXMethodDecl* trace); + + clang::CXXRecordDecl* GetLeftMostBase(clang::CXXRecordDecl* left_most); + + bool DeclaresVirtualMethods(clang::CXXRecordDecl* decl); + + void CheckLeftMostDerived(RecordInfo* info); + + void CheckDispatch(RecordInfo* info); + + void CheckFinalization(RecordInfo* info); + + // This is the main entry for tracing method definitions. + void CheckTracingMethod(clang::CXXMethodDecl* method); + + // Determine what type of tracing method this is (dispatch or trace). + void CheckTraceOrDispatchMethod(RecordInfo* parent, + clang::CXXMethodDecl* method); + + // Check an actual trace method. + void CheckTraceMethod(RecordInfo* parent, + clang::CXXMethodDecl* trace, + Config::TraceMethodType trace_type); + + void DumpClass(RecordInfo* info); + + // Adds either a warning or error, based on the current handling of -Werror. + clang::DiagnosticsEngine::Level getErrorLevel(); + + std::string GetLocString(clang::SourceLocation loc); + + bool IsIgnored(RecordInfo* info); + + bool IsIgnoredClass(RecordInfo* info); + + bool InIgnoredDirectory(RecordInfo* info); + + bool InCheckedNamespaceOrDirectory(RecordInfo* info); + + bool GetFilename(clang::SourceLocation loc, std::string* filename); + + clang::CompilerInstance& instance_; + DiagnosticsReporter reporter_; + BlinkGCPluginOptions options_; + RecordCache cache_; + JsonWriter* json_; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_BLINK_GC_PLUGIN_CONSUMER_H_ diff --git a/clang/blink_gc_plugin/BlinkGCPluginOptions.h b/clang/blink_gc_plugin/BlinkGCPluginOptions.h new file mode 100644 index 0000000000000000000000000000000000000000..83dcf3c68c9bf37ddf5243e8fd5d5e6dbbe98ef6 --- /dev/null +++ b/clang/blink_gc_plugin/BlinkGCPluginOptions.h @@ -0,0 +1,57 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_BLINK_GC_PLUGIN_BLINK_GC_PLUGIN_OPTIONS_H_ +#define TOOLS_BLINK_GC_PLUGIN_BLINK_GC_PLUGIN_OPTIONS_H_ + +#include +#include +#include + +struct BlinkGCPluginOptions { + bool dump_graph = false; + + // Persistent fields are not allowed in garbage collected classes to avoid + // memory leaks. Enabling this flag allows the plugin to check also for + // Persistent in types held by unique_ptr in garbage collected classes. The + // guideline for this check is that a Persistent should never be kept alive + // by a garbage collected class, which unique_ptr clearly conveys. + // + // This check is disabled by default since there are currently non-ignored + // violations of this rule in the code base, leading to compilation failures. + // TODO(chromium:1283867): Enable this checks once all violations are handled. + bool enable_persistent_in_unique_ptr_check = false; + + // On stack references to garbage collected objects should use raw pointers. + // Although using Members/WeakMembers on stack is not strictly incorrect, it + // is redundant and incurs additional costs that can mount up and become + // significant. Enabling this flag lets the plugin to check for instances of + // using Member/WeakMember on stack. These would include variable + // declarations, method arguments and return types. + // + // This check is disabled by default since there currently are violations + // of this rule in the code base, leading to compilation failures. + // TODO(chromium:1283720): Enable this checks once all violations are handled. + bool enable_members_on_stack_check = false; + + // Checks that any inlined classes (ones that could be a value-type of heap + // containers) don't have extra padding potentially introduced by Member (e.g + // due to pointer compression). + bool enable_extra_padding_check = false; + + // Enables checks for GCed objects, Members, and pointers or references to + // GCed objects and in stl and WTF collections. Do not remove this flag. It is + // needed for disabling the check for Pdfium builds. + bool enable_off_heap_collections_of_gced_check = true; + + // Enables checks for raw pointers, refs and unique_ptr of traceable types. + bool enable_ptrs_to_traceable_check = false; + + std::set ignored_classes; + std::set checked_namespaces; + std::vector checked_directories; + std::vector ignored_directories; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_BLINK_GC_PLUGIN_OPTIONS_H_ diff --git a/clang/blink_gc_plugin/CMakeLists.txt b/clang/blink_gc_plugin/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..21bce084ece04e4d3b147462b78766a655e690b7 --- /dev/null +++ b/clang/blink_gc_plugin/CMakeLists.txt @@ -0,0 +1,37 @@ +set(LIBRARYNAME BlinkGCPlugin) + +set(plugin_sources + BadPatternFinder.cpp + BlinkGCPlugin.cpp + BlinkGCPluginConsumer.cpp + CheckDispatchVisitor.cpp + CheckFieldsVisitor.cpp + CheckFinalizerVisitor.cpp + CheckForbiddenFieldsVisitor.cpp + CheckGCRootsVisitor.cpp + CheckTraceVisitor.cpp + CollectVisitor.cpp + Config.cpp + DiagnosticsReporter.cpp + Edge.cpp + RecordInfo.cpp) + +# Clang doesn't support loadable modules on Windows. Unfortunately, building +# the plugin as a static library and linking clang against it doesn't work. +# Since clang doesn't reference any symbols in our static library, the linker +# strips it out completely. +# Instead, we rely on the fact that the SOURCES property of a target is not +# read-only after CMake 3.1 and use it to compile the plugin directly into +# clang. +cmake_minimum_required(VERSION 3.1) +# Paths must be absolute, since we're modifying a target in another directory. +set(absolute_sources "") +foreach(source ${plugin_sources}) + list(APPEND absolute_sources ${CMAKE_CURRENT_SOURCE_DIR}/${source}) +endforeach() +set_property(TARGET clang APPEND PROPERTY SOURCES ${absolute_sources}) + +cr_add_test(blink_gc_plugin_test + python3 tests/test.py + ${CMAKE_BINARY_DIR}/bin/clang + ) diff --git a/clang/blink_gc_plugin/CheckDispatchVisitor.cpp b/clang/blink_gc_plugin/CheckDispatchVisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..594a1679732c9245a69ff9c13b954fff5141e717 --- /dev/null +++ b/clang/blink_gc_plugin/CheckDispatchVisitor.cpp @@ -0,0 +1,42 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "CheckDispatchVisitor.h" + +#include "Config.h" +#include "RecordInfo.h" + +using namespace clang; + +CheckDispatchVisitor::CheckDispatchVisitor(RecordInfo* receiver) + : receiver_(receiver), + dispatched_to_receiver_(false) { +} + +bool CheckDispatchVisitor::dispatched_to_receiver() { + return dispatched_to_receiver_; +} + +bool CheckDispatchVisitor::VisitMemberExpr(MemberExpr* member) { + if (CXXMethodDecl* fn = dyn_cast(member->getMemberDecl())) { + if (fn->getParent() == receiver_->record()) + dispatched_to_receiver_ = true; + } + return true; +} + +bool CheckDispatchVisitor::VisitUnresolvedMemberExpr( + UnresolvedMemberExpr* member) { + for (Decl* decl : member->decls()) { + if (CXXMethodDecl* method = dyn_cast(decl)) { + if (method->getParent() == receiver_->record() && + Config::GetTraceMethodType(method) == + Config::TRACE_AFTER_DISPATCH_METHOD) { + dispatched_to_receiver_ = true; + return true; + } + } + } + return true; +} diff --git a/clang/blink_gc_plugin/CheckDispatchVisitor.h b/clang/blink_gc_plugin/CheckDispatchVisitor.h new file mode 100644 index 0000000000000000000000000000000000000000..5f998f07814e0671b6f2082bea0732a5396652bd --- /dev/null +++ b/clang/blink_gc_plugin/CheckDispatchVisitor.h @@ -0,0 +1,30 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_BLINK_GC_PLUGIN_CHECK_DISPATCH_VISITOR_H_ +#define TOOLS_BLINK_GC_PLUGIN_CHECK_DISPATCH_VISITOR_H_ + +#include "clang/AST/RecursiveASTVisitor.h" + +class RecordInfo; + +// This visitor checks that a method contains within its body, a call to a +// method on the provided receiver class. This is used to check manual +// dispatching for trace and finalize methods. +class CheckDispatchVisitor + : public clang::RecursiveASTVisitor { + public: + explicit CheckDispatchVisitor(RecordInfo* receiver); + + bool dispatched_to_receiver(); + + bool VisitMemberExpr(clang::MemberExpr* member); + bool VisitUnresolvedMemberExpr(clang::UnresolvedMemberExpr* member); + + private: + RecordInfo* receiver_; + bool dispatched_to_receiver_; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_CHECK_DISPATCH_VISITOR_H_ diff --git a/clang/blink_gc_plugin/CheckFieldsVisitor.cpp b/clang/blink_gc_plugin/CheckFieldsVisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..239e915a9b02b217a5e0fc41fc8596187371b759 --- /dev/null +++ b/clang/blink_gc_plugin/CheckFieldsVisitor.cpp @@ -0,0 +1,167 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "CheckFieldsVisitor.h" + +#include + +#include "RecordInfo.h" +#include "llvm/Support/ErrorHandling.h" + +CheckFieldsVisitor::CheckFieldsVisitor(const BlinkGCPluginOptions& options) + : options_(options), current_(0), stack_allocated_host_(false) {} + +CheckFieldsVisitor::Errors& CheckFieldsVisitor::invalid_fields() { + return invalid_fields_; +} + +bool CheckFieldsVisitor::ContainsInvalidFields(RecordInfo* info) { + stack_allocated_host_ = info->IsStackAllocated(); + managed_host_ = + stack_allocated_host_ || info->IsGCAllocated() || info->IsNewDisallowed(); + for (RecordInfo::Fields::iterator it = info->GetFields().begin(); + it != info->GetFields().end(); + ++it) { + context().clear(); + current_ = &it->second; + current_->edge()->Accept(this); + } + return !invalid_fields_.empty(); +} + +void CheckFieldsVisitor::AtMember(Member*) { + if (managed_host_) + return; + // A member is allowed to appear in the context of a root. + for (Context::iterator it = context().begin(); + it != context().end(); + ++it) { + if ((*it)->Kind() == Edge::kRoot) + return; + } + bool is_ptr = Parent() && (Parent()->IsRawPtr() || Parent()->IsRefPtr()); + invalid_fields_.push_back(std::make_pair( + current_, is_ptr ? kPtrToMemberInUnmanaged : kMemberInUnmanaged)); +} + +void CheckFieldsVisitor::AtWeakMember(WeakMember*) { + AtMember(nullptr); +} + +void CheckFieldsVisitor::AtIterator(Iterator* edge) { + if (!managed_host_) + return; + + if (!stack_allocated_host_ && edge->on_heap()) + invalid_fields_.push_back(std::make_pair(current_, kIteratorToGCManaged)); +} + +namespace { + +CheckFieldsVisitor::Error InvalidSmartPtr(Edge* ptr, bool is_gced) { + if (ptr->IsRefPtr()) { + return is_gced ? CheckFieldsVisitor::Error::kRefPtrToGCManaged + : CheckFieldsVisitor::Error::kRefPtrToTraceable; + } + if (ptr->IsUniquePtr()) { + return is_gced ? CheckFieldsVisitor::Error::kUniquePtrToGCManaged + : CheckFieldsVisitor::Error::kUniquePtrToTraceable; + } + llvm_unreachable("Unknown smart pointer kind"); +} + +} // namespace + +void CheckFieldsVisitor::AtValue(Value* edge) { + RecordInfo* record = edge->value(); + + // TODO: what should we do to check unions? + if (record->record()->isUnion()) { + return; + } + + // Don't allow unmanaged classes to contain traceable part-objects. + const bool child_is_part_object = record->IsNewDisallowed() && !Parent(); + if (!managed_host_ && child_is_part_object && record->RequiresTraceMethod()) { + invalid_fields_.push_back( + std::make_pair(current_, kTraceablePartObjectInUnmanaged)); + return; + } + + if (!stack_allocated_host_ && record->IsStackAllocated()) { + invalid_fields_.push_back(std::make_pair(current_, kPtrFromHeapToStack)); + return; + } + + if (!Parent() && record->IsGCDerived() && !record->IsGCMixin()) { + invalid_fields_.push_back(std::make_pair(current_, kGCDerivedPartObject)); + return; + } + + // Members/WeakMembers are prohibited if the host is stack allocated, but + // heap collections with Members are okay. + if (stack_allocated_host_ && Parent() && + (Parent()->IsMember() || Parent()->IsWeakMember())) { + if (!GrandParent() || + (!GrandParent()->IsCollection() && !GrandParent()->IsRawPtr() && + !GrandParent()->IsRefPtr())) { + invalid_fields_.push_back( + std::make_pair(current_, kMemberInStackAllocated)); + return; + } + } + + // If in a stack allocated context, be fairly insistent that T in Member + // is GC allocated, as stack allocated objects do not have a trace() + // that separately verifies the validity of Member. + // + // Notice that an error is only reported if T's definition is in scope; + // we do not require that it must be brought into scope as that would + // prevent declarations of mutually dependent class types. + // + // (Note: Member<>'s constructor will at run-time verify that the + // pointer it wraps is indeed heap allocated.) + if (stack_allocated_host_ && Parent() && + (Parent()->IsMember() || Parent()->IsWeakMember()) && + edge->value()->HasDefinition() && !edge->value()->IsGCAllocated()) { + invalid_fields_.push_back(std::make_pair(current_, kMemberToGCUnmanaged)); + return; + } + + if (!Parent() || (!edge->value()->IsGCAllocated() && + (!options_.enable_ptrs_to_traceable_check || + !edge->value() + ->NeedsTracing(Edge::NeedsTracingOption::kRecursive) + .IsNeeded()))) { + return; + } + + // Disallow unique_ptr, scoped_refptr + if (Parent()->IsUniquePtr() || + (Parent()->IsRefPtr() && (Parent()->Kind() == Edge::kStrong))) { + invalid_fields_.push_back(std::make_pair( + current_, InvalidSmartPtr(Parent(), edge->value()->IsGCAllocated()))); + return; + } + if (Parent()->IsRawPtr() && !stack_allocated_host_) { + RawPtr* rawPtr = static_cast(Parent()); + Error error = edge->value()->IsGCAllocated() + ? (rawPtr->HasReferenceType() ? kReferencePtrToGCManaged + : kRawPtrToGCManaged) + : (rawPtr->HasReferenceType() ? kReferencePtrToTraceable + : kRawPtrToTraceable); + invalid_fields_.push_back(std::make_pair(current_, error)); + } +} + +void CheckFieldsVisitor::AtCollection(Collection* edge) { + if (GrandParent() && + (GrandParent()->IsRawPtr() || GrandParent()->IsRefPtr())) { + // Don't alert on pointers to unique_ptr. Alerting on the pointed unique_ptr + // should suffice. + return; + } + if (edge->on_heap() && Parent() && Parent()->IsUniquePtr()) + invalid_fields_.push_back(std::make_pair(current_, kUniquePtrToGCManaged)); +} diff --git a/clang/blink_gc_plugin/CheckFieldsVisitor.h b/clang/blink_gc_plugin/CheckFieldsVisitor.h new file mode 100644 index 0000000000000000000000000000000000000000..8698dea94f5a5c78827ae49b5696bb457c891731 --- /dev/null +++ b/clang/blink_gc_plugin/CheckFieldsVisitor.h @@ -0,0 +1,65 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_BLINK_GC_PLUGIN_CHECK_FIELDS_VISITOR_H_ +#define TOOLS_BLINK_GC_PLUGIN_CHECK_FIELDS_VISITOR_H_ + +#include + +#include "BlinkGCPluginOptions.h" +#include "Edge.h" + +class FieldPoint; + +// This visitor checks that the fields of a class are "well formed". +// - unique_ptr, scoped_refptr and WeakPtr must not point to a GC derived type. +// - Part objects must not be a GC derived type. +// - An on-heap class must never contain GC roots. +// - Only stack-allocated types may point to stack-allocated types. + +class CheckFieldsVisitor : public RecursiveEdgeVisitor { + public: + enum Error { + kRawPtrToGCManaged, + kRefPtrToGCManaged, + kReferencePtrToGCManaged, + kUniquePtrToGCManaged, + kMemberToGCUnmanaged, + kMemberInUnmanaged, + kPtrToMemberInUnmanaged, + kPtrFromHeapToStack, + kGCDerivedPartObject, + kIteratorToGCManaged, + kMemberInStackAllocated, + kTraceablePartObjectInUnmanaged, + kRawPtrToTraceable, + kRefPtrToTraceable, + kReferencePtrToTraceable, + kUniquePtrToTraceable, + }; + + using Errors = std::vector>; + + explicit CheckFieldsVisitor(const BlinkGCPluginOptions&); + + Errors& invalid_fields(); + + bool ContainsInvalidFields(RecordInfo* info); + + void AtMember(Member*) override; + void AtWeakMember(WeakMember*) override; + void AtValue(Value*) override; + void AtCollection(Collection*) override; + void AtIterator(Iterator*) override; + + private: + const BlinkGCPluginOptions& options_; + + FieldPoint* current_; + bool stack_allocated_host_; + bool managed_host_; + Errors invalid_fields_; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_CHECK_FIELDS_VISITOR_H_ diff --git a/clang/blink_gc_plugin/CheckFinalizerVisitor.cpp b/clang/blink_gc_plugin/CheckFinalizerVisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef7d91662cfc74e0074cf20f03dc361857db3f2d --- /dev/null +++ b/clang/blink_gc_plugin/CheckFinalizerVisitor.cpp @@ -0,0 +1,103 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "CheckFinalizerVisitor.h" + +using namespace clang; + +namespace { + +// Simple visitor to determine if the content of a field might be collected +// during finalization. +class MightBeCollectedVisitor : public EdgeVisitor { + public: + bool might_be_collected() const; + + void VisitMember(Member* edge) override; + void VisitCollection(Collection* edge) override; + + private: + bool might_be_collected_ = false; +}; + +bool MightBeCollectedVisitor::might_be_collected() const { + return might_be_collected_; +} + +void MightBeCollectedVisitor::VisitMember(Member* edge) { + might_be_collected_ = true; +} + +void MightBeCollectedVisitor::VisitCollection(Collection* edge) { + if (edge->on_heap()) { + might_be_collected_ = true; + } else { + edge->AcceptMembers(this); + } +} + +} // namespace + +CheckFinalizerVisitor::CheckFinalizerVisitor(RecordCache* cache) + : blocklist_context_(false), + cache_(cache) { +} + +CheckFinalizerVisitor::Errors& CheckFinalizerVisitor::finalized_fields() { + return finalized_fields_; +} + +bool CheckFinalizerVisitor::WalkUpFromCXXOperatorCallExpr( + CXXOperatorCallExpr* expr) { + // Only continue the walk-up if the operator is a blocklisted one. + switch (expr->getOperator()) { + case OO_Arrow: + case OO_Subscript: + this->WalkUpFromCallExpr(expr); + return true; + default: + return true; + } +} + +bool CheckFinalizerVisitor::WalkUpFromCallExpr(CallExpr* expr) { + // We consider all non-operator calls to be blocklisted contexts. + bool prev_blocklist_context = blocklist_context_; + blocklist_context_ = true; + for (size_t i = 0; i < expr->getNumArgs(); ++i) + this->TraverseStmt(expr->getArg(i)); + blocklist_context_ = prev_blocklist_context; + return true; +} + +bool CheckFinalizerVisitor::VisitMemberExpr(MemberExpr* member) { + FieldDecl* field = dyn_cast(member->getMemberDecl()); + if (!field) + return true; + + RecordInfo* info = cache_->Lookup(field->getParent()); + if (!info) + return true; + + RecordInfo::Fields::iterator it = info->GetFields().find(field); + if (it == info->GetFields().end()) + return true; + + if (seen_members_.find(member) != seen_members_.end()) + return true; + + if (blocklist_context_ && + MightBeCollected(&it->second)) { + finalized_fields_.push_back( + Error(member, &it->second)); + seen_members_.insert(member); + } + return true; +} + +bool CheckFinalizerVisitor::MightBeCollected(FieldPoint* point) { + MightBeCollectedVisitor visitor; + point->edge()->Accept(&visitor); + return visitor.might_be_collected(); +} diff --git a/clang/blink_gc_plugin/CheckFinalizerVisitor.h b/clang/blink_gc_plugin/CheckFinalizerVisitor.h new file mode 100644 index 0000000000000000000000000000000000000000..5f65901f2a38adf3bd07938a549b4bd2b40e4bf7 --- /dev/null +++ b/clang/blink_gc_plugin/CheckFinalizerVisitor.h @@ -0,0 +1,53 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_BLINK_GC_PLUGIN_CHECK_FINALIZER_VISITOR_H_ +#define TOOLS_BLINK_GC_PLUGIN_CHECK_FINALIZER_VISITOR_H_ + +#include +#include + +#include "Edge.h" +#include "RecordInfo.h" +#include "clang/AST/RecursiveASTVisitor.h" + +// This visitor checks that a finalizer method does not have invalid access to +// fields that are potentially finalized. A potentially finalized field is +// either a Member, a heap-allocated collection or an off-heap collection that +// contains Members. Invalid uses are currently identified as passing the field +// as the argument of a procedure call or using the -> or [] operators on it. +class CheckFinalizerVisitor + : public clang::RecursiveASTVisitor { + public: + struct Error { + Error(clang::MemberExpr* member, + FieldPoint* field) + : member(member), + field(field) {} + + clang::MemberExpr* member; + FieldPoint* field; + }; + + typedef std::vector Errors; + + explicit CheckFinalizerVisitor(RecordCache* cache); + + Errors& finalized_fields(); + + bool WalkUpFromCXXOperatorCallExpr(clang::CXXOperatorCallExpr* expr); + bool WalkUpFromCallExpr(clang::CallExpr* expr); + + bool VisitMemberExpr(clang::MemberExpr* member); + + private: + bool MightBeCollected(FieldPoint* point); + + bool blocklist_context_; + Errors finalized_fields_; + std::set seen_members_; + RecordCache* cache_; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_CHECK_FINALIZER_VISITOR_H_ diff --git a/clang/blink_gc_plugin/CheckForbiddenFieldsVisitor.cpp b/clang/blink_gc_plugin/CheckForbiddenFieldsVisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e4bde04e9d6027d2465ab684fa5f8da37844c93a --- /dev/null +++ b/clang/blink_gc_plugin/CheckForbiddenFieldsVisitor.cpp @@ -0,0 +1,119 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "CheckForbiddenFieldsVisitor.h" +#include "BlinkGCPluginOptions.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" + +CheckForbiddenFieldsVisitor::CheckForbiddenFieldsVisitor() {} + +CheckForbiddenFieldsVisitor::Errors& +CheckForbiddenFieldsVisitor::forbidden_fields() { + return forbidden_fields_; +} + +bool CheckForbiddenFieldsVisitor::ContainsForbiddenFields(RecordInfo* info) { + bool managed_host = info->IsStackAllocated() || info->IsGCAllocated() || + info->IsNewDisallowed(); + if (!managed_host) + return false; + + return ContainsForbiddenFieldsInternal(info); +} + +bool CheckForbiddenFieldsVisitor::ContainsForbiddenFieldsInternal( + RecordInfo* info) { + for (auto& field : info->GetFields()) { + current_.push_back(&field.second); + field.second.edge()->Accept(this); + current_.pop_back(); + } + return !forbidden_fields_.empty(); +} + +void CheckForbiddenFieldsVisitor::VisitValue(Value* edge) { + // TODO: what should we do to check unions? + if (edge->value()->record()->isUnion()) + return; + + // Prevent infinite regress for cyclic embedded objects. + if (visiting_set_.find(edge->value()) != visiting_set_.end()) + return; + + visiting_set_.insert(edge->value()); + + // We want to keep recursing into the current field if we did not encounter + // something else than a collection during our recursion. However, in case of + // pointers, we still want to check whether their template specializations + // are forbidden classes, and then stop the recursion. + bool keep_recursing = true; + bool check_for_forbidden_fields = true; + for (Edge* e : llvm::reverse(context())) { + if (!e->IsCollection()) { + keep_recursing = false; + check_for_forbidden_fields = false; + if (e->IsRawPtr() || e->IsRefPtr() || e->IsUniquePtr()) { + check_for_forbidden_fields = true; + } + } + } + + if (check_for_forbidden_fields && ContainsInvalidFieldTypes(edge)) { + visiting_set_.erase(edge->value()); + return; + } + + if (keep_recursing) { + ContainsForbiddenFieldsInternal(edge->value()); + } + + visiting_set_.erase(edge->value()); +} + +void CheckForbiddenFieldsVisitor::VisitArrayEdge(ArrayEdge* edge) { + if (edge->element()->IsValue()) { + edge->element()->Accept(this); + } +} + +bool CheckForbiddenFieldsVisitor::ContainsInvalidFieldTypes(Value* edge) { + constexpr std::pair kErrors[] = { + {"blink::TaskRunnerTimer", Error::kTaskRunnerInGCManaged}, + {"mojo::Receiver", Error::kMojoReceiverInGCManaged}, + {"mojo::Remote", Error::kMojoRemoteInGCManaged}, + }; + + constexpr std::pair kOptionalAssociatedErrors[] = { + {"mojo::AssociatedRemote", Error::kMojoAssociatedRemoteInGCManaged}, + {"mojo::AssociatedReceiver", Error::kMojoAssociatedReceiverInGCManaged}, + }; + + auto* decl = edge->value()->record()->getDefinition(); + if (!decl) { + return false; + } + + auto type_name = decl->getQualifiedNameAsString(); + auto it = std::find_if( + std::begin(kErrors), std::end(kErrors), + [&type_name](const auto& val) { return val.first == type_name; }); + + if (it != std::end(kErrors)) { + forbidden_fields_.push_back({current_, it->second}); + return true; + } + + it = std::find_if( + std::begin(kOptionalAssociatedErrors), + std::end(kOptionalAssociatedErrors), + [&type_name](const auto& val) { return val.first == type_name; }); + if (it != std::end(kOptionalAssociatedErrors)) { + forbidden_fields_.push_back({current_, it->second}); + return true; + } + + return false; +} diff --git a/clang/blink_gc_plugin/CheckForbiddenFieldsVisitor.h b/clang/blink_gc_plugin/CheckForbiddenFieldsVisitor.h new file mode 100644 index 0000000000000000000000000000000000000000..1da0ccc0bf7266dc8333552ecce8f7b46ec4253a --- /dev/null +++ b/clang/blink_gc_plugin/CheckForbiddenFieldsVisitor.h @@ -0,0 +1,61 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_BLINK_GC_PLUGIN_CHECKFORBIDDENFIELDSVISITOR_H_ +#define TOOLS_CLANG_BLINK_GC_PLUGIN_CHECKFORBIDDENFIELDSVISITOR_H_ + +#include +#include + +#include "Edge.h" +#include "RecordInfo.h" + +// This visitor checks that the fields of a class and the fields of +// its embedded objects don't define GC roots. +class CheckForbiddenFieldsVisitor : public RecursiveEdgeVisitor { + public: + enum class Error { + kTaskRunnerInGCManaged, + kMojoRemoteInGCManaged, + kMojoReceiverInGCManaged, + kMojoAssociatedRemoteInGCManaged, + kMojoAssociatedReceiverInGCManaged, + }; + + using RootPath = std::vector; + using VisitingSet = std::set; + using Errors = std::vector>; + + explicit CheckForbiddenFieldsVisitor(); + + // The forbidden fields found across the call(s) to + // `ContainsForbiddenFields`. + Errors& forbidden_fields(); + + // Checks whether the record recursively contains forbidden fields (either the + // record itself or an embedded object). + // Returns whether forbidden fields were found. + bool ContainsForbiddenFields(RecordInfo* info); + + void VisitValue(Value* edge) override; + void VisitArrayEdge(ArrayEdge* edge) override; + + private: + bool ContainsForbiddenFieldsInternal(RecordInfo* info); + bool ContainsInvalidFieldTypes(Value* edge); + + // The current path that we are working on. + // This permits to find the path back to the GCed type when handling embedded + // objects. + RootPath current_; + + // The currently visited nodes. This is used to handle cyclic dependencies + // between the embedded objects we are going through. + VisitingSet visiting_set_; + + // The actual fields that were found while inspecting the record. + Errors forbidden_fields_; +}; + +#endif // TOOLS_CLANG_BLINK_GC_PLUGIN_CHECKFORBIDDENFIELDSVISITOR_H_ diff --git a/clang/blink_gc_plugin/CheckGCRootsVisitor.cpp b/clang/blink_gc_plugin/CheckGCRootsVisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bf990db1bae07f3286c8da6bf6d8feb7e5e8c66c --- /dev/null +++ b/clang/blink_gc_plugin/CheckGCRootsVisitor.cpp @@ -0,0 +1,86 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "CheckGCRootsVisitor.h" +#include "BlinkGCPluginOptions.h" + +CheckGCRootsVisitor::CheckGCRootsVisitor(const BlinkGCPluginOptions& options) + : should_check_unique_ptrs_(options.enable_persistent_in_unique_ptr_check) { +} + +CheckGCRootsVisitor::Errors& CheckGCRootsVisitor::gc_roots() { + return gc_roots_; +} + +CheckGCRootsVisitor::Errors& CheckGCRootsVisitor::gc_root_refs() { + return gc_root_refs_; +} + +bool CheckGCRootsVisitor::ContainsGCRoots(RecordInfo* info) { + for (RecordInfo::Fields::iterator it = info->GetFields().begin(); + it != info->GetFields().end(); + ++it) { + current_.push_back(&it->second); + it->second.edge()->Accept(this); + current_.pop_back(); + } + return !gc_roots_.empty() || !gc_root_refs_.empty(); +} + +void CheckGCRootsVisitor::VisitValue(Value* edge) { + // TODO: what should we do to check unions? + if (edge->value()->record()->isUnion()) + return; + + // Prevent infinite regress for cyclic part objects. + if (visiting_set_.find(edge->value()) != visiting_set_.end()) + return; + + visiting_set_.insert(edge->value()); + // If the value is a part object, then continue checking for roots. + for (Context::iterator it = context().begin(); + it != context().end(); + ++it) { + if (!(*it)->IsCollection()) + return; + } + ContainsGCRoots(edge->value()); + visiting_set_.erase(edge->value()); +} + +void CheckGCRootsVisitor::VisitUniquePtr(UniquePtr* edge) { + if (!should_check_unique_ptrs_) + return; + edge->ptr()->Accept(this); +} + +void CheckGCRootsVisitor::VisitRawPtr(RawPtr* edge) { + bool previous_is_ref_ = is_ref_; + is_ref_ = true; + RecursiveEdgeVisitor::VisitRawPtr(edge); + is_ref_ = previous_is_ref_; +} + +void CheckGCRootsVisitor::VisitRefPtr(RefPtr* edge) { + bool previous_is_ref_ = is_ref_; + is_ref_ = true; + RecursiveEdgeVisitor::VisitRefPtr(edge); + is_ref_ = previous_is_ref_; +} + +void CheckGCRootsVisitor::VisitPersistent(Persistent* edge) { + if (is_ref_) { + gc_root_refs_.push_back(current_); + } else { + gc_roots_.push_back(current_); + } +} + +void CheckGCRootsVisitor::VisitCollection(Collection* edge) { + // There already is a matcher for handling this. + if (edge->IsSTDCollection()) { + return; + } + RecursiveEdgeVisitor::VisitCollection(edge); +} diff --git a/clang/blink_gc_plugin/CheckGCRootsVisitor.h b/clang/blink_gc_plugin/CheckGCRootsVisitor.h new file mode 100644 index 0000000000000000000000000000000000000000..03a5b377ad8cb9dfda6ecafbdf9ccd6330c73afb --- /dev/null +++ b/clang/blink_gc_plugin/CheckGCRootsVisitor.h @@ -0,0 +1,48 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_BLINK_GC_PLUGIN_CHECK_GC_ROOTS_VISITOR_H_ +#define TOOLS_BLINK_GC_PLUGIN_CHECK_GC_ROOTS_VISITOR_H_ + +#include +#include + +#include "Edge.h" +#include "RecordInfo.h" + +struct BlinkGCPluginOptions; + +// This visitor checks that the fields of a class and the fields of +// its part objects don't define GC roots. +class CheckGCRootsVisitor : public RecursiveEdgeVisitor { + public: + typedef std::vector RootPath; + typedef std::set VisitingSet; + typedef std::vector Errors; + + explicit CheckGCRootsVisitor(const BlinkGCPluginOptions&); + + Errors& gc_roots(); + Errors& gc_root_refs(); + + bool ContainsGCRoots(RecordInfo* info); + + void VisitValue(Value* edge) override; + void VisitUniquePtr(UniquePtr*) override; + void VisitRawPtr(RawPtr*) override; + void VisitRefPtr(RefPtr*) override; + void VisitPersistent(Persistent* edge) override; + void VisitCollection(Collection* edge) override; + + private: + RootPath current_; + VisitingSet visiting_set_; + Errors gc_roots_; + Errors gc_root_refs_; + bool is_ref_ = false; + + bool should_check_unique_ptrs_; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_CHECK_GC_ROOTS_VISITOR_H_ diff --git a/clang/blink_gc_plugin/CheckTraceVisitor.cpp b/clang/blink_gc_plugin/CheckTraceVisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec0edaed42a1b6ee41b6b66508f6f0c55038494b --- /dev/null +++ b/clang/blink_gc_plugin/CheckTraceVisitor.cpp @@ -0,0 +1,459 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "CheckTraceVisitor.h" + +#include + +#include "Config.h" +#include "Edge.h" +#include "RecordInfo.h" + +using namespace clang; + +CheckTraceVisitor::CheckTraceVisitor(CXXMethodDecl* trace, + RecordInfo* info, + RecordCache* cache) + : trace_(trace), info_(info), cache_(cache) {} + +bool CheckTraceVisitor::VisitMemberExpr(MemberExpr* member) { + // In weak callbacks, consider any occurrence as a correct usage. + // TODO: We really want to require that isAlive is checked on manually + // processed weak fields. + if (IsWeakCallback()) { + if (FieldDecl* field = dyn_cast(member->getMemberDecl())) + FoundField(field); + } + return true; +} + +bool CheckTraceVisitor::VisitCallExpr(CallExpr* call) { + // In weak callbacks we don't check calls (see VisitMemberExpr). + if (IsWeakCallback()) + return true; + + Expr* callee = call->getCallee(); + + // Trace calls from a templated derived class result in a + // DependentScopeMemberExpr because the concrete trace call depends on the + // instantiation of any shared template parameters. In this case the call is + // "unresolved" and we resort to comparing the syntactic type names. + if (DependentScopeDeclRefExpr* expr = + dyn_cast(callee)) { + CheckDependentScopeDeclRefExpr(call, expr); + return true; + } + + if (ImplicitCastExpr* expr = dyn_cast(callee)) { + if (CheckImplicitCastExpr(call, expr)) + return true; + } + + // A tracing call will have either a |visitor| or a |m_field| argument. + // A registerWeakMembers call will have a |this| argument. + if (call->getNumArgs() != 1) + return true; + Expr* arg = call->getArg(0); + + if (UnresolvedMemberExpr* expr = dyn_cast(callee)) { + // This could be a trace call of a base class, as explained in the + // comments of CheckTraceBaseCall(). + if (CheckTraceBaseCall(call)) + return true; + + if (expr->getMemberName().getAsString() == kRegisterWeakMembersName) + MarkAllWeakMembersTraced(); + + QualType base = expr->getBaseType(); + if (!base->isPointerType()) + return true; + CXXRecordDecl* decl = base->getPointeeType()->getAsCXXRecordDecl(); + if (decl) + CheckTraceFieldCall(expr->getMemberName().getAsString(), decl, arg); + return true; + } + + if (CXXMemberCallExpr* expr = dyn_cast(call)) { + if (CheckTraceFieldMemberCall(expr) || CheckRegisterWeakMembers(expr)) + return true; + } + + CheckTraceBaseCall(call); + return true; +} + +bool CheckTraceVisitor::IsTraceCallName(const std::string& name) { + // Currently, a manually dispatched class cannot have mixin bases (having + // one would add a vtable which we explicitly check against). This means + // that we can only make calls to a trace method of the same name. Revisit + // this if our mixin/vtable assumption changes. + return name == trace_->getName(); +} + +CXXRecordDecl* CheckTraceVisitor::GetDependentTemplatedDecl( + DependentScopeDeclRefExpr* expr) { + NestedNameSpecifier* qual = expr->getQualifier(); + if (!qual) + return 0; + + const Type* type = qual->getAsType(); + if (!type) + return 0; + + return RecordInfo::GetDependentTemplatedDecl(*type); +} + +namespace { + +class FindFieldVisitor : public RecursiveASTVisitor { + public: + FindFieldVisitor(); + FieldDecl* field() const; + bool TraverseMemberExpr(MemberExpr* member); + + private: + FieldDecl* field_; +}; + +FindFieldVisitor::FindFieldVisitor() : field_(0) {} + +FieldDecl* FindFieldVisitor::field() const { + return field_; +} + +bool FindFieldVisitor::TraverseMemberExpr(MemberExpr* member) { + if (FieldDecl* field = dyn_cast(member->getMemberDecl())) { + field_ = field; + return false; + } + return true; +} + +} // namespace + +void CheckTraceVisitor::CheckDependentScopeDeclRefExpr( + CallExpr* call, + DependentScopeDeclRefExpr* expr) { + std::string fn_name = expr->getDeclName().getAsString(); + + // Check for T::Trace(visitor). + if (NestedNameSpecifier* qual = expr->getQualifier()) { + if (const Type* type = qual->getAsType()) { + if (const TemplateTypeParmType* tmpl_parm_type = + type->getAs()) { + const unsigned param_index = tmpl_parm_type->getIndex(); + if (param_index >= info_->GetBases().size()) + return; + info_->GetBases()[param_index].second.MarkTraced(); + } + } + } + + CXXRecordDecl* tmpl = GetDependentTemplatedDecl(expr); + if (!tmpl) + return; + + // Check for Super::trace(visitor) + if (call->getNumArgs() == 1 && IsTraceCallName(fn_name)) { + RecordInfo::Bases::iterator it = info_->GetBases().begin(); + for (; it != info_->GetBases().end(); ++it) { + if (it->first->getName() == tmpl->getName()) + it->second.MarkTraced(); + } + } + + // Check for TraceIfNeeded::trace(visitor, &field) where T cannot be + // resolved + if (call->getNumArgs() == 2 && fn_name == kTraceName && + tmpl->getName() == kTraceIfNeededName) { + FindFieldVisitor finder; + finder.TraverseStmt(call->getArg(1)); + if (finder.field()) + FoundField(finder.field()); + } +} + +bool CheckTraceVisitor::CheckTraceBaseCall(CallExpr* call) { + // Checks for "Base::trace(visitor)"-like calls. + + // Checking code for these two variables is shared among MemberExpr* case + // and UnresolvedMemberCase* case below. + // + // For example, if we've got "Base::trace(visitor)" as |call|, + // callee_record will be "Base", and func_name will be "trace". + CXXRecordDecl* callee_record = nullptr; + std::string func_name; + + if (MemberExpr* callee = dyn_cast(call->getCallee())) { + if (!callee->hasQualifier()) + return false; + + FunctionDecl* trace_decl = + dyn_cast(callee->getMemberDecl()); + if (!trace_decl || !Config::IsTraceMethod(trace_decl)) + return false; + + const Type* type = callee->getQualifier()->getAsType(); + if (!type) + return false; + + callee_record = type->getAsCXXRecordDecl(); + func_name = std::string(trace_decl->getName()); + } else if (UnresolvedMemberExpr* callee = + dyn_cast(call->getCallee())) { + // Callee part may become unresolved if the type of the argument + // ("visitor") is a template parameter and the called function is + // overloaded. + // + // Here, we try to find a function that looks like trace() from the + // candidate overloaded functions, and if we find one, we assume it is + // called here. + + CXXMethodDecl* trace_decl = nullptr; + for (NamedDecl* named_decl : callee->decls()) { + if (CXXMethodDecl* method_decl = dyn_cast(named_decl)) { + if (Config::IsTraceMethod(method_decl)) { + trace_decl = method_decl; + break; + } + } + } + if (!trace_decl) + return false; + + // Check if the passed argument is named "visitor". + if (call->getNumArgs() != 1) + return false; + DeclRefExpr* arg = dyn_cast(call->getArg(0)); + if (!arg || arg->getNameInfo().getAsString() != kVisitorVarName) + return false; + + callee_record = trace_decl->getParent(); + func_name = callee->getMemberName().getAsString(); + } + + if (!callee_record) + return false; + + if (!IsTraceCallName(func_name)) + return false; + + for (auto& base : info_->GetBases()) { + // We want to deal with omitted trace() function in an intermediary + // class in the class hierarchy, e.g.: + // class A : public GarbageCollected { trace() { ... } }; + // class B : public A { /* No trace(); have nothing to trace. */ }; + // class C : public B { trace() { B::trace(visitor); } } + // where, B::trace() is actually A::trace(), and in some cases we get + // A as |callee_record| instead of B. We somehow need to mark B as + // traced if we find A::trace() call. + // + // To solve this, here we keep going up the class hierarchy as long as + // they are not required to have a trace method. The implementation is + // a simple DFS, where |base_records| represents the set of base classes + // we need to visit. + + std::vector base_records; + base_records.push_back(base.first); + + while (!base_records.empty()) { + CXXRecordDecl* base_record = base_records.back(); + base_records.pop_back(); + + if (base_record == callee_record) { + // If we find a matching trace method, pretend the user has written + // a correct trace() method of the base; in the example above, we + // find A::trace() here and mark B as correctly traced. + base.second.MarkTraced(); + return true; + } + + if (RecordInfo* base_info = cache_->Lookup(base_record)) { + if (!base_info->RequiresTraceMethod()) { + // If this base class is not required to have a trace method, then + // the actual trace method may be defined in an ancestor. + for (auto& inner_base : base_info->GetBases()) + base_records.push_back(inner_base.first); + } + } + } + } + + return false; +} + +bool CheckTraceVisitor::CheckTraceFieldMemberCall(CXXMemberCallExpr* call) { + return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), + call->getRecordDecl(), + call->getArg(0)); +} + +bool CheckTraceVisitor::CheckTraceFieldCall( + const std::string& name, + CXXRecordDecl* callee, + Expr* arg) { + if (name != kTraceName || !Config::IsVisitor(callee->getName())) + return false; + + FindFieldVisitor finder; + finder.TraverseStmt(arg); + if (finder.field()) + FoundField(finder.field()); + + return true; +} + +bool CheckTraceVisitor::CheckRegisterWeakMembers(CXXMemberCallExpr* call) { + CXXMethodDecl* fn = call->getMethodDecl(); + if (fn->getName() != kRegisterWeakMembersName) + return false; + + if (fn->isTemplateInstantiation()) { + const TemplateArgumentList& args = + *fn->getTemplateSpecializationInfo()->TemplateArguments; + // The second template argument is the callback method. + if (args.size() > 1 && + args[1].getKind() == TemplateArgument::Declaration) { + if (FunctionDecl* callback = + dyn_cast(args[1].getAsDecl())) { + if (callback->hasBody()) { + CheckTraceVisitor nested_visitor(nullptr, info_, nullptr); + nested_visitor.TraverseStmt(callback->getBody()); + } + } + // TODO: mark all WeakMember<>s as traced even if + // the body isn't available? + } + } + return true; +} + +bool CheckTraceVisitor::IsWeakCallback() const { + return !trace_; +} + +void CheckTraceVisitor::MarkTraced(RecordInfo::Fields::iterator it) { + // In a weak callback we can't mark strong fields as traced. + if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) + return; + it->second.MarkTraced(); +} + +namespace { +RecordInfo::Fields::iterator FindField(RecordInfo* info, FieldDecl* field) { + if (Config::IsTemplateInstantiation(info->record())) { + // Pointer equality on fields does not work for template instantiations. + // The trace method refers to fields of the template definition which + // are different from the instantiated fields that need to be traced. + const std::string& name = field->getNameAsString(); + for (RecordInfo::Fields::iterator it = info->GetFields().begin(); + it != info->GetFields().end(); ++it) { + if (it->first->getNameAsString() == name) { + return it; + } + } + return info->GetFields().end(); + } else { + return info->GetFields().find(field); + } +} +} // namespace + +void CheckTraceVisitor::FoundField(FieldDecl* field) { + RecordInfo::Fields::iterator it = FindField(info_, field); + if (it != info_->GetFields().end()) { + MarkTraced(it); + } +} + +void CheckTraceVisitor::MarkAllWeakMembersTraced() { + // If we find a call to registerWeakMembers which is unresolved we + // unsoundly consider all weak members as traced. + // TODO: Find out how to validate weak member tracing for unresolved call. + for (auto& field : info_->GetFields()) { + if (field.second.edge()->IsWeakMember()) + field.second.MarkTraced(); + } +} + +bool CheckTraceVisitor::CheckImplicitCastExpr(CallExpr* call, + ImplicitCastExpr* expr) { + DeclRefExpr* sub_expr = dyn_cast(expr->getSubExpr()); + if (!sub_expr) + return false; + NestedNameSpecifier* qualifier = sub_expr->getQualifier(); + if (!qualifier) + return false; + CXXRecordDecl* class_decl = qualifier->getAsRecordDecl(); + if (!class_decl) + return false; + NamedDecl* found_decl = sub_expr->getFoundDecl(); + std::string fn_name = found_decl->getNameAsString(); + // Check for TraceIfNeeded::trace(visitor, &field) where T can be resolved + if (call->getNumArgs() == 2 && fn_name == kTraceName && + class_decl->getName() == kTraceIfNeededName) { + FindFieldVisitor finder; + finder.TraverseStmt(call->getArg(1)); + if (finder.field()) + FoundField(finder.field()); + return true; + } + return false; +} + +namespace { +FieldDecl* GetRangeField(CXXForRangeStmt* for_range_stmt) { + DeclStmt* decl_stmt = for_range_stmt->getRangeStmt(); + if (!decl_stmt->isSingleDecl()) { + return nullptr; + } + VarDecl* var_decl = dyn_cast(decl_stmt->getSingleDecl()); + if (!var_decl) { + return nullptr; + } + MemberExpr* member_expr = dyn_cast(var_decl->getInit()); + if (!member_expr) { + return nullptr; + } + FieldDecl* field_decl = dyn_cast(member_expr->getMemberDecl()); + if (!field_decl) { + return nullptr; + } + return field_decl; +} +} // namespace + +bool CheckTraceVisitor::VisitStmt(Stmt* stmt) { + CXXForRangeStmt* for_range = dyn_cast(stmt); + if (!for_range) { + return true; + } + + // Array tracing could be phrased as a for-range statement over the array. + FieldDecl* field_decl = GetRangeField(for_range); + if (!field_decl) { + return true; + } + + // The range of the for-range statement references a field. If that field + // is an array, assume the array is being traced. + RecordInfo::Fields::iterator it = FindField(info_, field_decl); + if (it == info_->GetFields().end()) { + return true; + } + + Edge* field_edge = it->second.edge(); + if (field_edge->IsArray()) { + MarkTraced(it); + } + if (field_edge->IsCollection()) { + Collection* collection = static_cast(field_edge); + if (collection->IsSTDCollection() && + (collection->GetCollectionName() == "array")) { + MarkTraced(it); + } + } + + return true; +} diff --git a/clang/blink_gc_plugin/CheckTraceVisitor.h b/clang/blink_gc_plugin/CheckTraceVisitor.h new file mode 100644 index 0000000000000000000000000000000000000000..a79822e69ceaf5c22cfc7d8031f402f674ff2886 --- /dev/null +++ b/clang/blink_gc_plugin/CheckTraceVisitor.h @@ -0,0 +1,58 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_BLINK_GC_PLUGIN_CHECK_TRACE_VISITOR_H_ +#define TOOLS_BLINK_GC_PLUGIN_CHECK_TRACE_VISITOR_H_ + +#include + +#include "RecordInfo.h" +#include "clang/AST/AST.h" +#include "clang/AST/RecursiveASTVisitor.h" + +class RecordCache; +class RecordInfo; + +// This visitor checks a tracing method by traversing its body. +// - A member field is considered traced if it is referenced in the body. +// - A base is traced if a base-qualified call to a trace method is found. +class CheckTraceVisitor : public clang::RecursiveASTVisitor { + public: + CheckTraceVisitor(clang::CXXMethodDecl* trace, + RecordInfo* info, + RecordCache* cache); + + bool VisitMemberExpr(clang::MemberExpr* member); + bool VisitCallExpr(clang::CallExpr* call); + bool VisitStmt(clang::Stmt* stmt); + + private: + bool IsTraceCallName(const std::string& name); + + clang::CXXRecordDecl* GetDependentTemplatedDecl( + clang::DependentScopeDeclRefExpr* expr); + + void CheckDependentScopeDeclRefExpr(clang::CallExpr* call, + clang::DependentScopeDeclRefExpr* expr); + bool CheckTraceBaseCall(clang::CallExpr* call); + bool CheckTraceFieldMemberCall(clang::CXXMemberCallExpr* call); + bool CheckTraceFieldCall(const std::string& name, + clang::CXXRecordDecl* callee, + clang::Expr* arg); + bool CheckRegisterWeakMembers(clang::CXXMemberCallExpr* call); + bool CheckImplicitCastExpr(clang::CallExpr* call, + clang::ImplicitCastExpr* expr); + + bool IsWeakCallback() const; + + void MarkTraced(RecordInfo::Fields::iterator it); + void FoundField(clang::FieldDecl* field); + void MarkAllWeakMembersTraced(); + + clang::CXXMethodDecl* trace_; + RecordInfo* info_; + RecordCache* cache_; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_CHECK_TRACE_VISITOR_H_ diff --git a/clang/blink_gc_plugin/CollectVisitor.cpp b/clang/blink_gc_plugin/CollectVisitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..35ff885d625104e7dc320c9869712a50b5499536 --- /dev/null +++ b/clang/blink_gc_plugin/CollectVisitor.cpp @@ -0,0 +1,35 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "CollectVisitor.h" + +#include "Config.h" + +using namespace clang; + +CollectVisitor::CollectVisitor() { +} + +CollectVisitor::RecordVector& CollectVisitor::record_decls() { + return record_decls_; +} + +CollectVisitor::MethodVector& CollectVisitor::trace_decls() { + return trace_decls_; +} + +bool CollectVisitor::VisitCXXRecordDecl(CXXRecordDecl* record) { + if (record->hasDefinition() && record->isCompleteDefinition()) + record_decls_.push_back(record); + return true; +} + +bool CollectVisitor::VisitCXXMethodDecl(CXXMethodDecl* method) { + if (method->isThisDeclarationADefinition()) { + if (Config::IsTraceMethod(method)) { + trace_decls_.push_back(method); + } + } + return true; +} diff --git a/clang/blink_gc_plugin/CollectVisitor.h b/clang/blink_gc_plugin/CollectVisitor.h new file mode 100644 index 0000000000000000000000000000000000000000..ae9974762627d2793e0a6779f93f8d705d64f3d0 --- /dev/null +++ b/clang/blink_gc_plugin/CollectVisitor.h @@ -0,0 +1,35 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_BLINK_GC_PLUGIN_COLLECT_VISITOR_H_ +#define TOOLS_BLINK_GC_PLUGIN_COLLECT_VISITOR_H_ + +#include + +#include "clang/AST/AST.h" +#include "clang/AST/RecursiveASTVisitor.h" + +// This visitor collects the entry points for the checker. +class CollectVisitor : public clang::RecursiveASTVisitor { + public: + typedef std::vector RecordVector; + typedef std::vector MethodVector; + + CollectVisitor(); + + RecordVector& record_decls(); + MethodVector& trace_decls(); + + // Collect record declarations, including nested declarations. + bool VisitCXXRecordDecl(clang::CXXRecordDecl* record); + + // Collect tracing method definitions, but don't traverse method bodies. + bool VisitCXXMethodDecl(clang::CXXMethodDecl* method); + + private: + RecordVector record_decls_; + MethodVector trace_decls_; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_COLLECT_VISITOR_H_ diff --git a/clang/blink_gc_plugin/Config.cpp b/clang/blink_gc_plugin/Config.cpp new file mode 100644 index 0000000000000000000000000000000000000000..514ab02d131887cca9d8325e9a66dfe1eff28981 --- /dev/null +++ b/clang/blink_gc_plugin/Config.cpp @@ -0,0 +1,46 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "Config.h" + +#include + +#include "clang/AST/AST.h" + +using namespace clang; + +const char kNewOperatorName[] = "operator new"; +const char kCreateName[] = "Create"; +const char kTraceName[] = "Trace"; +const char kFinalizeName[] = "FinalizeGarbageCollectedObject"; +const char kTraceAfterDispatchName[] = "TraceAfterDispatch"; +const char kRegisterWeakMembersName[] = "RegisterWeakMembers"; +const char kHeapAllocatorName[] = "HeapAllocator"; +const char kTraceIfNeededName[] = "TraceIfNeeded"; +const char kVisitorDispatcherName[] = "VisitorDispatcher"; +const char kVisitorVarName[] = "visitor"; +const char kConstIteratorName[] = "const_iterator"; +const char kIteratorName[] = "iterator"; +const char kConstReverseIteratorName[] = "const_reverse_iterator"; +const char kReverseIteratorName[] = "reverse_iterator"; + +bool Config::IsTemplateInstantiation(CXXRecordDecl* record) { + ClassTemplateSpecializationDecl* spec = + dyn_cast(record); + if (!spec) + return false; + switch (spec->getTemplateSpecializationKind()) { + case TSK_ImplicitInstantiation: + case TSK_ExplicitInstantiationDefinition: + return true; + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + return false; + // TODO: unsupported cases. + case TSK_ExplicitInstantiationDeclaration: + return false; + } + assert(false && "Unknown template specialization kind"); + return false; +} diff --git a/clang/blink_gc_plugin/Config.h b/clang/blink_gc_plugin/Config.h new file mode 100644 index 0000000000000000000000000000000000000000..7b40982313cbd3e7c0b98574c3d5582880fb5f07 --- /dev/null +++ b/clang/blink_gc_plugin/Config.h @@ -0,0 +1,279 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file defines the names used by GC infrastructure. + +// TODO: Restructure the name determination to use fully qualified names (ala, +// blink::Foo) so that the plugin can be enabled for all of chromium. Doing so +// would allow us to catch errors with structures outside of blink that might +// have unsafe pointers to GC allocated blink structures. + +#ifndef TOOLS_BLINK_GC_PLUGIN_CONFIG_H_ +#define TOOLS_BLINK_GC_PLUGIN_CONFIG_H_ + +#include + +#include "RecordInfo.h" +#include "clang/AST/AST.h" +#include "clang/AST/Attr.h" + +extern const char kNewOperatorName[]; +extern const char kCreateName[]; +extern const char kTraceName[]; +extern const char kFinalizeName[]; +extern const char kTraceAfterDispatchName[]; +extern const char kRegisterWeakMembersName[]; +extern const char kHeapAllocatorName[]; +extern const char kTraceIfNeededName[]; +extern const char kVisitorDispatcherName[]; +extern const char kVisitorVarName[]; +extern const char kConstIteratorName[]; +extern const char kIteratorName[]; +extern const char kConstReverseIteratorName[]; +extern const char kReverseIteratorName[]; + +class Config { + private: + // Checks that the namespace matches the expected namespace and that the type + // takes at least |expected_minimum_arg_count| template arguments. If both + // requirements are fulfilled, populates |args| with the first + // |expected_minimum_arg_count| template arguments. Verifying only the minimum + // expected argument keeps the plugin resistant to changes in the type + // definitions (to some extent) + static bool VerifyNamespaceAndArgCount(std::string expected_ns_name, + int expected_minimum_arg_count, + llvm::StringRef ns_name, + RecordInfo* info, + RecordInfo::TemplateArgs* args) { + return (ns_name == expected_ns_name) && + info->GetTemplateArgs(expected_minimum_arg_count, args); + } + + public: + static bool IsMember(llvm::StringRef name, + llvm::StringRef ns_name, + RecordInfo* info, + RecordInfo::TemplateArgs* args) { + if (name == "BasicMember") { + if (!VerifyNamespaceAndArgCount("cppgc", 2, ns_name, info, args)) + return false; + return (*args)[1]->getAsRecordDecl()->getName() == "StrongMemberTag"; + } + return false; + } + + static bool IsWeakMember(llvm::StringRef name, + llvm::StringRef ns_name, + RecordInfo* info, + RecordInfo::TemplateArgs* args) { + if (name == "BasicMember") { + if (!VerifyNamespaceAndArgCount("cppgc", 2, ns_name, info, args)) + return false; + return (*args)[1]->getAsRecordDecl()->getName() == "WeakMemberTag"; + } + return false; + } + + static bool IsPersistent(llvm::StringRef name, + llvm::StringRef ns_name, + RecordInfo* info, + RecordInfo::TemplateArgs* args) { + if (name == "BasicPersistent") { + return VerifyNamespaceAndArgCount("cppgc", 1, ns_name, info, args); + } + return false; + } + + static bool IsCrossThreadPersistent(llvm::StringRef name, + llvm::StringRef ns_name, + RecordInfo* info, + RecordInfo::TemplateArgs* args) { + if (name == "BasicCrossThreadPersistent") { + return VerifyNamespaceAndArgCount("cppgc", 1, ns_name, info, args); + } + return false; + } + + static bool IsRefPtr(llvm::StringRef name) { return name == "scoped_refptr"; } + + static bool IsWeakPtr(llvm::StringRef name) { return name == "WeakPtr"; } + + static bool IsRefOrWeakPtr(llvm::StringRef name) { + return IsRefPtr(name) || IsWeakPtr(name); + } + + static bool IsUniquePtr(llvm::StringRef name) { + return name == "unique_ptr"; + } + + static bool IsTraceWrapperV8Reference(llvm::StringRef name, + llvm::StringRef ns_name, + RecordInfo* info, + RecordInfo::TemplateArgs* args) { + return name == "TracedReference" && + VerifyNamespaceAndArgCount("v8", 1, ns_name, info, args); + } + + static bool IsWTFCollection(llvm::StringRef name) { + return name == "Vector" || + name == "Deque" || + name == "HashSet" || + name == "LinkedHashSet" || + name == "HashCountedSet" || + name == "HashMap"; + } + + static bool IsSTDCollection(llvm::StringRef name) { + return name == "vector" || name == "map" || name == "unordered_map" || + name == "set" || name == "unordered_set" || name == "array" || + name == "optional" || name == "variant"; + } + + static bool IsGCCollection(llvm::StringRef name) { + return name == "HeapVector" || name == "HeapDeque" || + name == "HeapHashSet" || name == "HeapLinkedHashSet" || + name == "HeapHashCountedSet" || name == "HeapHashMap" || + name == "HeapLinkedStack"; + } + + static bool IsHashMap(llvm::StringRef name) { + return name == "HashMap" || name == "HeapHashMap" || name == "map" || + name == "unordered_map"; + } + + // Assumes name is a valid collection name. + static size_t CollectionDimension(llvm::StringRef name) { + // In case we're dealing with a variant, we want to collect the whole + // parameter pack. + if (name == "variant") { + return 0; + } + return (IsHashMap(name) || name == "pair") ? 2 : 1; + } + + static bool IsRefCountedBase(llvm::StringRef name) { + return name == "RefCounted" || + name == "ThreadSafeRefCounted"; + } + + static bool IsGCSimpleBase(llvm::StringRef name) { + return name == "GarbageCollected"; + } + + static bool IsGCMixinBase(llvm::StringRef name) { + return name == "GarbageCollectedMixin"; + } + + static bool IsGCBase(llvm::StringRef name) { + return IsGCSimpleBase(name) || IsGCMixinBase(name); + } + + static bool IsIterator(llvm::StringRef name) { + return name == kIteratorName || name == kConstIteratorName || + name == kReverseIteratorName || name == kConstReverseIteratorName; + } + + // Returns true of the base classes that do not need a vtable entry for trace + // because they cannot possibly initiate a GC during construction. + static bool IsSafePolymorphicBase(llvm::StringRef name) { + return IsGCBase(name) || IsRefCountedBase(name); + } + + static bool IsAnnotated(const clang::Decl* decl, const std::string& anno) { + clang::AnnotateAttr* attr = decl->getAttr(); + return attr && (attr->getAnnotation() == anno); + } + + static bool IsIgnoreAnnotated(const clang::Decl* decl) { + return IsAnnotated(decl, "blink_gc_plugin_ignore"); + } + + static bool IsVisitor(llvm::StringRef name) { return name == "Visitor"; } + + static bool IsVisitorPtrType(const clang::QualType& formal_type) { + if (!formal_type->isPointerType()) + return false; + + clang::CXXRecordDecl* pointee_type = + formal_type->getPointeeType()->getAsCXXRecordDecl(); + if (!pointee_type) + return false; + + if (!IsVisitor(pointee_type->getName())) + return false; + + return true; + } + + static bool IsVisitorDispatcherType(const clang::QualType& formal_type) { + if (const clang::SubstTemplateTypeParmType* subst_type = + clang::dyn_cast( + formal_type.getTypePtr())) { + if (IsVisitorPtrType(subst_type->getReplacementType())) { + // VisitorDispatcher template parameter substituted to Visitor*. + return true; + } + } else if (const clang::TemplateTypeParmType* parm_type = + clang::dyn_cast( + formal_type.getTypePtr())) { + if (parm_type->getDecl()->getName() == kVisitorDispatcherName) { + // Unresolved, but its parameter name is VisitorDispatcher. + return true; + } + } + + return IsVisitorPtrType(formal_type); + } + + enum TraceMethodType { + NOT_TRACE_METHOD, + TRACE_METHOD, + TRACE_AFTER_DISPATCH_METHOD, + }; + + static TraceMethodType GetTraceMethodType(const clang::FunctionDecl* method) { + if (method->getNumParams() != 1) + return NOT_TRACE_METHOD; + + const std::string& name = method->getNameAsString(); + if (name != kTraceName && name != kTraceAfterDispatchName) + return NOT_TRACE_METHOD; + + const clang::QualType& formal_type = method->getParamDecl(0)->getType(); + if (!IsVisitorPtrType(formal_type)) { + return NOT_TRACE_METHOD; + } + + if (name == kTraceName) + return TRACE_METHOD; + if (name == kTraceAfterDispatchName) + return TRACE_AFTER_DISPATCH_METHOD; + + assert(false && "Should not reach here"); + return NOT_TRACE_METHOD; + } + + static bool IsTraceMethod(const clang::FunctionDecl* method) { + return GetTraceMethodType(method) != NOT_TRACE_METHOD; + } + + static bool IsTraceWrappersMethod(const clang::FunctionDecl* method); + + static bool StartsWith(const std::string& str, const std::string& prefix) { + if (prefix.size() > str.size()) + return false; + return str.compare(0, prefix.size(), prefix) == 0; + } + + static bool EndsWith(const std::string& str, const std::string& suffix) { + if (suffix.size() > str.size()) + return false; + return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; + } + + // Test if a template specialization is an instantiation. + static bool IsTemplateInstantiation(clang::CXXRecordDecl* record); +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_CONFIG_H_ diff --git a/clang/blink_gc_plugin/DIR_METADATA b/clang/blink_gc_plugin/DIR_METADATA new file mode 100644 index 0000000000000000000000000000000000000000..a9fd745275412ab9f6f6a7a7e93c8293021b9883 --- /dev/null +++ b/clang/blink_gc_plugin/DIR_METADATA @@ -0,0 +1,7 @@ +monorail: { + component: "Blink>GarbageCollection" +} +team_email: "oilpan-reviews@chromium.org" +buganizer_public: { + component_id: 1456254 +} diff --git a/clang/blink_gc_plugin/DiagnosticsReporter.cpp b/clang/blink_gc_plugin/DiagnosticsReporter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f72eddf041d09ceae15b330056385389cf0b7e18 --- /dev/null +++ b/clang/blink_gc_plugin/DiagnosticsReporter.cpp @@ -0,0 +1,902 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "DiagnosticsReporter.h" + +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; + +namespace { + +const char kClassMustLeftMostlyDeriveGC[] = + "[blink-gc] Class %0 must derive from GarbageCollected in the left-most position."; + +const char kClassRequiresTraceMethod[] = + "[blink-gc] Class %0 requires a trace method."; + +const char kBaseRequiresTracing[] = + "[blink-gc] Base class %0 of derived class %1 requires tracing."; + +const char kBaseRequiresTracingNote[] = + "[blink-gc] Untraced base class %0 declared here:"; + +const char kFieldsRequireTracing[] = + "[blink-gc] Class %0 has untraced fields that require tracing."; + +const char kFieldsImproperlyTraced[] = + "[blink-gc] Class %0 has untraced or not traceable fields."; + +const char kFieldRequiresTracingNote[] = + "[blink-gc] Untraced field %0 declared here:"; + +const char kFieldShouldNotBeTracedNote[] = + "[blink-gc] Untraceable field %0 declared here:"; + +const char kClassContainsInvalidFields[] = + "[blink-gc] Class %0 contains invalid fields."; + +const char kClassContainsGCRoot[] = + "[blink-gc] Class %0 contains GC root in field %1."; + +const char kClassContainsGCRootRef[] = + "[blink-gc] Class %0 contains a reference to a GC root in field %1. Avoid " + "holding references to GC roots. This should generally not be needed."; + +const char kFinalizerAccessesFinalizedField[] = + "[blink-gc] Finalizer %0 accesses potentially finalized field %1."; + +const char kRawPtrToGCManagedClassNote[] = + "[blink-gc] Raw pointer field %0 to a GC managed class declared here:"; + +const char kRefPtrToGCManagedClassNote[] = + "[blink-gc] scoped_refptr field %0 to a GC managed class declared here:"; + +const char kReferencePtrToGCManagedClassNote[] = + "[blink-gc] Reference pointer field %0 to a GC managed class" + " declared here:"; + +const char kUniquePtrToGCManagedClassNote[] = + "[blink-gc] std::unique_ptr field %0 to a GC managed class declared here:"; + +const char kRawPtrToTraceableClassNote[] = + "[blink-gc] Raw pointer field %0 to a traceable class declared here:"; + +const char kRefPtrToTraceableClassNote[] = + "[blink-gc] scoped_refptr field %0 to a traceable class declared here:"; + +const char kReferencePtrToTraceableClassNote[] = + "[blink-gc] Reference pointer field %0 to a traceable class" + " declared here:"; + +const char kUniquePtrToTraceableClassNote[] = + "[blink-gc] std::unique_ptr field %0 to a traceable class declared here:"; + +const char kWeakPtrToGCManagedClass[] = + "[blink-gc] WeakPtr or WeakPtrFactory field %0 to a GC managed class %1 " + "declared here (use WeakCell or WeakCellFactory instead):"; + +const char kGCedField[] = + "[blink-gc] Using GC managed class %1 as field %0 is not allowed (Allocate " + "with MakeGarbageCollected and use Member or Persistent instead):"; + +const char kGCedVar[] = + "[blink-gc] Using GC managed class %1 as variable %0 is not allowed " + "(Allocate with MakeGarbageCollected and use raw pointer instead):"; + +const char kTaskRunnerInGCManagedClassNote[] = + "[blink-gc] TaskRunnerTimer field %0 used within a garbage collected " + "context. " + "Consider using HeapTaskRunnerTimer instead."; + +const char kMojoRemoteInGCManagedClassNote[] = + "[blink-gc] mojo::Remote field %0 used within a garbage collected " + "context. " + "Consider using blink::HeapMojoRemote instead."; + +const char kMojoReceiverInGCManagedClassNote[] = + "[blink-gc] mojo::Receiver field %0 used within a garbage collected " + "context. " + "Consider using blink::HeapMojoAssociatedRemote instead."; + +const char kMojoAssociatedRemoteInGCManagedClassNote[] = + "[blink-gc] mojo::AssociatedRemote field %0 used within a garbage " + "collected context. " + "Consider using blink::HeapMojoAssociatedReceiver instead."; + +const char kMojoAssociatedReceiverInGCManagedClassNote[] = + "[blink-gc] mojo::AssociatedReceiver field %0 used within a garbage " + "collected context. " + "Consider using blink::HeapMojoReceiver instead."; + +const char kForbiddenFieldPartObjectClassNote[] = + "[blink-gc] From part object field %0 here:"; + +const char kMemberToGCUnmanagedClassNote[] = + "[blink-gc] Member field %0 to non-GC managed class declared here:"; + +const char kStackAllocatedFieldNote[] = + "[blink-gc] Stack-allocated field %0 declared here:"; + +const char kMemberInUnmanagedClassNote[] = + "[blink-gc] Member field %0 in unmanaged class declared here:"; + +const char kPtrToMemberInUnmanagedClassNote[] = + "[blink-gc] Pointer to Member field %0 in unmanaged class declared here:"; + +const char kPartObjectToGCDerivedClassNote[] = + "[blink-gc] Part-object field %0 to a GC derived class declared here:"; + +const char kPartObjectContainsGCRootNote[] = + "[blink-gc] Field %0 with embedded GC root in %1 declared here:"; + +const char kPartObjectContainsGCRootRefNote[] = + "[blink-gc] Field %0 with embedded reference to a GC root in %1 declared " + "here:"; + +const char kFieldContainsGCRootNote[] = + "[blink-gc] Field %0 defining a GC root declared here:"; + +const char kFieldContainsGCRootRefNote[] = + "[blink-gc] Field %0 defining reference to a GC root declared here:"; + +const char kOverriddenNonVirtualTrace[] = + "[blink-gc] Class %0 overrides non-virtual trace of base class %1."; + +const char kOverriddenNonVirtualTraceNote[] = + "[blink-gc] Non-virtual trace method declared here:"; + +const char kMissingTraceDispatchMethod[] = + "[blink-gc] Class %0 is missing manual trace dispatch."; + +const char kVirtualAndManualDispatch[] = + "[blink-gc] Class %0 contains or inherits virtual methods" + " but implements manual dispatching."; + +const char kMissingTraceDispatch[] = + "[blink-gc] Missing dispatch to class %0 in manual trace dispatch."; + +const char kMissingFinalizeDispatch[] = + "[blink-gc] Missing dispatch to class %0 in manual finalize dispatch."; + +const char kFinalizedFieldNote[] = + "[blink-gc] Potentially finalized field %0 declared here:"; + +const char kManualDispatchMethodNote[] = + "[blink-gc] Manual dispatch %0 declared here:"; + +const char kStackAllocatedDerivesGarbageCollected[] = + "[blink-gc] Stack-allocated class %0 derives class %1" + " which is garbage collected."; + +const char kClassOverridesNew[] = + "[blink-gc] Garbage collected class %0" + " is not permitted to override its new operator."; + +const char kClassDeclaresPureVirtualTrace[] = + "[blink-gc] Garbage collected class %0" + " is not permitted to declare a pure-virtual trace method."; + +const char kLeftMostBaseMustBePolymorphic[] = + "[blink-gc] Left-most base class %0 of derived class %1" + " must be polymorphic."; + +const char kBaseClassMustDeclareVirtualTrace[] = + "[blink-gc] Left-most base class %0 of derived class %1" + " must define a virtual trace method."; + +const char kClassMustCRTPItself[] = + "[blink-gc] GC base class %0 must be specialized with the derived class " + "%1."; + +const char kIteratorToGCManagedCollectionNote[] = + "[blink-gc] Iterator field %0 to a GC managed collection declared here:"; + +const char kTraceMethodOfStackAllocatedParentNote[] = + "[blink-gc] The stack allocated class %0 provides an unnecessary " + "trace method:"; + +const char kMemberInStackAllocated[] = + "[blink-gc] Member field %0 in stack allocated class declared here (use " + "raw pointer or reference instead):"; + +const char kMemberOnStack[] = + "[blink-gc] Member variable %0 declared on stack here (use raw pointer or " + "reference instead):"; + +const char kAdditionalPadding[] = + "[blink-gc] Additional padding causes the sizeof(%0) to grow by %1. " + "Consider reordering fields."; + +const char kTraceablePartObjectInUnmanaged[] = + "[blink-gc] Traceable part object field %0 found in unmanaged class:"; + +const char kUniquePtrUsedWithGC[] = + "[blink-gc] Disallowed use of %0 found; %1 is a garbage-collected type. " + "std::unique_ptr cannot hold garbage-collected objects."; + +const char kOptionalDeclUsedWithGC[] = + "[blink-gc] Disallowed optional field or variable of type %0 found; %1 is " + "a " + "garbage-collected or traceable " + "type. Optional fields and variables cannot hold garbage-collected or " + "traceable objects."; + +const char kOptionalNewExprUsedWithGC[] = + "[blink-gc] Disallowed new-expression of %0 found; %1 is a " + "garbage-collected or traceable " + "type. Optional fields cannot hold garbage-collected or traceable objects."; + +const char kOptionalDeclUsedWithMember[] = + "[blink-gc] Disallowed optional field of type %0 found; %1 is " + "a Member/WeakMember type. Optional fields and variables cannot hold " + "Members."; + +const char kOptionalNewExprUsedWithMember[] = + "[blink-gc] Disallowed new-expression of %0 found; %1 is a " + "Member/WeakMember type. Optional fields cannot hold Members."; + +const char kRawPtrOrRefDeclUsedWithGC[] = + "[blink-gc] Disallowed raw_ptr or raw_ref field or variable of type %0 " + "found; %1 is a " + "garbage-collected or traceable " + "type. Raw_ptr and raw_ref field and variable cannot hold " + "garbage-collected or " + "traceable objects."; + +const char kRawPtrOrRefNewExprUsedWithGC[] = + "[blink-gc] Disallowed new-expression of %0 found; %1 is a " + "garbage-collected or traceable " + "type. Raw_ptr and raw_ref fields cannot hold garbage-collected or " + "traceable objects."; + +const char kVariantUsedWithGC[] = + "[blink-gc] Disallowed construction of %0 found; %1 is a garbage-collected " + "type. Variant cannot hold garbage-collected objects."; + +const char kCollectionOfGced[] = + "[blink-gc] Disallowed collection %0 found; %1 is a " + "garbage-collected " + "type. Use heap collections to hold garbage-collected objects."; + +const char kCollectionOfMembers[] = + "[blink-gc] Disallowed collection %0 found; %1 is a " + "Member type. Use heap collections to hold Members."; + +} // namespace + +DiagnosticBuilder DiagnosticsReporter::ReportDiagnostic( + SourceLocation location, + unsigned diag_id) { + SourceManager& manager = instance_.getSourceManager(); + FullSourceLoc full_loc(location, manager); + return diagnostic_.Report(full_loc, diag_id); +} + +DiagnosticsReporter::DiagnosticsReporter( + clang::CompilerInstance& instance) + : instance_(instance), + diagnostic_(instance.getDiagnostics()) +{ + // Register warning/error messages. + diag_class_must_left_mostly_derive_gc_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kClassMustLeftMostlyDeriveGC); + diag_class_requires_trace_method_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kClassRequiresTraceMethod); + diag_base_requires_tracing_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kBaseRequiresTracing); + diag_fields_require_tracing_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kFieldsRequireTracing); + diag_fields_improperly_traced_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kFieldsImproperlyTraced); + diag_class_contains_invalid_fields_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kClassContainsInvalidFields); + diag_class_contains_gc_root_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot); + diag_class_contains_gc_root_ref_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kClassContainsGCRootRef); + diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kFinalizerAccessesFinalizedField); + diag_overridden_non_virtual_trace_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kOverriddenNonVirtualTrace); + diag_missing_trace_dispatch_method_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kMissingTraceDispatchMethod); + diag_virtual_and_manual_dispatch_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kVirtualAndManualDispatch); + diag_missing_trace_dispatch_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kMissingTraceDispatch); + diag_missing_finalize_dispatch_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kMissingFinalizeDispatch); + diag_stack_allocated_derives_gc_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kStackAllocatedDerivesGarbageCollected); + diag_class_overrides_new_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kClassOverridesNew); + diag_class_declares_pure_virtual_trace_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kClassDeclaresPureVirtualTrace); + diag_left_most_base_must_be_polymorphic_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kLeftMostBaseMustBePolymorphic); + diag_base_class_must_declare_virtual_trace_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kBaseClassMustDeclareVirtualTrace); + diag_class_must_crtp_itself_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kClassMustCRTPItself); + diag_iterator_to_gc_managed_collection_note_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kIteratorToGCManagedCollectionNote); + diag_trace_method_of_stack_allocated_parent_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kTraceMethodOfStackAllocatedParentNote); + diag_member_in_stack_allocated_class_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kMemberInStackAllocated); + diag_member_on_stack_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kMemberOnStack); + diag_additional_padding_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kAdditionalPadding); + diag_part_object_in_unmanaged_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kTraceablePartObjectInUnmanaged); + diag_weak_ptr_to_gc_managed_class_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kWeakPtrToGCManagedClass); + diag_gced_field_ = diagnostic_.getCustomDiagID(getErrorLevel(), kGCedField); + diag_gced_var_ = diagnostic_.getCustomDiagID(getErrorLevel(), kGCedVar); + // Register note messages. + diag_base_requires_tracing_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kBaseRequiresTracingNote); + diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kFieldRequiresTracingNote); + diag_field_should_not_be_traced_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kFieldShouldNotBeTracedNote); + diag_raw_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kRawPtrToGCManagedClassNote); + diag_ref_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kRefPtrToGCManagedClassNote); + diag_reference_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kReferencePtrToGCManagedClassNote); + diag_unique_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kUniquePtrToGCManagedClassNote); + diag_task_runner_timer_in_gc_class_note = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kTaskRunnerInGCManagedClassNote); + diag_mojo_remote_in_gc_class_note = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kMojoRemoteInGCManagedClassNote); + diag_mojo_receiver_in_gc_class_note = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kMojoReceiverInGCManagedClassNote); + diag_mojo_associated_remote_in_gc_class_note = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kMojoAssociatedRemoteInGCManagedClassNote); + diag_mojo_associated_receiver_in_gc_class_note = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kMojoAssociatedReceiverInGCManagedClassNote); + diag_forbidden_field_part_object_class_note = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kForbiddenFieldPartObjectClassNote); + diag_member_to_gc_unmanaged_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kMemberToGCUnmanagedClassNote); + diag_stack_allocated_field_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kStackAllocatedFieldNote); + diag_member_in_unmanaged_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kMemberInUnmanagedClassNote); + diag_ptr_to_member_in_unmanaged_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kPtrToMemberInUnmanagedClassNote); + diag_part_object_to_gc_derived_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kPartObjectToGCDerivedClassNote); + diag_part_object_contains_gc_root_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kPartObjectContainsGCRootNote); + diag_part_object_contains_gc_root_ref_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kPartObjectContainsGCRootRefNote); + diag_field_contains_gc_root_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kFieldContainsGCRootNote); + diag_field_contains_gc_root_ref_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kFieldContainsGCRootRefNote); + diag_finalized_field_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kFinalizedFieldNote); + diag_overridden_non_virtual_trace_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kOverriddenNonVirtualTraceNote); + diag_manual_dispatch_method_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kManualDispatchMethodNote); + diag_raw_ptr_to_traceable_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kRawPtrToTraceableClassNote); + diag_ref_ptr_to_traceable_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kRefPtrToTraceableClassNote); + diag_reference_ptr_to_traceable_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kReferencePtrToTraceableClassNote); + diag_unique_ptr_to_traceable_class_note_ = diagnostic_.getCustomDiagID( + DiagnosticsEngine::Note, kUniquePtrToTraceableClassNote); + + diag_unique_ptr_used_with_gc_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kUniquePtrUsedWithGC); + diag_optional_decl_used_with_gc_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kOptionalDeclUsedWithGC); + diag_optional_new_expr_used_with_gc_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kOptionalNewExprUsedWithGC); + diag_optional_decl_used_with_member_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kOptionalDeclUsedWithMember); + diag_optional_new_expr_used_with_member_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kOptionalNewExprUsedWithMember); + diag_raw_ptr_or_ref_decl_used_with_gc_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kRawPtrOrRefDeclUsedWithGC); + diag_raw_ptr_or_ref_new_expr_used_with_gc_ = diagnostic_.getCustomDiagID( + getErrorLevel(), kRawPtrOrRefNewExprUsedWithGC); + diag_variant_used_with_gc_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kVariantUsedWithGC); + diag_collection_of_gced_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kCollectionOfGced); + diag_collection_of_members_ = + diagnostic_.getCustomDiagID(getErrorLevel(), kCollectionOfMembers); +} + +bool DiagnosticsReporter::hasErrorOccurred() const +{ + return diagnostic_.hasErrorOccurred(); +} + +DiagnosticsEngine::Level DiagnosticsReporter::getErrorLevel() const { + return diagnostic_.getWarningsAsErrors() ? DiagnosticsEngine::Error + : DiagnosticsEngine::Warning; +} + +void DiagnosticsReporter::ClassMustLeftMostlyDeriveGC( + RecordInfo* info) { + ReportDiagnostic(info->record()->getInnerLocStart(), + diag_class_must_left_mostly_derive_gc_) + << info->record(); +} + +void DiagnosticsReporter::ClassRequiresTraceMethod(RecordInfo* info) { + ReportDiagnostic(info->record()->getInnerLocStart(), + diag_class_requires_trace_method_) + << info->record(); + + for (auto& base : info->GetBases()) + if (base.second.NeedsTracing().IsNeeded()) + NoteBaseRequiresTracing(&base.second); + + for (auto& field : info->GetFields()) + if (!field.second.IsProperlyTraced()) + NoteFieldRequiresTracing(info, field.first); +} + +void DiagnosticsReporter::BaseRequiresTracing( + RecordInfo* derived, + CXXMethodDecl* trace, + CXXRecordDecl* base) { + ReportDiagnostic(trace->getBeginLoc(), diag_base_requires_tracing_) + << base << derived->record(); +} + +void DiagnosticsReporter::FieldsImproperlyTraced( + RecordInfo* info, + CXXMethodDecl* trace) { + // Only mention untraceable in header diagnostic if they appear. + unsigned diag = diag_fields_require_tracing_; + for (auto& field : info->GetFields()) { + if (field.second.IsInproperlyTraced()) { + diag = diag_fields_improperly_traced_; + break; + } + } + ReportDiagnostic(trace->getBeginLoc(), diag) << info->record(); + for (auto& field : info->GetFields()) { + if (!field.second.IsProperlyTraced()) + NoteFieldRequiresTracing(info, field.first); + if (field.second.IsInproperlyTraced()) + NoteFieldShouldNotBeTraced(info, field.first); + } +} + +void DiagnosticsReporter::ClassContainsInvalidFields( + RecordInfo* info, + const CheckFieldsVisitor::Errors& errors) { + ReportDiagnostic(info->record()->getBeginLoc(), + diag_class_contains_invalid_fields_) + << info->record(); + + for (auto& error : errors) { + unsigned note; + if (error.second == CheckFieldsVisitor::kRawPtrToGCManaged) { + note = diag_raw_ptr_to_gc_managed_class_note_; + } else if (error.second == CheckFieldsVisitor::kRefPtrToGCManaged) { + note = diag_ref_ptr_to_gc_managed_class_note_; + } else if (error.second == CheckFieldsVisitor::kReferencePtrToGCManaged) { + note = diag_reference_ptr_to_gc_managed_class_note_; + } else if (error.second == CheckFieldsVisitor::kUniquePtrToGCManaged) { + note = diag_unique_ptr_to_gc_managed_class_note_; + } else if (error.second == CheckFieldsVisitor::kMemberToGCUnmanaged) { + note = diag_member_to_gc_unmanaged_class_note_; + } else if (error.second == CheckFieldsVisitor::kMemberInUnmanaged) { + note = diag_member_in_unmanaged_class_note_; + } else if (error.second == CheckFieldsVisitor::kPtrToMemberInUnmanaged) { + note = diag_ptr_to_member_in_unmanaged_class_note_; + } else if (error.second == CheckFieldsVisitor::kPtrFromHeapToStack) { + note = diag_stack_allocated_field_note_; + } else if (error.second == CheckFieldsVisitor::kGCDerivedPartObject) { + note = diag_part_object_to_gc_derived_class_note_; + } else if (error.second == CheckFieldsVisitor::kIteratorToGCManaged) { + note = diag_iterator_to_gc_managed_collection_note_; + } else if (error.second == CheckFieldsVisitor::kMemberInStackAllocated) { + note = diag_member_in_stack_allocated_class_; + } else if (error.second == + CheckFieldsVisitor::kTraceablePartObjectInUnmanaged) { + note = diag_part_object_in_unmanaged_; + } else if (error.second == CheckFieldsVisitor::kRawPtrToTraceable) { + note = diag_raw_ptr_to_traceable_class_note_; + } else if (error.second == CheckFieldsVisitor::kRefPtrToTraceable) { + note = diag_ref_ptr_to_traceable_class_note_; + } else if (error.second == CheckFieldsVisitor::kReferencePtrToTraceable) { + note = diag_reference_ptr_to_traceable_class_note_; + } else if (error.second == CheckFieldsVisitor::kUniquePtrToTraceable) { + note = diag_unique_ptr_to_traceable_class_note_; + } else { + llvm_unreachable("Unknown field error."); + } + NoteField(error.first, note); + } +} + +void DiagnosticsReporter::ClassContainsGCRoots( + RecordInfo* info, + const CheckGCRootsVisitor::Errors& errors) { + for (auto& error : errors) { + FieldPoint* point = nullptr; + for (FieldPoint* path : error) { + if (!point) { + point = path; + ReportDiagnostic(info->record()->getBeginLoc(), + diag_class_contains_gc_root_) + << info->record() << point->field(); + continue; + } + NotePartObjectContainsGCRoot(point); + point = path; + } + NoteFieldContainsGCRoot(point); + } +} + +void DiagnosticsReporter::ClassContainsGCRootRefs( + RecordInfo* info, + const CheckGCRootsVisitor::Errors& errors) { + for (auto& error : errors) { + FieldPoint* point = nullptr; + for (FieldPoint* path : error) { + if (!point) { + point = path; + ReportDiagnostic(info->record()->getBeginLoc(), + diag_class_contains_gc_root_ref_) + << info->record() << point->field(); + continue; + } + NotePartObjectContainsGCRootRef(point); + point = path; + } + NoteFieldContainsGCRootRef(point); + } +} + +void DiagnosticsReporter::ClassContainsForbiddenFields( + RecordInfo* info, + const CheckForbiddenFieldsVisitor::Errors& errors) { + ReportDiagnostic(info->record()->getBeginLoc(), + diag_class_contains_invalid_fields_) + << info->record(); + for (const auto& error : errors) { + for (FieldPoint* field : error.first) { + if (field == error.first.back()) { + break; + } + NoteField(field, diag_forbidden_field_part_object_class_note); + } + unsigned note; + if (error.second == + CheckForbiddenFieldsVisitor::Error::kTaskRunnerInGCManaged) { + note = diag_task_runner_timer_in_gc_class_note; + } else if (error.second == + CheckForbiddenFieldsVisitor::Error::kMojoRemoteInGCManaged) { + note = diag_mojo_remote_in_gc_class_note; + } else if (error.second == + CheckForbiddenFieldsVisitor::Error::kMojoReceiverInGCManaged) { + note = diag_mojo_receiver_in_gc_class_note; + } else if (error.second == CheckForbiddenFieldsVisitor::Error:: + kMojoAssociatedRemoteInGCManaged) { + note = diag_mojo_associated_remote_in_gc_class_note; + } else if (error.second == CheckForbiddenFieldsVisitor::Error:: + kMojoAssociatedReceiverInGCManaged) { + note = diag_mojo_associated_receiver_in_gc_class_note; + } else { + llvm_unreachable("Unknown field error."); + } + NoteField(error.first.back(), note); + } +} + +void DiagnosticsReporter::FinalizerAccessesFinalizedFields( + CXXMethodDecl* dtor, + const CheckFinalizerVisitor::Errors& errors) { + for (auto& error : errors) { + ReportDiagnostic(error.member->getBeginLoc(), + diag_finalizer_accesses_finalized_field_) + << dtor << error.field->field(); + NoteField(error.field, diag_finalized_field_note_); + } +} + +void DiagnosticsReporter::OverriddenNonVirtualTrace( + RecordInfo* info, + CXXMethodDecl* trace, + CXXMethodDecl* overridden) { + ReportDiagnostic(trace->getBeginLoc(), diag_overridden_non_virtual_trace_) + << info->record() << overridden->getParent(); + NoteOverriddenNonVirtualTrace(overridden); +} + +void DiagnosticsReporter::MissingTraceDispatchMethod(RecordInfo* info) { + ReportMissingDispatchMethod(info, diag_missing_trace_dispatch_method_); +} + +void DiagnosticsReporter::ReportMissingDispatchMethod( + RecordInfo* info, + unsigned error) { + ReportDiagnostic(info->record()->getInnerLocStart(), error) + << info->record(); +} + +void DiagnosticsReporter::VirtualAndManualDispatch( + RecordInfo* info, + CXXMethodDecl* dispatch) { + ReportDiagnostic(info->record()->getInnerLocStart(), + diag_virtual_and_manual_dispatch_) + << info->record(); + NoteManualDispatchMethod(dispatch); +} + +void DiagnosticsReporter::MissingTraceDispatch( + const FunctionDecl* dispatch, + RecordInfo* receiver) { + ReportMissingDispatch(dispatch, receiver, diag_missing_trace_dispatch_); +} + +void DiagnosticsReporter::MissingFinalizeDispatch( + const FunctionDecl* dispatch, + RecordInfo* receiver) { + ReportMissingDispatch(dispatch, receiver, diag_missing_finalize_dispatch_); +} + +void DiagnosticsReporter::ReportMissingDispatch( + const FunctionDecl* dispatch, + RecordInfo* receiver, + unsigned error) { + ReportDiagnostic(dispatch->getBeginLoc(), error) << receiver->record(); +} + +void DiagnosticsReporter::StackAllocatedDerivesGarbageCollected( + RecordInfo* info, + BasePoint* base) { + ReportDiagnostic(base->spec().getBeginLoc(), diag_stack_allocated_derives_gc_) + << info->record() << base->info()->record(); +} + +void DiagnosticsReporter::ClassOverridesNew( + RecordInfo* info, + CXXMethodDecl* newop) { + ReportDiagnostic(newop->getBeginLoc(), diag_class_overrides_new_) + << info->record(); +} + +void DiagnosticsReporter::ClassDeclaresPureVirtualTrace( + RecordInfo* info, + CXXMethodDecl* trace) { + ReportDiagnostic(trace->getBeginLoc(), + diag_class_declares_pure_virtual_trace_) + << info->record(); +} + +void DiagnosticsReporter::LeftMostBaseMustBePolymorphic( + RecordInfo* derived, + CXXRecordDecl* base) { + ReportDiagnostic(base->getBeginLoc(), + diag_left_most_base_must_be_polymorphic_) + << base << derived->record(); +} + +void DiagnosticsReporter::BaseClassMustDeclareVirtualTrace( + RecordInfo* derived, + CXXRecordDecl* base) { + ReportDiagnostic(base->getBeginLoc(), + diag_base_class_must_declare_virtual_trace_) + << base << derived->record(); +} + +void DiagnosticsReporter::ClassMustCRTPItself( + const RecordInfo* derived, + const CXXRecordDecl* base, + const CXXBaseSpecifier* base_spec) { + ReportDiagnostic(base_spec->getBeginLoc(), diag_class_must_crtp_itself_) + << base << derived->record(); +} + +void DiagnosticsReporter::TraceMethodForStackAllocatedClass( + RecordInfo* info, + CXXMethodDecl* trace) { + ReportDiagnostic(trace->getBeginLoc(), + diag_trace_method_of_stack_allocated_parent_) + << info->record(); +} + +void DiagnosticsReporter::NoteManualDispatchMethod(CXXMethodDecl* dispatch) { + ReportDiagnostic(dispatch->getBeginLoc(), diag_manual_dispatch_method_note_) + << dispatch; +} + +void DiagnosticsReporter::NoteBaseRequiresTracing(BasePoint* base) { + ReportDiagnostic(base->spec().getBeginLoc(), diag_base_requires_tracing_note_) + << base->info()->record(); +} + +void DiagnosticsReporter::NoteFieldRequiresTracing( + RecordInfo* holder, + FieldDecl* field) { + NoteField(field, diag_field_requires_tracing_note_); +} + +void DiagnosticsReporter::NoteFieldShouldNotBeTraced( + RecordInfo* holder, + FieldDecl* field) { + NoteField(field, diag_field_should_not_be_traced_note_); +} + +void DiagnosticsReporter::NotePartObjectContainsGCRoot(FieldPoint* point) { + FieldDecl* field = point->field(); + ReportDiagnostic(field->getBeginLoc(), + diag_part_object_contains_gc_root_note_) + << field << field->getParent(); +} + +void DiagnosticsReporter::NotePartObjectContainsGCRootRef(FieldPoint* point) { + FieldDecl* field = point->field(); + ReportDiagnostic(field->getBeginLoc(), + diag_part_object_contains_gc_root_ref_note_) + << field << field->getParent(); +} + +void DiagnosticsReporter::NoteFieldContainsGCRoot(FieldPoint* point) { + NoteField(point, diag_field_contains_gc_root_note_); +} + +void DiagnosticsReporter::NoteFieldContainsGCRootRef(FieldPoint* point) { + NoteField(point, diag_field_contains_gc_root_ref_note_); +} + +void DiagnosticsReporter::NoteField(FieldPoint* point, unsigned note) { + NoteField(point->field(), note); +} + +void DiagnosticsReporter::NoteField(FieldDecl* field, unsigned note) { + ReportDiagnostic(field->getBeginLoc(), note) << field; +} + +void DiagnosticsReporter::NoteOverriddenNonVirtualTrace( + CXXMethodDecl* overridden) { + ReportDiagnostic(overridden->getBeginLoc(), + diag_overridden_non_virtual_trace_note_) + << overridden; +} + +void DiagnosticsReporter::UniquePtrUsedWithGC( + const clang::Expr* expr, + const clang::FunctionDecl* bad_function, + const clang::CXXRecordDecl* gc_type) { + ReportDiagnostic(expr->getBeginLoc(), diag_unique_ptr_used_with_gc_) + << bad_function << gc_type << expr->getSourceRange(); +} + +void DiagnosticsReporter::OptionalDeclUsedWithGC( + const clang::Decl* decl, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* gc_type) { + ReportDiagnostic(decl->getBeginLoc(), diag_optional_decl_used_with_gc_) + << optional << gc_type << decl->getSourceRange(); +} + +void DiagnosticsReporter::OptionalNewExprUsedWithGC( + const clang::Expr* expr, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* gc_type) { + ReportDiagnostic(expr->getBeginLoc(), diag_optional_new_expr_used_with_gc_) + << optional << gc_type << expr->getSourceRange(); +} + +void DiagnosticsReporter::OptionalDeclUsedWithMember( + const clang::Decl* decl, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* member) { + ReportDiagnostic(decl->getBeginLoc(), diag_optional_decl_used_with_member_) + << optional << member << decl->getSourceRange(); +} + +void DiagnosticsReporter::OptionalNewExprUsedWithMember( + const clang::Expr* expr, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* member) { + ReportDiagnostic(expr->getBeginLoc(), + diag_optional_new_expr_used_with_member_) + << optional << member << expr->getSourceRange(); +} + +void DiagnosticsReporter::RawPtrOrRefDeclUsedWithGC( + const clang::Decl* decl, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* gc_type) { + ReportDiagnostic(decl->getBeginLoc(), diag_raw_ptr_or_ref_decl_used_with_gc_) + << optional << gc_type << decl->getSourceRange(); +} + +void DiagnosticsReporter::RawPtrOrRefNewExprUsedWithGC( + const clang::Expr* expr, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* gc_type) { + ReportDiagnostic(expr->getBeginLoc(), + diag_raw_ptr_or_ref_new_expr_used_with_gc_) + << optional << gc_type << expr->getSourceRange(); +} + +void DiagnosticsReporter::VariantUsedWithGC( + const clang::Expr* expr, + const clang::CXXRecordDecl* variant, + const clang::CXXRecordDecl* gc_type) { + ReportDiagnostic(expr->getBeginLoc(), diag_variant_used_with_gc_) + << variant << gc_type << expr->getSourceRange(); +} + +void DiagnosticsReporter::CollectionOfGCed( + const clang::Decl* decl, + const clang::CXXRecordDecl* collection, + const clang::CXXRecordDecl* gc_type) { + ReportDiagnostic(decl->getBeginLoc(), diag_collection_of_gced_) + << collection << gc_type << decl->getSourceRange(); +} + +void DiagnosticsReporter::CollectionOfGCed( + const clang::Expr* expr, + const clang::CXXRecordDecl* collection, + const clang::CXXRecordDecl* gc_type) { + ReportDiagnostic(expr->getBeginLoc(), diag_collection_of_gced_) + << collection << gc_type << expr->getSourceRange(); +} + +void DiagnosticsReporter::CollectionOfMembers( + const clang::Decl* decl, + const clang::CXXRecordDecl* collection, + const clang::CXXRecordDecl* member) { + ReportDiagnostic(decl->getBeginLoc(), diag_collection_of_members_) + << collection << member << decl->getSourceRange(); +} + +void DiagnosticsReporter::CollectionOfMembers( + const clang::Expr* expr, + const clang::CXXRecordDecl* collection, + const clang::CXXRecordDecl* member) { + ReportDiagnostic(expr->getBeginLoc(), diag_collection_of_members_) + << collection << member << expr->getSourceRange(); +} + +void DiagnosticsReporter::MemberOnStack(const clang::VarDecl* var) { + ReportDiagnostic(var->getBeginLoc(), diag_member_on_stack_) + << var->getName() << var->getSourceRange(); +} + +void DiagnosticsReporter::AdditionalPadding(const clang::RecordDecl* record, + size_t padding_size) { + ReportDiagnostic(record->getBeginLoc(), diag_additional_padding_) + << record->getName() << padding_size << record->getSourceRange(); +} + +void DiagnosticsReporter::WeakPtrToGCed(const clang::Decl* decl, + const clang::CXXRecordDecl* weak_ptr, + const clang::CXXRecordDecl* gc_type) { + ReportDiagnostic(decl->getBeginLoc(), diag_weak_ptr_to_gc_managed_class_) + << weak_ptr << gc_type << decl->getSourceRange(); +} + +void DiagnosticsReporter::GCedField(const clang::FieldDecl* field, + const clang::CXXRecordDecl* gctype) { + ReportDiagnostic(field->getBeginLoc(), diag_gced_field_) + << field << gctype << field->getSourceRange(); +} +void DiagnosticsReporter::GCedVar(const clang::VarDecl* var, + const clang::CXXRecordDecl* gctype) { + ReportDiagnostic(var->getBeginLoc(), diag_gced_var_) + << var << gctype << var->getSourceRange(); +} diff --git a/clang/blink_gc_plugin/DiagnosticsReporter.h b/clang/blink_gc_plugin/DiagnosticsReporter.h new file mode 100644 index 0000000000000000000000000000000000000000..20f61ff9edc1cd4837507457f2d6dc8b7e9123f6 --- /dev/null +++ b/clang/blink_gc_plugin/DiagnosticsReporter.h @@ -0,0 +1,217 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_BLINK_GC_PLUGIN_DIAGNOSTICS_REPORTER_H_ +#define TOOLS_BLINK_GC_PLUGIN_DIAGNOSTICS_REPORTER_H_ + +#include "CheckFieldsVisitor.h" +#include "CheckFinalizerVisitor.h" +#include "CheckForbiddenFieldsVisitor.h" +#include "CheckGCRootsVisitor.h" +#include "Config.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/CompilerInstance.h" + +class RecordInfo; + +// All error/warning reporting methods under one roof. +// +class DiagnosticsReporter { + public: + explicit DiagnosticsReporter(clang::CompilerInstance&); + + bool hasErrorOccurred() const; + clang::DiagnosticsEngine::Level getErrorLevel() const; + + void ClassMustLeftMostlyDeriveGC(RecordInfo* info); + void ClassRequiresTraceMethod(RecordInfo* info); + void BaseRequiresTracing(RecordInfo* derived, + clang::CXXMethodDecl* trace, + clang::CXXRecordDecl* base); + void FieldsImproperlyTraced(RecordInfo* info, + clang::CXXMethodDecl* trace); + void ClassContainsInvalidFields( + RecordInfo* info, + const CheckFieldsVisitor::Errors& errors); + void ClassContainsGCRoots(RecordInfo* info, + const CheckGCRootsVisitor::Errors& errors); + void ClassContainsGCRootRefs(RecordInfo* info, + const CheckGCRootsVisitor::Errors& errors); + void ClassContainsForbiddenFields( + RecordInfo* info, + const CheckForbiddenFieldsVisitor::Errors& errors); + void FinalizerAccessesFinalizedFields( + clang::CXXMethodDecl* dtor, + const CheckFinalizerVisitor::Errors& errors); + void ClassMustDeclareGCMixinTraceMethod(RecordInfo* info); + void OverriddenNonVirtualTrace(RecordInfo* info, + clang::CXXMethodDecl* trace, + clang::CXXMethodDecl* overridden); + void MissingTraceDispatchMethod(RecordInfo* info); + void VirtualAndManualDispatch(RecordInfo* info, + clang::CXXMethodDecl* dispatch); + void MissingTraceDispatch(const clang::FunctionDecl* dispatch, + RecordInfo* receiver); + void MissingFinalizeDispatch(const clang::FunctionDecl* dispatch, + RecordInfo* receiver); + void StackAllocatedDerivesGarbageCollected(RecordInfo* info, BasePoint* base); + void ClassOverridesNew(RecordInfo* info, clang::CXXMethodDecl* newop); + void ClassDeclaresPureVirtualTrace(RecordInfo* info, + clang::CXXMethodDecl* trace); + void LeftMostBaseMustBePolymorphic(RecordInfo* derived, + clang::CXXRecordDecl* base); + void BaseClassMustDeclareVirtualTrace(RecordInfo* derived, + clang::CXXRecordDecl* base); + void ClassMustCRTPItself(const RecordInfo* derived, + const clang::CXXRecordDecl* base, + const clang::CXXBaseSpecifier* base_spec); + void TraceMethodForStackAllocatedClass(RecordInfo* parent, + clang::CXXMethodDecl* trace); + + void NoteManualDispatchMethod(clang::CXXMethodDecl* dispatch); + void NoteBaseRequiresTracing(BasePoint* base); + void NoteFieldRequiresTracing(RecordInfo* holder, clang::FieldDecl* field); + void NoteFieldShouldNotBeTraced(RecordInfo* holder, clang::FieldDecl* field); + void NotePartObjectContainsGCRoot(FieldPoint* point); + void NotePartObjectContainsGCRootRef(FieldPoint* point); + void NoteFieldContainsGCRoot(FieldPoint* point); + void NoteFieldContainsGCRootRef(FieldPoint* point); + void NoteField(FieldPoint* point, unsigned note); + void NoteField(clang::FieldDecl* field, unsigned note); + void NoteOverriddenNonVirtualTrace(clang::CXXMethodDecl* overridden); + + // Used by FindBadPatterns. + void UniquePtrUsedWithGC(const clang::Expr* expr, + const clang::FunctionDecl* bad_function, + const clang::CXXRecordDecl* gc_type); + void OptionalDeclUsedWithGC(const clang::Decl* decl, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* gc_type); + void OptionalNewExprUsedWithGC(const clang::Expr* expr, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* gc_type); + void OptionalDeclUsedWithMember(const clang::Decl* decl, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* member); + void OptionalNewExprUsedWithMember(const clang::Expr* expr, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* member); + void RawPtrOrRefDeclUsedWithGC(const clang::Decl* decl, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* gc_type); + void RawPtrOrRefNewExprUsedWithGC(const clang::Expr* expr, + const clang::CXXRecordDecl* optional, + const clang::CXXRecordDecl* gc_type); + void VariantUsedWithGC(const clang::Expr* expr, + const clang::CXXRecordDecl* variant, + const clang::CXXRecordDecl* gc_type); + void CollectionOfGCed(const clang::Decl* decl, + const clang::CXXRecordDecl* collection, + const clang::CXXRecordDecl* gc_type); + void CollectionOfGCed(const clang::Expr* expr, + const clang::CXXRecordDecl* collection, + const clang::CXXRecordDecl* gc_type); + void CollectionOfMembers(const clang::Decl* decl, + const clang::CXXRecordDecl* collection, + const clang::CXXRecordDecl* gc_type); + void CollectionOfMembers(const clang::Expr* expr, + const clang::CXXRecordDecl* collection, + const clang::CXXRecordDecl* gc_type); + void MemberOnStack(const clang::VarDecl* var); + void AdditionalPadding(const clang::RecordDecl* var, size_t padding); + void WeakPtrToGCed(const clang::Decl* decl, + const clang::CXXRecordDecl* weak_ptr, + const clang::CXXRecordDecl* gc_type); + void GCedField(const clang::FieldDecl* field, + const clang::CXXRecordDecl* gctype); + void GCedVar(const clang::VarDecl* var, const clang::CXXRecordDecl* gctype); + + private: + clang::DiagnosticBuilder ReportDiagnostic( + clang::SourceLocation location, + unsigned diag_id); + + void ReportMissingDispatchMethod(RecordInfo* info, unsigned error); + void ReportMissingDispatch(const clang::FunctionDecl* dispatch, + RecordInfo* receiver, + unsigned error); + + clang::CompilerInstance& instance_; + clang::DiagnosticsEngine& diagnostic_; + + unsigned diag_class_must_left_mostly_derive_gc_; + unsigned diag_class_requires_trace_method_; + unsigned diag_base_requires_tracing_; + unsigned diag_fields_require_tracing_; + unsigned diag_fields_improperly_traced_; + unsigned diag_class_contains_invalid_fields_; + unsigned diag_class_contains_gc_root_; + unsigned diag_class_contains_gc_root_ref_; + unsigned diag_finalizer_accesses_finalized_field_; + unsigned diag_overridden_non_virtual_trace_; + unsigned diag_missing_trace_dispatch_method_; + unsigned diag_virtual_and_manual_dispatch_; + unsigned diag_missing_trace_dispatch_; + unsigned diag_missing_finalize_dispatch_; + unsigned diag_stack_allocated_derives_gc_; + unsigned diag_class_overrides_new_; + unsigned diag_class_declares_pure_virtual_trace_; + unsigned diag_left_most_base_must_be_polymorphic_; + unsigned diag_base_class_must_declare_virtual_trace_; + unsigned diag_class_must_crtp_itself_; + unsigned diag_weak_ptr_to_gc_managed_class_; + unsigned diag_gced_field_; + unsigned diag_gced_var_; + + unsigned diag_base_requires_tracing_note_; + unsigned diag_field_requires_tracing_note_; + unsigned diag_field_should_not_be_traced_note_; + unsigned diag_raw_ptr_to_gc_managed_class_note_; + unsigned diag_ref_ptr_to_gc_managed_class_note_; + unsigned diag_reference_ptr_to_gc_managed_class_note_; + unsigned diag_unique_ptr_to_gc_managed_class_note_; + unsigned diag_raw_ptr_to_traceable_class_note_; + unsigned diag_ref_ptr_to_traceable_class_note_; + unsigned diag_reference_ptr_to_traceable_class_note_; + unsigned diag_unique_ptr_to_traceable_class_note_; + unsigned diag_member_to_gc_unmanaged_class_note_; + unsigned diag_stack_allocated_field_note_; + unsigned diag_member_in_unmanaged_class_note_; + unsigned diag_ptr_to_member_in_unmanaged_class_note_; + unsigned diag_part_object_to_gc_derived_class_note_; + unsigned diag_part_object_contains_gc_root_note_; + unsigned diag_part_object_contains_gc_root_ref_note_; + unsigned diag_field_contains_gc_root_note_; + unsigned diag_field_contains_gc_root_ref_note_; + unsigned diag_finalized_field_note_; + unsigned diag_overridden_non_virtual_trace_note_; + unsigned diag_manual_dispatch_method_note_; + unsigned diag_iterator_to_gc_managed_collection_note_; + unsigned diag_trace_method_of_stack_allocated_parent_; + unsigned diag_member_in_stack_allocated_class_; + unsigned diag_member_on_stack_; + unsigned diag_additional_padding_; + unsigned diag_part_object_in_unmanaged_; + unsigned diag_task_runner_timer_in_gc_class_note; + unsigned diag_forbidden_field_part_object_class_note; + unsigned diag_mojo_remote_in_gc_class_note; + unsigned diag_mojo_receiver_in_gc_class_note; + unsigned diag_mojo_associated_remote_in_gc_class_note; + unsigned diag_mojo_associated_receiver_in_gc_class_note; + + unsigned diag_unique_ptr_used_with_gc_; + unsigned diag_optional_decl_used_with_gc_; + unsigned diag_optional_new_expr_used_with_gc_; + unsigned diag_optional_decl_used_with_member_; + unsigned diag_optional_new_expr_used_with_member_; + unsigned diag_raw_ptr_or_ref_decl_used_with_gc_; + unsigned diag_raw_ptr_or_ref_new_expr_used_with_gc_; + unsigned diag_variant_used_with_gc_; + unsigned diag_collection_of_gced_; + unsigned diag_collection_of_members_; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_DIAGNOSTICS_REPORTER_H_ diff --git a/clang/blink_gc_plugin/Edge.cpp b/clang/blink_gc_plugin/Edge.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2c9bec1bc0963b15b6ccf654b52bf4c45383de70 --- /dev/null +++ b/clang/blink_gc_plugin/Edge.cpp @@ -0,0 +1,137 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "Config.h" +#include "Edge.h" +#include "RecordInfo.h" + +TracingStatus Value::NeedsTracing(NeedsTracingOption option) { + return value_->NeedsTracing(option); +} + +bool Value::NeedsFinalization() { return value_->NeedsFinalization(); } +bool Collection::NeedsFinalization() { return info_->NeedsFinalization(); } +bool Collection::IsSTDCollection() { + return Config::IsSTDCollection(info_->name()); +} +std::string Collection::GetCollectionName() const { + return info_->name(); +} + +TracingStatus Collection::NeedsTracing(NeedsTracingOption) { + if (on_heap_) { + return TracingStatus::Needed(); + } + + // This will be handled by matchers. + if (IsSTDCollection()) { + if ((GetCollectionName() == "array") && !members_.empty()) { + Edge* type = members_.at(0); + if (type->IsMember() || type->IsWeakMember() || + type->IsTraceWrapperV8Reference()) { + return TracingStatus::Needed(); + } + } + return TracingStatus::Unknown(); + } + + // For off-heap collections, determine tracing status of members. + TracingStatus status = TracingStatus::Unneeded(); + for (Members::iterator it = members_.begin(); it != members_.end(); ++it) { + // Do a non-recursive test here since members could equal the holder. + status = status.LUB((*it)->NeedsTracing(kNonRecursive)); + } + return status; +} + +void RecursiveEdgeVisitor::AtValue(Value*) {} +void RecursiveEdgeVisitor::AtRawPtr(RawPtr*) {} +void RecursiveEdgeVisitor::AtRefPtr(RefPtr*) {} +void RecursiveEdgeVisitor::AtUniquePtr(UniquePtr*) {} +void RecursiveEdgeVisitor::AtMember(Member*) {} +void RecursiveEdgeVisitor::AtWeakMember(WeakMember*) {} +void RecursiveEdgeVisitor::AtPersistent(Persistent*) {} +void RecursiveEdgeVisitor::AtCrossThreadPersistent(CrossThreadPersistent*) {} +void RecursiveEdgeVisitor::AtCollection(Collection*) {} +void RecursiveEdgeVisitor::AtIterator(Iterator*) {} +void RecursiveEdgeVisitor::AtTraceWrapperV8Reference(TraceWrapperV8Reference*) { +} +void RecursiveEdgeVisitor::AtArrayEdge(ArrayEdge*) {} + +void RecursiveEdgeVisitor::VisitValue(Value* e) { + AtValue(e); +} + +void RecursiveEdgeVisitor::VisitRawPtr(RawPtr* e) { + AtRawPtr(e); + Enter(e); + e->ptr()->Accept(this); + Leave(); +} + +void RecursiveEdgeVisitor::VisitRefPtr(RefPtr* e) { + AtRefPtr(e); + Enter(e); + e->ptr()->Accept(this); + Leave(); +} + +void RecursiveEdgeVisitor::VisitUniquePtr(UniquePtr* e) { + AtUniquePtr(e); + Enter(e); + e->ptr()->Accept(this); + Leave(); +} + +void RecursiveEdgeVisitor::VisitMember(Member* e) { + AtMember(e); + Enter(e); + e->ptr()->Accept(this); + Leave(); +} + +void RecursiveEdgeVisitor::VisitWeakMember(WeakMember* e) { + AtWeakMember(e); + Enter(e); + e->ptr()->Accept(this); + Leave(); +} + +void RecursiveEdgeVisitor::VisitPersistent(Persistent* e) { + AtPersistent(e); + Enter(e); + e->ptr()->Accept(this); + Leave(); +} + +void RecursiveEdgeVisitor::VisitCrossThreadPersistent( + CrossThreadPersistent* e) { + AtCrossThreadPersistent(e); + Enter(e); + e->ptr()->Accept(this); + Leave(); +} + +void RecursiveEdgeVisitor::VisitCollection(Collection* e) { + AtCollection(e); + Enter(e); + e->AcceptMembers(this); + Leave(); +} + +void RecursiveEdgeVisitor::VisitIterator(Iterator* e) { + AtIterator(e); +} + +void RecursiveEdgeVisitor::VisitTraceWrapperV8Reference( + TraceWrapperV8Reference* e) { + AtTraceWrapperV8Reference(e); + Enter(e); + e->ptr()->Accept(this); + Leave(); +} + +void RecursiveEdgeVisitor::VisitArrayEdge(ArrayEdge* e) { + AtArrayEdge(e); +} diff --git a/clang/blink_gc_plugin/Edge.h b/clang/blink_gc_plugin/Edge.h new file mode 100644 index 0000000000000000000000000000000000000000..9b51056ba80b47ec93166e8b7afc878448fd59e6 --- /dev/null +++ b/clang/blink_gc_plugin/Edge.h @@ -0,0 +1,327 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_BLINK_GC_PLUGIN_EDGE_H_ +#define TOOLS_BLINK_GC_PLUGIN_EDGE_H_ + +#include +#include +#include +#include + +#include "TracingStatus.h" + +class RecordInfo; + +class Edge; +class Collection; +class CrossThreadPersistent; +class Iterator; +class Member; +class Persistent; +class RawPtr; +class RefPtr; +class UniquePtr; +class Value; +class WeakMember; +class TraceWrapperV8Reference; +class ArrayEdge; + +// Bare-bones visitor. +class EdgeVisitor { + public: + virtual ~EdgeVisitor() {} + virtual void VisitValue(Value*) {} + virtual void VisitRawPtr(RawPtr*) {} + virtual void VisitRefPtr(RefPtr*) {} + virtual void VisitUniquePtr(UniquePtr*) {} + virtual void VisitMember(Member*) {} + virtual void VisitWeakMember(WeakMember*) {} + virtual void VisitPersistent(Persistent*) {} + virtual void VisitCrossThreadPersistent(CrossThreadPersistent*) {} + virtual void VisitCollection(Collection*) {} + virtual void VisitIterator(Iterator*) {} + virtual void VisitTraceWrapperV8Reference(TraceWrapperV8Reference*) {} + virtual void VisitArrayEdge(ArrayEdge*) {} +}; + +// Recursive edge visitor. The traversed path is accessible in context. +class RecursiveEdgeVisitor : public EdgeVisitor { + public: + // Overrides that recursively walk the edges and record the path. + void VisitValue(Value*) override; + void VisitRawPtr(RawPtr*) override; + void VisitRefPtr(RefPtr*) override; + void VisitUniquePtr(UniquePtr*) override; + void VisitMember(Member*) override; + void VisitWeakMember(WeakMember*) override; + void VisitPersistent(Persistent*) override; + void VisitCrossThreadPersistent(CrossThreadPersistent*) override; + void VisitCollection(Collection*) override; + void VisitIterator(Iterator*) override; + void VisitTraceWrapperV8Reference(TraceWrapperV8Reference*) override; + void VisitArrayEdge(ArrayEdge*) override; + + protected: + typedef std::deque Context; + Context& context() { return context_; } + Edge* Parent() { return context_.empty() ? 0 : context_.front(); } + Edge* GrandParent() { + return Parent() ? (context_.size() > 1 ? context_[1] : nullptr) : nullptr; + } + void Enter(Edge* e) { return context_.push_front(e); } + void Leave() { context_.pop_front(); } + + // Default callback to overwrite in visitor subclass. + virtual void AtValue(Value*); + virtual void AtRawPtr(RawPtr*); + virtual void AtRefPtr(RefPtr*); + virtual void AtUniquePtr(UniquePtr*); + virtual void AtMember(Member*); + virtual void AtWeakMember(WeakMember*); + virtual void AtTraceWrapperV8Reference(TraceWrapperV8Reference*); + virtual void AtPersistent(Persistent*); + virtual void AtCrossThreadPersistent(CrossThreadPersistent*); + virtual void AtCollection(Collection*); + virtual void AtIterator(Iterator*); + virtual void AtArrayEdge(ArrayEdge*); + + private: + Context context_; +}; + +// Base class for all edges. +class Edge { + public: + enum NeedsTracingOption { kRecursive, kNonRecursive }; + enum LivenessKind { kWeak, kStrong, kRoot }; + + virtual ~Edge() {} + virtual LivenessKind Kind() = 0; + virtual void Accept(EdgeVisitor*) = 0; + virtual bool NeedsFinalization() = 0; + virtual TracingStatus NeedsTracing(NeedsTracingOption) { + return TracingStatus::Unknown(); + } + + virtual bool IsValue() { return false; } + virtual bool IsRawPtr() { return false; } + virtual bool IsRefPtr() { return false; } + virtual bool IsUniquePtr() { return false; } + virtual bool IsMember() { return false; } + virtual bool IsWeakMember() { return false; } + virtual bool IsCollection() { return false; } + virtual bool IsArray() { return false; } + virtual bool IsTraceWrapperV8Reference() { return false; } +}; + +// A value edge is a direct edge to some type, eg, part-object edges. +class Value : public Edge { + public: + explicit Value(RecordInfo* value) : value_(value) {}; + bool IsValue() override { return true; } + LivenessKind Kind() override { return kStrong; } + bool NeedsFinalization() override; + TracingStatus NeedsTracing(NeedsTracingOption) override; + void Accept(EdgeVisitor* visitor) override { visitor->VisitValue(this); } + RecordInfo* value() { return value_; } + + private: + RecordInfo* value_; +}; + +class ArrayEdge : public Edge { + public: + explicit ArrayEdge(Edge* value) : value_(value){}; + LivenessKind Kind() override { return kStrong; } + bool NeedsFinalization() override { return false; } + TracingStatus NeedsTracing(NeedsTracingOption option) override { + return value_->NeedsTracing(option); + } + void Accept(EdgeVisitor* visitor) override { visitor->VisitArrayEdge(this); } + Edge* element() { return value_; } + bool IsArray() override { return true; } + + private: + Edge* value_; +}; + +// Shared base for smart-pointer edges. +class PtrEdge : public Edge { + public: + ~PtrEdge() { delete ptr_; } + Edge* ptr() { return ptr_; } + protected: + PtrEdge(Edge* ptr) : ptr_(ptr) { + assert(ptr && "EdgePtr pointer must be non-null"); + } + private: + Edge* ptr_; +}; + +class RawPtr : public PtrEdge { + public: + RawPtr(Edge* ptr, bool is_ref_type) + : PtrEdge(ptr) + , is_ref_type_(is_ref_type) + { + } + + bool IsRawPtr() override { return true; } + LivenessKind Kind() override { return kWeak; } + bool NeedsFinalization() override { return false; } + TracingStatus NeedsTracing(NeedsTracingOption) override { + return TracingStatus::Illegal(); + } + void Accept(EdgeVisitor* visitor) override { visitor->VisitRawPtr(this); } + + bool HasReferenceType() { return is_ref_type_; } + private: + bool is_ref_type_; +}; + +class RefPtr : public PtrEdge { + public: + RefPtr(Edge* ptr, LivenessKind kind) : PtrEdge(ptr), kind_(kind) {} + bool IsRefPtr() override { return true; } + LivenessKind Kind() override { return kind_; } + bool NeedsFinalization() override { return true; } + TracingStatus NeedsTracing(NeedsTracingOption) override { + return TracingStatus::Illegal(); + } + void Accept(EdgeVisitor* visitor) override { visitor->VisitRefPtr(this); } + + private: + LivenessKind kind_; +}; + +class UniquePtr : public PtrEdge { + public: + explicit UniquePtr(Edge* ptr) : PtrEdge(ptr) { } + bool IsUniquePtr() override { return true; } + LivenessKind Kind() override { return kStrong; } + bool NeedsFinalization() override { return true; } + TracingStatus NeedsTracing(NeedsTracingOption) override { + return TracingStatus::Illegal(); + } + void Accept(EdgeVisitor* visitor) override { visitor->VisitUniquePtr(this); } +}; + +class Member : public PtrEdge { + public: + explicit Member(Edge* ptr) : PtrEdge(ptr) { } + bool IsMember() override { return true; } + LivenessKind Kind() override { return kStrong; } + bool NeedsFinalization() override { return false; } + TracingStatus NeedsTracing(NeedsTracingOption) override { + return TracingStatus::Needed(); + } + void Accept(EdgeVisitor* visitor) override { visitor->VisitMember(this); } +}; + +class WeakMember : public PtrEdge { + public: + explicit WeakMember(Edge* ptr) : PtrEdge(ptr) { } + bool IsWeakMember() override { return true; } + LivenessKind Kind() override { return kWeak; } + bool NeedsFinalization() override { return false; } + TracingStatus NeedsTracing(NeedsTracingOption) override { + return TracingStatus::Needed(); + } + void Accept(EdgeVisitor* visitor) override { visitor->VisitWeakMember(this); } +}; + +class Persistent : public PtrEdge { + public: + explicit Persistent(Edge* ptr) : PtrEdge(ptr) { } + LivenessKind Kind() override { return kRoot; } + bool NeedsFinalization() override { return true; } + TracingStatus NeedsTracing(NeedsTracingOption) override { + return TracingStatus::Illegal(); + } + void Accept(EdgeVisitor* visitor) override { visitor->VisitPersistent(this); } +}; + +class CrossThreadPersistent : public PtrEdge { + public: + explicit CrossThreadPersistent(Edge* ptr) : PtrEdge(ptr) { } + LivenessKind Kind() override { return kRoot; } + bool NeedsFinalization() override { return true; } + TracingStatus NeedsTracing(NeedsTracingOption) override { + return TracingStatus::Illegal(); + } + void Accept(EdgeVisitor* visitor) override { + visitor->VisitCrossThreadPersistent(this); + } +}; + +class TraceWrapperV8Reference : public PtrEdge { + public: + explicit TraceWrapperV8Reference(Edge* ptr) : PtrEdge(ptr) {} + bool IsTraceWrapperV8Reference() override { return true; } + LivenessKind Kind() override { return kStrong; } + bool NeedsFinalization() override { return true; } + TracingStatus NeedsTracing(NeedsTracingOption) override { + return TracingStatus::Needed(); + } + void Accept(EdgeVisitor* visitor) override { + visitor->VisitTraceWrapperV8Reference(this); + } +}; + +class Collection : public Edge { + public: + typedef std::vector Members; + Collection(RecordInfo* info, bool on_heap) : info_(info), on_heap_(on_heap) {} + ~Collection() { + for (Members::iterator it = members_.begin(); it != members_.end(); ++it) { + assert(*it && "Collection-edge members must be non-null"); + delete *it; + } + } + bool IsCollection() override { return true; } + bool IsSTDCollection(); + LivenessKind Kind() override { return kStrong; } + bool on_heap() { return on_heap_; } + Members& members() { return members_; } + void Accept(EdgeVisitor* visitor) override { visitor->VisitCollection(this); } + void AcceptMembers(EdgeVisitor* visitor) { + for (Members::iterator it = members_.begin(); it != members_.end(); ++it) + (*it)->Accept(visitor); + } + bool NeedsFinalization() override; + TracingStatus NeedsTracing(NeedsTracingOption) override; + std::string GetCollectionName() const; + + private: + RecordInfo* info_; + Members members_; + bool on_heap_; +}; + +// An iterator edge is a direct edge to some iterator type. +class Iterator : public Edge { + public: + Iterator(RecordInfo* info, bool on_heap) : info_(info), on_heap_(on_heap) {} + ~Iterator() {} + + void Accept(EdgeVisitor* visitor) override { visitor->VisitIterator(this); } + LivenessKind Kind() override { return kStrong; } + bool NeedsFinalization() override { return false; } + TracingStatus NeedsTracing(NeedsTracingOption) override { + if (on_heap_) + return TracingStatus::Illegal(); + return TracingStatus::Unneeded(); + } + + RecordInfo* info() const { return info_; } + + bool on_heap() const { return on_heap_; } + + private: + RecordInfo* info_; + bool on_heap_; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_EDGE_H_ diff --git a/clang/blink_gc_plugin/JsonWriter.h b/clang/blink_gc_plugin/JsonWriter.h new file mode 100644 index 0000000000000000000000000000000000000000..ee05f246101ea0198e9a4e7d5cf60150356b17e9 --- /dev/null +++ b/clang/blink_gc_plugin/JsonWriter.h @@ -0,0 +1,82 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_BLINK_GC_PLUGIN_JSON_WRITER_H_ +#define TOOLS_BLINK_GC_PLUGIN_JSON_WRITER_H_ + +#include +#include + +#include "llvm/Support/raw_ostream.h" + +// Helper to write information for the points-to graph. +class JsonWriter { + public: + static JsonWriter* from(std::unique_ptr os) { + return os ? new JsonWriter(std::move(os)) : 0; + } + void OpenList() { + Separator(); + *os_ << "["; + state_.push(false); + } + void OpenList(const std::string& key) { + Write(key); + *os_ << ":"; + OpenList(); + } + void CloseList() { + *os_ << "]"; + state_.pop(); + } + void OpenObject() { + Separator(); + *os_ << "{"; + state_.push(false); + } + void CloseObject() { + *os_ << "}\n"; + state_.pop(); + } + void Write(const size_t val) { + Separator(); + *os_ << val; + } + void Write(const std::string& val) { + Separator(); + *os_ << "\"" << Escape(val) << "\""; + } + void Write(const std::string& key, const size_t val) { + Separator(); + *os_ << "\"" << Escape(key) << "\":" << val; + } + void Write(const std::string& key, const std::string& val) { + Separator(); + *os_ << "\"" << Escape(key) << "\":\"" << Escape(val) << "\""; + } + private: + JsonWriter(std::unique_ptr os) : os_(std::move(os)) {} + void Separator() { + if (state_.empty()) + return; + if (state_.top()) { + *os_ << ","; + return; + } + state_.top() = true; + } + std::string Escape(const std::string& s) { + std::string copy = s; + size_t i = 0; + while ((i = copy.find('\\', i)) != std::string::npos) { + copy.replace(i, 1, "\\\\"); + i += 2; + } + return copy; + } + std::unique_ptr os_; + std::stack state_; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_JSON_WRITER_H_ diff --git a/clang/blink_gc_plugin/NeedsTracing.h b/clang/blink_gc_plugin/NeedsTracing.h new file mode 100644 index 0000000000000000000000000000000000000000..c030e0c6b0341091e9d48f45156520b1a39dd008 --- /dev/null +++ b/clang/blink_gc_plugin/NeedsTracing.h @@ -0,0 +1,31 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// NeedsTracing is a three-point value ordered by unneeded < unknown < needed. +// Unneeded means that the point definitively does not need to be traced. + +#ifndef TOOLS_BLINK_GC_PLUGIN_NEEDS_TRACING_H_ +#define TOOLS_BLINK_GC_PLUGIN_NEEDS_TRACING_H_ + +class NeedsTracing { + public: + static NeedsTracing Unneeded() { return kUnneeded; } + static NeedsTracing Unknown() { return kUnknown; } + static NeedsTracing Needed() { return kNeeded; } + bool IsUnneeded() { return value_ == kUnneeded; } + bool IsUnknown() { return value_ == kUnknown; } + bool IsNeeded() { return value_ == kNeeded; } + NeedsTracing LUB(const NeedsTracing& other) { + return value_ > other.value_ ? value_ : other.value_; + } + bool operator==(const NeedsTracing& other) { + return value_ == other.value_; + } + private: + enum Value { kUnneeded, kUnknown, kNeeded }; + NeedsTracing(Value value) : value_(value) {} + Value value_; +}; + +#endif // TOOLS_BLINK_GC_PLUGIN_NEEDS_TRACING_H_ diff --git a/clang/blink_gc_plugin/OWNERS b/clang/blink_gc_plugin/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..132b4d9011e8ad34662bba82d9f14e5ec28cc834 --- /dev/null +++ b/clang/blink_gc_plugin/OWNERS @@ -0,0 +1,5 @@ +bikineev@chromium.org +haraken@chromium.org +kouhei@chromium.org +mlippautz@chromium.org +omerkatz@chromium.org diff --git a/clang/blink_gc_plugin/README.md b/clang/blink_gc_plugin/README.md new file mode 100644 index 0000000000000000000000000000000000000000..df5d3e461b0522f7c1f219ed5fcb5857f8e18ad8 --- /dev/null +++ b/clang/blink_gc_plugin/README.md @@ -0,0 +1,56 @@ +# Clang Blink GC plugin + +A Clang plugin that checks various invariants of the Blink garbage +collection infrastructure. + +Consider this plugin as a best effort. The plugin is not meant to +be exhaustive and/or cover all potential GC issues. The plugin +covers the most common Blink GC pitfalls, but may miss other +potential (sometimes known) issues. + +## Build + +Clang is built using CMake. To run cmake, this script can be used: +```bash + ./tools/clang/scripts/build.py \ + --without-android \ + --without-fuchsia +``` + +The build directory is created into: `third_party/llvm-build/Release+Asserts/` +and you can build it again using: +```bash + ninja -C third_party/llvm-build/Release+Asserts/ +``` + + +## Run the tests + +```bash + ./tools/clang/blink_gc_plugin/tests/test.py \ + $(pwd)/third_party/llvm-build/Release+Asserts/bin/clang +``` + +## Using the plugin + +To enable the plugin, add the following to your BUILD.gn file: +```bash +cflags += [ + "-Xclang", + "-add-plugin", + "-Xclang", + "blink-gc-plugin", +] +``` + +To further enable specific plugin options, add the following: +```bash +cflags += [ + "-Xclang", + "-plugin-arg-blink-gc-plugin", + "-Xclang", + " { +public: + virtual void Trace(Visitor*) const; +}; + +class B : public A { + // Does not need Trace +}; + +class C : public B { +public: + void Trace(Visitor*) const; + +private: + Member m_a; +}; + +class D : public C { +public: + void Trace(Visitor*) const; + +private: + Member m_a; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/base_requires_tracing.txt b/clang/blink_gc_plugin/tests/base_requires_tracing.txt new file mode 100644 index 0000000000000000000000000000000000000000..d3a4d62a4e097b6dc75ed53abfa2b344eb208946 --- /dev/null +++ b/clang/blink_gc_plugin/tests/base_requires_tracing.txt @@ -0,0 +1,4 @@ +base_requires_tracing.cpp:11:1: warning: [blink-gc] Base class 'B' of derived class 'C' requires tracing. +void C::Trace(Visitor* visitor) const { +^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/class_does_not_require_finalization.h b/clang/blink_gc_plugin/tests/class_does_not_require_finalization.h new file mode 100644 index 0000000000000000000000000000000000000000..c6fc98ab1a948fe6591a9fdedc2b85e99b981838 --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_does_not_require_finalization.h @@ -0,0 +1,43 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CLASS_DOES_NOT_REQUIRE_FINALIZATION_BASE_H_ +#define CLASS_DOES_NOT_REQUIRE_FINALIZATION_BASE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class DoesNeedFinalizer : public GarbageCollected { + public: + ~DoesNeedFinalizer() { ; } + void Trace(Visitor*) const; +}; + +class DoesNotNeedFinalizer : public GarbageCollected { + public: + void Trace(Visitor*) const; +}; + +class DoesNotNeedFinalizer2 : public GarbageCollected { + public: + ~DoesNotNeedFinalizer2(); + void Trace(Visitor*) const; +}; + +class HasEmptyDtor { +public: + virtual ~HasEmptyDtor() { } +}; + +// If there are any virtual destructors involved, give up. + +class DoesNeedFinalizer2 : public GarbageCollected, + public HasEmptyDtor { + public: + void Trace(Visitor*) const; +}; +} + +#endif diff --git a/clang/blink_gc_plugin/tests/class_multiple_trace_bases.cpp b/clang/blink_gc_plugin/tests/class_multiple_trace_bases.cpp new file mode 100644 index 0000000000000000000000000000000000000000..78dbbfc126d286684787ffa5ef97f3b7d48f4b7f --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_multiple_trace_bases.cpp @@ -0,0 +1,21 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "class_multiple_trace_bases.h" + +namespace blink { + +void Base::Trace(Visitor* visitor) const {} + +void Mixin1::Trace(Visitor* visitor) const {} + +void Mixin2::Trace(Visitor* visitor) const {} + +// Missing: void Derived1::Trace(Visitor* visitor) const; + +void Derived2::Trace(Visitor* visitor) const { + Base::Trace(visitor); + Mixin1::Trace(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/class_multiple_trace_bases.h b/clang/blink_gc_plugin/tests/class_multiple_trace_bases.h new file mode 100644 index 0000000000000000000000000000000000000000..7e4c2cb206331e47cc192746151f5b42d7536804 --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_multiple_trace_bases.h @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CLASS_MULTIPLE_TRACE_BASES_H_ +#define CLASS_MULTIPLE_TRACE_BASES_H_ + +#include "heap/stubs.h" + +namespace blink { + +class Base : public GarbageCollected { +public: + virtual void Trace(Visitor*) const; +}; + +class Mixin1 : public GarbageCollectedMixin { +public: + void Trace(Visitor*) const; +}; + +class Mixin2 : public GarbageCollectedMixin { +public: + void Trace(Visitor*) const; +}; + +class Derived1 : public Base, public Mixin1 { + // Requires Trace method. +}; + +class Derived2 : public Base, public Mixin1, public Mixin2 { + void Trace(Visitor*) const override; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/class_multiple_trace_bases.txt b/clang/blink_gc_plugin/tests/class_multiple_trace_bases.txt new file mode 100644 index 0000000000000000000000000000000000000000..da140b7150992b9ddbfa41d25a94edfeab57b842 --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_multiple_trace_bases.txt @@ -0,0 +1,14 @@ +In file included from class_multiple_trace_bases.cpp:5: +./class_multiple_trace_bases.h:27:1: warning: [blink-gc] Class 'Derived1' requires a trace method. +class Derived1 : public Base, public Mixin1 { +^ +./class_multiple_trace_bases.h:27:18: note: [blink-gc] Untraced base class 'Base' declared here: +class Derived1 : public Base, public Mixin1 { + ^ +./class_multiple_trace_bases.h:27:31: note: [blink-gc] Untraced base class 'Mixin1' declared here: +class Derived1 : public Base, public Mixin1 { + ^ +class_multiple_trace_bases.cpp:17:1: warning: [blink-gc] Base class 'Mixin2' of derived class 'Derived2' requires tracing. +void Derived2::Trace(Visitor* visitor) const { +^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/class_must_crtp_itself.cpp b/clang/blink_gc_plugin/tests/class_must_crtp_itself.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32947c3c514c3431ba31cf54e6ac1ac7c9066257 --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_must_crtp_itself.cpp @@ -0,0 +1,24 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "heap/stubs.h" + +namespace blink { + +class WrongClass {}; + +class RightClass : GarbageCollected {}; + +template +struct TemplatedClass : GarbageCollected> {}; + +template struct TemplatedClass; + +template +struct TemplatedClass2 : GarbageCollected> {}; + +extern template struct TemplatedClass2; +template struct TemplatedClass2; + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/class_must_crtp_itself.txt b/clang/blink_gc_plugin/tests/class_must_crtp_itself.txt new file mode 100644 index 0000000000000000000000000000000000000000..c20f6320f84c6c13d228448f6acb27bf594ad379 --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_must_crtp_itself.txt @@ -0,0 +1,4 @@ +class_must_crtp_itself.cpp:11:20: warning: [blink-gc] GC base class 'GarbageCollected' must be specialized with the derived class 'RightClass'. +class RightClass : GarbageCollected {}; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/class_overrides_new.cpp b/clang/blink_gc_plugin/tests/class_overrides_new.cpp new file mode 100644 index 0000000000000000000000000000000000000000..08c53f7f197a2d6ec5749782ab319406d5c4eedd --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_overrides_new.cpp @@ -0,0 +1,7 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "class_overrides_new.h" + +// Nothing to define. diff --git a/clang/blink_gc_plugin/tests/class_overrides_new.h b/clang/blink_gc_plugin/tests/class_overrides_new.h new file mode 100644 index 0000000000000000000000000000000000000000..a318fa653d00a82837a8c448386632ca284ffb7a --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_overrides_new.h @@ -0,0 +1,31 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CLASS_OVERRIDES_NEW_H_ +#define CLASS_OVERRIDES_NEW_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { + public: + void* operator new(size_t); + void Trace(Visitor*) const {} +}; + +class HeapObjectBase : public GarbageCollected { + public: + virtual ~HeapObjectBase() = default; + virtual void Trace(Visitor*) const {} +}; + +class HeapObjectDerived : public HeapObjectBase { + public: + void* operator new(size_t); + void Trace(Visitor*) const override; +}; +} + +#endif diff --git a/clang/blink_gc_plugin/tests/class_overrides_new.txt b/clang/blink_gc_plugin/tests/class_overrides_new.txt new file mode 100644 index 0000000000000000000000000000000000000000..6e3c079a11d5e57a113b0b853f9b193416994ef1 --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_overrides_new.txt @@ -0,0 +1,8 @@ +In file included from class_overrides_new.cpp:5: +./class_overrides_new.h:14:3: warning: [blink-gc] Garbage collected class 'HeapObject' is not permitted to override its new operator. + void* operator new(size_t); + ^ +./class_overrides_new.h:26:3: warning: [blink-gc] Garbage collected class 'HeapObjectDerived' is not permitted to override its new operator. + void* operator new(size_t); + ^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/class_requires_trace_method.cpp b/clang/blink_gc_plugin/tests/class_requires_trace_method.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f99bc8cb78eb9df145096ebfddf422195cb3822c --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_requires_trace_method.cpp @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "class_requires_trace_method.h" + +namespace blink { + +void Mixin2::Trace(Visitor* visitor) const { + Mixin::Trace(visitor); +} + +void Mixin3::Trace(Visitor* visitor) const { + Mixin::Trace(visitor); +} + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/class_requires_trace_method.h b/clang/blink_gc_plugin/tests/class_requires_trace_method.h new file mode 100644 index 0000000000000000000000000000000000000000..6e283a13a0c63649b8c10b734e832a202d7eab22 --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_requires_trace_method.h @@ -0,0 +1,56 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CLASS_REQUIRES_TRACE_METHOD_H_ +#define CLASS_REQUIRES_TRACE_METHOD_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject; + +class PartObject { + DISALLOW_NEW(); +private: + Member m_obj; +}; + +class HeapObject : public GarbageCollected { +private: + PartObject m_part; +}; + +class Mixin : public GarbageCollectedMixin { +public: + virtual void Trace(Visitor*) const override; + Member m_self; +}; + +class HeapObjectMixin : public GarbageCollected, public Mixin { +}; + +class Mixin2 : public Mixin { +public: + virtual void Trace(Visitor*) const override; +}; + +class HeapObjectMixin2 + : public GarbageCollected, public Mixin2 { +}; + +class Mixin3 : public Mixin { +public: + virtual void Trace(Visitor*) const override; +}; + +class HeapObjectMixin3 + : public GarbageCollected, public Mixin { +public: + virtual void Trace(Visitor*) const override; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/class_requires_trace_method.txt b/clang/blink_gc_plugin/tests/class_requires_trace_method.txt new file mode 100644 index 0000000000000000000000000000000000000000..de6fd949adda1b2c73e5762bac61a2800f7caea0 --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_requires_trace_method.txt @@ -0,0 +1,14 @@ +In file included from class_requires_trace_method.cpp:5: +./class_requires_trace_method.h:14:1: warning: [blink-gc] Class 'PartObject' requires a trace method. +class PartObject { +^ +./class_requires_trace_method.h:17:5: note: [blink-gc] Untraced field 'm_obj' declared here: + Member m_obj; + ^ +./class_requires_trace_method.h:20:1: warning: [blink-gc] Class 'HeapObject' requires a trace method. +class HeapObject : public GarbageCollected { +^ +./class_requires_trace_method.h:22:5: note: [blink-gc] Untraced field 'm_part' declared here: + PartObject m_part; + ^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/class_requires_trace_method_tmpl.cpp b/clang/blink_gc_plugin/tests/class_requires_trace_method_tmpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..27a9f2dd1ff654eacc554173337220c7906f1f94 --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_requires_trace_method_tmpl.cpp @@ -0,0 +1,15 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "class_requires_trace_method_tmpl.h" + +namespace blink { + +// Does not need a Trace method. +class NoTrace : public TemplatedObject { }; + +// Needs a Trace method. +class NeedsTrace : public TemplatedObject { }; + +} diff --git a/clang/blink_gc_plugin/tests/class_requires_trace_method_tmpl.h b/clang/blink_gc_plugin/tests/class_requires_trace_method_tmpl.h new file mode 100644 index 0000000000000000000000000000000000000000..09b3ac70d65f7164e498f091ff224435b7d1aa8d --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_requires_trace_method_tmpl.h @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CLASS_REQUIRES_TRACE_METHOD_TMPL_H_ +#define CLASS_REQUIRES_TRACE_METHOD_TMPL_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { }; + +class PartObjectA { + DISALLOW_NEW(); +}; + +class PartObjectB { + DISALLOW_NEW(); +public: + void Trace(Visitor* visitor) const { visitor->Trace(m_obj); } + +private: + Member m_obj; +}; + +template +class TemplatedObject { + DISALLOW_NEW(); + + private: + T m_part; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/class_requires_trace_method_tmpl.txt b/clang/blink_gc_plugin/tests/class_requires_trace_method_tmpl.txt new file mode 100644 index 0000000000000000000000000000000000000000..e20a4ca88ba6823e576d5feada09374d55a0fa3e --- /dev/null +++ b/clang/blink_gc_plugin/tests/class_requires_trace_method_tmpl.txt @@ -0,0 +1,8 @@ +In file included from class_requires_trace_method_tmpl.cpp:5: +./class_requires_trace_method_tmpl.h:28:1: warning: [blink-gc] Class 'TemplatedObject' requires a trace method. +class TemplatedObject { +^ +./class_requires_trace_method_tmpl.h:32:3: note: [blink-gc] Untraced field 'm_part' declared here: + T m_part; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/crash_on_invalid.cpp b/clang/blink_gc_plugin/tests/crash_on_invalid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8ecb66de45741881a490039caa55844a4281182 --- /dev/null +++ b/clang/blink_gc_plugin/tests/crash_on_invalid.cpp @@ -0,0 +1,7 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crash_on_invalid.h" + +// Nothing to define. diff --git a/clang/blink_gc_plugin/tests/crash_on_invalid.h b/clang/blink_gc_plugin/tests/crash_on_invalid.h new file mode 100644 index 0000000000000000000000000000000000000000..38e9229be297fd297a937d13daf3627ccd159f7e --- /dev/null +++ b/clang/blink_gc_plugin/tests/crash_on_invalid.h @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Regression test for http://crbug.com/421958 + +#ifndef CRASH_ON_INVALID_H_ +#define CRASH_ON_INVALID_H_ + +namespace blink { + +class Visitor; +class GamepadCommon {}; +class ScriptWrappable {}; + +class Gamepad final : public GarbageCollected, + public GamepadCommon, + public ScriptWrappable { + public: + virtual const WrapperTypeInfo* wrapperTypeInfo() const {} + void Trace(Visitor*) const; +}; +} + +#endif diff --git a/clang/blink_gc_plugin/tests/crash_on_invalid.txt b/clang/blink_gc_plugin/tests/crash_on_invalid.txt new file mode 100644 index 0000000000000000000000000000000000000000..fa50bfc6a1f7ca80fc1df5f53794ad02a2d1149e --- /dev/null +++ b/clang/blink_gc_plugin/tests/crash_on_invalid.txt @@ -0,0 +1,8 @@ +In file included from crash_on_invalid.cpp:5: +./crash_on_invalid.h:16:30: error: no template named 'GarbageCollected' +class Gamepad final : public GarbageCollected, + ^ +./crash_on_invalid.h:20:17: error: unknown type name 'WrapperTypeInfo' + virtual const WrapperTypeInfo* wrapperTypeInfo() const {} + ^ +2 errors generated. diff --git a/clang/blink_gc_plugin/tests/cycle_ptrs.cpp b/clang/blink_gc_plugin/tests/cycle_ptrs.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5d30933581ddad4dc89ee13e76bbf7f9c26a2151 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_ptrs.cpp @@ -0,0 +1,16 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cycle_ptrs.h" + +namespace blink { + +void A::Trace(Visitor* visitor) const { + visitor->Trace(m_b); +} + +void B::Trace(Visitor* visitor) const { + visitor->Trace(m_a); +} +} diff --git a/clang/blink_gc_plugin/tests/cycle_ptrs.flags b/clang/blink_gc_plugin/tests/cycle_ptrs.flags new file mode 100644 index 0000000000000000000000000000000000000000..a55c2f097c05fee091dd6987c499144a14f58c98 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_ptrs.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-blink-gc-plugin -Xclang dump-graph \ No newline at end of file diff --git a/clang/blink_gc_plugin/tests/cycle_ptrs.h b/clang/blink_gc_plugin/tests/cycle_ptrs.h new file mode 100644 index 0000000000000000000000000000000000000000..c00d0e7d301808ac1a271f5e2e6a40c9df06ffe6 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_ptrs.h @@ -0,0 +1,56 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CYCLE_PTRS_H_ +#define CYCLE_PTRS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class B; +class C; +class D; +class E; + +// This contains a leaking cycle: +// E -per-> A -mem-> B -ref-> C -own-> D -own-vec-> E + +// The traced cycle from A -> B -> A does not leak. + +class A : public GarbageCollected { +public: + virtual void Trace(Visitor*) const; + +private: + Member m_b; +}; + +class B : public GarbageCollected { + public: + virtual void Trace(Visitor*) const; + + private: + Member m_a; + scoped_refptr m_c; +}; + +class C : public RefCounted { +private: + std::unique_ptr m_d; +}; + +class D { +private: + Vector > m_es; +}; + +class E { +private: + Persistent m_a; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/cycle_ptrs.txt b/clang/blink_gc_plugin/tests/cycle_ptrs.txt new file mode 100644 index 0000000000000000000000000000000000000000..0ad59f9ba4808b24cfe729dcd7419d226b5d7e73 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_ptrs.txt @@ -0,0 +1,8 @@ + +Found a potentially leaking cycle starting from a GC root: +./cycle_ptrs.h:51:5: blink::E (m_a) => blink::A +./cycle_ptrs.h:27:5: blink::A (m_b) => blink::B +./cycle_ptrs.h:36:3: blink::B (m_c) => blink::C +./cycle_ptrs.h:41:5: blink::C (m_d) => blink::D +./cycle_ptrs.h:46:5: blink::D (m_es) => blink::E + diff --git a/clang/blink_gc_plugin/tests/cycle_sub.cpp b/clang/blink_gc_plugin/tests/cycle_sub.cpp new file mode 100644 index 0000000000000000000000000000000000000000..659e0846e545983f2297f4d4d78f2fcc4f546937 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_sub.cpp @@ -0,0 +1,13 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cycle_sub.h" + +namespace blink { + +void B::Trace(Visitor* visitor) const { + visitor->Trace(m_c); + A::Trace(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/cycle_sub.flags b/clang/blink_gc_plugin/tests/cycle_sub.flags new file mode 100644 index 0000000000000000000000000000000000000000..a55c2f097c05fee091dd6987c499144a14f58c98 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_sub.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-blink-gc-plugin -Xclang dump-graph \ No newline at end of file diff --git a/clang/blink_gc_plugin/tests/cycle_sub.h b/clang/blink_gc_plugin/tests/cycle_sub.h new file mode 100644 index 0000000000000000000000000000000000000000..3946778d270e5983da753a5af2fd05d2bc9fc2f7 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_sub.h @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CYCLE_SUB_H_ +#define CYCLE_SUB_H_ + +#include "heap/stubs.h" + +namespace blink { + +class C; + +// This contains a leaking cycle: +// C -per-> A -sub-> B -ref-> C + +class A : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +class B : public A { +public: + virtual void Trace(Visitor*) const; + +private: + scoped_refptr m_c; +}; + +class C : public RefCounted { +private: + Persistent m_a; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/cycle_sub.txt b/clang/blink_gc_plugin/tests/cycle_sub.txt new file mode 100644 index 0000000000000000000000000000000000000000..0ba2ae67b7fbf17f944c88413cae2b09fae63ac4 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_sub.txt @@ -0,0 +1,6 @@ + +Found a potentially leaking cycle starting from a GC root: +./cycle_sub.h:32:5: blink::C (m_a) => blink::A +./cycle_sub.h:22:11: blink::A () => blink::B +./cycle_sub.h:27:5: blink::B (m_c) => blink::C + diff --git a/clang/blink_gc_plugin/tests/cycle_super.cpp b/clang/blink_gc_plugin/tests/cycle_super.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bdf249383a88ad929bf7efdb1674450420cb2735 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_super.cpp @@ -0,0 +1,20 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cycle_super.h" + +namespace blink { + +void A::Trace(Visitor* visitor) const { + visitor->Trace(m_d); +} + +void B::Trace(Visitor* visitor) const { + A::Trace(visitor); +} + +void C::Trace(Visitor* visitor) const { + B::Trace(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/cycle_super.flags b/clang/blink_gc_plugin/tests/cycle_super.flags new file mode 100644 index 0000000000000000000000000000000000000000..a55c2f097c05fee091dd6987c499144a14f58c98 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_super.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-blink-gc-plugin -Xclang dump-graph \ No newline at end of file diff --git a/clang/blink_gc_plugin/tests/cycle_super.h b/clang/blink_gc_plugin/tests/cycle_super.h new file mode 100644 index 0000000000000000000000000000000000000000..533d8191e0353e924a4259661b0b0d818a1fa84c --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_super.h @@ -0,0 +1,42 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CYCLE_SUPER_H_ +#define CYCLE_SUPER_H_ + +#include "heap/stubs.h" + +namespace blink { + +class D; + +// This contains a leaking cycle: +// D -per-> C -sup-> B -sup-> A -ref-> D + +class A : public GarbageCollected { + public: + virtual void Trace(Visitor*) const; + + private: + scoped_refptr m_d; +}; + +class B : public A { +public: + virtual void Trace(Visitor*) const; +}; + +class C : public B { +public: + virtual void Trace(Visitor*) const; +}; + +class D : public RefCounted { +private: + Persistent m_c; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/cycle_super.txt b/clang/blink_gc_plugin/tests/cycle_super.txt new file mode 100644 index 0000000000000000000000000000000000000000..62b7b98fc2f6dcf0ef6b86edd37d7a2e1140855c --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_super.txt @@ -0,0 +1,5 @@ + +Found a potentially leaking cycle starting from a GC root: +./cycle_super.h:37:5: blink::D (m_c) => blink::C +./cycle_super.h:22:3: blink::C (blink::B <: blink::A <: m_d) => blink::D + diff --git a/clang/blink_gc_plugin/tests/cycle_super_neg.cpp b/clang/blink_gc_plugin/tests/cycle_super_neg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fe9f80f3782323c3a046c7cd54b067e52077f9aa --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_super_neg.cpp @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cycle_super_neg.h" + +namespace blink { + +void B::Trace(Visitor* visitor) const { + A::Trace(visitor); +} + +void D::Trace(Visitor* visitor) const { + visitor->Trace(m_c); + A::Trace(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/cycle_super_neg.flags b/clang/blink_gc_plugin/tests/cycle_super_neg.flags new file mode 100644 index 0000000000000000000000000000000000000000..a55c2f097c05fee091dd6987c499144a14f58c98 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_super_neg.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-blink-gc-plugin -Xclang dump-graph \ No newline at end of file diff --git a/clang/blink_gc_plugin/tests/cycle_super_neg.h b/clang/blink_gc_plugin/tests/cycle_super_neg.h new file mode 100644 index 0000000000000000000000000000000000000000..463d8c09023d551f29ed1ff5137e51df2efa6836 --- /dev/null +++ b/clang/blink_gc_plugin/tests/cycle_super_neg.h @@ -0,0 +1,45 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CYCLE_SUPER_NEG_H_ +#define CYCLE_SUPER_NEG_H_ + +#include "heap/stubs.h" + +namespace blink { + +class C; + +// The chain: +// C -per-> B -sup-> A -sub-> D -ref-> C +// is not a leaking cycle, because the super-class relationship +// should not transitively imply sub-class relationships. +// I.e. B -/-> D + +class A : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +class B : public A { +public: + virtual void Trace(Visitor*) const; +}; + +class C : public RefCounted { +private: + Persistent m_b; +}; + +class D : public A { +public: + virtual void Trace(Visitor*) const; + +private: + scoped_refptr m_c; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/cycle_super_neg.txt b/clang/blink_gc_plugin/tests/cycle_super_neg.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/blink_gc_plugin/tests/destructor_access_finalized_field.cpp b/clang/blink_gc_plugin/tests/destructor_access_finalized_field.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d103381ec70ac69af42a17e36d72a1f6f172a85f --- /dev/null +++ b/clang/blink_gc_plugin/tests/destructor_access_finalized_field.cpp @@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "destructor_access_finalized_field.h" + +namespace blink { + +HeapObject::~HeapObject() +{ + // Valid access to fields. + if (m_ref->foo() && !m_obj) { + m_objs.size(); + m_part.obj(); + } + + // Invalid access to fields. + bar(m_obj); + m_obj->foo(); + m_objs[0]; +} + +void HeapObject::Trace(Visitor* visitor) const { + visitor->Trace(m_obj); + visitor->Trace(m_objs); + visitor->Trace(m_part); +} + +void PartOther::Trace(Visitor* visitor) const { + visitor->Trace(m_obj); +} +} diff --git a/clang/blink_gc_plugin/tests/destructor_access_finalized_field.h b/clang/blink_gc_plugin/tests/destructor_access_finalized_field.h new file mode 100644 index 0000000000000000000000000000000000000000..8db5685756040e8c98b7f69275736b67732247c5 --- /dev/null +++ b/clang/blink_gc_plugin/tests/destructor_access_finalized_field.h @@ -0,0 +1,46 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DESTRUCTOR_ACCESS_FINALIZED_FIELD_H_ +#define DESTRUCTOR_ACCESS_FINALIZED_FIELD_H_ + +#include "heap/stubs.h" + +namespace blink { + +class Other : public RefCounted { +public: + bool foo() { return true; } +}; + +class HeapObject; + +class PartOther { + DISALLOW_NEW(); + + public: + void Trace(Visitor*) const; + + HeapObject* obj() { return m_obj; } + + private: + Member m_obj; +}; + +class HeapObject : public GarbageCollected { + public: + ~HeapObject(); + void Trace(Visitor*) const; + bool foo() { return true; } + void bar(HeapObject*) {} + + private: + scoped_refptr m_ref; + Member m_obj; + HeapVector> m_objs; + PartOther m_part; +}; +} + +#endif diff --git a/clang/blink_gc_plugin/tests/destructor_access_finalized_field.txt b/clang/blink_gc_plugin/tests/destructor_access_finalized_field.txt new file mode 100644 index 0000000000000000000000000000000000000000..edaa3adc4a982b324fcb766a187d6d74155b5a92 --- /dev/null +++ b/clang/blink_gc_plugin/tests/destructor_access_finalized_field.txt @@ -0,0 +1,19 @@ +destructor_access_finalized_field.cpp:18:9: warning: [blink-gc] Finalizer '~HeapObject' accesses potentially finalized field 'm_obj'. + bar(m_obj); + ^ +./destructor_access_finalized_field.h:40:3: note: [blink-gc] Potentially finalized field 'm_obj' declared here: + Member m_obj; + ^ +destructor_access_finalized_field.cpp:19:5: warning: [blink-gc] Finalizer '~HeapObject' accesses potentially finalized field 'm_obj'. + m_obj->foo(); + ^ +./destructor_access_finalized_field.h:40:3: note: [blink-gc] Potentially finalized field 'm_obj' declared here: + Member m_obj; + ^ +destructor_access_finalized_field.cpp:20:5: warning: [blink-gc] Finalizer '~HeapObject' accesses potentially finalized field 'm_objs'. + m_objs[0]; + ^ +./destructor_access_finalized_field.h:41:3: note: [blink-gc] Potentially finalized field 'm_objs' declared here: + HeapVector> m_objs; + ^ +3 warnings generated. diff --git a/clang/blink_gc_plugin/tests/extra_padding.cpp b/clang/blink_gc_plugin/tests/extra_padding.cpp new file mode 100644 index 0000000000000000000000000000000000000000..56c81f935435fd072657b6a45e4eb31e8d0c26ef --- /dev/null +++ b/clang/blink_gc_plugin/tests/extra_padding.cpp @@ -0,0 +1,127 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// The test relies on a 64bit target (test.py sets the triple explicitly). + +#include "heap/stubs.h" + +namespace blink { + +class Object : public GarbageCollected { + void Trace(Visitor*) const {} +}; + +namespace { + +struct DisallowNewWithPadding { + DISALLOW_NEW(); + + public: + virtual void Trace(Visitor* v) const { + v->Trace(a); + v->Trace(b); + } + + // The plugin should warn that reordering would make sense here. + Member a; + void* raw; + Member b; +}; + +struct DisallowNewWithoutPadding { + DISALLOW_NEW(); + + public: + virtual void Trace(Visitor* v) const { + v->Trace(a); + v->Trace(b); + } + + // The plugin shouldn't warn, e.g. reordering wouldn't eliminate padding. + Member a; + Member b; + void* raw; +}; + +// Don't warn for templates until instantiated. +template +struct DisallowNewWithPaddingTemplate { + DISALLOW_NEW(); + + public: + virtual void Trace(Visitor* v) const { + v->Trace(a); + v->Trace(b); + } + + Member a; + void* raw; + Member b; +}; + +template struct DisallowNewWithPaddingTemplate; + +// A GarbageCollected class shall not be checked. +class GCed : GarbageCollected { + void Trace(Visitor* v) const { + v->Trace(a); + v->Trace(b); + } + + Member a; + void* raw; + Member b; +}; + +// Explicitly setting the alignment requirement on fields should disable the +// check. +struct DisallowNewWithExplicitAlignment { + DISALLOW_NEW(); + + void Trace(Visitor* v) const { + v->Trace(a); + v->Trace(b); + } + + Member a; + void* raw; + alignas(32) Member b; +}; + +// Disable the check with classes containing bitfields. +struct DisallowNewWithBitfield { + DISALLOW_NEW(); + + void Trace(Visitor* v) const { + v->Trace(a); + v->Trace(b); + } + + Member a; + void* raw; + int b1 : 1; + int b2 : 2; + Member b; +}; + +// Disable the check with classes containing [[no_unique_address]]. +struct DisallowNewWithNoUniqueAddress { + DISALLOW_NEW(); + + class Empty {}; + + void Trace(Visitor* v) const { + v->Trace(a); + v->Trace(b); + } + + Member a; + void* raw; + [[no_unique_address]] Empty empty; + Member b; +}; + +} // namespace + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/extra_padding.flags b/clang/blink_gc_plugin/tests/extra_padding.flags new file mode 100644 index 0000000000000000000000000000000000000000..ff123e6b348165662c4011278c8dd80ab6c8394c --- /dev/null +++ b/clang/blink_gc_plugin/tests/extra_padding.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-blink-gc-plugin -Xclang enable-extra-padding-check --target=x86_64-unknown-linux-gnu diff --git a/clang/blink_gc_plugin/tests/extra_padding.txt b/clang/blink_gc_plugin/tests/extra_padding.txt new file mode 100644 index 0000000000000000000000000000000000000000..609a45fa13330fea4b43af05cc6f88d344c57e83 --- /dev/null +++ b/clang/blink_gc_plugin/tests/extra_padding.txt @@ -0,0 +1,7 @@ +extra_padding.cpp:17:1: warning: [blink-gc] Additional padding causes the sizeof(DisallowNewWithPadding) to grow by 8. Consider reordering fields. +struct DisallowNewWithPadding { +^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +extra_padding.cpp:63:10: warning: [blink-gc] Additional padding causes the sizeof(DisallowNewWithPaddingTemplate) to grow by 8. Consider reordering fields. +template struct DisallowNewWithPaddingTemplate; +~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/fields_illegal_tracing.cpp b/clang/blink_gc_plugin/tests/fields_illegal_tracing.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6bd37035cdb5b083ee6df9a2685e0a8cb403d978 --- /dev/null +++ b/clang/blink_gc_plugin/tests/fields_illegal_tracing.cpp @@ -0,0 +1,20 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "fields_illegal_tracing.h" + +namespace blink { + +void PartObjectWithTrace::Trace(Visitor* visitor) const { + visitor->Trace(m_obj2); + visitor->Trace(m_obj3); + visitor->Trace(m_obj4); +} + +void HeapObject::Trace(Visitor* visitor) const { + visitor->Trace(m_obj2); + visitor->Trace(m_obj3); + visitor->Trace(m_obj4); +} +} diff --git a/clang/blink_gc_plugin/tests/fields_illegal_tracing.h b/clang/blink_gc_plugin/tests/fields_illegal_tracing.h new file mode 100644 index 0000000000000000000000000000000000000000..48c43ef744284197f9fd97da11a5d8a24685287c --- /dev/null +++ b/clang/blink_gc_plugin/tests/fields_illegal_tracing.h @@ -0,0 +1,77 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FIELDS_ILLEGAL_TRACING_H_ +#define FIELDS_ILLEGAL_TRACING_H_ + +#include "heap/stubs.h" + +namespace blink { + +namespace bar { + +// check that (only) std::unique_ptr<> is reported +// as an illegal smart pointer type. +template class unique_ptr { +public: + ~unique_ptr() { } + operator T*() const { return 0; } + T* operator->() { return 0; } + + void Trace(Visitor* visitor) const {} +}; + +} + +class HeapObject; +class PartObject; + +class PartObjectWithTrace { + DISALLOW_NEW(); + + public: + void Trace(Visitor*) const; + + private: + scoped_refptr m_obj2; + bar::unique_ptr m_obj3; + std::unique_ptr m_obj4; + Vector::iterator m_iterator1; + HeapVector>::iterator m_iterator2; + HeapHashSet::const_iterator m_iterator3; +}; + +class PartObject { + DISALLOW_NEW(); +}; + +class HeapObject : public GarbageCollected { + public: + void Trace(Visitor*) const; + + private: + PartObject m_part; + scoped_refptr m_obj2; + bar::unique_ptr m_obj3; + std::unique_ptr m_obj4; + HeapHashMap>::reverse_iterator m_iterator3; + HeapDeque>::const_reverse_iterator m_iterator4; + HeapLinkedHashSet>::const_iterator m_iterator6; +}; + +class StackAllocatedObject { + STACK_ALLOCATED(); + + private: + scoped_refptr m_obj2; + bar::unique_ptr m_obj3; + std::unique_ptr m_obj4; + Vector::iterator m_iterator1; + HeapVector>::iterator m_iterator2; + HeapHashSet::const_iterator m_iterator3; +}; + +} // namespace blink + +#endif diff --git a/clang/blink_gc_plugin/tests/fields_illegal_tracing.txt b/clang/blink_gc_plugin/tests/fields_illegal_tracing.txt new file mode 100644 index 0000000000000000000000000000000000000000..650b298b81000cc7f2710b499d9c66c4c84aa85c --- /dev/null +++ b/clang/blink_gc_plugin/tests/fields_illegal_tracing.txt @@ -0,0 +1,62 @@ +In file included from fields_illegal_tracing.cpp:5: +./fields_illegal_tracing.h:30:1: warning: [blink-gc] Class 'PartObjectWithTrace' contains invalid fields. +class PartObjectWithTrace { +^ +./fields_illegal_tracing.h:37:3: note: [blink-gc] scoped_refptr field 'm_obj2' to a GC managed class declared here: + scoped_refptr m_obj2; + ^ +./fields_illegal_tracing.h:39:3: note: [blink-gc] std::unique_ptr field 'm_obj4' to a GC managed class declared here: + std::unique_ptr m_obj4; + ^ +./fields_illegal_tracing.h:41:3: warning: [blink-gc] Iterator field 'm_iterator2' to a GC managed collection declared here: + HeapVector>::iterator m_iterator2; + ^ +./fields_illegal_tracing.h:42:3: warning: [blink-gc] Iterator field 'm_iterator3' to a GC managed collection declared here: + HeapHashSet::const_iterator m_iterator3; + ^ +./fields_illegal_tracing.h:49:1: warning: [blink-gc] Class 'HeapObject' contains invalid fields. +class HeapObject : public GarbageCollected { +^ +./fields_illegal_tracing.h:55:3: note: [blink-gc] scoped_refptr field 'm_obj2' to a GC managed class declared here: + scoped_refptr m_obj2; + ^ +./fields_illegal_tracing.h:57:3: note: [blink-gc] std::unique_ptr field 'm_obj4' to a GC managed class declared here: + std::unique_ptr m_obj4; + ^ +./fields_illegal_tracing.h:58:3: warning: [blink-gc] Iterator field 'm_iterator3' to a GC managed collection declared here: + HeapHashMap>::reverse_iterator m_iterator3; + ^ +./fields_illegal_tracing.h:59:3: warning: [blink-gc] Iterator field 'm_iterator4' to a GC managed collection declared here: + HeapDeque>::const_reverse_iterator m_iterator4; + ^ +./fields_illegal_tracing.h:60:3: warning: [blink-gc] Iterator field 'm_iterator6' to a GC managed collection declared here: + HeapLinkedHashSet>::const_iterator m_iterator6; + ^ +./fields_illegal_tracing.h:63:1: warning: [blink-gc] Class 'StackAllocatedObject' contains invalid fields. +class StackAllocatedObject { +^ +./fields_illegal_tracing.h:67:3: note: [blink-gc] scoped_refptr field 'm_obj2' to a GC managed class declared here: + scoped_refptr m_obj2; + ^ +./fields_illegal_tracing.h:69:3: note: [blink-gc] std::unique_ptr field 'm_obj4' to a GC managed class declared here: + std::unique_ptr m_obj4; + ^ +fields_illegal_tracing.cpp:9:1: warning: [blink-gc] Class 'PartObjectWithTrace' has untraced or not traceable fields. +void PartObjectWithTrace::Trace(Visitor* visitor) const { +^ +./fields_illegal_tracing.h:37:3: note: [blink-gc] Untraceable field 'm_obj2' declared here: + scoped_refptr m_obj2; + ^ +./fields_illegal_tracing.h:39:3: note: [blink-gc] Untraceable field 'm_obj4' declared here: + std::unique_ptr m_obj4; + ^ +fields_illegal_tracing.cpp:15:1: warning: [blink-gc] Class 'HeapObject' has untraced or not traceable fields. +void HeapObject::Trace(Visitor* visitor) const { +^ +./fields_illegal_tracing.h:55:3: note: [blink-gc] Untraceable field 'm_obj2' declared here: + scoped_refptr m_obj2; + ^ +./fields_illegal_tracing.h:57:3: note: [blink-gc] Untraceable field 'm_obj4' declared here: + std::unique_ptr m_obj4; + ^ +10 warnings generated. diff --git a/clang/blink_gc_plugin/tests/fields_require_tracing.cpp b/clang/blink_gc_plugin/tests/fields_require_tracing.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8ea0615697f530757117285ca59071ddb8303532 --- /dev/null +++ b/clang/blink_gc_plugin/tests/fields_require_tracing.cpp @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "fields_require_tracing.h" + +namespace blink { + +void PartObject::Trace(Visitor* visitor) const { + m_obj1->Trace(visitor); // Don't allow direct tracing. + visitor->Trace(m_obj2); + // Missing visitor->Trace(m_obj3); + visitor->Trace(m_parts); +} + +void PartBObject::Trace(Visitor* visitor) const { + // Missing visitor->Trace(m_set); + visitor->Trace(m_vector); +} + +void HeapObject::Trace(Visitor* visitor) const { + // Missing visitor->Trace(m_part); + for (auto& member : m_traced_array1) { + visitor->Trace(member); + } + for (int i = 0; i < array_size; ++i) { + visitor->Trace(m_traced_array2[i]); + } + for (auto& member : m_traced_array3) { + visitor->Trace(member); + } + for (int i = 0; i < array_size; ++i) { + visitor->Trace(m_traced_array4[i]); + } + visitor->Trace(m_obj); +} +} diff --git a/clang/blink_gc_plugin/tests/fields_require_tracing.h b/clang/blink_gc_plugin/tests/fields_require_tracing.h new file mode 100644 index 0000000000000000000000000000000000000000..9d4bf1be30555e8c8f905885b80aeb8cef2f8d96 --- /dev/null +++ b/clang/blink_gc_plugin/tests/fields_require_tracing.h @@ -0,0 +1,56 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FIELDS_REQUIRE_TRACING_H_ +#define FIELDS_REQUIRE_TRACING_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject; +class PartObject; + +class PartBObject { + DISALLOW_NEW(); +public: + void Trace(Visitor*) const; + +private: + HeapHashSet m_set; + HeapVector m_vector; +}; + +class PartObject { + DISALLOW_NEW(); +public: + void Trace(Visitor*) const; + +private: + Member m_obj1; + Member m_obj2; + Member m_obj3; + + HeapVector m_parts; +}; + +class HeapObject : public GarbageCollected { + static constexpr int array_size = 2; +public: + void Trace(Visitor*) const; + +private: + PartObject m_part; + Member m_array1[array_size]; + std::array, array_size> m_array2; + Member m_traced_array1[array_size]; + Member m_traced_array2[array_size]; + std::array, array_size> m_traced_array3; + std::array, array_size> m_traced_array4; + Member m_obj; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/fields_require_tracing.txt b/clang/blink_gc_plugin/tests/fields_require_tracing.txt new file mode 100644 index 0000000000000000000000000000000000000000..5bf048d9eb147e1fe7659c10916e28258811e6fe --- /dev/null +++ b/clang/blink_gc_plugin/tests/fields_require_tracing.txt @@ -0,0 +1,28 @@ +fields_require_tracing.cpp:9:1: warning: [blink-gc] Class 'PartObject' has untraced fields that require tracing. +void PartObject::Trace(Visitor* visitor) const { +^ +./fields_require_tracing.h:31:5: note: [blink-gc] Untraced field 'm_obj1' declared here: + Member m_obj1; + ^ +./fields_require_tracing.h:33:5: note: [blink-gc] Untraced field 'm_obj3' declared here: + Member m_obj3; + ^ +fields_require_tracing.cpp:16:1: warning: [blink-gc] Class 'PartBObject' has untraced fields that require tracing. +void PartBObject::Trace(Visitor* visitor) const { +^ +./fields_require_tracing.h:21:5: note: [blink-gc] Untraced field 'm_set' declared here: + HeapHashSet m_set; + ^ +fields_require_tracing.cpp:21:1: warning: [blink-gc] Class 'HeapObject' has untraced fields that require tracing. +void HeapObject::Trace(Visitor* visitor) const { +^ +./fields_require_tracing.h:44:5: note: [blink-gc] Untraced field 'm_part' declared here: + PartObject m_part; + ^ +./fields_require_tracing.h:45:5: note: [blink-gc] Untraced field 'm_array1' declared here: + Member m_array1[array_size]; + ^ +./fields_require_tracing.h:46:5: note: [blink-gc] Untraced field 'm_array2' declared here: + std::array, array_size> m_array2; + ^ +3 warnings generated. diff --git a/clang/blink_gc_plugin/tests/finalize_after_dispatch.cpp b/clang/blink_gc_plugin/tests/finalize_after_dispatch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..88eaa13deecfedc2be93b43bfb31e3ab1803a18d --- /dev/null +++ b/clang/blink_gc_plugin/tests/finalize_after_dispatch.cpp @@ -0,0 +1,58 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "finalize_after_dispatch.h" + +namespace blink { + +static const B* toB(const A* a) { + return static_cast(a); +} + +void A::Trace(Visitor* visitor) const { + switch (m_type) { + case TB: + toB(this)->TraceAfterDispatch(visitor); + break; + case TC: + static_cast(this)->TraceAfterDispatch(visitor); + break; + case TD: + static_cast(this)->TraceAfterDispatch(visitor); + break; + } +} + +void A::TraceAfterDispatch(Visitor* visitor) const {} + +void A::FinalizeGarbageCollectedObject() +{ + switch (m_type) { + case TB: + toB(this)->~B(); + break; + case TC: + static_cast(this)->~C(); + break; + case TD: + // Missing static_cast(this)->~D(); + break; + } +} + +void B::TraceAfterDispatch(Visitor* visitor) const { + visitor->Trace(m_a); + A::TraceAfterDispatch(visitor); +} + +void C::TraceAfterDispatch(Visitor* visitor) const { + visitor->Trace(m_a); + A::TraceAfterDispatch(visitor); +} + +void D::TraceAfterDispatch(Visitor* visitor) const { + visitor->Trace(m_a); + Abstract::TraceAfterDispatch(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/finalize_after_dispatch.h b/clang/blink_gc_plugin/tests/finalize_after_dispatch.h new file mode 100644 index 0000000000000000000000000000000000000000..5ba0fdb8d3c32d688b73b16676efc60ec240f4d0 --- /dev/null +++ b/clang/blink_gc_plugin/tests/finalize_after_dispatch.h @@ -0,0 +1,82 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FINALIZE_AFTER_DISPATCH_H_ +#define FINALIZE_AFTER_DISPATCH_H_ + +#include "heap/stubs.h" + +namespace blink { + +class NeedsDispatch : public GarbageCollected { + public: + void Trace(Visitor*) const; + // Needs a TraceAfterDispatch method. + void FinalizeGarbageCollectedObject() {} + + protected: + NeedsDispatch() = default; +}; + +class NeedsFinalizedBase : public GarbageCollected { + public: + void Trace(Visitor*) const {} + void TraceAfterDispatch(Visitor*) const {} + void FinalizeGarbageCollectedObject() {} + + protected: + NeedsFinalizedBase() = default; +}; + +class A : GarbageCollected { + public: + void Trace(Visitor*) const; + void TraceAfterDispatch(Visitor*) const; + void FinalizeGarbageCollectedObject(); + + protected: + enum Type { TB, TC, TD }; + A(Type type) : m_type(type) {} + + private: + Type m_type; +}; + +class B : public A { +public: + B() : A(TB) { } + ~B() { } + void TraceAfterDispatch(Visitor*) const; + + private: + Member m_a; +}; + +class C : public A { +public: + C() : A(TC) { } + void TraceAfterDispatch(Visitor*) const; + + private: + Member m_a; +}; + +// This class is considered abstract does not need to be dispatched to. +class Abstract : public A { +protected: + Abstract(Type type) : A(type) { } +}; + +class D : public Abstract { +public: + D() : Abstract(TD) { } + void TraceAfterDispatch(Visitor*) const; + + private: + Member m_a; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/finalize_after_dispatch.txt b/clang/blink_gc_plugin/tests/finalize_after_dispatch.txt new file mode 100644 index 0000000000000000000000000000000000000000..520c0df9044b27881f13454b4b07b5b0fff175a7 --- /dev/null +++ b/clang/blink_gc_plugin/tests/finalize_after_dispatch.txt @@ -0,0 +1,8 @@ +In file included from finalize_after_dispatch.cpp:5: +./finalize_after_dispatch.h:12:1: warning: [blink-gc] Class 'NeedsDispatch' is missing manual trace dispatch. +class NeedsDispatch : public GarbageCollected { +^ +finalize_after_dispatch.cpp:29:1: warning: [blink-gc] Missing dispatch to class 'D' in manual finalize dispatch. +void A::FinalizeGarbageCollectedObject() +^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/forbidden_fields.cpp b/clang/blink_gc_plugin/tests/forbidden_fields.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e02358f932ee607b90a15f7b2a2ed1a82c6159c1 --- /dev/null +++ b/clang/blink_gc_plugin/tests/forbidden_fields.cpp @@ -0,0 +1,7 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "forbidden_fields.h" + +// Nothing to define. diff --git a/clang/blink_gc_plugin/tests/forbidden_fields.h b/clang/blink_gc_plugin/tests/forbidden_fields.h new file mode 100644 index 0000000000000000000000000000000000000000..547b1c1c324ad11b792f3c83abad8e19765ebf89 --- /dev/null +++ b/clang/blink_gc_plugin/tests/forbidden_fields.h @@ -0,0 +1,53 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_BLINK_GC_PLUGIN_TESTS_FORBIDDEN_FIELDS_H_ +#define TOOLS_CLANG_BLINK_GC_PLUGIN_TESTS_FORBIDDEN_FIELDS_H_ + +#include "heap/stubs.h" + +namespace blink { + +template +class TaskRunnerTimer {}; + +class SecondLevelPartObject { + private: + TaskRunnerTimer timer_; +}; + +class FirstLevelPartObject { + private: + SecondLevelPartObject obj_; + std::map map_of_embedded; +}; + +class HeapObject : public GarbageCollected { + private: + FirstLevelPartObject obj_; +}; + +class AnotherHeapObject : public GarbageCollected { + private: + TaskRunnerTimer timer_; + Vector> vec_of_timers_; + Vector vec_of_embedded_of_timers; + TaskRunnerTimer array_of_bad_typ_e[2]; + SecondLevelPartObject array_of_embedded_object_[2]; + std::vector> std_vec_of_timers_; + std::optional> optional_timer_; + std::optional> optional_timer2_; + std::optional optional_embedded_object_; + std::optional optional_embedded_object2_; + absl::variant variant_embedded_object_; + std::variant variant_embedded_object2_; + std::unique_ptr> unique_ptr_timer_; + TaskRunnerTimer* raw_ptr_timer_; + scoped_refptr> scoped_refptr_timer_; + base::WeakPtr> weak_ptr_timer_; +}; + +} // namespace blink + +#endif /* TOOLS_CLANG_BLINK_GC_PLUGIN_TESTS_FORBIDDEN_FIELDS_H_ */ diff --git a/clang/blink_gc_plugin/tests/forbidden_fields.txt b/clang/blink_gc_plugin/tests/forbidden_fields.txt new file mode 100644 index 0000000000000000000000000000000000000000..c9c54c2244c350631c661f612935eaed24a49d38 --- /dev/null +++ b/clang/blink_gc_plugin/tests/forbidden_fields.txt @@ -0,0 +1,92 @@ +In file included from forbidden_fields.cpp:5: +./forbidden_fields.h:26:1: warning: [blink-gc] Class 'HeapObject' contains invalid fields. +class HeapObject : public GarbageCollected { +^ +./forbidden_fields.h:28:3: note: [blink-gc] From part object field 'obj_' here: + FirstLevelPartObject obj_; + ^ +./forbidden_fields.h:22:3: note: [blink-gc] From part object field 'obj_' here: + SecondLevelPartObject obj_; + ^ +./forbidden_fields.h:17:3: note: [blink-gc] TaskRunnerTimer field 'timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + TaskRunnerTimer timer_; + ^ +./forbidden_fields.h:28:3: note: [blink-gc] From part object field 'obj_' here: + FirstLevelPartObject obj_; + ^ +./forbidden_fields.h:23:3: note: [blink-gc] From part object field 'map_of_embedded' here: + std::map map_of_embedded; + ^ +./forbidden_fields.h:17:3: note: [blink-gc] TaskRunnerTimer field 'timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + TaskRunnerTimer timer_; + ^ +./forbidden_fields.h:31:1: warning: [blink-gc] Class 'AnotherHeapObject' contains invalid fields. +class AnotherHeapObject : public GarbageCollected { +^ +./forbidden_fields.h:33:3: note: [blink-gc] TaskRunnerTimer field 'timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + TaskRunnerTimer timer_; + ^ +./forbidden_fields.h:34:3: note: [blink-gc] TaskRunnerTimer field 'vec_of_timers_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + Vector> vec_of_timers_; + ^ +./forbidden_fields.h:35:3: note: [blink-gc] From part object field 'vec_of_embedded_of_timers' here: + Vector vec_of_embedded_of_timers; + ^ +./forbidden_fields.h:17:3: note: [blink-gc] TaskRunnerTimer field 'timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + TaskRunnerTimer timer_; + ^ +./forbidden_fields.h:36:3: note: [blink-gc] TaskRunnerTimer field 'array_of_bad_typ_e' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + TaskRunnerTimer array_of_bad_typ_e[2]; + ^ +./forbidden_fields.h:37:3: note: [blink-gc] From part object field 'array_of_embedded_object_' here: + SecondLevelPartObject array_of_embedded_object_[2]; + ^ +./forbidden_fields.h:17:3: note: [blink-gc] TaskRunnerTimer field 'timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + TaskRunnerTimer timer_; + ^ +./forbidden_fields.h:38:3: note: [blink-gc] TaskRunnerTimer field 'std_vec_of_timers_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + std::vector> std_vec_of_timers_; + ^ +./forbidden_fields.h:39:3: note: [blink-gc] TaskRunnerTimer field 'optional_timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + std::optional> optional_timer_; + ^ +./forbidden_fields.h:40:3: note: [blink-gc] TaskRunnerTimer field 'optional_timer2_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + std::optional> optional_timer2_; + ^ +./forbidden_fields.h:41:3: note: [blink-gc] From part object field 'optional_embedded_object_' here: + std::optional optional_embedded_object_; + ^ +./forbidden_fields.h:17:3: note: [blink-gc] TaskRunnerTimer field 'timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + TaskRunnerTimer timer_; + ^ +./forbidden_fields.h:42:3: note: [blink-gc] From part object field 'optional_embedded_object2_' here: + std::optional optional_embedded_object2_; + ^ +./forbidden_fields.h:17:3: note: [blink-gc] TaskRunnerTimer field 'timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + TaskRunnerTimer timer_; + ^ +./forbidden_fields.h:43:3: note: [blink-gc] From part object field 'variant_embedded_object_' here: + absl::variant variant_embedded_object_; + ^ +./forbidden_fields.h:17:3: note: [blink-gc] TaskRunnerTimer field 'timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + TaskRunnerTimer timer_; + ^ +./forbidden_fields.h:44:3: note: [blink-gc] From part object field 'variant_embedded_object2_' here: + std::variant variant_embedded_object2_; + ^ +./forbidden_fields.h:17:3: note: [blink-gc] TaskRunnerTimer field 'timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + TaskRunnerTimer timer_; + ^ +./forbidden_fields.h:45:3: note: [blink-gc] TaskRunnerTimer field 'unique_ptr_timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + std::unique_ptr> unique_ptr_timer_; + ^ +./forbidden_fields.h:46:3: note: [blink-gc] TaskRunnerTimer field 'raw_ptr_timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + TaskRunnerTimer* raw_ptr_timer_; + ^ +./forbidden_fields.h:47:3: note: [blink-gc] TaskRunnerTimer field 'scoped_refptr_timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + scoped_refptr> scoped_refptr_timer_; + ^ +./forbidden_fields.h:48:3: note: [blink-gc] TaskRunnerTimer field 'weak_ptr_timer_' used within a garbage collected context. Consider using HeapTaskRunnerTimer instead. + base::WeakPtr> weak_ptr_timer_; + ^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/garbage_collected_mixin.cpp b/clang/blink_gc_plugin/tests/garbage_collected_mixin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3bffed6dd0bc60d2ce4a4d1015d31f21bd69b428 --- /dev/null +++ b/clang/blink_gc_plugin/tests/garbage_collected_mixin.cpp @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "garbage_collected_mixin.h" + +namespace blink { + +void Mixin::Trace(Visitor* visitor) const { + // Missing: visitor->Trace(m_self); +} + +void HeapObject::Trace(Visitor* visitor) const { + visitor->Trace(m_mix); + // Missing: Mixin::Trace(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/garbage_collected_mixin.h b/clang/blink_gc_plugin/tests/garbage_collected_mixin.h new file mode 100644 index 0000000000000000000000000000000000000000..705d0f1beb70379605bde33e1af1f6ec0573ccab --- /dev/null +++ b/clang/blink_gc_plugin/tests/garbage_collected_mixin.h @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GARBAGE_COLLECTED_MIXIN_H_ +#define GARBAGE_COLLECTED_MIXIN_H_ + +#include "heap/stubs.h" + +namespace blink { + +class Mixin : public GarbageCollectedMixin { +public: + virtual void Trace(Visitor*) const override; + +private: + Member m_self; +}; + +class HeapObject : public GarbageCollected, public Mixin { +public: + virtual void Trace(Visitor*) const override; + +private: + Member m_mix; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/garbage_collected_mixin.txt b/clang/blink_gc_plugin/tests/garbage_collected_mixin.txt new file mode 100644 index 0000000000000000000000000000000000000000..eb8cbbc78a19af8dd0c3abe0159046591e787efe --- /dev/null +++ b/clang/blink_gc_plugin/tests/garbage_collected_mixin.txt @@ -0,0 +1,10 @@ +garbage_collected_mixin.cpp:9:1: warning: [blink-gc] Class 'Mixin' has untraced fields that require tracing. +void Mixin::Trace(Visitor* visitor) const { +^ +./garbage_collected_mixin.h:17:5: note: [blink-gc] Untraced field 'm_self' declared here: + Member m_self; + ^ +garbage_collected_mixin.cpp:13:1: warning: [blink-gc] Base class 'Mixin' of derived class 'HeapObject' requires tracing. +void HeapObject::Trace(Visitor* visitor) const { +^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/gced_as_field_or_variable.cpp b/clang/blink_gc_plugin/tests/gced_as_field_or_variable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5defcfb2a606ec6adac3c4409a05a069a9038723 --- /dev/null +++ b/clang/blink_gc_plugin/tests/gced_as_field_or_variable.cpp @@ -0,0 +1,18 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gced_as_field_or_variable.h" + +namespace blink { + +void Foo() { + GCed gced; + (void)gced; + Mixin mixin; + (void)mixin; + HeapVector vector; // OK + HeapHashMap map; +} + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/gced_as_field_or_variable.h b/clang/blink_gc_plugin/tests/gced_as_field_or_variable.h new file mode 100644 index 0000000000000000000000000000000000000000..2d94cd5dc0ce077cc4d0f6c3232f7124b35818b7 --- /dev/null +++ b/clang/blink_gc_plugin/tests/gced_as_field_or_variable.h @@ -0,0 +1,40 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef GCED_AS_FIELD_OR_VARIABLE_H_ +#define GCED_AS_FIELD_OR_VARIABLE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class GCed : public GarbageCollected { + public: + void Trace(Visitor*) const {} +}; + +class Mixin : public GarbageCollectedMixin { + public: + void Trace(Visitor*) const override {} +}; + +class OtherGCed : public GarbageCollected { + public: + void Trace(Visitor* v) const { + v->Trace(gced_); + v->Trace(mixin_); + v->Trace(vector_); + v->Trace(map_); + } + + private: + GCed gced_; + Mixin mixin_; + HeapVector vector_; // OK + HeapHashMap map_; // OK +}; + +} // namespace blink + +#endif // GCED_AS_FIELD_OR_VARIABLE_H_ diff --git a/clang/blink_gc_plugin/tests/gced_as_field_or_variable.txt b/clang/blink_gc_plugin/tests/gced_as_field_or_variable.txt new file mode 100644 index 0000000000000000000000000000000000000000..d29a49bfaa594d43d662e7b39af964ec1ccf0e6e --- /dev/null +++ b/clang/blink_gc_plugin/tests/gced_as_field_or_variable.txt @@ -0,0 +1,20 @@ +In file included from gced_as_field_or_variable.cpp:5: +./gced_as_field_or_variable.h:22:1: warning: [blink-gc] Class 'OtherGCed' contains invalid fields. +class OtherGCed : public GarbageCollected { +^ +./gced_as_field_or_variable.h:32:3: note: [blink-gc] Part-object field 'gced_' to a GC derived class declared here: + GCed gced_; + ^ +./gced_as_field_or_variable.h:32:3: warning: [blink-gc] Using GC managed class 'GCed' as field 'gced_' is not allowed (Allocate with MakeGarbageCollected and use Member or Persistent instead): + GCed gced_; + ^~~~~~~~~~ +./gced_as_field_or_variable.h:33:3: warning: [blink-gc] Using GC managed class 'Mixin' as field 'mixin_' is not allowed (Allocate with MakeGarbageCollected and use Member or Persistent instead): + Mixin mixin_; + ^~~~~~~~~~~~ +gced_as_field_or_variable.cpp:10:3: warning: [blink-gc] Using GC managed class 'GCed' as variable 'gced' is not allowed (Allocate with MakeGarbageCollected and use raw pointer instead): + GCed gced; + ^~~~~~~~~ +gced_as_field_or_variable.cpp:12:3: warning: [blink-gc] Using GC managed class 'Mixin' as variable 'mixin' is not allowed (Allocate with MakeGarbageCollected and use raw pointer instead): + Mixin mixin; + ^~~~~~~~~~~ +5 warnings generated. diff --git a/clang/blink_gc_plugin/tests/heap/stubs.h b/clang/blink_gc_plugin/tests/heap/stubs.h new file mode 100644 index 0000000000000000000000000000000000000000..46ea603b86a2c613c9dbeaf4e22cb26272b05038 --- /dev/null +++ b/clang/blink_gc_plugin/tests/heap/stubs.h @@ -0,0 +1,449 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef HEAP_STUBS_H_ +#define HEAP_STUBS_H_ + +#include +#include + +#define GC_PLUGIN_IGNORE(reason) \ + __attribute__((annotate("blink_gc_plugin_ignore"))) + +namespace base { + +template +class WeakPtr { + public: + ~WeakPtr() {} + operator T*() const { return 0; } + T* operator->() const { return 0; } +}; + +template +class WeakPtrFactory { + public: + explicit WeakPtrFactory(T*) {} + ~WeakPtrFactory() {} + WeakPtr GetWeakPtr() { return WeakPtr(); } +}; + +template +class raw_ptr {}; + +template +class raw_ref {}; + +} // namespace base +namespace WTF { + +template class RefCounted { }; + +template class RawPtr { + public: + operator T*() const { return 0; } + T* operator->() const { return 0; } +}; + +template class scoped_refptr { + public: + ~scoped_refptr() {} + operator T*() const { return 0; } + T* operator->() const { return 0; } +}; + +class PartitionAllocator { + public: + static const bool isGarbageCollected = false; +}; + +template +class Vector { + public: + using iterator = T*; + using const_iterator = const T*; + using reverse_iterator = T*; + using const_reverse_iterator = const T*; + + size_t size(); + T& operator[](size_t); + + ~Vector() {} +}; + +template +class Deque { + public: + using iterator = T*; + using const_iterator = const T*; + using reverse_iterator = T*; + using const_reverse_iterator = const T*; + + ~Deque() {} +}; + +template +class HashSet { + public: + typedef ValueArg* iterator; + typedef const ValueArg* const_iterator; + typedef ValueArg* reverse_iterator; + typedef const ValueArg* const_reverse_iterator; + + ~HashSet() {} +}; + +template +class LinkedHashSet { + public: + typedef ValueArg* iterator; + typedef const ValueArg* const_iterator; + typedef ValueArg* reverse_iterator; + typedef const ValueArg* const_reverse_iterator; + + ~LinkedHashSet() {} +}; + +template +class HashCountedSet { + public: + ~HashCountedSet() {} +}; + +template +class HashMap { + public: + typedef MappedArg* iterator; + typedef const MappedArg* const_iterator; + typedef MappedArg* reverse_iterator; + typedef const MappedArg* const_reverse_iterator; + + ~HashMap() {} +}; +} + +// Empty namespace declaration to exercise internal +// handling of namespace equality. +namespace std { + /* empty */ +} + +namespace std { + +template class unique_ptr { + public: + ~unique_ptr() {} + operator T*() const { return 0; } + T* operator->() const { return 0; } +}; + +template +unique_ptr make_unique(Args&&... args) { + return unique_ptr(); +} + +template +class set {}; +template +class unordered_set {}; +template +class map {}; +template +class unordered_map {}; +template +class vector {}; +template +class array { + public: + const Elem& operator[](size_t n) const { return elems_[n]; } + + const Elem* begin() const { return &elems_[0]; } + const Elem* end() const { return &elems_[N]; } + + private: + GC_PLUGIN_IGNORE("A mock of an array for testing") Elem elems_[N]; +}; +template +class pair {}; +template +class optional {}; +template +class variant {}; + +} // namespace std + +namespace base { + +template +std::unique_ptr WrapUnique(T* ptr) { + return std::unique_ptr(); +} + +} // namespace base + +namespace absl { + +template +class optional {}; + +template +class variant {}; + +} // namespace absl + +namespace cppgc { + +class Visitor { + public: + template + void RegisterWeakMembers(const T* obj); + + template + void Trace(const T&); +}; + +namespace internal { +class WriteBarrierPolicyImpl; +class NoWriteBarrierPolicyImpl; +class CheckingPolicyImpl; +class StorateTypeImpl; +class LocationPolicyImpl; + +class StrongMemberTag; +class WeakMemberTag; +class UntracedMemberTag; + +template +class MemberBase {}; + +template +class BasicMember : public MemberBase { + public: + operator T*() const { return 0; } + T* operator->() const { return 0; } + bool operator!() const { return false; } +}; + +class StrongPersistentPolicy; +class WeakPersistentPolicy; + +class PersistentBase {}; + +template +class BasicPersistent : public PersistentBase { + public: + operator T*() const { return 0; } + T* operator->() const { return 0; } + bool operator!() const { return false; } +}; + +class StrongCrossThreadPersistentPolicy; +class WeakCrossThreadPersistentPolicy; + +class CrossThreadPersistentBase : public PersistentBase {}; + +template +class BasicCrossThreadPersistent : public CrossThreadPersistentBase { + public: + operator T*() const { return 0; } + T* operator->() const { return 0; } + bool operator!() const { return false; } +}; + +} // namespace internal + +template +class GarbageCollected { + public: + void* operator new(size_t, void* location) { return location; } + + private: + void* operator new(size_t) = delete; + void* operator new[](size_t) = delete; +}; + +template +T* MakeGarbageCollected(int, Args&&... args) { + return new (reinterpret_cast(0x87654321)) T(args...); +} + +class GarbageCollectedMixin { + public: + virtual void Trace(Visitor*) const {} +}; + +template +using Member = internal::BasicMember; +template +using WeakMember = internal::BasicMember; + +template +using UntracedMember = internal::BasicMember; + +template +using Persistent = internal::BasicPersistent; +template +using WeakPersistent = internal::BasicPersistent; + +namespace subtle { + +template +using CrossThreadPersistent = internal::BasicCrossThreadPersistent< + T, + internal::StrongCrossThreadPersistentPolicy, + internal::LocationPolicyImpl, + internal::CheckingPolicyImpl>; +template +using CrossThreadWeakPersistent = internal::BasicCrossThreadPersistent< + T, + internal::WeakCrossThreadPersistentPolicy, + internal::LocationPolicyImpl, + internal::CheckingPolicyImpl>; + +} // namespace subtle + +} // namespace cppgc + +namespace v8 { + +template +class TracedReference { + public: + operator T*() const { return 0; } + T* operator->() const { return 0; } + bool operator!() const { return false; } +}; + +} // namespace v8 + +namespace blink { + +using Visitor = cppgc::Visitor; + +template +using GarbageCollected = cppgc::GarbageCollected; +template +T* MakeGarbageCollected(Args&&... args) { + return cppgc::MakeGarbageCollected(0, args...); +} + +using GarbageCollectedMixin = cppgc::GarbageCollectedMixin; + +template +using Member = cppgc::Member; +template +using WeakMember = cppgc::WeakMember; +template +using UntracedMember = cppgc::UntracedMember; +template +using Persistent = cppgc::Persistent; +template +using WeakPersistent = cppgc::WeakPersistent; +template +using CrossThreadPersistent = cppgc::subtle::CrossThreadPersistent; +template +using CrossThreadWeakPersistent = cppgc::subtle::CrossThreadWeakPersistent; + +template +using TraceWrapperV8Reference = v8::TracedReference; + +using namespace WTF; + +#define DISALLOW_NEW() \ + public: \ + void* operator new(size_t, void* location) { return location; } \ + \ + private: \ + void* operator new(size_t) = delete + +#define STACK_ALLOCATED() \ + public: \ + using IsStackAllocatedTypeMarker [[maybe_unused]] = int; \ + \ + private: \ + void* operator new(size_t) = delete; \ + void* operator new(size_t, void*) = delete + +template +class RefCountedGarbageCollected : public GarbageCollected {}; + +class HeapAllocator { +public: + static const bool isGarbageCollected = true; +}; + +template +class HeapVector : public GarbageCollected>, + public Vector {}; + +template +class HeapDeque : public GarbageCollected>, + public Vector {}; + +template +class HeapHashSet : public GarbageCollected>, + public HashSet {}; + +template +class HeapLinkedHashSet : public GarbageCollected>, + public LinkedHashSet {}; + +template +class HeapHashCountedSet : public GarbageCollected>, + public HashCountedSet {}; + +template +class HeapHashMap : public GarbageCollected>, + public HashMap {}; + +template +struct TraceIfNeeded { + static void Trace(Visitor*, const T&); +}; + +} // namespace blink + +#endif diff --git a/clang/blink_gc_plugin/tests/ignore_class.cpp b/clang/blink_gc_plugin/tests/ignore_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fbf3af6f0fcedc7e882d213935821744f39e0a78 --- /dev/null +++ b/clang/blink_gc_plugin/tests/ignore_class.cpp @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ignore_class.h" + +namespace blink { + +void B::Trace(Visitor* visitor) const { + // Class is ignored so no checking here. +} + +void C::Trace(Visitor* visitor) const { + // Missing Trace of m_obj. + // Ignored base class B does not need tracing. +} +} diff --git a/clang/blink_gc_plugin/tests/ignore_class.h b/clang/blink_gc_plugin/tests/ignore_class.h new file mode 100644 index 0000000000000000000000000000000000000000..9d32bfdc1384e4cc074ff1f27b7b5d6eda209188 --- /dev/null +++ b/clang/blink_gc_plugin/tests/ignore_class.h @@ -0,0 +1,42 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IGNORE_CLASS_H_ +#define IGNORE_CLASS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { }; + +// Don't require Trace method on ignored class. +class GC_PLUGIN_IGNORE("http://crbug.com/12345") A; +class A : public GarbageCollected { +private: + Member m_obj; +}; + +// Don't require tracing of fields on ignored class. +class GC_PLUGIN_IGNORE("http://crbug.com/12345") B; +class B : public GarbageCollected { +public: + virtual void Trace(Visitor*) const; + +private: + Member m_obj; +}; + +// Don't require tracing of an ignored base class. +class C : public B { +public: + void Trace(Visitor*) const; + +private: + Member m_obj; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/ignore_class.txt b/clang/blink_gc_plugin/tests/ignore_class.txt new file mode 100644 index 0000000000000000000000000000000000000000..873a8a9f4ade6b9c9afec5f84306a592b52be497 --- /dev/null +++ b/clang/blink_gc_plugin/tests/ignore_class.txt @@ -0,0 +1,7 @@ +ignore_class.cpp:13:1: warning: [blink-gc] Class 'C' has untraced fields that require tracing. +void C::Trace(Visitor* visitor) const { +^ +./ignore_class.h:37:5: note: [blink-gc] Untraced field 'm_obj' declared here: + Member m_obj; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/ignore_fields.cpp b/clang/blink_gc_plugin/tests/ignore_fields.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f508089909fffd4630d495518ffa765c6cf134a7 --- /dev/null +++ b/clang/blink_gc_plugin/tests/ignore_fields.cpp @@ -0,0 +1,13 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ignore_fields.h" + +namespace blink { + +void C::Trace(Visitor* visitor) const { + // Missing Trace of m_one. + // Not missing ignored field m_two. +} +} diff --git a/clang/blink_gc_plugin/tests/ignore_fields.h b/clang/blink_gc_plugin/tests/ignore_fields.h new file mode 100644 index 0000000000000000000000000000000000000000..5a9ad85ff8b2944aaf14106057b72f5839ce8225 --- /dev/null +++ b/clang/blink_gc_plugin/tests/ignore_fields.h @@ -0,0 +1,44 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IGNORE_FIELDS_H_ +#define IGNORE_FIELDS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { +public: + virtual void Trace(Visitor*) const {} +}; + +// Don't warn about raw pointers to heap allocated objects. +class A : public GarbageCollected{ +private: + GC_PLUGIN_IGNORE("http://crbug.com/12345") + HeapObject* m_obj; +}; + +// Don't require Trace method when (all) GC fields are ignored. +class B : public GarbageCollected { +private: + GC_PLUGIN_IGNORE("http://crbug.com/12345") + Member m_one; +}; + +// Don't require tracing an ignored field. +class C : public GarbageCollected { +public: + void Trace(Visitor*) const; + +private: + Member m_one; + GC_PLUGIN_IGNORE("http://crbug.com/12345") + Member m_two; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/ignore_fields.txt b/clang/blink_gc_plugin/tests/ignore_fields.txt new file mode 100644 index 0000000000000000000000000000000000000000..14e008147f3ebf83e2131417a05b7e5f2ec21229 --- /dev/null +++ b/clang/blink_gc_plugin/tests/ignore_fields.txt @@ -0,0 +1,7 @@ +ignore_fields.cpp:9:1: warning: [blink-gc] Class 'C' has untraced fields that require tracing. +void C::Trace(Visitor* visitor) const { +^ +./ignore_fields.h:37:5: note: [blink-gc] Untraced field 'm_one' declared here: + Member m_one; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/inner_class.cpp b/clang/blink_gc_plugin/tests/inner_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3a020f96594cdf5f373e13265b96899bb2720b66 --- /dev/null +++ b/clang/blink_gc_plugin/tests/inner_class.cpp @@ -0,0 +1,12 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "inner_class.h" + +namespace blink { + +void SomeObject::InnerObject::Trace(Visitor* visitor) const { + // Missing: visitor->Trace(m_obj); +} +} diff --git a/clang/blink_gc_plugin/tests/inner_class.h b/clang/blink_gc_plugin/tests/inner_class.h new file mode 100644 index 0000000000000000000000000000000000000000..cbdf919b79171159a48e5e3d1eb9bf2763fa0ccd --- /dev/null +++ b/clang/blink_gc_plugin/tests/inner_class.h @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INNER_CLASS_H_ +#define INNER_CLASS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class SomeObject { +private: + class InnerObject : public GarbageCollected { + public: + void Trace(Visitor*) const; + + private: + Member m_obj; + }; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/inner_class.txt b/clang/blink_gc_plugin/tests/inner_class.txt new file mode 100644 index 0000000000000000000000000000000000000000..b7dbededc34264c05c7dffb8d0bc02b9b400af11 --- /dev/null +++ b/clang/blink_gc_plugin/tests/inner_class.txt @@ -0,0 +1,7 @@ +inner_class.cpp:9:1: warning: [blink-gc] Class 'InnerObject' has untraced fields that require tracing. +void SomeObject::InnerObject::Trace(Visitor* visitor) const { +^ +./inner_class.h:19:9: note: [blink-gc] Untraced field 'm_obj' declared here: + Member m_obj; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/left_most_gc_base.cpp b/clang/blink_gc_plugin/tests/left_most_gc_base.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e52e178f59026fd37f5e4151f1c3cc468283d57e --- /dev/null +++ b/clang/blink_gc_plugin/tests/left_most_gc_base.cpp @@ -0,0 +1,7 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "left_most_gc_base.h" + +// Nothing to define. diff --git a/clang/blink_gc_plugin/tests/left_most_gc_base.h b/clang/blink_gc_plugin/tests/left_most_gc_base.h new file mode 100644 index 0000000000000000000000000000000000000000..fd7f813968f0068e9d3b846eae00fb0b54fe01a9 --- /dev/null +++ b/clang/blink_gc_plugin/tests/left_most_gc_base.h @@ -0,0 +1,38 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LEFT_MOST_GC_BASE_H_ +#define LEFT_MOST_GC_BASE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class A { }; +class B { }; + +class Right : public A, public B, public GarbageCollected { }; // Error +class Left : public GarbageCollected, public B, public A { }; + +class DerivedRight : public Right, public Left { }; // Error +class DerivedLeft : public Left, public Right { }; + +class C : public GarbageCollected { +public: + virtual void Trace(Visitor*) const; +}; + +class IllFormed : public A, public C { }; // Error + +class LeftMixin : public GarbageCollectedMixin { +public: + virtual void Trace(Visitor*) const; +}; + +class DerivedLeftMixin : public LeftMixin, public GarbageCollected { +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/left_most_gc_base.txt b/clang/blink_gc_plugin/tests/left_most_gc_base.txt new file mode 100644 index 0000000000000000000000000000000000000000..4865dccebb0221b923590b91b69accdea8f8d8f4 --- /dev/null +++ b/clang/blink_gc_plugin/tests/left_most_gc_base.txt @@ -0,0 +1,17 @@ +In file included from left_most_gc_base.cpp:5: +./left_most_gc_base.h:15:1: warning: [blink-gc] Class 'Right' must derive from GarbageCollected in the left-most position. +class Right : public A, public B, public GarbageCollected { }; // Error +^ +./left_most_gc_base.h:18:1: warning: [blink-gc] Class 'DerivedRight' must derive from GarbageCollected in the left-most position. +class DerivedRight : public Right, public Left { }; // Error +^ +./left_most_gc_base.h:12:1: warning: [blink-gc] Left-most base class 'A' of derived class 'IllFormed' must be polymorphic. +class A { }; +^ +./left_most_gc_base.h:26:1: warning: [blink-gc] Class 'IllFormed' must derive from GarbageCollected in the left-most position. +class IllFormed : public A, public C { }; // Error +^ +./left_most_gc_base.h:33:1: warning: [blink-gc] Class 'DerivedLeftMixin' must derive from GarbageCollected in the left-most position. +class DerivedLeftMixin : public LeftMixin, public GarbageCollected { +^ +5 warnings generated. diff --git a/clang/blink_gc_plugin/tests/make_unique_gc_object.cpp b/clang/blink_gc_plugin/tests/make_unique_gc_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..407810805ca0887d8bf5dcb0aa1c1b11dfc55040 --- /dev/null +++ b/clang/blink_gc_plugin/tests/make_unique_gc_object.cpp @@ -0,0 +1,16 @@ +// Copyright 2017 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "make_unique_gc_object.h" + +namespace blink { + +void DisallowedUseOfUniquePtr() { + auto owned_base = std::make_unique(); + auto owned_base_array = std::make_unique(1); + auto owned_derived = std::make_unique(); + auto owned_mixin = base::WrapUnique(static_cast(nullptr)); +} + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/make_unique_gc_object.h b/clang/blink_gc_plugin/tests/make_unique_gc_object.h new file mode 100644 index 0000000000000000000000000000000000000000..1cac355ade51471eab1893df06781316d140d18b --- /dev/null +++ b/clang/blink_gc_plugin/tests/make_unique_gc_object.h @@ -0,0 +1,29 @@ +// Copyright 2017 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MAKE_UNIQUE_GC_OBJECT_H_ +#define MAKE_UNIQUE_GC_OBJECT_H_ + +#include "heap/stubs.h" + +namespace blink { + +class Base : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +class Derived : public Base { + public: + void Trace(Visitor* visitor) const override { Base::Trace(visitor); } +}; + +class Mixin : public GarbageCollectedMixin { + public: + void Trace(Visitor*) const {} +}; + +} // namespace blink + +#endif // MAKE_UNIQUE_GC_OBJECT_H_ diff --git a/clang/blink_gc_plugin/tests/make_unique_gc_object.txt b/clang/blink_gc_plugin/tests/make_unique_gc_object.txt new file mode 100644 index 0000000000000000000000000000000000000000..99cf7652b8166f068c968370bbfc301fb916e5d0 --- /dev/null +++ b/clang/blink_gc_plugin/tests/make_unique_gc_object.txt @@ -0,0 +1,13 @@ +make_unique_gc_object.cpp:10:21: warning: [blink-gc] Disallowed use of 'make_unique' found; 'Base' is a garbage-collected type. std::unique_ptr cannot hold garbage-collected objects. + auto owned_base = std::make_unique(); + ^~~~~~~~~~~~~~~~~~~~~~~~ +make_unique_gc_object.cpp:11:27: warning: [blink-gc] Disallowed use of 'make_unique' found; 'Base' is a garbage-collected type. std::unique_ptr cannot hold garbage-collected objects. + auto owned_base_array = std::make_unique(1); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +make_unique_gc_object.cpp:12:24: warning: [blink-gc] Disallowed use of 'make_unique' found; 'Derived' is a garbage-collected type. std::unique_ptr cannot hold garbage-collected objects. + auto owned_derived = std::make_unique(); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +make_unique_gc_object.cpp:13:22: warning: [blink-gc] Disallowed use of 'WrapUnique' found; 'Mixin' is a garbage-collected type. std::unique_ptr cannot hold garbage-collected objects. + auto owned_mixin = base::WrapUnique(static_cast(nullptr)); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +4 warnings generated. diff --git a/clang/blink_gc_plugin/tests/member_in_offheap_class.cpp b/clang/blink_gc_plugin/tests/member_in_offheap_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..23530f5c6b7de405c5545b679f38526bf4304311 --- /dev/null +++ b/clang/blink_gc_plugin/tests/member_in_offheap_class.cpp @@ -0,0 +1,22 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "member_in_offheap_class.h" + +namespace blink { + +void OffHeapObject::Trace(Visitor* visitor) const { + visitor->Trace(m_obj); + visitor->Trace(m_weak); +} + +void PartObject::Trace(Visitor* visitor) const { + visitor->Trace(m_obj); +} + +void DerivedPartObject::Trace(Visitor* visitor) const { + visitor->Trace(m_obj1); + PartObject::Trace(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/member_in_offheap_class.h b/clang/blink_gc_plugin/tests/member_in_offheap_class.h new file mode 100644 index 0000000000000000000000000000000000000000..ba003f47ff2873f2294d4bb0c53ee0ec18bb02e9 --- /dev/null +++ b/clang/blink_gc_plugin/tests/member_in_offheap_class.h @@ -0,0 +1,64 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEMBER_IN_OFFHEAP_CLASS_H_ +#define MEMBER_IN_OFFHEAP_CLASS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { }; + +class OffHeapObject { +public: + OffHeapObject(Member& ref) : m_ref(ref) {} + + void Trace(Visitor*) const; + +private: + Member m_obj; // Must not contain Member. + WeakMember m_weak; // Must not contain WeakMember. + Persistent > > m_objs; // OK + Member* m_ptr; // Member may move + Member& m_ref; // Member may move +}; + +class StackObject { + STACK_ALLOCATED(); + StackObject(Member& ref) : m_ref(ref) {} + + private: + HeapObject* m_obj; // OK + Member* m_ptr; // OK + Member& m_ref; // OK + HeapVector> m_heapVectorMemberOff; // NOT OK +}; + +class DerivedStackObject : public StackObject { + private: + HeapObject* m_obj1; // OK + HeapVector> m_heapVectorMemberOff1; // NOT OK +}; + +class PartObject { + DISALLOW_NEW(); + + public: + virtual void Trace(Visitor*) const; + + private: + Member m_obj; // OK +}; + +class DerivedPartObject : public PartObject { + public: + void Trace(Visitor*) const override; + + private: + Member m_obj1; // OK +}; +} + +#endif diff --git a/clang/blink_gc_plugin/tests/member_in_offheap_class.txt b/clang/blink_gc_plugin/tests/member_in_offheap_class.txt new file mode 100644 index 0000000000000000000000000000000000000000..ae7d92f867624a46c34f59d63ac3523ab0cd361b --- /dev/null +++ b/clang/blink_gc_plugin/tests/member_in_offheap_class.txt @@ -0,0 +1,29 @@ +In file included from member_in_offheap_class.cpp:5: +./member_in_offheap_class.h:14:1: warning: [blink-gc] Class 'OffHeapObject' contains invalid fields. +class OffHeapObject { +^ +./member_in_offheap_class.h:21:5: note: [blink-gc] Member field 'm_obj' in unmanaged class declared here: + Member m_obj; // Must not contain Member. + ^ +./member_in_offheap_class.h:22:5: note: [blink-gc] Member field 'm_weak' in unmanaged class declared here: + WeakMember m_weak; // Must not contain WeakMember. + ^ +./member_in_offheap_class.h:24:5: note: [blink-gc] Pointer to Member field 'm_ptr' in unmanaged class declared here: + Member* m_ptr; // Member may move + ^ +./member_in_offheap_class.h:25:5: note: [blink-gc] Pointer to Member field 'm_ref' in unmanaged class declared here: + Member& m_ref; // Member may move + ^ +./member_in_offheap_class.h:28:1: warning: [blink-gc] Class 'StackObject' contains invalid fields. +class StackObject { +^ +./member_in_offheap_class.h:36:3: note: [blink-gc] Member field 'm_heapVectorMemberOff' to non-GC managed class declared here: + HeapVector> m_heapVectorMemberOff; // NOT OK + ^ +./member_in_offheap_class.h:39:1: warning: [blink-gc] Class 'DerivedStackObject' contains invalid fields. +class DerivedStackObject : public StackObject { +^ +./member_in_offheap_class.h:42:3: note: [blink-gc] Member field 'm_heapVectorMemberOff1' to non-GC managed class declared here: + HeapVector> m_heapVectorMemberOff1; // NOT OK + ^ +3 warnings generated. diff --git a/clang/blink_gc_plugin/tests/member_on_stack.cpp b/clang/blink_gc_plugin/tests/member_on_stack.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f6aa09ce637c882b13b7006c5e94ff77cce1449 --- /dev/null +++ b/clang/blink_gc_plugin/tests/member_on_stack.cpp @@ -0,0 +1,31 @@ +// Copyright 2021 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "member_on_stack.h" + +namespace blink { + +namespace { + +void FreeMethod() { + Member strong; + WeakMember weak; + UntracedMember untraced; + Member* ptr; + Member& ref = strong; +} + +void MethodWithArg(Member) {} + +void MethodWithConstArg(const Member) {} + +} // namespace + +void HeapObject::Trace(Visitor* visitor) const {} + +void GCedWithMember::Trace(Visitor* v) const { + v->Trace(member_); +} + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/member_on_stack.flags b/clang/blink_gc_plugin/tests/member_on_stack.flags new file mode 100644 index 0000000000000000000000000000000000000000..00c50361f62ac60cbebc86cb118134273bddad6c --- /dev/null +++ b/clang/blink_gc_plugin/tests/member_on_stack.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-blink-gc-plugin -Xclang enable-members-on-stack-check diff --git a/clang/blink_gc_plugin/tests/member_on_stack.h b/clang/blink_gc_plugin/tests/member_on_stack.h new file mode 100644 index 0000000000000000000000000000000000000000..c150d03e969e120e938185284a650a1905857c1e --- /dev/null +++ b/clang/blink_gc_plugin/tests/member_on_stack.h @@ -0,0 +1,33 @@ +// Copyright 2021 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEMBER_ON_STACK_H +#define MEMBER_ON_STACK_H + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { + public: + void Trace(Visitor*) const; + + void DoSomething() { + Member strong; + WeakMember weak; + Member* ptr; + Member& ref = strong; + } +}; + +class GCedWithMember : public GarbageCollected { + public: + void Trace(Visitor*) const; + + Member member_; +}; + +} // namespace blink + +#endif // MEMBER_ON_STACK_H \ No newline at end of file diff --git a/clang/blink_gc_plugin/tests/member_on_stack.txt b/clang/blink_gc_plugin/tests/member_on_stack.txt new file mode 100644 index 0000000000000000000000000000000000000000..a9e4b69a78ee1697a950fcaf16bd8399f843b348 --- /dev/null +++ b/clang/blink_gc_plugin/tests/member_on_stack.txt @@ -0,0 +1,20 @@ +In file included from member_on_stack.cpp:5: +./member_on_stack.h:17:5: warning: [blink-gc] Member variable strong declared on stack here (use raw pointer or reference instead): + Member strong; + ^~~~~~~~~~~~~~~~~~~~~~~~~ +./member_on_stack.h:18:5: warning: [blink-gc] Member variable weak declared on stack here (use raw pointer or reference instead): + WeakMember weak; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +member_on_stack.cpp:12:3: warning: [blink-gc] Member variable strong declared on stack here (use raw pointer or reference instead): + Member strong; + ^~~~~~~~~~~~~~~~~~~~~~~~~ +member_on_stack.cpp:13:3: warning: [blink-gc] Member variable weak declared on stack here (use raw pointer or reference instead): + WeakMember weak; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +member_on_stack.cpp:19:20: warning: [blink-gc] Member variable declared on stack here (use raw pointer or reference instead): +void MethodWithArg(Member) {} + ^~~~~~~~~~~~~~~~~~ +member_on_stack.cpp:21:25: warning: [blink-gc] Member variable declared on stack here (use raw pointer or reference instead): +void MethodWithConstArg(const Member) {} + ^~~~~~~~~~~~~~~~~~~~~~~~ +6 warnings generated. diff --git a/clang/blink_gc_plugin/tests/non_virtual_trace.cpp b/clang/blink_gc_plugin/tests/non_virtual_trace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e10f03c75736e308df376a3cc2d1c138187315da --- /dev/null +++ b/clang/blink_gc_plugin/tests/non_virtual_trace.cpp @@ -0,0 +1,18 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "non_virtual_trace.h" + +namespace blink { + +void A::Trace(Visitor* visitor) const {} + +void C::Trace(Visitor* visitor) const { + B::Trace(visitor); +} + +void D::Trace(Visitor* visitor) const { + B::Trace(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/non_virtual_trace.h b/clang/blink_gc_plugin/tests/non_virtual_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..6b820d8498dde236469ff52993376852c7a0f9ba --- /dev/null +++ b/clang/blink_gc_plugin/tests/non_virtual_trace.h @@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NON_VIRTUAL_TRACE_H_ +#define NON_VIRTUAL_TRACE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class A : public GarbageCollected { +public: + void Trace(Visitor*) const; +}; + +class B : public A { +}; + +class C : public B { +public: + void Trace(Visitor*) const; // Cannot override a non-virtual Trace. +}; + +class D : public B { +public: + virtual void Trace(Visitor*) const; // Cannot override a non-virtual Trace. +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/non_virtual_trace.txt b/clang/blink_gc_plugin/tests/non_virtual_trace.txt new file mode 100644 index 0000000000000000000000000000000000000000..de7cd90efc11af4510e04a96f9fd44ce4f630b2e --- /dev/null +++ b/clang/blink_gc_plugin/tests/non_virtual_trace.txt @@ -0,0 +1,17 @@ +In file included from non_virtual_trace.cpp:5: +./non_virtual_trace.h:12:1: warning: [blink-gc] Left-most base class 'A' of derived class 'D' must define a virtual trace method. +class A : public GarbageCollected { +^ +non_virtual_trace.cpp:11:1: warning: [blink-gc] Class 'C' overrides non-virtual trace of base class 'A'. +void C::Trace(Visitor* visitor) const { +^ +./non_virtual_trace.h:14:2: note: [blink-gc] Non-virtual trace method declared here: + void Trace(Visitor*) const; + ^ +non_virtual_trace.cpp:15:1: warning: [blink-gc] Class 'D' overrides non-virtual trace of base class 'A'. +void D::Trace(Visitor* visitor) const { +^ +./non_virtual_trace.h:14:2: note: [blink-gc] Non-virtual trace method declared here: + void Trace(Visitor*) const; + ^ +3 warnings generated. diff --git a/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.cpp b/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fb7149d096af6db3988c1448a60ff6d4e338d275 --- /dev/null +++ b/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.cpp @@ -0,0 +1,137 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "off-heap-collections-of-gced.h" + +namespace blink { + +class WithCollections : public GarbageCollected { + public: + virtual void Trace(Visitor* v) const { + (void)set_; + (void)vector_; + (void)map_key_; + (void)map_value_; + (void)set_ptr_; + (void)vector_ref_; + (void)map_const_; + (void)vector_pair_; + (void)ignored_set_; + (void)array_; + (void)array_of_vectors_; + + v->Trace(heap_hash_set_); + v->Trace(heap_deque_); + v->Trace(heap_vector_); + v->Trace(heap_linked_hash_set_); + v->Trace(heap_hash_counted_set_); + v->Trace(heap_hash_map_key_); + v->Trace(heap_hash_map_value_); + + // The following calls are not needed and are added merely to silence false + // untraced field errors. + v->Trace(wtf_hash_set_); + v->Trace(wtf_deque_); + v->Trace(wtf_vector_); + v->Trace(wtf_hash_map_key_); + v->Trace(wtf_hash_map_value_); + } + + private: + // Bad stl collections: + std::set set_; + std::vector vector_; + std::map map_key_; + std::unordered_map map_value_; + std::unordered_set set_ptr_; + std::vector vector_ref_; + std::map map_const_; + std::vector> vector_pair_; + std::array array_; + std::array, 4> array_of_vectors_; + + // Bad WTF collections: + WTF::HashSet wtf_hash_set_; + WTF::Deque wtf_deque_; + WTF::Vector wtf_vector_; + WTF::LinkedHashSet wtf_linked_hash_set_; + WTF::HashCountedSet wtf_hash_counted_set_; + WTF::HashMap wtf_hash_map_key_; + WTF::HashMap wtf_hash_map_value_; + + // Good collections: + blink::HeapHashSet heap_hash_set_; + blink::HeapDeque heap_deque_; + blink::HeapVector heap_vector_; + blink::HeapLinkedHashSet heap_linked_hash_set_; + blink::HeapHashCountedSet heap_hash_counted_set_; + blink::HeapHashMap heap_hash_map_key_; + blink::HeapHashMap heap_hash_map_value_; + + GC_PLUGIN_IGNORE("For testing") + std::set ignored_set_; +}; + +class StackAllocated { + STACK_ALLOCATED(); + + public: + StackAllocated() { + (void)array_; + (void)array_of_vectors_; + } + + private: + std::array array_; + std::array, 4> array_of_vectors_; +}; + +void DisallowedUseOfCollections() { + // Bad stl collections: + std::set set; + (void)set; + std::vector vector; + (void)vector; + std::map map_key; + (void)map_key; + std::unordered_map map_value; + (void)map_value; + std::unordered_set set_ptr; + (void)set_ptr; + std::vector vector_ref; + (void)vector_ref; + std::map map_const; + (void)map_const; + std::vector> vector_pair; + (void)vector_pair; + + // Bad WTF collections: + WTF::HashSet wtf_hash_set; + WTF::Deque wtf_deque; + WTF::Vector wtf_vector; + WTF::LinkedHashSet wtf_linked_hash_set; + WTF::HashCountedSet wtf_hash_counted_set; + WTF::HashMap wtf_hash_map_key; + WTF::HashMap wtf_hash_map_value; + + // Good collections: + blink::HeapHashSet heap_hash_set; + blink::HeapDeque heap_deque; + blink::HeapVector heap_vector; + blink::HeapLinkedHashSet heap_linked_hash_set; + blink::HeapHashCountedSet heap_hash_counted_set; + blink::HeapHashMap heap_hash_map_key; + blink::HeapHashMap heap_hash_map_value; + + std::array array; + (void)array; + std::array, 4> array_of_vectors; + (void)array_of_vectors; + + GC_PLUGIN_IGNORE("For testing") + std::set ignored_set; + (void)ignored_set; +} + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.h b/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.h new file mode 100644 index 0000000000000000000000000000000000000000..64c8ebf342343a728e7ccd128b1e17a0adc736af --- /dev/null +++ b/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.h @@ -0,0 +1,29 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COLLECTION_OF_GCED_H_ +#define COLLECTION_OF_GCED_H_ + +#include "heap/stubs.h" + +namespace blink { + +class Base : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +class Derived : public Base { + public: + void Trace(Visitor* visitor) const override { Base::Trace(visitor); } +}; + +class Mixin : public GarbageCollectedMixin { + public: + void Trace(Visitor*) const {} +}; + +} // namespace blink + +#endif // COLLECTION_OF_GCED_H_ diff --git a/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.txt b/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.txt new file mode 100644 index 0000000000000000000000000000000000000000..d50681c3a3aff645765b57f133169b51728e51ac --- /dev/null +++ b/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.txt @@ -0,0 +1,115 @@ +off-heap-collections-of-gced.cpp:9:1: warning: [blink-gc] Class 'WithCollections' contains invalid fields. +class WithCollections : public GarbageCollected { +^ +off-heap-collections-of-gced.cpp:47:3: note: [blink-gc] Raw pointer field 'set_ptr_' to a GC managed class declared here: + std::unordered_set set_ptr_; + ^ +off-heap-collections-of-gced.cpp:48:3: note: [blink-gc] Reference pointer field 'vector_ref_' to a GC managed class declared here: + std::vector vector_ref_; + ^ +off-heap-collections-of-gced.cpp:58:3: note: [blink-gc] Raw pointer field 'wtf_linked_hash_set_' to a GC managed class declared here: + WTF::LinkedHashSet wtf_linked_hash_set_; + ^ +off-heap-collections-of-gced.cpp:59:3: note: [blink-gc] Reference pointer field 'wtf_hash_counted_set_' to a GC managed class declared here: + WTF::HashCountedSet wtf_hash_counted_set_; + ^ +off-heap-collections-of-gced.cpp:43:3: warning: [blink-gc] Disallowed collection 'set' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::set set_; + ^~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:44:3: warning: [blink-gc] Disallowed collection 'vector' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::vector vector_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:45:3: warning: [blink-gc] Disallowed collection 'map' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::map map_key_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:46:3: warning: [blink-gc] Disallowed collection 'unordered_map' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::unordered_map map_value_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:47:3: warning: [blink-gc] Disallowed collection 'unordered_set' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::unordered_set set_ptr_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:48:3: warning: [blink-gc] Disallowed collection 'vector' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::vector vector_ref_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:49:3: warning: [blink-gc] Disallowed collection 'map' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::map map_const_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:50:3: warning: [blink-gc] Disallowed collection 'vector>' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::vector> vector_pair_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:51:3: warning: [blink-gc] Disallowed collection 'array' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::array array_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:55:3: warning: [blink-gc] Disallowed collection 'HashSet' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::HashSet wtf_hash_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:56:3: warning: [blink-gc] Disallowed collection 'Deque' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::Deque wtf_deque_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:57:3: warning: [blink-gc] Disallowed collection 'Vector' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::Vector wtf_vector_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:58:3: warning: [blink-gc] Disallowed collection 'LinkedHashSet' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::LinkedHashSet wtf_linked_hash_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:59:3: warning: [blink-gc] Disallowed collection 'HashCountedSet' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::HashCountedSet wtf_hash_counted_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:60:3: warning: [blink-gc] Disallowed collection 'HashMap' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::HashMap wtf_hash_map_key_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:61:3: warning: [blink-gc] Disallowed collection 'HashMap' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::HashMap wtf_hash_map_value_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:86:3: warning: [blink-gc] Disallowed collection 'array' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::array array_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:92:3: warning: [blink-gc] Disallowed collection 'set' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::set set; + ^~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:94:3: warning: [blink-gc] Disallowed collection 'vector' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::vector vector; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:96:3: warning: [blink-gc] Disallowed collection 'map' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::map map_key; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:98:3: warning: [blink-gc] Disallowed collection 'unordered_map' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::unordered_map map_value; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:100:3: warning: [blink-gc] Disallowed collection 'unordered_set' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::unordered_set set_ptr; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:102:3: warning: [blink-gc] Disallowed collection 'vector' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::vector vector_ref; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:104:3: warning: [blink-gc] Disallowed collection 'map' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::map map_const; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:106:3: warning: [blink-gc] Disallowed collection 'vector>' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::vector> vector_pair; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:110:3: warning: [blink-gc] Disallowed collection 'HashSet' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::HashSet wtf_hash_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:111:3: warning: [blink-gc] Disallowed collection 'Deque' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::Deque wtf_deque; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:112:3: warning: [blink-gc] Disallowed collection 'Vector' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::Vector wtf_vector; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:113:3: warning: [blink-gc] Disallowed collection 'LinkedHashSet' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::LinkedHashSet wtf_linked_hash_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:114:3: warning: [blink-gc] Disallowed collection 'HashCountedSet' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::HashCountedSet wtf_hash_counted_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:115:3: warning: [blink-gc] Disallowed collection 'HashMap' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::HashMap wtf_hash_map_key; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:116:3: warning: [blink-gc] Disallowed collection 'HashMap' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + WTF::HashMap wtf_hash_map_value; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-gced.cpp:127:3: warning: [blink-gc] Disallowed collection 'array' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. + std::array array; + ^~~~~~~~~~~~~~~~~~~~~~~~~ +34 warnings generated. diff --git a/clang/blink_gc_plugin/tests/off-heap-collections-of-members.cpp b/clang/blink_gc_plugin/tests/off-heap-collections-of-members.cpp new file mode 100644 index 0000000000000000000000000000000000000000..894d0b9046e72fbe1cfa7a9a67f9836327d895a4 --- /dev/null +++ b/clang/blink_gc_plugin/tests/off-heap-collections-of-members.cpp @@ -0,0 +1,148 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "off-heap-collections-of-members.h" + +namespace blink { + +class WithCollections : public GarbageCollected { + public: + virtual void Trace(Visitor* v) const { + (void)set_; + (void)vector_; + (void)map_key_; + (void)map_value_; + (void)set_ptr_; + (void)vector_ref_; + (void)map_const_; + (void)set_untraced_; + (void)vector_untraced_; + (void)map_key_untraced_; + (void)map_value_untraced_; + (void)vector_pair_; + (void)ignored_set_; + + for (int i = 0; i < 4; ++i) { + v->Trace(array_[i]); + } + + v->Trace(heap_hash_set_); + v->Trace(heap_deque_); + v->Trace(heap_vector_); + v->Trace(heap_linked_hash_set_); + v->Trace(heap_hash_counted_set_); + v->Trace(heap_hash_map_key_); + v->Trace(heap_hash_map_value_); + + // The following calls are not needed and are added merely to silence false + // untraced field errors. + v->Trace(wtf_hash_set_); + v->Trace(wtf_deque_); + v->Trace(wtf_vector_); + v->Trace(wtf_hash_map_key_); + v->Trace(wtf_hash_map_value_); + } + + private: + // Bad stl collections: + std::set> set_; + std::vector> vector_; + std::map, int> map_key_; + std::unordered_map> map_value_; + std::unordered_set*> set_ptr_; + std::vector&> vector_ref_; + std::map, int> map_const_; + std::vector, int>> vector_pair_; + std::array, 4> array_; + + // Bad WTF collections: + WTF::HashSet> wtf_hash_set_; + WTF::Deque> wtf_deque_; + WTF::Vector> wtf_vector_; + WTF::LinkedHashSet*> wtf_linked_hash_set_; + WTF::HashCountedSet&> wtf_hash_counted_set_; + WTF::HashMap, bool> wtf_hash_map_key_; + WTF::HashMap> wtf_hash_map_value_; + + // Good collections: + blink::HeapHashSet> heap_hash_set_; + blink::HeapDeque> heap_deque_; + blink::HeapVector> heap_vector_; + blink::HeapLinkedHashSet> heap_linked_hash_set_; + blink::HeapHashCountedSet> heap_hash_counted_set_; + blink::HeapHashMap, bool> heap_hash_map_key_; + blink::HeapHashMap> heap_hash_map_value_; + std::set> set_untraced_; + std::vector> vector_untraced_; + std::map, int> map_key_untraced_; + std::map> map_value_untraced_; + + GC_PLUGIN_IGNORE("For testing") + std::set> ignored_set_; +}; + +class StackAllocated { + STACK_ALLOCATED(); + + public: + StackAllocated() { (void)array_; } + + private: + std::array, 4> array_; +}; + +void DisallowedUseOfCollections() { + // Bad stl collections: + std::set> set; + (void)set; + std::vector> vector; + (void)vector; + std::map, int> map_key; + (void)map_key; + std::unordered_map> map_value; + (void)map_value; + std::unordered_set*> set_ptr; + (void)set_ptr; + std::vector&> vector_ref; + (void)vector_ref; + std::map, int> map_const; + (void)map_const; + std::vector, int>> vector_pair; + (void)vector_pair; + + // Bad WTF collections: + WTF::HashSet> wtf_hash_set; + WTF::Deque> wtf_deque; + WTF::Vector> wtf_vector; + WTF::LinkedHashSet*> wtf_linked_hash_set; + WTF::HashCountedSet&> wtf_hash_counted_set; + WTF::HashMap, bool> wtf_hash_map_key; + WTF::HashMap> wtf_hash_map_value; + + // Good collections: + blink::HeapHashSet> heap_hash_set; + blink::HeapDeque> heap_deque; + blink::HeapVector> heap_vector; + blink::HeapLinkedHashSet> heap_linked_hash_set; + blink::HeapHashCountedSet> heap_hash_counted_set; + blink::HeapHashMap, bool> heap_hash_map_key; + blink::HeapHashMap> heap_hash_map_value; + std::set> set_untraced; + (void)set_untraced; + std::vector> vector_untraced; + (void)vector_untraced; + std::map, int> map_key_untraced; + (void)map_key_untraced; + std::map> map_value_untraced; + (void)map_value_untraced; + + std::array, 4> array; + (void)array; + + GC_PLUGIN_IGNORE("For testing") + std::set> ignored_set; + (void)ignored_set; +} + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/off-heap-collections-of-members.h b/clang/blink_gc_plugin/tests/off-heap-collections-of-members.h new file mode 100644 index 0000000000000000000000000000000000000000..fb137f3a1856be641d82ed58f38f59abae7cc957 --- /dev/null +++ b/clang/blink_gc_plugin/tests/off-heap-collections-of-members.h @@ -0,0 +1,19 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COLLECTION_OF_MEMBERS_H_ +#define COLLECTION_OF_MEMBERS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class Base : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +} // namespace blink + +#endif // COLLECTION_OF_MEMBERS_H_ diff --git a/clang/blink_gc_plugin/tests/off-heap-collections-of-members.txt b/clang/blink_gc_plugin/tests/off-heap-collections-of-members.txt new file mode 100644 index 0000000000000000000000000000000000000000..58b860f30ca603748e4d300938b06ac47ad2404f --- /dev/null +++ b/clang/blink_gc_plugin/tests/off-heap-collections-of-members.txt @@ -0,0 +1,91 @@ +off-heap-collections-of-members.cpp:49:3: warning: [blink-gc] Disallowed collection 'set>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::set> set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:50:3: warning: [blink-gc] Disallowed collection 'vector>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::vector> vector_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:51:3: warning: [blink-gc] Disallowed collection 'map, int>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::map, int> map_key_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:52:3: warning: [blink-gc] Disallowed collection 'unordered_map>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::unordered_map> map_value_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:53:3: warning: [blink-gc] Disallowed collection 'unordered_set *>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::unordered_set*> set_ptr_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:54:3: warning: [blink-gc] Disallowed collection 'vector &>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::vector&> vector_ref_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:55:3: warning: [blink-gc] Disallowed collection 'map, int>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::map, int> map_const_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:56:3: warning: [blink-gc] Disallowed collection 'vector, int>>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::vector, int>> vector_pair_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:60:3: warning: [blink-gc] Disallowed collection 'HashSet>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::HashSet> wtf_hash_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:61:3: warning: [blink-gc] Disallowed collection 'Deque>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::Deque> wtf_deque_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:62:3: warning: [blink-gc] Disallowed collection 'Vector>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::Vector> wtf_vector_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:63:3: warning: [blink-gc] Disallowed collection 'LinkedHashSet *>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::LinkedHashSet*> wtf_linked_hash_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:64:3: warning: [blink-gc] Disallowed collection 'HashCountedSet &>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::HashCountedSet&> wtf_hash_counted_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:65:3: warning: [blink-gc] Disallowed collection 'HashMap, bool>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::HashMap, bool> wtf_hash_map_key_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:66:3: warning: [blink-gc] Disallowed collection 'HashMap>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::HashMap> wtf_hash_map_value_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:97:3: warning: [blink-gc] Disallowed collection 'set>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::set> set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:99:3: warning: [blink-gc] Disallowed collection 'vector>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::vector> vector; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:101:3: warning: [blink-gc] Disallowed collection 'map, int>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::map, int> map_key; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:103:3: warning: [blink-gc] Disallowed collection 'unordered_map>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::unordered_map> map_value; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:105:3: warning: [blink-gc] Disallowed collection 'unordered_set *>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::unordered_set*> set_ptr; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:107:3: warning: [blink-gc] Disallowed collection 'vector &>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::vector&> vector_ref; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:109:3: warning: [blink-gc] Disallowed collection 'map, int>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::map, int> map_const; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:111:3: warning: [blink-gc] Disallowed collection 'vector, int>>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + std::vector, int>> vector_pair; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:115:3: warning: [blink-gc] Disallowed collection 'HashSet>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::HashSet> wtf_hash_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:116:3: warning: [blink-gc] Disallowed collection 'Deque>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::Deque> wtf_deque; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:117:3: warning: [blink-gc] Disallowed collection 'Vector>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::Vector> wtf_vector; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:118:3: warning: [blink-gc] Disallowed collection 'LinkedHashSet *>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::LinkedHashSet*> wtf_linked_hash_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:119:3: warning: [blink-gc] Disallowed collection 'HashCountedSet &>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::HashCountedSet&> wtf_hash_counted_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:120:3: warning: [blink-gc] Disallowed collection 'HashMap, bool>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::HashMap, bool> wtf_hash_map_key; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +off-heap-collections-of-members.cpp:121:3: warning: [blink-gc] Disallowed collection 'HashMap>' found; 'BasicMember' is a Member type. Use heap collections to hold Members. + WTF::HashMap> wtf_hash_map_value; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +30 warnings generated. diff --git a/clang/blink_gc_plugin/tests/optional_gc_object.cpp b/clang/blink_gc_plugin/tests/optional_gc_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..04471b2a1888c4d2104ebef14af576910a0c3321 --- /dev/null +++ b/clang/blink_gc_plugin/tests/optional_gc_object.cpp @@ -0,0 +1,138 @@ +// Copyright 2017 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "optional_gc_object.h" + +namespace blink { + +class WithOpt : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} + + private: + absl::optional optional_field_; // Optional fields are disallowed. + std::optional optional_field2_; + absl::optional + optional_field3_; // Optional fields are disallowed. + std::optional optional_field4_; + absl::optional> optional_field5_; + std::optional> optional_field6_; + base::raw_ptr raw_ptr_field_; + base::raw_ptr raw_ptr_field2_; + base::raw_ref raw_ref_field_; + base::raw_ref raw_ref_field2_; +}; + +void DisallowedUseOfOptional() { + { + absl::optional optional_base; + (void)optional_base; + + absl::optional optional_derived; + (void)optional_derived; + + absl::optional optional_traceable; // Must be okay. + (void)optional_traceable; + + absl::optional> optional_member; // Must be okay. + (void)optional_member; + + new absl::optional; // New expression with gced optionals are not + // allowed. + + new absl::optional; // New expression with traceable optionals + // are not allowed. + + new absl::optional>; + } + + { + std::optional optional_base; + (void)optional_base; + + std::optional optional_derived; + (void)optional_derived; + + std::optional optional_traceable; // Must be okay. + (void)optional_traceable; + + std::optional> optional_member; // Must be okay. + (void)optional_member; + + new std::optional; // New expression with gced optionals are not + // allowed. + + new std::optional; // New expression with traceable optionals + // are not allowed. + + new std::optional>; + } + + { + base::raw_ptr raw_ptr_base; + (void)raw_ptr_base; + + base::raw_ptr raw_ptr_derived; + (void)raw_ptr_derived; + + base::raw_ptr raw_ptr_traceable; + (void)raw_ptr_traceable; + + new base::raw_ptr; // New expression with gced raw_ptrs are not + // allowed. + + new base::raw_ptr; // New expression with traceable raw_ptrs + // are not allowed. + } + + { + base::raw_ref raw_ref_base; + (void)raw_ref_base; + + base::raw_ref raw_ref_derived; + (void)raw_ref_derived; + + base::raw_ref raw_ref_traceable; + (void)raw_ref_traceable; + + new base::raw_ref; // New expression with gced raw_refs are not + // allowed. + + new base::raw_ref; // New expression with traceable raw_refs + // are not allowed. + } +} + +class OnStack { + STACK_ALLOCATED(); + + public: + OnStack() { + (void)optional_field_; + (void)optional_field2_; + (void)optional_field3_; + (void)optional_field4_; + (void)optional_field5_; + (void)optional_field6_; + (void)raw_ptr_field_; + (void)raw_ptr_field2_; + (void)raw_ref_field_; + (void)raw_ref_field2_; + } + + private: + // All fields are ok since the class is stack allocated. + absl::optional optional_field_; + std::optional optional_field2_; + absl::optional optional_field3_; + std::optional optional_field4_; + absl::optional> optional_field5_; + std::optional> optional_field6_; + base::raw_ptr raw_ptr_field_; + base::raw_ptr raw_ptr_field2_; + base::raw_ref raw_ref_field_; + base::raw_ref raw_ref_field2_; +}; + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/optional_gc_object.h b/clang/blink_gc_plugin/tests/optional_gc_object.h new file mode 100644 index 0000000000000000000000000000000000000000..b2213c956155bcd7f43155ca3c8cb6179f07e667 --- /dev/null +++ b/clang/blink_gc_plugin/tests/optional_gc_object.h @@ -0,0 +1,36 @@ +// Copyright 2017 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef OPTIONAL_GC_OBJECT_H_ +#define OPTIONAL_GC_OBJECT_H_ + +#include "heap/stubs.h" + +namespace blink { + +class Base : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +class Derived : public Base { + public: + void Trace(Visitor* visitor) const override { Base::Trace(visitor); } +}; + +class Mixin : public GarbageCollectedMixin { + public: + void Trace(Visitor*) const {} +}; + +class Traceable { + DISALLOW_NEW(); + + public: + void Trace(Visitor*) const {} +}; + +} // namespace blink + +#endif // OPTIONAL_GC_OBJECT_H_ diff --git a/clang/blink_gc_plugin/tests/optional_gc_object.txt b/clang/blink_gc_plugin/tests/optional_gc_object.txt new file mode 100644 index 0000000000000000000000000000000000000000..e53aeeca6d51ba30f1d329c515d84fe07647d646 --- /dev/null +++ b/clang/blink_gc_plugin/tests/optional_gc_object.txt @@ -0,0 +1,109 @@ +optional_gc_object.cpp:14:3: warning: [blink-gc] Disallowed optional field or variable of type 'optional' found; 'Base' is a garbage-collected or traceable type. Optional fields and variables cannot hold garbage-collected or traceable objects. + absl::optional optional_field_; // Optional fields are disallowed. + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:15:3: warning: [blink-gc] Disallowed optional field or variable of type 'optional' found; 'Base' is a garbage-collected or traceable type. Optional fields and variables cannot hold garbage-collected or traceable objects. + std::optional optional_field2_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:16:3: warning: [blink-gc] Disallowed optional field or variable of type 'optional' found; 'Traceable' is a garbage-collected or traceable type. Optional fields and variables cannot hold garbage-collected or traceable objects. + absl::optional + ^~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:18:3: warning: [blink-gc] Disallowed optional field or variable of type 'optional' found; 'Traceable' is a garbage-collected or traceable type. Optional fields and variables cannot hold garbage-collected or traceable objects. + std::optional optional_field4_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:19:3: warning: [blink-gc] Disallowed optional field of type 'optional>' found; 'BasicMember' is a Member/WeakMember type. Optional fields and variables cannot hold Members. + absl::optional> optional_field5_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:20:3: warning: [blink-gc] Disallowed optional field of type 'optional>' found; 'BasicMember' is a Member/WeakMember type. Optional fields and variables cannot hold Members. + std::optional> optional_field6_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:21:3: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ptr' found; 'Base' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ptr raw_ptr_field_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:22:3: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ptr' found; 'Traceable' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ptr raw_ptr_field2_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:23:3: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ref' found; 'Base' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ref raw_ref_field_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:24:3: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ref' found; 'Traceable' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ref raw_ref_field2_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:41:5: warning: [blink-gc] Disallowed new-expression of 'optional' found; 'Base' is a garbage-collected or traceable type. Optional fields cannot hold garbage-collected or traceable objects. + new absl::optional; // New expression with gced optionals are not + ^~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:44:5: warning: [blink-gc] Disallowed new-expression of 'optional' found; 'Traceable' is a garbage-collected or traceable type. Optional fields cannot hold garbage-collected or traceable objects. + new absl::optional; // New expression with traceable optionals + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:47:5: warning: [blink-gc] Disallowed new-expression of 'optional>' found; 'BasicMember' is a Member/WeakMember type. Optional fields cannot hold Members. + new absl::optional>; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:29:5: warning: [blink-gc] Disallowed optional field or variable of type 'optional' found; 'Base' is a garbage-collected or traceable type. Optional fields and variables cannot hold garbage-collected or traceable objects. + absl::optional optional_base; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:32:5: warning: [blink-gc] Disallowed optional field or variable of type 'optional' found; 'Derived' is a garbage-collected or traceable type. Optional fields and variables cannot hold garbage-collected or traceable objects. + absl::optional optional_derived; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:63:5: warning: [blink-gc] Disallowed new-expression of 'optional' found; 'Base' is a garbage-collected or traceable type. Optional fields cannot hold garbage-collected or traceable objects. + new std::optional; // New expression with gced optionals are not + ^~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:66:5: warning: [blink-gc] Disallowed new-expression of 'optional' found; 'Traceable' is a garbage-collected or traceable type. Optional fields cannot hold garbage-collected or traceable objects. + new std::optional; // New expression with traceable optionals + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:69:5: warning: [blink-gc] Disallowed new-expression of 'optional>' found; 'BasicMember' is a Member/WeakMember type. Optional fields cannot hold Members. + new std::optional>; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:51:5: warning: [blink-gc] Disallowed optional field or variable of type 'optional' found; 'Base' is a garbage-collected or traceable type. Optional fields and variables cannot hold garbage-collected or traceable objects. + std::optional optional_base; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:54:5: warning: [blink-gc] Disallowed optional field or variable of type 'optional' found; 'Derived' is a garbage-collected or traceable type. Optional fields and variables cannot hold garbage-collected or traceable objects. + std::optional optional_derived; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:82:5: warning: [blink-gc] Disallowed new-expression of 'raw_ptr' found; 'Base' is a garbage-collected or traceable type. Raw_ptr and raw_ref fields cannot hold garbage-collected or traceable objects. + new base::raw_ptr; // New expression with gced raw_ptrs are not + ^~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:85:5: warning: [blink-gc] Disallowed new-expression of 'raw_ptr' found; 'Traceable' is a garbage-collected or traceable type. Raw_ptr and raw_ref fields cannot hold garbage-collected or traceable objects. + new base::raw_ptr; // New expression with traceable raw_ptrs + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:73:5: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ptr' found; 'Base' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ptr raw_ptr_base; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:76:5: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ptr' found; 'Derived' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ptr raw_ptr_derived; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:79:5: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ptr' found; 'Traceable' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ptr raw_ptr_traceable; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:99:5: warning: [blink-gc] Disallowed new-expression of 'raw_ref' found; 'Base' is a garbage-collected or traceable type. Raw_ptr and raw_ref fields cannot hold garbage-collected or traceable objects. + new base::raw_ref; // New expression with gced raw_refs are not + ^~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:102:5: warning: [blink-gc] Disallowed new-expression of 'raw_ref' found; 'Traceable' is a garbage-collected or traceable type. Raw_ptr and raw_ref fields cannot hold garbage-collected or traceable objects. + new base::raw_ref; // New expression with traceable raw_refs + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:90:5: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ref' found; 'Base' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ref raw_ref_base; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:93:5: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ref' found; 'Derived' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ref raw_ref_derived; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:96:5: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ref' found; 'Traceable' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ref raw_ref_traceable; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:126:3: warning: [blink-gc] Disallowed optional field or variable of type 'optional' found; 'Base' is a garbage-collected or traceable type. Optional fields and variables cannot hold garbage-collected or traceable objects. + absl::optional optional_field_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:127:3: warning: [blink-gc] Disallowed optional field or variable of type 'optional' found; 'Base' is a garbage-collected or traceable type. Optional fields and variables cannot hold garbage-collected or traceable objects. + std::optional optional_field2_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:132:3: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ptr' found; 'Base' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ptr raw_ptr_field_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:133:3: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ptr' found; 'Traceable' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ptr raw_ptr_field2_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:134:3: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ref' found; 'Base' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ref raw_ref_field_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +optional_gc_object.cpp:135:3: warning: [blink-gc] Disallowed raw_ptr or raw_ref field or variable of type 'raw_ref' found; 'Traceable' is a garbage-collected or traceable type. Raw_ptr and raw_ref field and variable cannot hold garbage-collected or traceable objects. + base::raw_ref raw_ref_field2_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +36 warnings generated. diff --git a/clang/blink_gc_plugin/tests/part_object_to_gc_derived_class.cpp b/clang/blink_gc_plugin/tests/part_object_to_gc_derived_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ac4f41dbf8e82cd077366f35ff597cda91438d0 --- /dev/null +++ b/clang/blink_gc_plugin/tests/part_object_to_gc_derived_class.cpp @@ -0,0 +1,12 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "part_object_to_gc_derived_class.h" + +namespace blink { + +void B::Trace(Visitor* visitor) const { + visitor->Trace(m_a); +} +} diff --git a/clang/blink_gc_plugin/tests/part_object_to_gc_derived_class.h b/clang/blink_gc_plugin/tests/part_object_to_gc_derived_class.h new file mode 100644 index 0000000000000000000000000000000000000000..194b08193a751ea58694f3d9eb776d8d7bbe7d60 --- /dev/null +++ b/clang/blink_gc_plugin/tests/part_object_to_gc_derived_class.h @@ -0,0 +1,24 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PART_OBJECT_TO_GC_DERIVED_CLASS_H_ +#define PART_OBJECT_TO_GC_DERIVED_CLASS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class A : public GarbageCollected { }; + +class B : public GarbageCollected { +public: + void Trace(Visitor*) const; + +private: + A m_a; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/part_object_to_gc_derived_class.txt b/clang/blink_gc_plugin/tests/part_object_to_gc_derived_class.txt new file mode 100644 index 0000000000000000000000000000000000000000..7c850579bbaaa3db267b7f0ca1e77d26e2733b67 --- /dev/null +++ b/clang/blink_gc_plugin/tests/part_object_to_gc_derived_class.txt @@ -0,0 +1,11 @@ +In file included from part_object_to_gc_derived_class.cpp:5: +./part_object_to_gc_derived_class.h:14:1: warning: [blink-gc] Class 'B' contains invalid fields. +class B : public GarbageCollected { +^ +./part_object_to_gc_derived_class.h:19:5: note: [blink-gc] Part-object field 'm_a' to a GC derived class declared here: + A m_a; + ^ +./part_object_to_gc_derived_class.h:19:5: warning: [blink-gc] Using GC managed class 'A' as field 'm_a' is not allowed (Allocate with MakeGarbageCollected and use Member or Persistent instead): + A m_a; + ^~~~~ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.cpp b/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..acc8a6fd5aa6796073231d4e42a212ef45311247 --- /dev/null +++ b/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.cpp @@ -0,0 +1,12 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "persistent_field_in_gc_managed_class.h" + +namespace blink { + +void HeapObject::Trace(Visitor* visitor) const { + visitor->Trace(m_parts); +} +} diff --git a/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.flags b/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.flags new file mode 100644 index 0000000000000000000000000000000000000000..a8f79287a603a656df4a0239a05a6a1f43d70270 --- /dev/null +++ b/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-blink-gc-plugin -Xclang enable-persistent-in-unique-ptr-check diff --git a/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.h b/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.h new file mode 100644 index 0000000000000000000000000000000000000000..5893b4bf562e4827526033af47896a775dffb791 --- /dev/null +++ b/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.h @@ -0,0 +1,38 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PERSISTENT_FIELD_IN_GC_MANAGED_CLASS_H_ +#define PERSISTENT_FIELD_IN_GC_MANAGED_CLASS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject; + +class PartObject { + DISALLOW_NEW(); +private: + Persistent m_obj; +}; + +class HeapObject : public GarbageCollected { +public: + explicit HeapObject(Persistent& ref) : m_ref(ref) {} + + void Trace(Visitor*) const; + +private: + PartObject m_part; + HeapVector m_parts; + std::unique_ptr m_unique_part; + Persistent>> m_objs; + WeakPersistent m_weakPersistent; + Persistent& m_ref; + Persistent* m_ptr; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.txt b/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.txt new file mode 100644 index 0000000000000000000000000000000000000000..38475c0514d8181e5d17843fd3518064cf5893f6 --- /dev/null +++ b/clang/blink_gc_plugin/tests/persistent_field_in_gc_managed_class.txt @@ -0,0 +1,53 @@ +In file included from persistent_field_in_gc_managed_class.cpp:5: +./persistent_field_in_gc_managed_class.h:20:1: warning: [blink-gc] Class 'HeapObject' contains GC root in field 'm_part'. +class HeapObject : public GarbageCollected { +^ +./persistent_field_in_gc_managed_class.h:27:5: note: [blink-gc] Field 'm_part' with embedded GC root in 'HeapObject' declared here: + PartObject m_part; + ^ +./persistent_field_in_gc_managed_class.h:17:5: note: [blink-gc] Field 'm_obj' defining a GC root declared here: + Persistent m_obj; + ^ +./persistent_field_in_gc_managed_class.h:20:1: warning: [blink-gc] Class 'HeapObject' contains GC root in field 'm_parts'. +class HeapObject : public GarbageCollected { +^ +./persistent_field_in_gc_managed_class.h:28:5: note: [blink-gc] Field 'm_parts' with embedded GC root in 'HeapObject' declared here: + HeapVector m_parts; + ^ +./persistent_field_in_gc_managed_class.h:17:5: note: [blink-gc] Field 'm_obj' defining a GC root declared here: + Persistent m_obj; + ^ +./persistent_field_in_gc_managed_class.h:20:1: warning: [blink-gc] Class 'HeapObject' contains GC root in field 'm_unique_part'. +class HeapObject : public GarbageCollected { +^ +./persistent_field_in_gc_managed_class.h:29:5: note: [blink-gc] Field 'm_unique_part' with embedded GC root in 'HeapObject' declared here: + std::unique_ptr m_unique_part; + ^ +./persistent_field_in_gc_managed_class.h:17:5: note: [blink-gc] Field 'm_obj' defining a GC root declared here: + Persistent m_obj; + ^ +./persistent_field_in_gc_managed_class.h:20:1: warning: [blink-gc] Class 'HeapObject' contains GC root in field 'm_objs'. +class HeapObject : public GarbageCollected { +^ +./persistent_field_in_gc_managed_class.h:30:5: note: [blink-gc] Field 'm_objs' defining a GC root declared here: + Persistent>> m_objs; + ^ +./persistent_field_in_gc_managed_class.h:20:1: warning: [blink-gc] Class 'HeapObject' contains GC root in field 'm_weakPersistent'. +class HeapObject : public GarbageCollected { +^ +./persistent_field_in_gc_managed_class.h:31:5: note: [blink-gc] Field 'm_weakPersistent' defining a GC root declared here: + WeakPersistent m_weakPersistent; + ^ +./persistent_field_in_gc_managed_class.h:20:1: warning: [blink-gc] Class 'HeapObject' contains a reference to a GC root in field 'm_ref'. Avoid holding references to GC roots. This should generally not be needed. +class HeapObject : public GarbageCollected { +^ +./persistent_field_in_gc_managed_class.h:32:5: note: [blink-gc] Field 'm_ref' defining reference to a GC root declared here: + Persistent& m_ref; + ^ +./persistent_field_in_gc_managed_class.h:20:1: warning: [blink-gc] Class 'HeapObject' contains a reference to a GC root in field 'm_ptr'. Avoid holding references to GC roots. This should generally not be needed. +class HeapObject : public GarbageCollected { +^ +./persistent_field_in_gc_managed_class.h:33:5: note: [blink-gc] Field 'm_ptr' defining reference to a GC root declared here: + Persistent* m_ptr; + ^ +7 warnings generated. diff --git a/clang/blink_gc_plugin/tests/persistent_no_trace.cpp b/clang/blink_gc_plugin/tests/persistent_no_trace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a7f4ae72fcc7412759ee12fa086010380f08d25 --- /dev/null +++ b/clang/blink_gc_plugin/tests/persistent_no_trace.cpp @@ -0,0 +1,15 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "persistent_no_trace.h" + +namespace blink { + +void Object::Trace(Visitor* visitor) const { + visitor->Trace(m_persistent); + visitor->Trace(m_weakPersistent); + visitor->Trace(m_crossThreadPersistent); + visitor->Trace(m_crossThreadWeakPersistent); +} +} diff --git a/clang/blink_gc_plugin/tests/persistent_no_trace.h b/clang/blink_gc_plugin/tests/persistent_no_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..8167417b0c0a6f2c3076e3ba9beeb60cf495866a --- /dev/null +++ b/clang/blink_gc_plugin/tests/persistent_no_trace.h @@ -0,0 +1,29 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PERSISTENT_NO_TRACE_H_ +#define PERSISTENT_NO_TRACE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { + public: + void Trace(Visitor*) const; +}; + +class Object { + public: + void Trace(Visitor*) const; + + private: + Persistent m_persistent; + WeakPersistent m_weakPersistent; + CrossThreadPersistent m_crossThreadPersistent; + CrossThreadWeakPersistent m_crossThreadWeakPersistent; +}; +} + +#endif diff --git a/clang/blink_gc_plugin/tests/persistent_no_trace.txt b/clang/blink_gc_plugin/tests/persistent_no_trace.txt new file mode 100644 index 0000000000000000000000000000000000000000..f3ce62cb30e5c7bbbfc60e663dd7ad7ab9153f5f --- /dev/null +++ b/clang/blink_gc_plugin/tests/persistent_no_trace.txt @@ -0,0 +1,16 @@ +persistent_no_trace.cpp:9:1: warning: [blink-gc] Class 'Object' has untraced or not traceable fields. +void Object::Trace(Visitor* visitor) const { +^ +./persistent_no_trace.h:22:3: note: [blink-gc] Untraceable field 'm_persistent' declared here: + Persistent m_persistent; + ^ +./persistent_no_trace.h:23:3: note: [blink-gc] Untraceable field 'm_weakPersistent' declared here: + WeakPersistent m_weakPersistent; + ^ +./persistent_no_trace.h:24:3: note: [blink-gc] Untraceable field 'm_crossThreadPersistent' declared here: + CrossThreadPersistent m_crossThreadPersistent; + ^ +./persistent_no_trace.h:25:3: note: [blink-gc] Untraceable field 'm_crossThreadWeakPersistent' declared here: + CrossThreadWeakPersistent m_crossThreadWeakPersistent; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/polymorphic_class_with_non_virtual_trace.cpp b/clang/blink_gc_plugin/tests/polymorphic_class_with_non_virtual_trace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..423b3077d7dc984fcf8edd0918b3c5af4b0e39e0 --- /dev/null +++ b/clang/blink_gc_plugin/tests/polymorphic_class_with_non_virtual_trace.cpp @@ -0,0 +1,16 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "polymorphic_class_with_non_virtual_trace.h" + +namespace blink { + +void IsLeftMostPolymorphic::Trace(Visitor* visitor) const { + visitor->Trace(m_obj); +} + +void IsNotLeftMostPolymorphic::Trace(Visitor* visitor) const { + visitor->Trace(m_obj); +} +} diff --git a/clang/blink_gc_plugin/tests/polymorphic_class_with_non_virtual_trace.h b/clang/blink_gc_plugin/tests/polymorphic_class_with_non_virtual_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..23550158e651c47ec55cc344f659118f535fadb3 --- /dev/null +++ b/clang/blink_gc_plugin/tests/polymorphic_class_with_non_virtual_trace.h @@ -0,0 +1,64 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef POLYMORPHIC_CLASS_WITH_NON_VIRTUAL_TRACE_H_ +#define POLYMORPHIC_CLASS_WITH_NON_VIRTUAL_TRACE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { +public: + void Trace(Visitor*) const {} +}; + +class NonPolymorphicBase { +}; + +class PolymorphicBase { +public: + virtual void foo(); +}; + +class IsLeftMostPolymorphic + : public GarbageCollected, + public PolymorphicBase { +public: + void Trace(Visitor*) const; + +private: + Member m_obj; +}; + +class IsNotLeftMostPolymorphic + : public GarbageCollected, + public NonPolymorphicBase, + public PolymorphicBase { +public: + void Trace(Visitor*) const; + +private: + Member m_obj; +}; + +template +class TemplatedNonPolymorphicBase + : public GarbageCollected > { +public: + void Trace(Visitor* visitor) const { visitor->Trace(m_obj); } + +private: + Member m_obj; +}; + +// Looks OK, but will result in an incorrect object pointer when marking. +class TemplatedIsNotLeftMostPolymorphic + : public TemplatedNonPolymorphicBase, + public PolymorphicBase { +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/polymorphic_class_with_non_virtual_trace.txt b/clang/blink_gc_plugin/tests/polymorphic_class_with_non_virtual_trace.txt new file mode 100644 index 0000000000000000000000000000000000000000..3797b87531b8ebfc3c0781c9dbf5dfa0cbec61eb --- /dev/null +++ b/clang/blink_gc_plugin/tests/polymorphic_class_with_non_virtual_trace.txt @@ -0,0 +1,8 @@ +In file included from polymorphic_class_with_non_virtual_trace.cpp:5: +./polymorphic_class_with_non_virtual_trace.h:17:1: warning: [blink-gc] Left-most base class 'NonPolymorphicBase' of derived class 'IsNotLeftMostPolymorphic' must be polymorphic. +class NonPolymorphicBase { +^ +./polymorphic_class_with_non_virtual_trace.h:47:1: warning: [blink-gc] Left-most base class 'TemplatedNonPolymorphicBase' of derived class 'TemplatedIsNotLeftMostPolymorphic' must be polymorphic. +class TemplatedNonPolymorphicBase +^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.cpp b/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f9afb80803110cbb771f637b59bfd0e53d56fd68 --- /dev/null +++ b/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.cpp @@ -0,0 +1,20 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ptrs_to_traceable_class.h" + +namespace blink { + +void Foo() { + Traceable traceable; + // The following pointers should be ok. + Traceable* raw_ptr = &traceable; + (void)raw_ptr; + Traceable& ref_ptr = traceable; + (void)ref_ptr; + std::unique_ptr unique; + (void)unique; +} + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.flags b/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.flags new file mode 100644 index 0000000000000000000000000000000000000000..ebd1d328801d30f13ea397576eedcd7c6edf5f0d --- /dev/null +++ b/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-blink-gc-plugin -Xclang enable-ptrs-to-traceable-check diff --git a/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.h b/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.h new file mode 100644 index 0000000000000000000000000000000000000000..737d7e121218a67d2aeddaf84b1402b940755271 --- /dev/null +++ b/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.h @@ -0,0 +1,57 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PTRS_TO_TRACEABLE_CLASS_H_ +#define PTRS_TO_TRACEABLE_CLASS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { + public: + void Trace(Visitor*) const {} +}; + +class Traceable { + DISALLOW_NEW(); + + public: + void Trace(Visitor* v) const { v->Trace(member_); } + + private: + Member member_; +}; + +class OffGCedHeap { + public: + explicit OffGCedHeap(Traceable* traceable) + : raw_ptr(traceable), ref_ptr(*traceable) { + (void)raw_ptr; + (void)ref_ptr; + } + + private: + Traceable* raw_ptr; + Traceable& ref_ptr; + std::unique_ptr unique; +}; + +class OnGCedHeap : public GarbageCollected { + public: + explicit OnGCedHeap(Traceable* traceable) + : raw_ptr(traceable), ref_ptr(*traceable) { + (void)raw_ptr; + (void)ref_ptr; + } + + private: + Traceable* raw_ptr; + Traceable& ref_ptr; + std::unique_ptr unique; +}; + +} // namespace blink + +#endif // PTRS_TO_TRACEABLE_CLASS_H_ diff --git a/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.txt b/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.txt new file mode 100644 index 0000000000000000000000000000000000000000..65f84a2ea4031b85a99c162963e85e5376c8907d --- /dev/null +++ b/clang/blink_gc_plugin/tests/ptrs_to_traceable_class.txt @@ -0,0 +1,26 @@ +In file included from ptrs_to_traceable_class.cpp:5: +./ptrs_to_traceable_class.h:27:1: warning: [blink-gc] Class 'OffGCedHeap' contains invalid fields. +class OffGCedHeap { +^ +./ptrs_to_traceable_class.h:36:3: note: [blink-gc] Raw pointer field 'raw_ptr' to a traceable class declared here: + Traceable* raw_ptr; + ^ +./ptrs_to_traceable_class.h:37:3: note: [blink-gc] Reference pointer field 'ref_ptr' to a traceable class declared here: + Traceable& ref_ptr; + ^ +./ptrs_to_traceable_class.h:38:3: note: [blink-gc] std::unique_ptr field 'unique' to a traceable class declared here: + std::unique_ptr unique; + ^ +./ptrs_to_traceable_class.h:41:1: warning: [blink-gc] Class 'OnGCedHeap' contains invalid fields. +class OnGCedHeap : public GarbageCollected { +^ +./ptrs_to_traceable_class.h:50:3: note: [blink-gc] Raw pointer field 'raw_ptr' to a traceable class declared here: + Traceable* raw_ptr; + ^ +./ptrs_to_traceable_class.h:51:3: note: [blink-gc] Reference pointer field 'ref_ptr' to a traceable class declared here: + Traceable& ref_ptr; + ^ +./ptrs_to_traceable_class.h:52:3: note: [blink-gc] std::unique_ptr field 'unique' to a traceable class declared here: + std::unique_ptr unique; + ^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/pure_virtual_trace.cpp b/clang/blink_gc_plugin/tests/pure_virtual_trace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..504d40ae51084fd436e9188b3499341d2f864598 --- /dev/null +++ b/clang/blink_gc_plugin/tests/pure_virtual_trace.cpp @@ -0,0 +1,7 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "pure_virtual_trace.h" + +// Nothing to define diff --git a/clang/blink_gc_plugin/tests/pure_virtual_trace.h b/clang/blink_gc_plugin/tests/pure_virtual_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..bf615bf1a43b49d74de9699511a75c2698cae261 --- /dev/null +++ b/clang/blink_gc_plugin/tests/pure_virtual_trace.h @@ -0,0 +1,19 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PURE_VIRTUAL_TRACE_H_ +#define PURE_VIRTUAL_TRACE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class A : public GarbageCollected { +public: + virtual void Trace(Visitor*) const = 0; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/pure_virtual_trace.txt b/clang/blink_gc_plugin/tests/pure_virtual_trace.txt new file mode 100644 index 0000000000000000000000000000000000000000..3b6c6e141154c27642765a2e9f848775e914b7b2 --- /dev/null +++ b/clang/blink_gc_plugin/tests/pure_virtual_trace.txt @@ -0,0 +1,5 @@ +In file included from pure_virtual_trace.cpp:5: +./pure_virtual_trace.h:14:2: warning: [blink-gc] Garbage collected class 'A' is not permitted to declare a pure-virtual trace method. + virtual void Trace(Visitor*) const = 0; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class.cpp b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0408e5dc126e2e084ccbd1fac611513e94f2cd48 --- /dev/null +++ b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class.cpp @@ -0,0 +1,12 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "raw_ptr_to_gc_managed_class.h" + +namespace blink { + +void HeapObject::Trace(Visitor* visitor) const { + visitor->Trace(m_objs); +} +} diff --git a/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class.h b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class.h new file mode 100644 index 0000000000000000000000000000000000000000..6a02f23d55f291379c2727617293a61bb6ca05e3 --- /dev/null +++ b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class.h @@ -0,0 +1,34 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RAW_PTR_TO_GC_MANAGED_CLASS_H_ +#define RAW_PTR_TO_GC_MANAGED_CLASS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject; + +class PartObject { + DISALLOW_NEW(); +private: + PartObject(); + + HeapObject* m_rawObj; + HeapObject& m_refObj; +}; + +class HeapObject : public GarbageCollected { +public: + void Trace(Visitor*) const; + +private: + PartObject m_part; + HeapVector m_objs; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class.txt b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class.txt new file mode 100644 index 0000000000000000000000000000000000000000..3a12029b80b88da5fe5e2bad6efead2acc0c571b --- /dev/null +++ b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class.txt @@ -0,0 +1,17 @@ +In file included from raw_ptr_to_gc_managed_class.cpp:5: +./raw_ptr_to_gc_managed_class.h:14:1: warning: [blink-gc] Class 'PartObject' contains invalid fields. +class PartObject { +^ +./raw_ptr_to_gc_managed_class.h:19:5: note: [blink-gc] Raw pointer field 'm_rawObj' to a GC managed class declared here: + HeapObject* m_rawObj; + ^ +./raw_ptr_to_gc_managed_class.h:20:5: note: [blink-gc] Reference pointer field 'm_refObj' to a GC managed class declared here: + HeapObject& m_refObj; + ^ +./raw_ptr_to_gc_managed_class.h:23:1: warning: [blink-gc] Class 'HeapObject' contains invalid fields. +class HeapObject : public GarbageCollected { +^ +./raw_ptr_to_gc_managed_class.h:29:5: note: [blink-gc] Raw pointer field 'm_objs' to a GC managed class declared here: + HeapVector m_objs; + ^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.cpp b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d039dd1b986379edff8a1b78248e6fe6ac23c06 --- /dev/null +++ b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.cpp @@ -0,0 +1,12 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "raw_ptr_to_gc_managed_class_error.h" + +namespace blink { + +void HeapObject::Trace(Visitor* visitor) const { + visitor->Trace(m_objs); +} +} diff --git a/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.flags b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.flags new file mode 100644 index 0000000000000000000000000000000000000000..2f41be663b282ab659a92b0d2d23de174913a639 --- /dev/null +++ b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.flags @@ -0,0 +1 @@ +-Werror diff --git a/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.h b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.h new file mode 100644 index 0000000000000000000000000000000000000000..2786f2aded6270cf4398344d0280b2880201ed7f --- /dev/null +++ b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.h @@ -0,0 +1,34 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RAW_PTR_TO_GC_MANAGED_CLASS_ERROR_H_ +#define RAW_PTR_TO_GC_MANAGED_CLASS_ERROR_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject; + +class PartObject { + DISALLOW_NEW(); +private: + PartObject(); + + HeapObject* m_rawObj; + HeapObject& m_refObj; +}; + +class HeapObject : public GarbageCollected { +public: + void Trace(Visitor*) const; + +private: + PartObject m_part; + HeapVector m_objs; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.txt b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.txt new file mode 100644 index 0000000000000000000000000000000000000000..a728877a9a26b88015190bc713273adf434c5b8a --- /dev/null +++ b/clang/blink_gc_plugin/tests/raw_ptr_to_gc_managed_class_error.txt @@ -0,0 +1,17 @@ +In file included from raw_ptr_to_gc_managed_class_error.cpp:5: +./raw_ptr_to_gc_managed_class_error.h:14:1: error: [blink-gc] Class 'PartObject' contains invalid fields. +class PartObject { +^ +./raw_ptr_to_gc_managed_class_error.h:19:5: note: [blink-gc] Raw pointer field 'm_rawObj' to a GC managed class declared here: + HeapObject* m_rawObj; + ^ +./raw_ptr_to_gc_managed_class_error.h:20:5: note: [blink-gc] Reference pointer field 'm_refObj' to a GC managed class declared here: + HeapObject& m_refObj; + ^ +./raw_ptr_to_gc_managed_class_error.h:23:1: error: [blink-gc] Class 'HeapObject' contains invalid fields. +class HeapObject : public GarbageCollected { +^ +./raw_ptr_to_gc_managed_class_error.h:29:5: note: [blink-gc] Raw pointer field 'm_objs' to a GC managed class declared here: + HeapVector m_objs; + ^ +2 errors generated. diff --git a/clang/blink_gc_plugin/tests/ref_ptr_to_gc_managed_class.cpp b/clang/blink_gc_plugin/tests/ref_ptr_to_gc_managed_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7c298cd027bb4ff5ef645061609f548a46a36f08 --- /dev/null +++ b/clang/blink_gc_plugin/tests/ref_ptr_to_gc_managed_class.cpp @@ -0,0 +1,10 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ref_ptr_to_gc_managed_class.h" + +namespace blink { + +void HeapObject::Trace(Visitor*) const {} +} diff --git a/clang/blink_gc_plugin/tests/ref_ptr_to_gc_managed_class.h b/clang/blink_gc_plugin/tests/ref_ptr_to_gc_managed_class.h new file mode 100644 index 0000000000000000000000000000000000000000..15ac216869b4b81d94878ffcb7b4765513a0a108 --- /dev/null +++ b/clang/blink_gc_plugin/tests/ref_ptr_to_gc_managed_class.h @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REF_PTR_TO_GC_MANAGED_CLASS_H_ +#define REF_PTR_TO_GC_MANAGED_CLASS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject; + +class PartObject { + DISALLOW_NEW(); +private: + scoped_refptr m_obj; +}; + +class HeapObject : public GarbageCollected { + public: + void Trace(Visitor*) const; + + private: + PartObject m_part; + Vector> m_objs; +}; +} + +#endif diff --git a/clang/blink_gc_plugin/tests/ref_ptr_to_gc_managed_class.txt b/clang/blink_gc_plugin/tests/ref_ptr_to_gc_managed_class.txt new file mode 100644 index 0000000000000000000000000000000000000000..12486cd057be43f0ede21b30f6aad969038a0498 --- /dev/null +++ b/clang/blink_gc_plugin/tests/ref_ptr_to_gc_managed_class.txt @@ -0,0 +1,14 @@ +In file included from ref_ptr_to_gc_managed_class.cpp:5: +./ref_ptr_to_gc_managed_class.h:14:1: warning: [blink-gc] Class 'PartObject' contains invalid fields. +class PartObject { +^ +./ref_ptr_to_gc_managed_class.h:17:5: note: [blink-gc] scoped_refptr field 'm_obj' to a GC managed class declared here: + scoped_refptr m_obj; + ^ +./ref_ptr_to_gc_managed_class.h:20:1: warning: [blink-gc] Class 'HeapObject' contains invalid fields. +class HeapObject : public GarbageCollected { +^ +./ref_ptr_to_gc_managed_class.h:26:3: note: [blink-gc] scoped_refptr field 'm_objs' to a GC managed class declared here: + Vector> m_objs; + ^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/register_weak_members_template.cpp b/clang/blink_gc_plugin/tests/register_weak_members_template.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c2b17a697881617cb2c125878a4d32c7417b77ef --- /dev/null +++ b/clang/blink_gc_plugin/tests/register_weak_members_template.cpp @@ -0,0 +1,7 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "register_weak_members_template.h" + +// Nothing to define here. diff --git a/clang/blink_gc_plugin/tests/register_weak_members_template.h b/clang/blink_gc_plugin/tests/register_weak_members_template.h new file mode 100644 index 0000000000000000000000000000000000000000..06fa7ba8797aa7de1592b15ef41aac17493a0364 --- /dev/null +++ b/clang/blink_gc_plugin/tests/register_weak_members_template.h @@ -0,0 +1,33 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REGISTER_WEAK_MEMBERS_TEMPLATE_H_ +#define REGISTER_WEAK_MEMBERS_TEMPLATE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class X : public GarbageCollected { + public: + void Trace(Visitor* visitor) const {} +}; + +class HasUntracedWeakMembers : public GarbageCollected { + public: + void Trace(Visitor* visitor) const { + visitor->template RegisterWeakMembers< + HasUntracedWeakMembers, &HasUntracedWeakMembers::ClearWeakMembers>( + this); + } + + void ClearWeakMembers(Visitor* visitor); + + private: + WeakMember x_; +}; + +} + +#endif // REGISTER_WEAK_MEMBERS_TEMPLATE_H_ diff --git a/clang/blink_gc_plugin/tests/register_weak_members_template.txt b/clang/blink_gc_plugin/tests/register_weak_members_template.txt new file mode 100644 index 0000000000000000000000000000000000000000..35ef9f3775107e148f8ff2fdefa9ddcadbb981ef --- /dev/null +++ b/clang/blink_gc_plugin/tests/register_weak_members_template.txt @@ -0,0 +1,8 @@ +In file included from register_weak_members_template.cpp:5: +./register_weak_members_template.h:19:3: warning: [blink-gc] Class 'HasUntracedWeakMembers' has untraced fields that require tracing. + void Trace(Visitor* visitor) const { + ^ +./register_weak_members_template.h:28:3: note: [blink-gc] Untraced field 'x_' declared here: + WeakMember x_; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/stack_allocated.cpp b/clang/blink_gc_plugin/tests/stack_allocated.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2566ce6da9fc99b1aa698f2f23490bef2b51c2e1 --- /dev/null +++ b/clang/blink_gc_plugin/tests/stack_allocated.cpp @@ -0,0 +1,20 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stack_allocated.h" + +namespace blink { + +// Verify that anon namespaces are checked. +namespace { + +class AnonStackObject : public StackObject { +public: + HeapObject* m_obj; +}; + +} + +void HeapObject::Trace(Visitor* visitor) const {} +} diff --git a/clang/blink_gc_plugin/tests/stack_allocated.h b/clang/blink_gc_plugin/tests/stack_allocated.h new file mode 100644 index 0000000000000000000000000000000000000000..7f3a7acb55c7b865d34ebddc8bc5ed9f4da87923 --- /dev/null +++ b/clang/blink_gc_plugin/tests/stack_allocated.h @@ -0,0 +1,55 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef STACK_ALLOCATED_H_ +#define STACK_ALLOCATED_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject; + +class PartObject { + DISALLOW_NEW(); +private: + Member m_obj; // Needs tracing. +}; + +class StackObject { + STACK_ALLOCATED(); + + // Redundant trace() method, warning/error expected. + void Trace(Visitor* visitor) const { visitor->Trace(m_obj); } + + private: + HeapObject* m_obj; // Does not need tracing. +}; + +class HeapObject : public GarbageCollected { +public: + void Trace(Visitor*) const; + +private: + StackObject m_part; // Cannot embed a stack allocated object. +}; + +// Cannot derive from both heap- and stack-allocated objects. +class DerivedHeapObject : public HeapObject, public StackObject { +}; + +// Cannot be stack-allocated and derive from a heap-allocated object. +class DerivedHeapObject2 : public HeapObject { + STACK_ALLOCATED(); +}; + +// STACK_ALLOCATED is inherited. +class DerivedStackObject : public StackObject { +private: + StackObject m_anotherPart; // Also fine. +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/stack_allocated.txt b/clang/blink_gc_plugin/tests/stack_allocated.txt new file mode 100644 index 0000000000000000000000000000000000000000..1b33271dfb2173a84a16956b24bb56a15524ce7d --- /dev/null +++ b/clang/blink_gc_plugin/tests/stack_allocated.txt @@ -0,0 +1,29 @@ +In file included from stack_allocated.cpp:5: +./stack_allocated.h:14:1: warning: [blink-gc] Class 'PartObject' requires a trace method. +class PartObject { +^ +./stack_allocated.h:17:5: note: [blink-gc] Untraced field 'm_obj' declared here: + Member m_obj; // Needs tracing. + ^ +./stack_allocated.h:24:5: warning: [blink-gc] The stack allocated class 'StackObject' provides an unnecessary trace method: + void Trace(Visitor* visitor) const { visitor->Trace(m_obj); } + ^ +./stack_allocated.h:30:1: warning: [blink-gc] Class 'HeapObject' contains invalid fields. +class HeapObject : public GarbageCollected { +^ +./stack_allocated.h:35:5: note: [blink-gc] Stack-allocated field 'm_part' declared here: + StackObject m_part; // Cannot embed a stack allocated object. + ^ +./stack_allocated.h:39:27: warning: [blink-gc] Stack-allocated class 'DerivedHeapObject' derives class 'HeapObject' which is garbage collected. +class DerivedHeapObject : public HeapObject, public StackObject { + ^ +./stack_allocated.h:43:28: warning: [blink-gc] Stack-allocated class 'DerivedHeapObject2' derives class 'HeapObject' which is garbage collected. +class DerivedHeapObject2 : public HeapObject { + ^ +./stack_allocated.h:24:5: warning: [blink-gc] Class 'StackObject' has untraced or not traceable fields. + void Trace(Visitor* visitor) const { visitor->Trace(m_obj); } + ^ +./stack_allocated.h:27:5: note: [blink-gc] Untraceable field 'm_obj' declared here: + HeapObject* m_obj; // Does not need tracing. + ^ +6 warnings generated. diff --git a/clang/blink_gc_plugin/tests/templated_class_with_local_class_requires_trace.cpp b/clang/blink_gc_plugin/tests/templated_class_with_local_class_requires_trace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e2ec3a860ac1de27a3e7d987b6954ff2859145ce --- /dev/null +++ b/clang/blink_gc_plugin/tests/templated_class_with_local_class_requires_trace.cpp @@ -0,0 +1,24 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "templated_class_with_local_class_requires_trace.h" + +namespace blink { + +template +void TemplatedObject::Trace(Visitor* visitor) const { + visitor->Trace(m_local); + visitor->Trace(m_memberRef); +} + +class Test { + public: + static void test() { + HeapObject* obj = MakeGarbageCollected(); + TemplatedObject* instance = + MakeGarbageCollected>(obj); + } +}; + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/templated_class_with_local_class_requires_trace.h b/clang/blink_gc_plugin/tests/templated_class_with_local_class_requires_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..83f19c70a5c88bd9d7d87fe99ad31db21509c8cf --- /dev/null +++ b/clang/blink_gc_plugin/tests/templated_class_with_local_class_requires_trace.h @@ -0,0 +1,49 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TEMPLATED_CLASS_WITH_LOCAL_CLASS_REQUIRES_TRACE_H +#define TEMPLATED_CLASS_WITH_LOCAL_CLASS_REQUIRES_TRACE_H + +#include "heap/stubs.h" + +namespace blink { + +class NonHeapObject { }; + +class HeapObject : public GarbageCollected { + public: + HeapObject() {} + + void Trace(Visitor*) const {} +}; + +template +class TemplatedObject final : public GarbageCollected> { + public: + TemplatedObject(T*) {} + + void Trace(Visitor*) const; + + private: + class Local final : public GarbageCollected { + public: + void Trace(Visitor* visitor) const { + visitor->Trace(m_heapObject); + visitor->Trace(m_object); + } + + private: + Member m_heapObject; + std::unique_ptr m_object; + }; + + Member m_local; + Member m_memberRef; + std::unique_ptr m_uniqueRef; +}; + +} // namespace blink + +#endif // TEMPLATED_CLASS_WITH_LOCAL_CLASS_REQUIRES_TRACE_H + diff --git a/clang/blink_gc_plugin/tests/templated_class_with_local_class_requires_trace.txt b/clang/blink_gc_plugin/tests/templated_class_with_local_class_requires_trace.txt new file mode 100644 index 0000000000000000000000000000000000000000..0a2df29a7f5378269a410e7adc9ab67deacd7c47 --- /dev/null +++ b/clang/blink_gc_plugin/tests/templated_class_with_local_class_requires_trace.txt @@ -0,0 +1,20 @@ +In file included from templated_class_with_local_class_requires_trace.cpp:5: +./templated_class_with_local_class_requires_trace.h:22:1: warning: [blink-gc] Class 'TemplatedObject' contains invalid fields. +class TemplatedObject final : public GarbageCollected> { +^ +./templated_class_with_local_class_requires_trace.h:43:3: note: [blink-gc] std::unique_ptr field 'm_uniqueRef' to a GC managed class declared here: + std::unique_ptr m_uniqueRef; + ^ +./templated_class_with_local_class_requires_trace.h:29:3: warning: [blink-gc] Class 'Local' contains invalid fields. + class Local final : public GarbageCollected { + ^ +./templated_class_with_local_class_requires_trace.h:38:5: note: [blink-gc] std::unique_ptr field 'm_object' to a GC managed class declared here: + std::unique_ptr m_object; + ^ +./templated_class_with_local_class_requires_trace.h:31:5: warning: [blink-gc] Class 'Local' has untraced or not traceable fields. + void Trace(Visitor* visitor) const { + ^ +./templated_class_with_local_class_requires_trace.h:38:5: note: [blink-gc] Untraceable field 'm_object' declared here: + std::unique_ptr m_object; + ^ +3 warnings generated. diff --git a/clang/blink_gc_plugin/tests/test.py b/clang/blink_gc_plugin/tests/test.py new file mode 100755 index 0000000000000000000000000000000000000000..20eb4bdf188a15ab37623102dc2e998cbe5abbb4 --- /dev/null +++ b/clang/blink_gc_plugin/tests/test.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# Copyright 2015 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import os +import subprocess +import sys + +script_dir = os.path.dirname(os.path.realpath(__file__)) +tool_dir = os.path.abspath(os.path.join(script_dir, '../../pylib')) +sys.path.insert(0, tool_dir) + +from clang import plugin_testing + + +class BlinkGcPluginTest(plugin_testing.ClangPluginTest): + """Test harness for the Blink GC plugin.""" + + def __init__(self, *args, **kwargs): + super(BlinkGcPluginTest, self).__init__(*args, **kwargs) + + def AdjustClangArguments(self, clang_cmd): + clang_cmd.append('-Wno-inaccessible-base') + + def ProcessOneResult(self, test_name, actual): + # Some Blink GC plugins dump a JSON representation of the object graph, and + # use the processed results as the actual results of the test. + if os.path.exists('%s.graph.json' % test_name): + try: + actual = subprocess.check_output([ + sys.executable, '../process-graph.py', '-c', + '%s.graph.json' % test_name + ], + stderr=subprocess.STDOUT, + universal_newlines=True) + except subprocess.CalledProcessError as e: + # The graph processing script returns a failure exit code if the graph + # is bad (e.g. it has a cycle). The output still needs to be captured in + # that case, since the expected results capture the errors. + actual = e.output + finally: + # Clean up the .graph.json file to prevent false passes from stale + # results from a previous run. + os.remove('%s.graph.json' % test_name) + return super(BlinkGcPluginTest, self).ProcessOneResult(test_name, actual) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--reset-results', + action='store_true', + help='If specified, overwrites the expected results in place.') + parser.add_argument('clang_path', help='The path to the clang binary.') + args = parser.parse_args() + + dir_name = os.path.dirname(os.path.realpath(__file__)) + + return BlinkGcPluginTest(dir_name, args.clang_path, ['blink-gc-plugin'], + args.reset_results).Run() + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/clang/blink_gc_plugin/tests/trace_after_dispatch.cpp b/clang/blink_gc_plugin/tests/trace_after_dispatch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6cfd8ea015770cee1cc1139882352b1f484a379d --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_after_dispatch.cpp @@ -0,0 +1,45 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "trace_after_dispatch.h" + +namespace blink { + +static const B* toB(const A* a) { + return static_cast(a); +} + +void A::Trace(Visitor* visitor) const { + switch (m_type) { + case TB: + toB(this)->TraceAfterDispatch(visitor); + break; + case TC: + static_cast(this)->TraceAfterDispatch(visitor); + break; + case TD: + // Missing static_cast(this)->TraceAfterDispatch(visitor); + break; + } +} + +void A::TraceAfterDispatch(Visitor* visitor) const {} + +void B::TraceAfterDispatch(Visitor* visitor) const { + visitor->Trace(m_a); + // Missing A::TraceAfterDispatch(visitor); + // Also check that calling Trace does not count. + A::Trace(visitor); +} + +void C::TraceAfterDispatch(Visitor* visitor) const { + // Missing visitor->Trace(m_a); + A::TraceAfterDispatch(visitor); +} + +void D::TraceAfterDispatch(Visitor* visitor) const { + visitor->Trace(m_a); + Abstract::TraceAfterDispatch(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/trace_after_dispatch.h b/clang/blink_gc_plugin/tests/trace_after_dispatch.h new file mode 100644 index 0000000000000000000000000000000000000000..4c0acc65b17071c6b53e5da68dde91559c14fe9d --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_after_dispatch.h @@ -0,0 +1,60 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACE_AFTER_DISPATCH_H_ +#define TRACE_AFTER_DISPATCH_H_ + +#include "heap/stubs.h" + +namespace blink { + +class A : public GarbageCollected { + public: + void Trace(Visitor*) const; + void TraceAfterDispatch(Visitor*) const; + + protected: + enum Type { TB, TC, TD }; + A(Type type) : m_type(type) {} + + private: + Type m_type; +}; + +class B : public A { +public: + B() : A(TB) { } + void TraceAfterDispatch(Visitor*) const; + + private: + Member m_a; +}; + +class C : public A { +public: + C() : A(TC) { } + void TraceAfterDispatch(Visitor*) const; + + private: + Member m_a; +}; + +// This class is considered abstract does not need to be dispatched to. +class Abstract : public A { +protected: + Abstract(Type type) : A(type) { } +}; + +class D : public Abstract { +public: + D() : Abstract(TD) { } + void TraceAfterDispatch(Visitor*) const; + + private: + Member m_a; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/trace_after_dispatch.txt b/clang/blink_gc_plugin/tests/trace_after_dispatch.txt new file mode 100644 index 0000000000000000000000000000000000000000..5fd0087fa7d53face8c412c841f6778bb4d68240 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_after_dispatch.txt @@ -0,0 +1,13 @@ +trace_after_dispatch.cpp:13:1: warning: [blink-gc] Missing dispatch to class 'D' in manual trace dispatch. +void A::Trace(Visitor* visitor) const { +^ +trace_after_dispatch.cpp:29:1: warning: [blink-gc] Base class 'A' of derived class 'B' requires tracing. +void B::TraceAfterDispatch(Visitor* visitor) const { +^ +trace_after_dispatch.cpp:36:1: warning: [blink-gc] Class 'C' has untraced fields that require tracing. +void C::TraceAfterDispatch(Visitor* visitor) const { +^ +./trace_after_dispatch.h:40:5: note: [blink-gc] Untraced field 'm_a' declared here: + Member m_a; + ^ +3 warnings generated. diff --git a/clang/blink_gc_plugin/tests/trace_after_dispatch_impl.cpp b/clang/blink_gc_plugin/tests/trace_after_dispatch_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..df09cb37b494722be022d68e6f9dedd033ce3916 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_after_dispatch_impl.cpp @@ -0,0 +1,41 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "trace_after_dispatch_impl.h" + +namespace blink { + +void TraceAfterDispatchInlinedBase::Trace(Visitor* visitor) const { + // Implement a simple form of manual dispatching, because BlinkGCPlugin + // checks if the tracing is dispatched to all derived classes. + // + // This function has to be implemented out-of-line, since we need to know the + // definition of derived classes here. + if (tag_ == DERIVED) { + static_cast(this) + ->TraceAfterDispatch(visitor); + } else { + TraceAfterDispatch(visitor); + } +} + +void TraceAfterDispatchExternBase::Trace(Visitor* visitor) const { + if (tag_ == DERIVED) { + static_cast(this) + ->TraceAfterDispatch(visitor); + } else { + TraceAfterDispatch(visitor); + } +} + +void TraceAfterDispatchExternBase::TraceAfterDispatch(Visitor* visitor) const { + visitor->Trace(x_base_); +} + +void TraceAfterDispatchExternDerived::TraceAfterDispatch( + Visitor* visitor) const { + visitor->Trace(x_derived_); + TraceAfterDispatchExternBase::TraceAfterDispatch(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/trace_after_dispatch_impl.h b/clang/blink_gc_plugin/tests/trace_after_dispatch_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..90952a6aafcf760ef6f514b7146e074b956b0ffa --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_after_dispatch_impl.h @@ -0,0 +1,74 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACE_AFTER_DISPATCH_IMPL_H_ +#define TRACE_AFTER_DISPATCH_IMPL_H_ + +#include "heap/stubs.h" + +namespace blink { + +class X : public GarbageCollected { + public: + void Trace(Visitor*) const {} +}; + +enum ClassTag { + BASE, DERIVED +}; + +class TraceAfterDispatchInlinedBase + : public GarbageCollected { + public: + explicit TraceAfterDispatchInlinedBase(ClassTag tag) : tag_(tag) {} + + void Trace(Visitor*) const; + + void TraceAfterDispatch(Visitor* visitor) const { visitor->Trace(x_base_); } + + private: + ClassTag tag_; + Member x_base_; +}; + +class TraceAfterDispatchInlinedDerived : public TraceAfterDispatchInlinedBase { + public: + TraceAfterDispatchInlinedDerived() : TraceAfterDispatchInlinedBase(DERIVED) {} + + void TraceAfterDispatch(Visitor* visitor) const { + visitor->Trace(x_derived_); + TraceAfterDispatchInlinedBase::TraceAfterDispatch(visitor); + } + + private: + Member x_derived_; +}; + +class TraceAfterDispatchExternBase + : public GarbageCollected { + public: + explicit TraceAfterDispatchExternBase(ClassTag tag) : tag_(tag) {} + + void Trace(Visitor* visitor) const; + + void TraceAfterDispatch(Visitor* visitor) const; + + private: + ClassTag tag_; + Member x_base_; +}; + +class TraceAfterDispatchExternDerived : public TraceAfterDispatchExternBase { + public: + TraceAfterDispatchExternDerived() : TraceAfterDispatchExternBase(DERIVED) {} + + void TraceAfterDispatch(Visitor* visitor) const; + + private: + Member x_derived_; +}; + +} + +#endif // TRACE_AFTER_DISPATCH_IMPL_H_ diff --git a/clang/blink_gc_plugin/tests/trace_after_dispatch_impl.txt b/clang/blink_gc_plugin/tests/trace_after_dispatch_impl.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/blink_gc_plugin/tests/trace_after_dispatch_impl_error.cpp b/clang/blink_gc_plugin/tests/trace_after_dispatch_impl_error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f564ab05c2b7eb507db7e266f0f671a52941b32c --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_after_dispatch_impl_error.cpp @@ -0,0 +1,42 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "trace_after_dispatch_impl_error.h" + +namespace blink { + +inline void TraceAfterDispatchInlinedBase::Trace(Visitor* visitor) const { + // Implement a simple form of manual dispatching, because BlinkGCPlugin + // checks if the tracing is dispatched to all derived classes. + // + // This function has to be implemented out-of-line, since we need to know the + // definition of derived classes here. + if (tag_ == DERIVED) { + // Missing dispatch call: + // static_cast(this)->TraceAfterDispatch( + // visitor); + } else { + TraceAfterDispatch(visitor); + } +} + +void TraceAfterDispatchExternBase::Trace(Visitor* visitor) const { + if (tag_ == DERIVED) { + // Missing dispatch call: + // static_cast(this)->TraceAfterDispatch( + // visitor); + } else { + TraceAfterDispatch(visitor); + } +} + +void TraceAfterDispatchExternBase::TraceAfterDispatch(Visitor* visitor) const { + // No Trace call. +} + +void TraceAfterDispatchExternDerived::TraceAfterDispatch( + Visitor* visitor) const { + // Ditto. +} +} diff --git a/clang/blink_gc_plugin/tests/trace_after_dispatch_impl_error.h b/clang/blink_gc_plugin/tests/trace_after_dispatch_impl_error.h new file mode 100644 index 0000000000000000000000000000000000000000..64db896946f9b80fad944202d75e18e50aa8a535 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_after_dispatch_impl_error.h @@ -0,0 +1,75 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACE_AFTER_DISPATCH_IMPL_ERROR_H_ +#define TRACE_AFTER_DISPATCH_IMPL_ERROR_H_ + +#include "heap/stubs.h" + +namespace blink { + +class X : public GarbageCollected { + public: + void Trace(Visitor*) const {} +}; + +enum ClassTag { + BASE, DERIVED +}; + +class TraceAfterDispatchInlinedBase + : public GarbageCollected { + public: + explicit TraceAfterDispatchInlinedBase(ClassTag tag) : tag_(tag) {} + + void Trace(Visitor*) const; + + void TraceAfterDispatch(Visitor* visitor) const { + // No Trace call; should get a warning. + } + + private: + ClassTag tag_; + Member x_base_; +}; + +class TraceAfterDispatchInlinedDerived : public TraceAfterDispatchInlinedBase { + public: + TraceAfterDispatchInlinedDerived() : TraceAfterDispatchInlinedBase(DERIVED) {} + + void TraceAfterDispatch(Visitor* visitor) const { + // No Trace call (for member and base class). + } + + private: + Member x_derived_; +}; + +class TraceAfterDispatchExternBase + : public GarbageCollected { + public: + explicit TraceAfterDispatchExternBase(ClassTag tag) : tag_(tag) {} + + void Trace(Visitor* visitor) const; + + void TraceAfterDispatch(Visitor* visitor) const; + + private: + ClassTag tag_; + Member x_base_; +}; + +class TraceAfterDispatchExternDerived : public TraceAfterDispatchExternBase { + public: + TraceAfterDispatchExternDerived() : TraceAfterDispatchExternBase(DERIVED) {} + + void TraceAfterDispatch(Visitor* visitor) const; + + private: + Member x_derived_; +}; + +} + +#endif // TRACE_AFTER_DISPATCH_IMPL_ERROR_H_ diff --git a/clang/blink_gc_plugin/tests/trace_after_dispatch_impl_error.txt b/clang/blink_gc_plugin/tests/trace_after_dispatch_impl_error.txt new file mode 100644 index 0000000000000000000000000000000000000000..e26dace6a77359c34904ba890510c92f28a3a854 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_after_dispatch_impl_error.txt @@ -0,0 +1,34 @@ +trace_after_dispatch_impl_error.cpp:9:1: warning: [blink-gc] Missing dispatch to class 'TraceAfterDispatchInlinedDerived' in manual trace dispatch. +inline void TraceAfterDispatchInlinedBase::Trace(Visitor* visitor) const { +^ +trace_after_dispatch_impl_error.cpp:24:1: warning: [blink-gc] Missing dispatch to class 'TraceAfterDispatchExternDerived' in manual trace dispatch. +void TraceAfterDispatchExternBase::Trace(Visitor* visitor) const { +^ +In file included from trace_after_dispatch_impl_error.cpp:5: +./trace_after_dispatch_impl_error.h:28:3: warning: [blink-gc] Class 'TraceAfterDispatchInlinedBase' has untraced fields that require tracing. + void TraceAfterDispatch(Visitor* visitor) const { + ^ +./trace_after_dispatch_impl_error.h:34:3: note: [blink-gc] Untraced field 'x_base_' declared here: + Member x_base_; + ^ +./trace_after_dispatch_impl_error.h:41:3: warning: [blink-gc] Base class 'TraceAfterDispatchInlinedBase' of derived class 'TraceAfterDispatchInlinedDerived' requires tracing. + void TraceAfterDispatch(Visitor* visitor) const { + ^ +./trace_after_dispatch_impl_error.h:41:3: warning: [blink-gc] Class 'TraceAfterDispatchInlinedDerived' has untraced fields that require tracing. +./trace_after_dispatch_impl_error.h:46:3: note: [blink-gc] Untraced field 'x_derived_' declared here: + Member x_derived_; + ^ +trace_after_dispatch_impl_error.cpp:34:1: warning: [blink-gc] Class 'TraceAfterDispatchExternBase' has untraced fields that require tracing. +void TraceAfterDispatchExternBase::TraceAfterDispatch(Visitor* visitor) const { +^ +./trace_after_dispatch_impl_error.h:60:3: note: [blink-gc] Untraced field 'x_base_' declared here: + Member x_base_; + ^ +trace_after_dispatch_impl_error.cpp:38:1: warning: [blink-gc] Base class 'TraceAfterDispatchExternBase' of derived class 'TraceAfterDispatchExternDerived' requires tracing. +void TraceAfterDispatchExternDerived::TraceAfterDispatch( +^ +trace_after_dispatch_impl_error.cpp:38:1: warning: [blink-gc] Class 'TraceAfterDispatchExternDerived' has untraced fields that require tracing. +./trace_after_dispatch_impl_error.h:70:3: note: [blink-gc] Untraced field 'x_derived_' declared here: + Member x_derived_; + ^ +8 warnings generated. diff --git a/clang/blink_gc_plugin/tests/trace_collections.cpp b/clang/blink_gc_plugin/tests/trace_collections.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4aeb1e826bebd0dcea0291c3e7e9450af76c4fb7 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_collections.cpp @@ -0,0 +1,10 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "trace_collections.h" + +namespace blink { + +void HeapObject::Trace(Visitor* visitor) const {} +} diff --git a/clang/blink_gc_plugin/tests/trace_collections.h b/clang/blink_gc_plugin/tests/trace_collections.h new file mode 100644 index 0000000000000000000000000000000000000000..6ce28f657dfba7261ea1e832739ee9f050c0ab15 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_collections.h @@ -0,0 +1,40 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACE_COLLECTIONS_H_ +#define TRACE_COLLECTIONS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { +public: + void Trace(Visitor*) const; + +private: + HeapVector > m_heapVector; + Vector, 0, HeapAllocator> m_wtfVector; + + HeapDeque > m_heapDeque; + Deque, 0, HeapAllocator> m_wtfDeque; + + HeapHashSet > m_heapSet; + HashSet, void, HeapAllocator> m_wtfSet; + + HeapLinkedHashSet > m_heapLinkedSet; + LinkedHashSet, void, HeapAllocator> m_wtfLinkedSet; + + HeapHashCountedSet > m_heapCountedSet; + HashCountedSet, void, HeapAllocator> m_wtfCountedSet; + + HeapHashMap > m_heapMapKey; + HeapHashMap, int > m_heapMapVal; + HashMap, void, void, HeapAllocator> m_wtfMapKey; + HashMap, int, void, void, HeapAllocator> m_wtfMapVal; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/trace_collections.txt b/clang/blink_gc_plugin/tests/trace_collections.txt new file mode 100644 index 0000000000000000000000000000000000000000..3cfc6a4e62ee0313f3f6a9bed9f79a25520ef59f --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_collections.txt @@ -0,0 +1,46 @@ +trace_collections.cpp:9:1: warning: [blink-gc] Class 'HeapObject' has untraced fields that require tracing. +void HeapObject::Trace(Visitor* visitor) const {} +^ +./trace_collections.h:17:5: note: [blink-gc] Untraced field 'm_heapVector' declared here: + HeapVector > m_heapVector; + ^ +./trace_collections.h:18:5: note: [blink-gc] Untraced field 'm_wtfVector' declared here: + Vector, 0, HeapAllocator> m_wtfVector; + ^ +./trace_collections.h:20:5: note: [blink-gc] Untraced field 'm_heapDeque' declared here: + HeapDeque > m_heapDeque; + ^ +./trace_collections.h:21:5: note: [blink-gc] Untraced field 'm_wtfDeque' declared here: + Deque, 0, HeapAllocator> m_wtfDeque; + ^ +./trace_collections.h:23:5: note: [blink-gc] Untraced field 'm_heapSet' declared here: + HeapHashSet > m_heapSet; + ^ +./trace_collections.h:24:5: note: [blink-gc] Untraced field 'm_wtfSet' declared here: + HashSet, void, HeapAllocator> m_wtfSet; + ^ +./trace_collections.h:26:5: note: [blink-gc] Untraced field 'm_heapLinkedSet' declared here: + HeapLinkedHashSet > m_heapLinkedSet; + ^ +./trace_collections.h:27:5: note: [blink-gc] Untraced field 'm_wtfLinkedSet' declared here: + LinkedHashSet, void, HeapAllocator> m_wtfLinkedSet; + ^ +./trace_collections.h:29:5: note: [blink-gc] Untraced field 'm_heapCountedSet' declared here: + HeapHashCountedSet > m_heapCountedSet; + ^ +./trace_collections.h:30:5: note: [blink-gc] Untraced field 'm_wtfCountedSet' declared here: + HashCountedSet, void, HeapAllocator> m_wtfCountedSet; + ^ +./trace_collections.h:32:5: note: [blink-gc] Untraced field 'm_heapMapKey' declared here: + HeapHashMap > m_heapMapKey; + ^ +./trace_collections.h:33:5: note: [blink-gc] Untraced field 'm_heapMapVal' declared here: + HeapHashMap, int > m_heapMapVal; + ^ +./trace_collections.h:34:5: note: [blink-gc] Untraced field 'm_wtfMapKey' declared here: + HashMap, void, void, HeapAllocator> m_wtfMapKey; + ^ +./trace_collections.h:35:5: note: [blink-gc] Untraced field 'm_wtfMapVal' declared here: + HashMap, int, void, void, HeapAllocator> m_wtfMapVal; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/trace_if_needed.cpp b/clang/blink_gc_plugin/tests/trace_if_needed.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a020c14b2c617c4a1d3a34bfb503d2adf3f8ea1 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_if_needed.cpp @@ -0,0 +1,14 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "trace_if_needed.h" + +namespace blink { + +template +void TemplatedObject::Trace(Visitor* visitor) const { + TraceIfNeeded::Trace(visitor, &m_one); + // Missing Trace of m_two +} +} diff --git a/clang/blink_gc_plugin/tests/trace_if_needed.h b/clang/blink_gc_plugin/tests/trace_if_needed.h new file mode 100644 index 0000000000000000000000000000000000000000..14aa923bac7e6ac14eca3a240d6bc14f6496517e --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_if_needed.h @@ -0,0 +1,28 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACE_IF_NEEDED_H_ +#define TRACE_IF_NEEDED_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { }; + +template +class TemplatedObject : public GarbageCollected > { +public: + virtual void Trace(Visitor*) const; + +private: + T m_one; + T m_two; +}; + +class InstantiatedObject : public TemplatedObject > { }; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/trace_if_needed.txt b/clang/blink_gc_plugin/tests/trace_if_needed.txt new file mode 100644 index 0000000000000000000000000000000000000000..a39af8b3f77d6c595d51f4e01105bca838fb3a9d --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_if_needed.txt @@ -0,0 +1,7 @@ +trace_if_needed.cpp:9:1: warning: [blink-gc] Class 'TemplatedObject>' has untraced fields that require tracing. +template +^ +./trace_if_needed.h:21:5: note: [blink-gc] Untraced field 'm_two' declared here: + T m_two; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/trace_if_needed_resolved.cpp b/clang/blink_gc_plugin/tests/trace_if_needed_resolved.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0cba0baa503d670b91f1f3f018f017a79fe05fb3 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_if_needed_resolved.cpp @@ -0,0 +1,16 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "trace_if_needed_resolved.h" + +namespace blink { + +void HeapObject::Trace(Visitor* visitor) const { + // Using TraceIfNeeded with a non-template type should count as tracing a + // field. + TraceIfNeeded>::Trace(visitor, m_one); + TraceIfNeeded::Trace(visitor, m_two); +} + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/trace_if_needed_resolved.h b/clang/blink_gc_plugin/tests/trace_if_needed_resolved.h new file mode 100644 index 0000000000000000000000000000000000000000..8cb6d9613940ce5d1ddabcdba5252dbea680a625 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_if_needed_resolved.h @@ -0,0 +1,23 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACE_IF_NEEDED_RESOLVED_H_ +#define TRACE_IF_NEEDED_RESOLVED_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { + public: + virtual void Trace(Visitor*) const; + + private: + Member m_one; + int m_two; +}; + +} // namespace blink + +#endif diff --git a/clang/blink_gc_plugin/tests/trace_if_needed_resolved.txt b/clang/blink_gc_plugin/tests/trace_if_needed_resolved.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/blink_gc_plugin/tests/trace_templated_super.cpp b/clang/blink_gc_plugin/tests/trace_templated_super.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d5cca9f6efcc54c4e24b7439509b3aa561932bd --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_templated_super.cpp @@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "trace_templated_super.h" + +namespace blink { + +template +void Super::clearWeakMembers(Visitor* visitor) +{ + (void)m_weak; +} + +template +void Super::Trace(Visitor* visitor) const { + visitor->RegisterWeakMembers, &Super::clearWeakMembers>(this); + visitor->Trace(m_obj); + Mixin::Trace(visitor); +} + +template +void Sub::Trace(Visitor* visitor) const { + // Missing Trace of m_obj. + Super::Trace(visitor); +} + +void HeapObject::Trace(Visitor* visitor) const { + visitor->Trace(m_obj); + Sub::Trace(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/trace_templated_super.h b/clang/blink_gc_plugin/tests/trace_templated_super.h new file mode 100644 index 0000000000000000000000000000000000000000..b7b4e4131085cd0c2f432fa80c44e19fb8fac74b --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_templated_super.h @@ -0,0 +1,49 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACE_TEMPLATED_SUPER_H_ +#define TRACE_TEMPLATED_SUPER_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject; + +class Mixin : public GarbageCollectedMixin { +public: + virtual void Trace(Visitor*) const override {} +}; + +template +class Super : public GarbageCollected >, public Mixin { +public: + virtual void Trace(Visitor*) const override; + void clearWeakMembers(Visitor*); + +private: + Member m_obj; + WeakMember m_weak; +}; + +template +class Sub : public Super { +public: + virtual void Trace(Visitor* visitor) const override; + +private: + Member m_obj; +}; + +class HeapObject : public Sub { +public: + virtual void Trace(Visitor*) const override; + +private: + Member m_obj; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/trace_templated_super.txt b/clang/blink_gc_plugin/tests/trace_templated_super.txt new file mode 100644 index 0000000000000000000000000000000000000000..f31cb306281a25086ddefeaf1938b31bbe26e888 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_templated_super.txt @@ -0,0 +1,7 @@ +trace_templated_super.cpp:22:1: warning: [blink-gc] Class 'Sub' has untraced fields that require tracing. +template +^ +./trace_templated_super.h:36:5: note: [blink-gc] Untraced field 'm_obj' declared here: + Member m_obj; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/trace_wrapper.cpp b/clang/blink_gc_plugin/tests/trace_wrapper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..636f5556d882abee194e13f5ed8f6473c47396e6 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_wrapper.cpp @@ -0,0 +1,13 @@ +// Copyright 2018 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "trace_wrapper.h" + +namespace blink { + +void B::Trace(Visitor* visitor) const { + static_cast(this)->TraceAfterDispatch(visitor); +} + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/trace_wrapper.h b/clang/blink_gc_plugin/tests/trace_wrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..2fbaf6359bdddcd564407e9fb0a18576f89b357c --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_wrapper.h @@ -0,0 +1,47 @@ +// Copyright 2018 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACE_WRAPPER_H_ +#define TRACE_WRAPPER_H_ + +#include "heap/stubs.h" + +namespace v8 { +class String; +} + +namespace blink { + +class A : public GarbageCollected { + public: + void Trace(Visitor*) const { + // Missing visitor->Trace(str_); + } + + private: + TraceWrapperV8Reference str_; +}; + +class B : public GarbageCollected { + public: + void Trace(Visitor* visitor) const; + void TraceAfterDispatch(Visitor*) const {} + + protected: + B() = default; +}; + +class C : public B { + public: + void TraceAfterDispatch(Visitor*) const { + // Missing visitor->Trace(str_); + } + + private: + TraceWrapperV8Reference str_; +}; + +} // namespace blink + +#endif // TRACE_WRAPPER_H_ diff --git a/clang/blink_gc_plugin/tests/trace_wrapper.txt b/clang/blink_gc_plugin/tests/trace_wrapper.txt new file mode 100644 index 0000000000000000000000000000000000000000..d73a8068410cf704a1d13de03615b84e0eb3cf77 --- /dev/null +++ b/clang/blink_gc_plugin/tests/trace_wrapper.txt @@ -0,0 +1,15 @@ +In file included from trace_wrapper.cpp:5: +./trace_wrapper.h:18:3: warning: [blink-gc] Class 'A' has untraced fields that require tracing. + void Trace(Visitor*) const { + ^ +./trace_wrapper.h:23:3: note: [blink-gc] Untraced field 'str_' declared here: + TraceWrapperV8Reference str_; + ^ +./trace_wrapper.h:37:3: warning: [blink-gc] Base class 'B' of derived class 'C' requires tracing. + void TraceAfterDispatch(Visitor*) const { + ^ +./trace_wrapper.h:37:3: warning: [blink-gc] Class 'C' has untraced fields that require tracing. +./trace_wrapper.h:42:3: note: [blink-gc] Untraced field 'str_' declared here: + TraceWrapperV8Reference str_; + ^ +3 warnings generated. diff --git a/clang/blink_gc_plugin/tests/traceable_part_object_in_unmanaged.cpp b/clang/blink_gc_plugin/tests/traceable_part_object_in_unmanaged.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b2d8a07774f0fee65195a3d86d413f4c0abe4fb2 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceable_part_object_in_unmanaged.cpp @@ -0,0 +1,53 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject; + +class PartObject { + DISALLOW_NEW(); + void Trace(Visitor* v) const { v->Trace(m_obj); } + + private: + Member m_obj; +}; + +class DisallowNewUntraceable { + DISALLOW_NEW(); + int a [[maybe_unused]] = 0; +}; + +class OffHeapObjectGood1 { + private: + DisallowNewUntraceable m_part; +}; + +class OffHeapObjectBad1 { + void Trace(Visitor* v) const { v->Trace(m_part); } + + private: + PartObject m_part; +}; + +class OffHeapObjectGood2 { + DISALLOW_NEW(); + + void Trace(Visitor* v) const { v->Trace(m_part); } + + private: + PartObject m_part; +}; + +template +class TemplatedObject { + private: + T m_part; +}; + +class OffHeapObjectBad2 : public TemplatedObject {}; + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/traceable_part_object_in_unmanaged.txt b/clang/blink_gc_plugin/tests/traceable_part_object_in_unmanaged.txt new file mode 100644 index 0000000000000000000000000000000000000000..be442429a1ecefaf217c54860499b845a3468e1b --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceable_part_object_in_unmanaged.txt @@ -0,0 +1,19 @@ +traceable_part_object_in_unmanaged.cpp:29:1: warning: [blink-gc] Class 'OffHeapObjectBad1' contains invalid fields. +class OffHeapObjectBad1 { +^ +traceable_part_object_in_unmanaged.cpp:33:3: warning: [blink-gc] Traceable part object field 'm_part' found in unmanaged class: + PartObject m_part; + ^ +traceable_part_object_in_unmanaged.cpp:46:1: warning: [blink-gc] Class 'TemplatedObject' requires a trace method. +class TemplatedObject { +^ +traceable_part_object_in_unmanaged.cpp:48:3: note: [blink-gc] Untraced field 'm_part' declared here: + T m_part; + ^ +traceable_part_object_in_unmanaged.cpp:46:1: warning: [blink-gc] Class 'TemplatedObject' contains invalid fields. +class TemplatedObject { +^ +traceable_part_object_in_unmanaged.cpp:48:3: warning: [blink-gc] Traceable part object field 'm_part' found in unmanaged class: + T m_part; + ^ +5 warnings generated. diff --git a/clang/blink_gc_plugin/tests/traceimpl.cpp b/clang/blink_gc_plugin/tests/traceimpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0d3308e42673768057c75a3e1a68b40229994804 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl.cpp @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "traceimpl.h" + +namespace blink { + +void TraceImplExtern::Trace(Visitor* visitor) const { + visitor->Trace(x_); +} + +void TraceImplBaseExtern::Trace(Visitor* visitor) const { + visitor->Trace(x_); + Base::Trace(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/traceimpl.h b/clang/blink_gc_plugin/tests/traceimpl.h new file mode 100644 index 0000000000000000000000000000000000000000..4ac2dfd8ce65f26cfe369759ffd480c247fb9bb2 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl.h @@ -0,0 +1,53 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACEIMPL_H_ +#define TRACEIMPL_H_ + +#include "heap/stubs.h" + +namespace blink { + +class X : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +class TraceImplInlined : public GarbageCollected { + public: + void Trace(Visitor* visitor) const { visitor->Trace(x_); } + + private: + Member x_; +}; + +class TraceImplExtern : public GarbageCollected { + public: + void Trace(Visitor* visitor) const; + + private: + Member x_; +}; + +class Base : public GarbageCollected { + public: + virtual void Trace(Visitor* visitor) const {} +}; + +class TraceImplBaseInlined : public Base { + public: + void Trace(Visitor* visitor) const override { Base::Trace(visitor); } +}; + +class TraceImplBaseExtern : public Base { + public: + void Trace(Visitor* visitor) const override; + + private: + Member x_; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/traceimpl.txt b/clang/blink_gc_plugin/tests/traceimpl.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/blink_gc_plugin/tests/traceimpl_dependent_scope.cpp b/clang/blink_gc_plugin/tests/traceimpl_dependent_scope.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e714ad85e41eca064c772b5a40ccf44da4db4729 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_dependent_scope.cpp @@ -0,0 +1,19 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "traceimpl_dependent_scope.h" + +namespace blink { + +struct Empty {}; + +// Template instantiation. +template class Derived; +template class DerivedMissingTrace; +template class Mixin; +template class MixinMissingTrace; +template class MixinTwoBases; +template class MixinTwoBasesMissingTrace; +template class MixinTwoBasesMissingTrace; // This should be fine. +} diff --git a/clang/blink_gc_plugin/tests/traceimpl_dependent_scope.h b/clang/blink_gc_plugin/tests/traceimpl_dependent_scope.h new file mode 100644 index 0000000000000000000000000000000000000000..bc8fd13dd9d9859b503c8732aa355af22becf42e --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_dependent_scope.h @@ -0,0 +1,75 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACEIMPL_DEPENDENT_SCOPE_H_ +#define TRACEIMPL_DEPENDENT_SCOPE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class X : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +class Y : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +template +class Base : public GarbageCollected > { + public: + virtual void Trace(Visitor* visitor) const {} +}; + +template +class Derived : public Base { + public: + void Trace(Visitor* visitor) const override { Base::Trace(visitor); } +}; + +template +class DerivedMissingTrace : public Base { + public: + void Trace(Visitor* visitor) const override { + // Missing Base::Trace(visitor). + } +}; + +template +class Mixin : T { + public: + void Trace(Visitor* visitor) const override { T::Trace(visitor); } +}; + +template +class MixinMissingTrace : T { + public: + void Trace(Visitor* visitor) const override { + // Missing T::Trace(visitor). + } +}; + +template +class MixinTwoBases : T1, T2 { + public: + void Trace(Visitor* visitor) const override { + T1::Trace(visitor); + T2::Trace(visitor); + } +}; + +template +class MixinTwoBasesMissingTrace : T1, T2 { + public: + void Trace(Visitor* visitor) const override { + T1::Trace(visitor); + // Missing T2::Trace(visitor). + } +}; +} + +#endif // TRACEIMPL_DEPENDENT_SCOPE_H_ diff --git a/clang/blink_gc_plugin/tests/traceimpl_dependent_scope.txt b/clang/blink_gc_plugin/tests/traceimpl_dependent_scope.txt new file mode 100644 index 0000000000000000000000000000000000000000..d177b3106394b0930b0619ccb685c6144551fd58 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_dependent_scope.txt @@ -0,0 +1,11 @@ +In file included from traceimpl_dependent_scope.cpp:5: +./traceimpl_dependent_scope.h:37:3: warning: [blink-gc] Base class 'Base' of derived class 'DerivedMissingTrace' requires tracing. + void Trace(Visitor* visitor) const override { + ^ +./traceimpl_dependent_scope.h:51:3: warning: [blink-gc] Base class 'X' of derived class 'MixinMissingTrace' requires tracing. + void Trace(Visitor* visitor) const override { + ^ +./traceimpl_dependent_scope.h:68:3: warning: [blink-gc] Base class 'Y' of derived class 'MixinTwoBasesMissingTrace' requires tracing. + void Trace(Visitor* visitor) const override { + ^ +3 warnings generated. diff --git a/clang/blink_gc_plugin/tests/traceimpl_derived_from_templated_base.cpp b/clang/blink_gc_plugin/tests/traceimpl_derived_from_templated_base.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef811714978bc8d581ad8ec5383260977e7f8aa8 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_derived_from_templated_base.cpp @@ -0,0 +1,7 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "traceimpl_derived_from_templated_base.h" + +// Nothing to define. diff --git a/clang/blink_gc_plugin/tests/traceimpl_derived_from_templated_base.h b/clang/blink_gc_plugin/tests/traceimpl_derived_from_templated_base.h new file mode 100644 index 0000000000000000000000000000000000000000..4997fa41cd9d7fb224b8b1b3e3522afec235288c --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_derived_from_templated_base.h @@ -0,0 +1,32 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACEIMPL_DERIVED_FROM_TEMPLATED_BASE_H_ +#define TRACEIMPL_DERIVED_FROM_TEMPLATED_BASE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class X : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +template +class TraceImplTemplatedBase + : public GarbageCollected > { + public: + void Trace(Visitor* visitor) const { visitor->Trace(x_); } + + private: + Member x_; +}; + +class TraceImplDerivedFromTemplatedBase : public TraceImplTemplatedBase<0> { +}; + +} + +#endif // TRACEIMPL_DERIVED_FROM_TEMPLATED_BASE_H_ diff --git a/clang/blink_gc_plugin/tests/traceimpl_derived_from_templated_base.txt b/clang/blink_gc_plugin/tests/traceimpl_derived_from_templated_base.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/blink_gc_plugin/tests/traceimpl_error.cpp b/clang/blink_gc_plugin/tests/traceimpl_error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e756c1313202d58a38b3456e11eb5ecff223d15 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_error.cpp @@ -0,0 +1,16 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "traceimpl_error.h" + +namespace blink { + +void TraceImplExternWithUntracedMember::Trace(Visitor* visitor) const { + // Should get a warning as well. +} + +void TraceImplExternWithUntracedBase::Trace(Visitor* visitor) const { + // Ditto. +} +} diff --git a/clang/blink_gc_plugin/tests/traceimpl_error.h b/clang/blink_gc_plugin/tests/traceimpl_error.h new file mode 100644 index 0000000000000000000000000000000000000000..e78fd09700c645d320c9460a2b496949f88ce9eb --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_error.h @@ -0,0 +1,56 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACEIMPL_ERROR_H_ +#define TRACEIMPL_ERROR_H_ + +#include "heap/stubs.h" + +namespace blink { + +class X : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +class TraceImplInlinedWithUntracedMember + : public GarbageCollected { + public: + void Trace(Visitor* visitor) const { + // Empty; should get complaints from the plugin for untraced x_. + } + + private: + Member x_; +}; + +class TraceImplExternWithUntracedMember + : public GarbageCollected { + public: + void Trace(Visitor* visitor) const; + + private: + Member x_; +}; + +class Base : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +class TraceImplInlineWithUntracedBase : public Base { + public: + void Trace(Visitor* visitor) const override { + // Empty; should get complaints from the plugin for untraced Base. + } +}; + +class TraceImplExternWithUntracedBase : public Base { + public: + void Trace(Visitor*) const override; +}; + +} + +#endif // TRACEIMPL_ERROR_H_ diff --git a/clang/blink_gc_plugin/tests/traceimpl_error.txt b/clang/blink_gc_plugin/tests/traceimpl_error.txt new file mode 100644 index 0000000000000000000000000000000000000000..dbc57e4bb179fc77d52840326856582b01343332 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_error.txt @@ -0,0 +1,20 @@ +In file included from traceimpl_error.cpp:5: +./traceimpl_error.h:20:3: warning: [blink-gc] Class 'TraceImplInlinedWithUntracedMember' has untraced fields that require tracing. + void Trace(Visitor* visitor) const { + ^ +./traceimpl_error.h:25:3: note: [blink-gc] Untraced field 'x_' declared here: + Member x_; + ^ +./traceimpl_error.h:44:3: warning: [blink-gc] Base class 'Base' of derived class 'TraceImplInlineWithUntracedBase' requires tracing. + void Trace(Visitor* visitor) const override { + ^ +traceimpl_error.cpp:9:1: warning: [blink-gc] Class 'TraceImplExternWithUntracedMember' has untraced fields that require tracing. +void TraceImplExternWithUntracedMember::Trace(Visitor* visitor) const { +^ +./traceimpl_error.h:34:3: note: [blink-gc] Untraced field 'x_' declared here: + Member x_; + ^ +traceimpl_error.cpp:13:1: warning: [blink-gc] Base class 'Base' of derived class 'TraceImplExternWithUntracedBase' requires tracing. +void TraceImplExternWithUntracedBase::Trace(Visitor* visitor) const { +^ +4 warnings generated. diff --git a/clang/blink_gc_plugin/tests/traceimpl_omitted_trace.cpp b/clang/blink_gc_plugin/tests/traceimpl_omitted_trace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..09880b8321a985b47f545ff003bfd7462e871845 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_omitted_trace.cpp @@ -0,0 +1,7 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "traceimpl_omitted_trace.h" + +// Nothing to define here. diff --git a/clang/blink_gc_plugin/tests/traceimpl_omitted_trace.h b/clang/blink_gc_plugin/tests/traceimpl_omitted_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..64e2604693166d16744e0bc999976ad458dad06e --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_omitted_trace.h @@ -0,0 +1,33 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACEIMPL_OMITTED_TRACE_H_ +#define TRACEIMPL_OMITTED_TRACE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class A : public GarbageCollected { + public: + virtual void Trace(Visitor* visitor) const {} +}; + +class B : public A { + // Trace() isn't necessary because we've got nothing to trace here. +}; + +class C : public B { + public: + void Trace(Visitor* visitor) const override { + // B::Trace() is actually A::Trace(), and in certain cases we only get + // limited information like "there is a function call that will be resolved + // to A::Trace()". We still want to mark B as Traced. + B::Trace(visitor); + } +}; + +} + +#endif // TRACEIMPL_OMITTED_TRACE_H_ diff --git a/clang/blink_gc_plugin/tests/traceimpl_omitted_trace.txt b/clang/blink_gc_plugin/tests/traceimpl_omitted_trace.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/blink_gc_plugin/tests/traceimpl_overloaded.cpp b/clang/blink_gc_plugin/tests/traceimpl_overloaded.cpp new file mode 100644 index 0000000000000000000000000000000000000000..044d50cb9bdfdc234941aeadd8463410d3ef1630 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_overloaded.cpp @@ -0,0 +1,17 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "traceimpl_overloaded.h" + +namespace blink { + +void ExternBase::Trace(Visitor* visitor) const { + visitor->Trace(x_base_); +} + +void ExternDerived::Trace(Visitor* visitor) const { + visitor->Trace(x_derived_); + ExternBase::Trace(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/traceimpl_overloaded.h b/clang/blink_gc_plugin/tests/traceimpl_overloaded.h new file mode 100644 index 0000000000000000000000000000000000000000..47c44a20312cae3e86d2a3c5087fa07d16380363 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_overloaded.h @@ -0,0 +1,53 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACEIMPL_OVERLOADED_H_ +#define TRACEIMPL_OVERLOADED_H_ + +#include "heap/stubs.h" + +namespace blink { + +class X : public GarbageCollected { + public: + void Trace(Visitor*) const {} +}; + +class InlinedBase : public GarbageCollected { + public: + virtual void Trace(Visitor* visitor) const { visitor->Trace(x_base_); } + + private: + Member x_base_; +}; + +class InlinedDerived : public InlinedBase { + public: + void Trace(Visitor* visitor) const override { + visitor->Trace(x_derived_); + InlinedBase::Trace(visitor); + } + + Member x_derived_; +}; + +class ExternBase : public GarbageCollected { + public: + virtual void Trace(Visitor*) const; + + private: + Member x_base_; +}; + +class ExternDerived : public ExternBase { + public: + void Trace(Visitor*) const override; + + private: + Member x_derived_; +}; + +} + +#endif // TRACEIMPL_OVERLOADED_H_ diff --git a/clang/blink_gc_plugin/tests/traceimpl_overloaded.txt b/clang/blink_gc_plugin/tests/traceimpl_overloaded.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/blink_gc_plugin/tests/traceimpl_overloaded_error.cpp b/clang/blink_gc_plugin/tests/traceimpl_overloaded_error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2473f33ff14d5ef1625fee0377b27dd571044035 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_overloaded_error.cpp @@ -0,0 +1,16 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "traceimpl_overloaded_error.h" + +namespace blink { + +void ExternBase::Trace(Visitor* visitor) const { + // Missing visitor->Trace(x_base_). +} + +void ExternDerived::Trace(Visitor* visitor) const { + // Missing visitor->Trace(x_derived_) and ExternBase::Trace(visitor). +} +} diff --git a/clang/blink_gc_plugin/tests/traceimpl_overloaded_error.h b/clang/blink_gc_plugin/tests/traceimpl_overloaded_error.h new file mode 100644 index 0000000000000000000000000000000000000000..9cb57bcdd3490e88a48bf04e35971555ab751483 --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_overloaded_error.h @@ -0,0 +1,55 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TRACEIMPL_OVERLOADED_ERROR_H_ +#define TRACEIMPL_OVERLOADED_ERROR_H_ + +#include "heap/stubs.h" + +namespace blink { + +class X : public GarbageCollected { + public: + void Trace(Visitor*) const {} +}; + +class InlinedBase : public GarbageCollected { + public: + virtual void Trace(Visitor* visitor) const { + // Missing visitor->Trace(x_base_). + } + + private: + Member x_base_; +}; + +class InlinedDerived : public InlinedBase { + public: + void Trace(Visitor* visitor) const override { + // Missing visitor->Trace(x_derived_) and InlinedBase::Trace(visitor). + } + + private: + Member x_derived_; +}; + +class ExternBase : public GarbageCollected { + public: + virtual void Trace(Visitor*) const; + + private: + Member x_base_; +}; + +class ExternDerived : public ExternBase { + public: + void Trace(Visitor*) const override; + + private: + Member x_derived_; +}; + +} + +#endif // TRACEIMPL_OVERLOADED_ERROR_H_ diff --git a/clang/blink_gc_plugin/tests/traceimpl_overloaded_error.txt b/clang/blink_gc_plugin/tests/traceimpl_overloaded_error.txt new file mode 100644 index 0000000000000000000000000000000000000000..b70ffcc8cda5b2c290e37c01e0b87019fa773bcb --- /dev/null +++ b/clang/blink_gc_plugin/tests/traceimpl_overloaded_error.txt @@ -0,0 +1,28 @@ +In file included from traceimpl_overloaded_error.cpp:5: +./traceimpl_overloaded_error.h:19:3: warning: [blink-gc] Class 'InlinedBase' has untraced fields that require tracing. + virtual void Trace(Visitor* visitor) const { + ^ +./traceimpl_overloaded_error.h:24:3: note: [blink-gc] Untraced field 'x_base_' declared here: + Member x_base_; + ^ +./traceimpl_overloaded_error.h:29:3: warning: [blink-gc] Base class 'InlinedBase' of derived class 'InlinedDerived' requires tracing. + void Trace(Visitor* visitor) const override { + ^ +./traceimpl_overloaded_error.h:29:3: warning: [blink-gc] Class 'InlinedDerived' has untraced fields that require tracing. +./traceimpl_overloaded_error.h:34:3: note: [blink-gc] Untraced field 'x_derived_' declared here: + Member x_derived_; + ^ +traceimpl_overloaded_error.cpp:9:1: warning: [blink-gc] Class 'ExternBase' has untraced fields that require tracing. +void ExternBase::Trace(Visitor* visitor) const { +^ +./traceimpl_overloaded_error.h:42:3: note: [blink-gc] Untraced field 'x_base_' declared here: + Member x_base_; + ^ +traceimpl_overloaded_error.cpp:13:1: warning: [blink-gc] Base class 'ExternBase' of derived class 'ExternDerived' requires tracing. +void ExternDerived::Trace(Visitor* visitor) const { +^ +traceimpl_overloaded_error.cpp:13:1: warning: [blink-gc] Class 'ExternDerived' has untraced fields that require tracing. +./traceimpl_overloaded_error.h:50:3: note: [blink-gc] Untraced field 'x_derived_' declared here: + Member x_derived_; + ^ +6 warnings generated. diff --git a/clang/blink_gc_plugin/tests/unique_ptr_to_gc_managed_class.cpp b/clang/blink_gc_plugin/tests/unique_ptr_to_gc_managed_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..15493e0cd4bcad117d527dc8d68da9cb45c583be --- /dev/null +++ b/clang/blink_gc_plugin/tests/unique_ptr_to_gc_managed_class.cpp @@ -0,0 +1,10 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "unique_ptr_to_gc_managed_class.h" + +namespace blink { + +void HeapObject::Trace(Visitor* visitor) const {} +} diff --git a/clang/blink_gc_plugin/tests/unique_ptr_to_gc_managed_class.h b/clang/blink_gc_plugin/tests/unique_ptr_to_gc_managed_class.h new file mode 100644 index 0000000000000000000000000000000000000000..c4eecd61b65be2ee39179403cbda694661c60b9c --- /dev/null +++ b/clang/blink_gc_plugin/tests/unique_ptr_to_gc_managed_class.h @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef OWN_PTR_TO_GC_MANAGED_CLASS_H_ +#define OWN_PTR_TO_GC_MANAGED_CLASS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject; + +class PartObject { + DISALLOW_NEW(); +private: + std::unique_ptr m_obj; +}; + +class HeapObject : public GarbageCollected { + public: + void Trace(Visitor*) const; + + private: + Vector> m_objs; + std::unique_ptr>> m_objs2; +}; +} + +#endif diff --git a/clang/blink_gc_plugin/tests/unique_ptr_to_gc_managed_class.txt b/clang/blink_gc_plugin/tests/unique_ptr_to_gc_managed_class.txt new file mode 100644 index 0000000000000000000000000000000000000000..9ed12733e6c10be014eaf35a59889c419d7081c0 --- /dev/null +++ b/clang/blink_gc_plugin/tests/unique_ptr_to_gc_managed_class.txt @@ -0,0 +1,17 @@ +In file included from unique_ptr_to_gc_managed_class.cpp:5: +./unique_ptr_to_gc_managed_class.h:14:1: warning: [blink-gc] Class 'PartObject' contains invalid fields. +class PartObject { +^ +./unique_ptr_to_gc_managed_class.h:17:5: note: [blink-gc] std::unique_ptr field 'm_obj' to a GC managed class declared here: + std::unique_ptr m_obj; + ^ +./unique_ptr_to_gc_managed_class.h:20:1: warning: [blink-gc] Class 'HeapObject' contains invalid fields. +class HeapObject : public GarbageCollected { +^ +./unique_ptr_to_gc_managed_class.h:25:3: note: [blink-gc] std::unique_ptr field 'm_objs' to a GC managed class declared here: + Vector> m_objs; + ^ +./unique_ptr_to_gc_managed_class.h:26:3: note: [blink-gc] std::unique_ptr field 'm_objs2' to a GC managed class declared here: + std::unique_ptr>> m_objs2; + ^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/variant_of_gced_type.cpp b/clang/blink_gc_plugin/tests/variant_of_gced_type.cpp new file mode 100644 index 0000000000000000000000000000000000000000..196336d0cd78d79348166cc4ff48b47e3970506f --- /dev/null +++ b/clang/blink_gc_plugin/tests/variant_of_gced_type.cpp @@ -0,0 +1,43 @@ +// Copyright 2017 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "variant_of_gced_type.h" + +namespace blink { + +void ForbidsVariantsOfGcedTypes() { + { + absl::variant not_ok; + (void)not_ok; + + absl::variant similarly_not_ok; + (void)similarly_not_ok; + + absl::variant not_ok_either; + (void)not_ok_either; + + absl::variant ditto; + (void)ditto; + + new absl::variant; + } + + { + std::variant not_ok; + (void)not_ok; + + std::variant similarly_not_ok; + (void)similarly_not_ok; + + std::variant not_ok_either; + (void)not_ok_either; + + std::variant ditto; + (void)ditto; + + new std::variant; + } +} + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/variant_of_gced_type.h b/clang/blink_gc_plugin/tests/variant_of_gced_type.h new file mode 100644 index 0000000000000000000000000000000000000000..33c77ea99c5f76dd3a8b38c40b0ebb240015b849 --- /dev/null +++ b/clang/blink_gc_plugin/tests/variant_of_gced_type.h @@ -0,0 +1,29 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_BLINK_GC_PLUGIN_TESTS_VARIANT_OF_GCED_TYPE_H_ +#define TOOLS_CLANG_BLINK_GC_PLUGIN_TESTS_VARIANT_OF_GCED_TYPE_H_ + +#include "heap/stubs.h" + +namespace blink { + +class Base : public GarbageCollected { + public: + virtual void Trace(Visitor*) const {} +}; + +class Derived : public Base { + public: + void Trace(Visitor* visitor) const override { Base::Trace(visitor); } +}; + +class Mixin : public GarbageCollectedMixin { + public: + void Trace(Visitor*) const {} +}; + +} // namespace blink + +#endif // TOOLS_CLANG_BLINK_GC_PLUGIN_TESTS_VARIANT_OF_GCED_TYPE_H_ diff --git a/clang/blink_gc_plugin/tests/variant_of_gced_type.txt b/clang/blink_gc_plugin/tests/variant_of_gced_type.txt new file mode 100644 index 0000000000000000000000000000000000000000..c00bf8961561148320ac5bbfe7d5159b4defd74f --- /dev/null +++ b/clang/blink_gc_plugin/tests/variant_of_gced_type.txt @@ -0,0 +1,31 @@ +variant_of_gced_type.cpp:11:25: warning: [blink-gc] Disallowed construction of 'variant' found; 'Base' is a garbage-collected type. Variant cannot hold garbage-collected objects. + absl::variant not_ok; + ^~~~~~ +variant_of_gced_type.cpp:14:31: warning: [blink-gc] Disallowed construction of 'variant' found; 'Base' is a garbage-collected type. Variant cannot hold garbage-collected objects. + absl::variant similarly_not_ok; + ^~~~~~~~~~~~~~~~ +variant_of_gced_type.cpp:17:30: warning: [blink-gc] Disallowed construction of 'variant' found; 'Base' is a garbage-collected type. Variant cannot hold garbage-collected objects. + absl::variant not_ok_either; + ^~~~~~~~~~~~~ +variant_of_gced_type.cpp:20:33: warning: [blink-gc] Disallowed construction of 'variant' found; 'Derived' is a garbage-collected type. Variant cannot hold garbage-collected objects. + absl::variant ditto; + ^~~~~ +variant_of_gced_type.cpp:23:9: warning: [blink-gc] Disallowed construction of 'variant' found; 'Mixin' is a garbage-collected type. Variant cannot hold garbage-collected objects. + new absl::variant; + ^~~~ +variant_of_gced_type.cpp:27:24: warning: [blink-gc] Disallowed construction of 'variant' found; 'Base' is a garbage-collected type. Variant cannot hold garbage-collected objects. + std::variant not_ok; + ^~~~~~ +variant_of_gced_type.cpp:30:30: warning: [blink-gc] Disallowed construction of 'variant' found; 'Base' is a garbage-collected type. Variant cannot hold garbage-collected objects. + std::variant similarly_not_ok; + ^~~~~~~~~~~~~~~~ +variant_of_gced_type.cpp:33:29: warning: [blink-gc] Disallowed construction of 'variant' found; 'Base' is a garbage-collected type. Variant cannot hold garbage-collected objects. + std::variant not_ok_either; + ^~~~~~~~~~~~~ +variant_of_gced_type.cpp:36:32: warning: [blink-gc] Disallowed construction of 'variant' found; 'Derived' is a garbage-collected type. Variant cannot hold garbage-collected objects. + std::variant ditto; + ^~~~~ +variant_of_gced_type.cpp:39:9: warning: [blink-gc] Disallowed construction of 'variant' found; 'Mixin' is a garbage-collected type. Variant cannot hold garbage-collected objects. + new std::variant; + ^~~ +10 warnings generated. diff --git a/clang/blink_gc_plugin/tests/virtual_and_trace_after_dispatch.cpp b/clang/blink_gc_plugin/tests/virtual_and_trace_after_dispatch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b67ad4895a827fe14cfe011fd31fc622985a86b8 --- /dev/null +++ b/clang/blink_gc_plugin/tests/virtual_and_trace_after_dispatch.cpp @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "virtual_and_trace_after_dispatch.h" + +namespace blink { + +static const B* toB(const A* a) { + return static_cast(a); +} + +void A::Trace(Visitor* visitor) const { + switch (m_type) { + case TB: + toB(this)->TraceAfterDispatch(visitor); + break; + } +} + +void A::TraceAfterDispatch(Visitor* visitor) const {} + +void B::TraceAfterDispatch(Visitor* visitor) const { + visitor->Trace(m_a); + A::TraceAfterDispatch(visitor); +} +} diff --git a/clang/blink_gc_plugin/tests/virtual_and_trace_after_dispatch.h b/clang/blink_gc_plugin/tests/virtual_and_trace_after_dispatch.h new file mode 100644 index 0000000000000000000000000000000000000000..ab39e899c9a1b17526a64a7ef6169aca17fde930 --- /dev/null +++ b/clang/blink_gc_plugin/tests/virtual_and_trace_after_dispatch.h @@ -0,0 +1,36 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef VIRTUAL_AND_TRACE_AFTER_DISPATCH_H_ +#define VIRTUAL_AND_TRACE_AFTER_DISPATCH_H_ + +#include "heap/stubs.h" + +namespace blink { + +class A : public GarbageCollected { +public: + void Trace(Visitor*) const; + void TraceAfterDispatch(Visitor*) const; + +protected: + enum Type { TB }; + A(Type type) : m_type(type) {} + +private: + Type m_type; +}; + +class B : public A { +public: + B() : A(TB) { } + void TraceAfterDispatch(Visitor*) const; + virtual void foo() { } +private: + Member m_a; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/virtual_and_trace_after_dispatch.txt b/clang/blink_gc_plugin/tests/virtual_and_trace_after_dispatch.txt new file mode 100644 index 0000000000000000000000000000000000000000..9c900769dd1e901fe6ae9d3e6da87b99ca74f8dd --- /dev/null +++ b/clang/blink_gc_plugin/tests/virtual_and_trace_after_dispatch.txt @@ -0,0 +1,11 @@ +In file included from virtual_and_trace_after_dispatch.cpp:5: +./virtual_and_trace_after_dispatch.h:12:1: warning: [blink-gc] Left-most base class 'A' of derived class 'B' must be polymorphic. +class A : public GarbageCollected { +^ +./virtual_and_trace_after_dispatch.h:25:1: warning: [blink-gc] Class 'B' contains or inherits virtual methods but implements manual dispatching. +class B : public A { +^ +./virtual_and_trace_after_dispatch.h:14:2: note: [blink-gc] Manual dispatch 'Trace' declared here: + void Trace(Visitor*) const; + ^ +2 warnings generated. diff --git a/clang/blink_gc_plugin/tests/weak_fields_require_tracing.cpp b/clang/blink_gc_plugin/tests/weak_fields_require_tracing.cpp new file mode 100644 index 0000000000000000000000000000000000000000..02f5180090c09d3923b8b46d33cf2a4ed42fa6b3 --- /dev/null +++ b/clang/blink_gc_plugin/tests/weak_fields_require_tracing.cpp @@ -0,0 +1,26 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "weak_fields_require_tracing.h" + +namespace blink { + +void HeapObject::Trace(Visitor* visitor) const { + // Missing visitor->Trace(m_obj1); + // Missing visitor->Trace(m_obj2); + // visitor->Trace(m_obj3) in callback. + // Missing visitor->Trace(m_set1); + visitor->Trace(m_set2); + visitor->RegisterWeakMembers(this); +} + +void HeapObject::clearWeakMembers(Visitor* visitor) +{ + visitor->Trace(m_obj1); // Does not count. + // Missing visitor->Trace(m_obj2); + visitor->Trace(m_obj3); // OK. + visitor->Trace(m_set1); // Does not count. +} + +} diff --git a/clang/blink_gc_plugin/tests/weak_fields_require_tracing.h b/clang/blink_gc_plugin/tests/weak_fields_require_tracing.h new file mode 100644 index 0000000000000000000000000000000000000000..4476fb8eab71f2c2f556539aa3a561963cc576e4 --- /dev/null +++ b/clang/blink_gc_plugin/tests/weak_fields_require_tracing.h @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEAK_FIELDS_REQUIRE_TRACING_H_ +#define WEAK_FIELDS_REQUIRE_TRACING_H_ + +#include "heap/stubs.h" + +namespace blink { + +class HeapObject : public GarbageCollected { +public: + void Trace(Visitor*) const; + void clearWeakMembers(Visitor*); + +private: + Member m_obj1; + WeakMember m_obj2; + WeakMember m_obj3; + HeapHashSet > m_set1; + HeapHashSet > m_set2; +}; + +} + +#endif diff --git a/clang/blink_gc_plugin/tests/weak_fields_require_tracing.txt b/clang/blink_gc_plugin/tests/weak_fields_require_tracing.txt new file mode 100644 index 0000000000000000000000000000000000000000..909fa2b0062b72469299b2a42c6b57aa10157b87 --- /dev/null +++ b/clang/blink_gc_plugin/tests/weak_fields_require_tracing.txt @@ -0,0 +1,13 @@ +weak_fields_require_tracing.cpp:9:1: warning: [blink-gc] Class 'HeapObject' has untraced fields that require tracing. +void HeapObject::Trace(Visitor* visitor) const { +^ +./weak_fields_require_tracing.h:18:5: note: [blink-gc] Untraced field 'm_obj1' declared here: + Member m_obj1; + ^ +./weak_fields_require_tracing.h:19:5: note: [blink-gc] Untraced field 'm_obj2' declared here: + WeakMember m_obj2; + ^ +./weak_fields_require_tracing.h:21:5: note: [blink-gc] Untraced field 'm_set1' declared here: + HeapHashSet > m_set1; + ^ +1 warning generated. diff --git a/clang/blink_gc_plugin/tests/weak_ptr_to_gc_managed_class.cpp b/clang/blink_gc_plugin/tests/weak_ptr_to_gc_managed_class.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45cb4c1a3b47069e12a689f8554a87d1344fa983 --- /dev/null +++ b/clang/blink_gc_plugin/tests/weak_ptr_to_gc_managed_class.cpp @@ -0,0 +1,15 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "weak_ptr_to_gc_managed_class.h" + +namespace blink { + +void Foo() { + base::WeakPtr gced; + base::WeakPtr mixin; + base::WeakPtr nongced; // OK +} + +} // namespace blink diff --git a/clang/blink_gc_plugin/tests/weak_ptr_to_gc_managed_class.h b/clang/blink_gc_plugin/tests/weak_ptr_to_gc_managed_class.h new file mode 100644 index 0000000000000000000000000000000000000000..5a1085da0e25e44c7c5a32288159ca43a27ffd73 --- /dev/null +++ b/clang/blink_gc_plugin/tests/weak_ptr_to_gc_managed_class.h @@ -0,0 +1,45 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEAK_PTR_TO_GC_MANAGED_CLASS_H_ +#define WEAK_PTR_TO_GC_MANAGED_CLASS_H_ + +#include "heap/stubs.h" + +namespace blink { + +class NonGCed; + +class Mixin : public GarbageCollectedMixin { + public: + void Trace(Visitor*) const override {} + + private: + base::WeakPtrFactory m_factory{this}; +}; + +class GCed : public GarbageCollected { + public: + void Trace(Visitor*) const {} + + private: + base::WeakPtr m_gced; + base::WeakPtr m_mixin; + base::WeakPtr m_nongced; // OK + + base::WeakPtrFactory m_factory{this}; +}; + +class NonGCed { + private: + base::WeakPtr m_gced; + base::WeakPtr m_mixin; + base::WeakPtr m_nongced; // OK + + GC_PLUGIN_IGNORE("test") base::WeakPtr m_ignored_gced; +}; + +} // namespace blink + +#endif // WEAK_PTR_TO_GC_MANAGED_CLASS_H_ diff --git a/clang/blink_gc_plugin/tests/weak_ptr_to_gc_managed_class.txt b/clang/blink_gc_plugin/tests/weak_ptr_to_gc_managed_class.txt new file mode 100644 index 0000000000000000000000000000000000000000..689b8e359ee54b5015d4cc2a603f36f6a9f1d7f3 --- /dev/null +++ b/clang/blink_gc_plugin/tests/weak_ptr_to_gc_managed_class.txt @@ -0,0 +1,26 @@ +In file included from weak_ptr_to_gc_managed_class.cpp:5: +./weak_ptr_to_gc_managed_class.h:19:3: warning: [blink-gc] WeakPtr or WeakPtrFactory field 'WeakPtrFactory' to a GC managed class 'Mixin' declared here (use WeakCell or WeakCellFactory instead): + base::WeakPtrFactory m_factory{this}; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +./weak_ptr_to_gc_managed_class.h:27:3: warning: [blink-gc] WeakPtr or WeakPtrFactory field 'WeakPtr' to a GC managed class 'GCed' declared here (use WeakCell or WeakCellFactory instead): + base::WeakPtr m_gced; + ^~~~~~~~~~~~~~~~~~~~~~~~~~ +./weak_ptr_to_gc_managed_class.h:28:3: warning: [blink-gc] WeakPtr or WeakPtrFactory field 'WeakPtr' to a GC managed class 'Mixin' declared here (use WeakCell or WeakCellFactory instead): + base::WeakPtr m_mixin; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ +./weak_ptr_to_gc_managed_class.h:31:3: warning: [blink-gc] WeakPtr or WeakPtrFactory field 'WeakPtrFactory' to a GC managed class 'GCed' declared here (use WeakCell or WeakCellFactory instead): + base::WeakPtrFactory m_factory{this}; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +./weak_ptr_to_gc_managed_class.h:36:3: warning: [blink-gc] WeakPtr or WeakPtrFactory field 'WeakPtr' to a GC managed class 'GCed' declared here (use WeakCell or WeakCellFactory instead): + base::WeakPtr m_gced; + ^~~~~~~~~~~~~~~~~~~~~~~~~~ +./weak_ptr_to_gc_managed_class.h:37:3: warning: [blink-gc] WeakPtr or WeakPtrFactory field 'WeakPtr' to a GC managed class 'Mixin' declared here (use WeakCell or WeakCellFactory instead): + base::WeakPtr m_mixin; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ +weak_ptr_to_gc_managed_class.cpp:10:3: warning: [blink-gc] WeakPtr or WeakPtrFactory field 'WeakPtr' to a GC managed class 'GCed' declared here (use WeakCell or WeakCellFactory instead): + base::WeakPtr gced; + ^~~~~~~~~~~~~~~~~~~~~~~~ +weak_ptr_to_gc_managed_class.cpp:11:3: warning: [blink-gc] WeakPtr or WeakPtrFactory field 'WeakPtr' to a GC managed class 'Mixin' declared here (use WeakCell or WeakCellFactory instead): + base::WeakPtr mixin; + ^~~~~~~~~~~~~~~~~~~~~~~~~~ +8 warnings generated. diff --git a/clang/raw_ptr_plugin/CMakeLists.txt b/clang/raw_ptr_plugin/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..81cc9b6c16ece205adaeb475814f1fe9232cbd09 --- /dev/null +++ b/clang/raw_ptr_plugin/CMakeLists.txt @@ -0,0 +1,27 @@ +set(plugin_sources + RawPtrPlugin.cpp + FindBadRawPtrPatterns.cpp + RawPtrHelpers.cpp + StackAllocatedChecker.cpp + Util.cpp +) + +# Clang doesn't support loadable modules on Windows. Unfortunately, building +# the plugin as a static library and linking clang against it doesn't work. +# Since clang doesn't reference any symbols in our static library, the linker +# strips it out completely. +# Instead, we rely on the fact that the SOURCES property of a target is not +# read-only after CMake 3.1 and use it to compile the plugin directly into +# clang. +cmake_minimum_required(VERSION 3.1) +# Paths must be absolute, since we're modifying a target in another directory. +set(absolute_sources "") +foreach(source ${plugin_sources}) + list(APPEND absolute_sources ${CMAKE_CURRENT_SOURCE_DIR}/${source}) +endforeach() +set_property(TARGET clang APPEND PROPERTY SOURCES ${absolute_sources}) + +cr_add_test(raw_ptr_plugin_test + python3 tests/test.py + ${CMAKE_BINARY_DIR}/bin/clang + ) diff --git a/clang/raw_ptr_plugin/FindBadRawPtrPatterns.cpp b/clang/raw_ptr_plugin/FindBadRawPtrPatterns.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7eddbfc2dba1301a9949c0616b4b64541cdde5ff --- /dev/null +++ b/clang/raw_ptr_plugin/FindBadRawPtrPatterns.cpp @@ -0,0 +1,444 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "FindBadRawPtrPatterns.h" + +#include + +#include "RawPtrHelpers.h" +#include "RawPtrManualPathsToIgnore.h" +#include "SeparateRepositoryPaths.h" +#include "StackAllocatedChecker.h" +#include "TypePredicateUtil.h" +#include "Util.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Attr.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/TypeLoc.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/TimeProfiler.h" + +using namespace clang; +using namespace clang::ast_matchers; + +namespace raw_ptr_plugin { + +constexpr char kBadCastDiagnostic[] = + "[chromium-style] casting '%0' to '%1 is not allowed."; +constexpr char kBadCastDiagnosticNoteExplanation[] = + "[chromium-style] '%0' manages BackupRefPtr refcounts; bypassing its C++ " + "interface or treating it as a POD will lead to memory safety errors."; +constexpr char kBadCastDiagnosticNoteType[] = + "[chromium-style] '%0' manages BackupRefPtr or its container here."; + +class BadCastMatcher : public MatchFinder::MatchCallback { + public: + explicit BadCastMatcher(clang::CompilerInstance& compiler, + const FilterFile& exclude_files, + const FilterFile& exclude_functions) + : compiler_(compiler), + exclude_files_(exclude_files), + exclude_functions_(exclude_functions) { + error_bad_cast_signature_ = compiler_.getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, kBadCastDiagnostic); + note_bad_cast_signature_explanation_ = + compiler_.getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Note, kBadCastDiagnosticNoteExplanation); + note_bad_cast_signature_type_ = compiler_.getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Note, kBadCastDiagnosticNoteType); + } + + void Register(MatchFinder& match_finder) { + auto cast_matcher = BadRawPtrCastExpr(casting_unsafe_predicate_, + exclude_files_, exclude_functions_); + match_finder.addMatcher(cast_matcher, this); + } + + void run(const MatchFinder::MatchResult& result) override { + const clang::CastExpr* cast_expr = + result.Nodes.getNodeAs("castExpr"); + assert(cast_expr && "matcher should bind 'castExpr'"); + + const clang::SourceManager& source_manager = *result.SourceManager; + clang::SourceLocation loc = cast_expr->getSourceRange().getBegin(); + std::string file_path = + GetFilename(source_manager, loc, FilenameLocationType::kSpellingLoc); + + clang::PrintingPolicy printing_policy(result.Context->getLangOpts()); + const std::string src_name = + cast_expr->getSubExpr()->getType().getAsString(printing_policy); + const std::string dst_name = + cast_expr->getType().getAsString(printing_policy); + + const auto* src_type = result.Nodes.getNodeAs("srcType"); + const auto* dst_type = result.Nodes.getNodeAs("dstType"); + assert((src_type || dst_type) && + "matcher should bind 'srcType' or 'dstType'"); + + const auto* enclosing_cast_expr = + result.Nodes.getNodeAs("enclosingCastExpr"); + const auto* cast_expr_for_display = + enclosing_cast_expr ? enclosing_cast_expr : cast_expr; + + compiler_.getDiagnostics().Report(cast_expr_for_display->getEndLoc(), + error_bad_cast_signature_) + << src_name << dst_name; + + std::shared_ptr type_note; + if (src_type != nullptr) { + compiler_.getDiagnostics().Report(cast_expr_for_display->getEndLoc(), + note_bad_cast_signature_explanation_) + << src_name; + type_note = casting_unsafe_predicate_.GetMatchResult(src_type); + } else { + compiler_.getDiagnostics().Report(cast_expr_for_display->getEndLoc(), + note_bad_cast_signature_explanation_) + << dst_name; + type_note = casting_unsafe_predicate_.GetMatchResult(dst_type); + } + + while (type_note) { + if (type_note->source_loc()) { + const auto& type_name = clang::QualType::getAsString( + type_note->type(), {}, printing_policy); + compiler_.getDiagnostics().Report(*type_note->source_loc(), + note_bad_cast_signature_type_) + << type_name; + } + type_note = type_note->source(); + } + } + + llvm::StringRef getID() const override { return "BadCastMatcher"; }; + + private: + clang::CompilerInstance& compiler_; + const FilterFile& exclude_files_; + const FilterFile& exclude_functions_; + CastingUnsafePredicate casting_unsafe_predicate_; + unsigned error_bad_cast_signature_; + unsigned note_bad_cast_signature_explanation_; + unsigned note_bad_cast_signature_type_; +}; + +const char kNeedRawPtrSignature[] = + "[chromium-rawptr] Use raw_ptr instead of a raw pointer."; + +class RawPtrFieldMatcher : public MatchFinder::MatchCallback { + public: + explicit RawPtrFieldMatcher( + clang::CompilerInstance& compiler, + const RawPtrAndRefExclusionsOptions& exclusion_options) + : compiler_(compiler), exclusion_options_(exclusion_options) { + error_need_raw_ptr_signature_ = compiler_.getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, kNeedRawPtrSignature); + } + + void Register(MatchFinder& match_finder) { + auto field_decl_matcher = AffectedRawPtrFieldDecl(exclusion_options_); + match_finder.addMatcher(field_decl_matcher, this); + } + void run(const MatchFinder::MatchResult& result) override { + const clang::FieldDecl* field_decl = + result.Nodes.getNodeAs("affectedFieldDecl"); + assert(field_decl && "matcher should bind 'fieldDecl'"); + + const clang::TypeSourceInfo* type_source_info = + field_decl->getTypeSourceInfo(); + assert(type_source_info && "assuming |type_source_info| is always present"); + + assert(type_source_info->getType()->isPointerType() && + "matcher should only match pointer types"); + + compiler_.getDiagnostics().Report(field_decl->getLocation(), + error_need_raw_ptr_signature_); + } + + llvm::StringRef getID() const override { return "RawPtrFieldMatcher"; }; + + private: + clang::CompilerInstance& compiler_; + unsigned error_need_raw_ptr_signature_; + const RawPtrAndRefExclusionsOptions& exclusion_options_; +}; + +const char kNeedRawRefSignature[] = + "[chromium-rawref] Use raw_ref instead of a native reference."; + +class RawRefFieldMatcher : public MatchFinder::MatchCallback { + public: + explicit RawRefFieldMatcher( + clang::CompilerInstance& compiler, + const RawPtrAndRefExclusionsOptions& exclusion_options) + : compiler_(compiler), exclusion_options_(exclusion_options) { + error_need_raw_ref_signature_ = compiler_.getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, kNeedRawRefSignature); + } + + void Register(MatchFinder& match_finder) { + auto field_decl_matcher = AffectedRawRefFieldDecl(exclusion_options_); + match_finder.addMatcher(field_decl_matcher, this); + } + void run(const MatchFinder::MatchResult& result) override { + const clang::FieldDecl* field_decl = + result.Nodes.getNodeAs("affectedFieldDecl"); + assert(field_decl && "matcher should bind 'fieldDecl'"); + + const clang::TypeSourceInfo* type_source_info = + field_decl->getTypeSourceInfo(); + assert(type_source_info && "assuming |type_source_info| is always present"); + + assert(type_source_info->getType()->isReferenceType() && + "matcher should only match reference types"); + + compiler_.getDiagnostics().Report(field_decl->getEndLoc(), + error_need_raw_ref_signature_); + } + + llvm::StringRef getID() const override { return "RawRefFieldMatcher"; }; + + private: + clang::CompilerInstance& compiler_; + unsigned error_need_raw_ref_signature_; + const RawPtrAndRefExclusionsOptions exclusion_options_; +}; + +const char kNoRawPtrToStackAllocatedSignature[] = + "[chromium-raw-ptr-to-stack-allocated] Do not use '%0' on a " + "`STACK_ALLOCATED` object '%1'."; + +class RawPtrToStackAllocatedMatcher : public MatchFinder::MatchCallback { + public: + explicit RawPtrToStackAllocatedMatcher(clang::CompilerInstance& compiler) + : compiler_(compiler), stack_allocated_predicate_() { + error_no_raw_ptr_to_stack_ = compiler_.getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, kNoRawPtrToStackAllocatedSignature); + } + + void Register(MatchFinder& match_finder) { + auto value_decl_matcher = + RawPtrToStackAllocatedTypeLoc(&stack_allocated_predicate_); + match_finder.addMatcher(value_decl_matcher, this); + } + void run(const MatchFinder::MatchResult& result) override { + const auto* pointer = + result.Nodes.getNodeAs("pointerRecordDecl"); + assert(pointer && "matcher should bind 'pointerRecordDecl'"); + + const auto* pointee = + result.Nodes.getNodeAs("pointeeQualType"); + assert(pointee && "matcher should bind 'pointeeQualType'"); + clang::PrintingPolicy printing_policy(result.Context->getLangOpts()); + const std::string pointee_name = pointee->getAsString(printing_policy); + + const auto* type_loc = + result.Nodes.getNodeAs("stackAllocatedRawPtrTypeLoc"); + assert(type_loc && "matcher should bind 'stackAllocatedRawPtrTypeLoc'"); + + compiler_.getDiagnostics().Report(type_loc->getEndLoc(), + error_no_raw_ptr_to_stack_) + << pointer->getNameAsString() << pointee_name; + } + + llvm::StringRef getID() const override { + return "RawPtrToStackAllocatedMatcher"; + }; + + private: + clang::CompilerInstance& compiler_; + StackAllocatedPredicate stack_allocated_predicate_; + unsigned error_no_raw_ptr_to_stack_; +}; + +const char kNeedRawSpanSignature[] = + "[chromium-rawptr] Use raw_span instead of a span."; + +const char kNeedContainerSpanSignature[] = + "[chromium-rawptr] Use raw_span instead of a span in the field " + "type's template arguments."; + +class SpanFieldMatcher : public MatchFinder::MatchCallback { + public: + explicit SpanFieldMatcher( + clang::CompilerInstance& compiler, + const RawPtrAndRefExclusionsOptions& exclusion_options) + : compiler_(compiler), exclusion_options_(exclusion_options) { + error_need_span_signature_ = compiler_.getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, kNeedRawSpanSignature); + + error_need_container_span_signature_ = + compiler_.getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, kNeedContainerSpanSignature); + } + + void Register(MatchFinder& match_finder) { + auto raw_span = hasTemplateArgument( + 2, refersToType(qualType(hasCanonicalType(qualType(hasDeclaration( + mapAnyOf(classTemplateSpecializationDecl, classTemplateDecl) + .with(hasName("raw_ptr")))))))); + + auto string_literals_span = hasTemplateArgument( + 0, refersToType(qualType(hasCanonicalType( + anyOf(asString("const char"), asString("const wchar_t"), + asString("const char8_t"), asString("const char16_t"), + asString("const char32_t")))))); + + auto excluded_spans = anyOf(raw_span, string_literals_span); + + auto span_type = anyOf( + qualType(hasCanonicalType( + qualType(hasDeclaration(classTemplateSpecializationDecl( + hasName("base::span"), unless(excluded_spans)))))), + qualType(hasCanonicalType(qualType(type(templateSpecializationType( + hasDeclaration(classTemplateDecl(hasName("base::span"))), + unless(excluded_spans))))))); + + auto optional_span_type = anyOf( + qualType( + hasCanonicalType(hasDeclaration(classTemplateSpecializationDecl( + hasName("optional"), + hasTemplateArgument(0, refersToType(span_type)))))), + qualType(hasCanonicalType(qualType(type(templateSpecializationType( + hasDeclaration(classTemplateDecl(hasName("optional"))), + hasAnyTemplateArgument(refersToType(span_type)))))))); + + auto container_methods = + anyOf(allOf(hasMethod(hasName("push_back")), + hasMethod(hasName("pop_back")), hasMethod(hasName("size"))), + allOf(hasMethod(hasName("insert")), hasMethod(hasName("erase")), + hasMethod(hasName("size"))), + allOf(hasMethod(hasName("push")), hasMethod(hasName("pop")), + hasMethod(hasName("size")))); + + auto template_argument = + templateArgument(refersToType(anyOf(span_type, optional_span_type))); + auto template_arguments = anyOf(hasTemplateArgument(0, template_argument), + hasTemplateArgument(1, template_argument)); + + auto container_of_span_type = + qualType(hasCanonicalType(anyOf( + qualType(hasDeclaration(classTemplateSpecializationDecl( + container_methods, template_arguments))), + qualType(type(templateSpecializationType( + hasDeclaration(classTemplateDecl( + has(cxxRecordDecl(container_methods)))), + template_arguments)))))) + .bind("container_type"); + + auto field_decl_matcher = + traverse(clang::TK_IgnoreUnlessSpelledInSource, + fieldDecl(hasType(qualType(anyOf(span_type, optional_span_type, + container_of_span_type))), + unless(PtrAndRefExclusions(exclusion_options_))) + .bind("affectedFieldDecl")); + match_finder.addMatcher(field_decl_matcher, this); + } + + void run(const MatchFinder::MatchResult& result) override { + const clang::FieldDecl* field_decl = + result.Nodes.getNodeAs("affectedFieldDecl"); + assert(field_decl && "matcher should bind 'fieldDecl'"); + + if (result.Nodes.getNodeAs("container_type")) { + compiler_.getDiagnostics().Report(field_decl->getLocation(), + error_need_container_span_signature_); + } else { + compiler_.getDiagnostics().Report(field_decl->getLocation(), + error_need_span_signature_); + } + } + + llvm::StringRef getID() const override { return "SpanFieldMatcher"; }; + + private: + clang::CompilerInstance& compiler_; + unsigned error_need_span_signature_; + unsigned error_need_container_span_signature_; + const RawPtrAndRefExclusionsOptions& exclusion_options_; +}; + +void FindBadRawPtrPatterns(const Options& options, + clang::ASTContext& ast_context, + clang::CompilerInstance& compiler) { + llvm::StringMap Records; + MatchFinder::MatchFinderOptions FinderOptions; + if (options.enable_match_profiling) { + FinderOptions.CheckProfiling.emplace(Records); + } + MatchFinder match_finder(std::move(FinderOptions)); + + std::vector paths_to_exclude_lines; + std::vector check_bad_raw_ptr_cast_exclude_paths; + for (auto* const line : kRawPtrManualPathsToIgnore) { + paths_to_exclude_lines.push_back(line); + } + for (auto* const line : kSeparateRepositoryPaths) { + paths_to_exclude_lines.push_back(line); + check_bad_raw_ptr_cast_exclude_paths.push_back(line); + } + paths_to_exclude_lines.insert(paths_to_exclude_lines.end(), + options.raw_ptr_paths_to_exclude_lines.begin(), + options.raw_ptr_paths_to_exclude_lines.end()); + check_bad_raw_ptr_cast_exclude_paths.insert( + check_bad_raw_ptr_cast_exclude_paths.end(), + options.check_bad_raw_ptr_cast_exclude_paths.begin(), + options.check_bad_raw_ptr_cast_exclude_paths.end()); + + FilterFile exclude_fields(options.exclude_fields_file, "exclude-fields"); + FilterFile exclude_lines(paths_to_exclude_lines); + + StackAllocatedPredicate stack_allocated_predicate; + RawPtrAndRefExclusionsOptions exclusion_options{ + &exclude_fields, &exclude_lines, options.check_raw_ptr_to_stack_allocated, + &stack_allocated_predicate, options.check_ptrs_to_non_string_literals}; + + FilterFile filter_check_bad_raw_ptr_cast_exclude_paths( + check_bad_raw_ptr_cast_exclude_paths); + FilterFile filter_check_bad_raw_ptr_cast_exclude_funcs( + options.check_bad_raw_ptr_cast_exclude_funcs); + BadCastMatcher bad_cast_matcher(compiler, + filter_check_bad_raw_ptr_cast_exclude_paths, + filter_check_bad_raw_ptr_cast_exclude_funcs); + if (options.check_bad_raw_ptr_cast) { + bad_cast_matcher.Register(match_finder); + } + + RawPtrFieldMatcher field_matcher(compiler, exclusion_options); + if (options.check_raw_ptr_fields) { + field_matcher.Register(match_finder); + } + + RawRefFieldMatcher ref_field_matcher(compiler, exclusion_options); + if (options.check_raw_ref_fields) { + ref_field_matcher.Register(match_finder); + } + + RawPtrToStackAllocatedMatcher raw_ptr_to_stack(compiler); + if (options.check_raw_ptr_to_stack_allocated && + !options.disable_check_raw_ptr_to_stack_allocated_error) { + raw_ptr_to_stack.Register(match_finder); + } + + SpanFieldMatcher raw_span_matcher(compiler, exclusion_options); + if (options.check_span_fields) { + raw_span_matcher.Register(match_finder); + } + + { + llvm::TimeTraceScope TimeScope( + "match_finder.matchAST in FindBadRawPtrPatterns"); + match_finder.matchAST(ast_context); + } + + if (options.enable_match_profiling) { + llvm::TimerGroup TG("FindBadRawPtrPatterns", + "FindBadRawPtrPatterns match profiling", Records); + TG.print(llvm::errs()); + } +} + +} // namespace raw_ptr_plugin diff --git a/clang/raw_ptr_plugin/FindBadRawPtrPatterns.h b/clang/raw_ptr_plugin/FindBadRawPtrPatterns.h new file mode 100644 index 0000000000000000000000000000000000000000..e73342bc39b189847a84675bd37c17703b8bcd09 --- /dev/null +++ b/clang/raw_ptr_plugin/FindBadRawPtrPatterns.h @@ -0,0 +1,20 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_FINDBADRAWPTRPATTERNS_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_FINDBADRAWPTRPATTERNS_H_ + +#include "Options.h" +#include "clang/AST/ASTContext.h" +#include "clang/Frontend/CompilerInstance.h" + +namespace raw_ptr_plugin { + +void FindBadRawPtrPatterns(const Options& options, + clang::ASTContext& ast_context, + clang::CompilerInstance& compiler); + +} // namespace raw_ptr_plugin + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_FINDBADRAWPTRPATTERNS_H_ diff --git a/clang/raw_ptr_plugin/OWNERS b/clang/raw_ptr_plugin/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..859c2c608c3d0960b8627fa0ff3d448da99aee1e --- /dev/null +++ b/clang/raw_ptr_plugin/OWNERS @@ -0,0 +1 @@ +dcheng@chromium.org diff --git a/clang/raw_ptr_plugin/Options.h b/clang/raw_ptr_plugin/Options.h new file mode 100644 index 0000000000000000000000000000000000000000..5aeff9bba7855d854267e8a9ea83977f1aa20445 --- /dev/null +++ b/clang/raw_ptr_plugin/Options.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_OPTIONS_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_OPTIONS_H_ + +#include +#include + +namespace raw_ptr_plugin { + +struct Options { + bool check_bad_raw_ptr_cast = false; + bool check_raw_ptr_fields = false; + bool check_raw_ref_fields = false; + // `check_raw_ptr_to_stack_allocated` enables following features: + // - Disallow `raw_ptr` + // - Allow `StackAllocated*` (bypasses `check_raw_ptr_fields`) + // `disable_check_raw_ptr_to_stack_allocated_error` overwrites first feature + // to allow both of `raw_ptr` and `StackAllocated*`. + bool check_raw_ptr_to_stack_allocated = false; + bool disable_check_raw_ptr_to_stack_allocated_error = false; + bool check_ptrs_to_non_string_literals = false; + bool check_span_fields = false; + bool enable_match_profiling = false; + std::string exclude_fields_file; + std::vector raw_ptr_paths_to_exclude_lines; + std::vector check_bad_raw_ptr_cast_exclude_funcs; + std::vector check_bad_raw_ptr_cast_exclude_paths; +}; + +} // namespace raw_ptr_plugin + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_OPTIONS_H_ diff --git a/clang/raw_ptr_plugin/README.md b/clang/raw_ptr_plugin/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c2ca0192dc03d6146b9e609f2768a18bea68cb21 --- /dev/null +++ b/clang/raw_ptr_plugin/README.md @@ -0,0 +1,4 @@ +Documentation for this code is: + +- https://chromium.googlesource.com/chromium/src/+/main/docs/clang.md +- https://chromium.googlesource.com/chromium/src/+/main/docs/writing_clang_plugins.md diff --git a/clang/raw_ptr_plugin/RawPtrCastingUnsafeChecker.h b/clang/raw_ptr_plugin/RawPtrCastingUnsafeChecker.h new file mode 100644 index 0000000000000000000000000000000000000000..6b92518ee19e66e21447daa0fd5ed9a33a6ef9c8 --- /dev/null +++ b/clang/raw_ptr_plugin/RawPtrCastingUnsafeChecker.h @@ -0,0 +1,45 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRCASTINGUNSAFECHECKER_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRCASTINGUNSAFECHECKER_H_ + +#include + +#include "TypePredicateUtil.h" +#include "clang/AST/Type.h" + +// Determines a type is "casting unsafe" or not. +// A type is considered "casting unsafe" if ref counting can be broken as a +// result of casting. We determine "casting unsafe" types by applying rules here +// recursively. +class CastingUnsafePredicate + : public TypePredicate< + // Pointers, references and arrays to "casting unsafe" element(s) are + // "casting unsafe" + InductionRule::kPointerPointee | InductionRule::kObjCPointerPointee | + InductionRule::kReferencePointee | InductionRule::kArrayElement | + // Typedef, using aliases to "casting unsafe" type are "casting + // unsafe" + InductionRule::kUnqualifiedDesugaredType | + // Classes and structs having a "casting unsafe" base class are + // "casting unsafe" + InductionRule::kBaseClass | InductionRule::kVirtualBaseClass | + // Classes and structs having a "casting unsafe" member are "casting + // unsafe" + InductionRule::kField> { + // Base case: |raw_ptr| and |raw_ref| are never safe to cast to/from. + // When implemented as BackupRefPtr, assignment needs to go through the C++ + // operators to ensure the refcount is properly maintained. + bool IsBaseMatch(const clang::Type* type) const override { + const auto* decl = type->getAsRecordDecl(); + if (!decl) { + return false; + } + const std::string record_name = decl->getQualifiedNameAsString(); + return record_name == "base::raw_ptr" || record_name == "base::raw_ref"; + } +}; + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRCASTINGUNSAFECHECKER_H_ diff --git a/clang/raw_ptr_plugin/RawPtrHelpers.cpp b/clang/raw_ptr_plugin/RawPtrHelpers.cpp new file mode 100644 index 0000000000000000000000000000000000000000..904da574a1715d341b7dc02992fc2d0ca114c985 --- /dev/null +++ b/clang/raw_ptr_plugin/RawPtrHelpers.cpp @@ -0,0 +1,520 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "RawPtrHelpers.h" + +#include "StackAllocatedChecker.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace clang::ast_matchers; + +namespace raw_ptr_plugin { + +FilterFile::FilterFile(const std::vector& lines) { + for (const auto& line : lines) { + file_lines_.insert(line); + } +} + +bool FilterFile::ContainsLine(llvm::StringRef line) const { + auto it = file_lines_.find(line); + return it != file_lines_.end(); +} + +bool FilterFile::ContainsSubstringOf(llvm::StringRef string_to_match) const { + if (!inclusion_substring_regex_.has_value()) { + std::vector regex_escaped_inclusion_file_lines; + std::vector regex_escaped_exclusion_file_lines; + regex_escaped_inclusion_file_lines.reserve(file_lines_.size()); + for (const llvm::StringRef& file_line : file_lines_.keys()) { + if (file_line.starts_with("!")) { + regex_escaped_exclusion_file_lines.push_back( + llvm::Regex::escape(file_line.substr(1))); + } else { + regex_escaped_inclusion_file_lines.push_back( + llvm::Regex::escape(file_line)); + } + } + std::string inclusion_substring_regex_pattern = + llvm::join(regex_escaped_inclusion_file_lines.begin(), + regex_escaped_inclusion_file_lines.end(), "|"); + inclusion_substring_regex_.emplace(inclusion_substring_regex_pattern); + std::string exclusion_substring_regex_pattern = + llvm::join(regex_escaped_exclusion_file_lines.begin(), + regex_escaped_exclusion_file_lines.end(), "|"); + exclusion_substring_regex_.emplace(exclusion_substring_regex_pattern); + } + return inclusion_substring_regex_->match(string_to_match) && + !exclusion_substring_regex_->match(string_to_match); +} + +void FilterFile::ParseInputFile(const std::string& filepath, + const std::string& arg_name) { + if (filepath.empty()) { + return; + } + + llvm::ErrorOr> file_or_err = + llvm::MemoryBuffer::getFile(filepath); + if (std::error_code err = file_or_err.getError()) { + llvm::errs() << "ERROR: Cannot open the file specified in --" << arg_name + << " argument: " << filepath << ": " << err.message() << "\n"; + assert(false); + return; + } + + llvm::line_iterator it(**file_or_err, true /* SkipBlanks */, '#'); + for (; !it.is_at_eof(); ++it) { + llvm::StringRef line = *it; + + // Remove trailing location information. + size_t loc_info_start_pos = line.find('@'); + if (loc_info_start_pos != llvm::StringRef::npos) { + line = line.substr(0, loc_info_start_pos); + } else { + // Remove trailing comments. + size_t comment_start_pos = line.find('#'); + if (comment_start_pos != llvm::StringRef::npos) { + line = line.substr(0, comment_start_pos); + } + } + line = line.trim(); + + if (line.empty()) { + continue; + } + + file_lines_.insert(line); + } +} + +clang::ast_matchers::internal::Matcher ImplicitFieldDeclaration() { + auto implicit_class_specialization_matcher = + classTemplateSpecializationDecl(isImplicitClassTemplateSpecialization()); + auto implicit_function_specialization_matcher = + functionDecl(isImplicitFunctionTemplateSpecialization()); + auto implicit_field_decl_matcher = fieldDecl(hasParent(cxxRecordDecl(anyOf( + isLambda(), implicit_class_specialization_matcher, + hasAncestor(decl(anyOf(implicit_class_specialization_matcher, + implicit_function_specialization_matcher))))))); + + return implicit_field_decl_matcher; +} + +clang::ast_matchers::internal::Matcher StackAllocatedQualType( + const raw_ptr_plugin::StackAllocatedPredicate* checker) { + return qualType(recordType(hasDeclaration( + cxxRecordDecl(isStackAllocated(*checker))))) + .bind("pointeeQualType"); +} + +// These represent the common conditions to skip the rewrite for reference and +// pointer decls. This includes decls that are: +// - listed in the --exclude-fields cmdline param or located in paths +// matched by --exclude-paths cmdline param +// - "implicit" (i.e. field decls that are not explicitly present in +// the source code) +// - located in Extern C context, in generated code or annotated with +// RAW_PTR_EXCLUSION +// - located under third_party/ except under third_party/blink as Blink +// is part of chromium git repo. +// +// Additionally, if |options.should_exclude_stack_allocated_records|, +// - Pointer pointing to a STACK_ALLOCATED() object. +// - Pointer that are a member of STACK_ALLOCATED() object. +// struct Foo { +// STACK_ALLOCATED(); +// int* ptr2; // isDeclaredInStackAllocated(...) +// } +// struct Bar { +// Foo* ptr2; // hasDescendant(StackAllocatedQualType(...)) +// } +clang::ast_matchers::internal::Matcher PtrAndRefExclusions( + const RawPtrAndRefExclusionsOptions& options) { + if (!options.should_exclude_stack_allocated_records) { + return anyOf(isSpellingInSystemHeader(), isInExternCContext(), + isRawPtrExclusionAnnotated(), isInThirdPartyLocation(), + isInGeneratedLocation(), isNotSpelledInSource(), + isInLocationListedInFilterFile(options.paths_to_exclude), + isFieldDeclListedInFilterFile(options.fields_to_exclude), + ImplicitFieldDeclaration(), isObjCSynthesize()); + } else { + return anyOf( + isSpellingInSystemHeader(), isInExternCContext(), + isRawPtrExclusionAnnotated(), isInThirdPartyLocation(), + isInGeneratedLocation(), isNotSpelledInSource(), + isInLocationListedInFilterFile(options.paths_to_exclude), + isFieldDeclListedInFilterFile(options.fields_to_exclude), + ImplicitFieldDeclaration(), isObjCSynthesize(), + hasDescendant( + StackAllocatedQualType(options.stack_allocated_predicate)), + isDeclaredInStackAllocated(*options.stack_allocated_predicate)); + } +} + +// These represent the common conditions to skip the check on existing +// |raw_ptr| and |raw_ref|. This includes decls that are: +// - located in system headers. +// - located under third_party/ except under third_party/blink as Blink +// is part of chromium git repo. +clang::ast_matchers::internal::Matcher +PtrAndRefTypeLocExclusions() { + return anyOf(isSpellingInSystemHeader(), isInThirdPartyLocation()); +} + +// Unsupported pointer types ========= +// Example: +// struct MyStruct { +// int (*func_ptr)(); +// int (MyStruct::* member_func_ptr)(char); +// int (*ptr_to_array_of_ints)[123]; +// }; +// The above pointer types are not supported for the rewrite. +static const auto unsupported_pointee_types = + pointee(hasUnqualifiedDesugaredType( + anyOf(functionType(), memberPointerType(), arrayType()))); + +clang::ast_matchers::internal::Matcher supported_pointer_type() { + return pointerType(unless(unsupported_pointee_types)); +} + +clang::ast_matchers::internal::Matcher const_char_pointer_type( + bool should_rewrite_non_string_literals) { + if (should_rewrite_non_string_literals) { + return pointerType(pointee(qualType(hasCanonicalType( + anyOf(asString("const char"), asString("const wchar_t"), + asString("const char8_t"), asString("const char16_t"), + asString("const char32_t")))))); + } + return pointerType(pointee(qualType( + allOf(isConstQualified(), hasUnqualifiedDesugaredType(anyCharType()))))); +} + +clang::ast_matchers::internal::Matcher AffectedRawPtrFieldDecl( + const RawPtrAndRefExclusionsOptions& options) { + // TODO(crbug.com/40245402): Skipping const char pointers as it likely points + // to string literals where raw_ptr isn't necessary. Remove when we have + // implement const char support. + auto const_char_pointer_matcher = fieldDecl(hasType( + const_char_pointer_type(options.should_rewrite_non_string_literals))); + + auto field_decl_matcher = + fieldDecl(allOf(hasType(supported_pointer_type()), + unless(anyOf(const_char_pointer_matcher, + PtrAndRefExclusions(options))))) + .bind("affectedFieldDecl"); + return field_decl_matcher; +} + +clang::ast_matchers::internal::Matcher AffectedRawRefFieldDecl( + const RawPtrAndRefExclusionsOptions& options) { + // Supported reference types ========= + // Given + // struct MyStruct { + // int& int_ref; + // int i; + // int (&func_ref)(); + // int (&ref_to_array_of_ints)[123]; + // }; + // matches |int&|, but not the other types. + auto supported_ref_types_matcher = + referenceType(unless(unsupported_pointee_types)); + + // Field declarations ========= + // Given + // struct S { + // int& y; + // }; + // matches |int& y|. Doesn't match: + // - non-reference types + // - fields matching criteria elaborated in PtrAndRefExclusions + auto field_decl_matcher = + fieldDecl(allOf(has(referenceTypeLoc().bind("affectedFieldDeclType")), + hasType(supported_ref_types_matcher), + unless(PtrAndRefExclusions(options)))) + .bind("affectedFieldDecl"); + + return field_decl_matcher; +} + +clang::ast_matchers::internal::Matcher +RawPtrToStackAllocatedTypeLoc( + const raw_ptr_plugin::StackAllocatedPredicate* predicate) { + // Given + // class StackAllocatedType { STACK_ALLOCATED(); }; + // class StackAllocatedSubType : public StackAllocatedType {}; + // class NonStackAllocatedType {}; + // + // struct MyStruct { + // raw_ptr a; + // raw_ptr b; + // raw_ptr c; + // raw_ptr> d; + // raw_ptr> e; + // raw_ptr> f; + // some_container> g; + // some_container> h; + // some_container> i; + // }; + // matches fields a,b,d,e,g,h, and not c,f,i. + // Similarly, given + // void my_func() { + // raw_ptr a; + // raw_ptr b; + // raw_ptr c; + // raw_ptr> d; + // raw_ptr> e; + // raw_ptr> f; + // some_container> g; + // some_container> h; + // some_container> i; + // } + // matches variables a,b,d,e,g,h, and not c,f,i. + + // Matches records |raw_ptr| or |raw_ref|. + auto pointer_record = + cxxRecordDecl(hasAnyName("base::raw_ptr", "base::raw_ref")) + .bind("pointerRecordDecl"); + + // Matches qual types having a record with |isStackAllocated| = true. + auto pointee_type = + qualType(StackAllocatedQualType(predicate)).bind("pointeeQualType"); + + // Matches type locs like |raw_ptr| or + // |raw_ref|. + auto stack_allocated_rawptr_type_loc = + templateSpecializationTypeLoc( + allOf(unless(PtrAndRefTypeLocExclusions()), + loc(templateSpecializationType(hasDeclaration( + allOf(pointer_record, + classTemplateSpecializationDecl(hasTemplateArgument( + 0, refersToType(pointee_type))))))))) + .bind("stackAllocatedRawPtrTypeLoc"); + return stack_allocated_rawptr_type_loc; +} + +clang::ast_matchers::internal::Matcher BadRawPtrCastExpr( + const CastingUnsafePredicate& casting_unsafe_predicate, + const FilterFile& exclude_files, + const FilterFile& exclude_functions) { + // Matches anything contains |raw_ptr| / |raw_ref|. + auto src_type = + type(isCastingUnsafe(casting_unsafe_predicate)).bind("srcType"); + auto dst_type = + type(isCastingUnsafe(casting_unsafe_predicate)).bind("dstType"); + // Matches |static_cast| on pointers, all |bit_cast| + // and all |reinterpret_cast|. + auto cast_kind = castExpr(anyOf(hasCastKind(clang::CK_BitCast), + hasCastKind(clang::CK_LValueBitCast), + hasCastKind(clang::CK_LValueToRValueBitCast), + hasCastKind(clang::CK_PointerToIntegral), + hasCastKind(clang::CK_IntegralToPointer))); + + // Matches implicit casts happening in invocation inside template context. + // void f(int v); + // void f(void* p); + // template + // void call_f(T t) { f(t); } + // ^ implicit cast here if |T| = |int*| + // We exclude this cast from check because we cannot apply + // |base::unsafe_raw_ptr_*_cast(t)| here. + auto in_template_invocation_ctx = implicitCastExpr( + allOf(isInTemplateInstantiation(), hasParent(invocation()))); + + // Matches implicit casts happening in comparison. + // int* x; + // void* y; + // if (x < y) f(); + // ^~~~~ |x| is implicit casted into |void*| here + // This cast is guaranteed to be safe because it cannot break ref count. + auto in_comparison_ctx = + implicitCastExpr(hasParent(binaryOperator(isComparisonOperator()))); + + // Matches implicit casts happening in invocation to allow-listed + // declarations. + auto in_allowlisted_invocation_ctx = + implicitCastExpr(hasParent(invocation(hasDeclaration( + namedDecl(isFieldDeclListedInFilterFile(&exclude_functions)))))); + + // Matches casts to const pointer types pointing to built-in types. + // e.g. matches |const char*| and |const void*| but neither |const int**| nor + // |int* const*|. + // They are safe as long as const qualifier is kept because const means we + // shouldn't be writing to the memory and won't mutate the value in a way that + // causes BRP's refcount inconsistency. + auto const_builtin_pointer_type = + type(hasUnqualifiedDesugaredType(pointerType( + pointee(qualType(allOf(isConstQualified(), builtinType())))))); + auto cast_expr_to_const_pointer = anyOf( + implicitCastExpr(hasImplicitDestinationType(const_builtin_pointer_type)), + explicitCastExpr(hasDestinationType(const_builtin_pointer_type))); + + // Unsafe castings are allowed if: + // - In locations developers have no control + // - In system headers + // - In third party libraries + // - In non-source locations (e.g. ) + // - In separate repository locations (e.g. //internal) + // - In locations that are likely to be safe + // - In pointer comparison context + // - In allowlisted function/constructor invocations + // - To const-qualified void/char pointers + // - In cases that the cast is indispensable and developers can guarantee it + // will not break BRP's refcount + // - In |base::unsafe_raw_ptr_static_cast(...)| + // - In |base::unsafe_raw_ptr_reinterpret_cast(...)| + // - In |base::unsafe_raw_ptr_bit_cast(...)| + // - In cases that the cast is indispensable but developers cannot use the + // cast exclusion listed above + // - Implicit casts inside template context as there can be multiple + // destination types depending on how template is instantiated + auto exclusions = + anyOf(isSpellingInSystemHeader(), isInThirdPartyLocation(), + isNotSpelledInSource(), + isInLocationListedInFilterFile(&exclude_files), in_comparison_ctx, + in_allowlisted_invocation_ctx, cast_expr_to_const_pointer, + isInRawPtrCastHeader(), in_template_invocation_ctx); + + // To correctly display the error location, bind enclosing castExpr if + // available. + auto enclosingCastExpr = hasEnclosingExplicitCastExpr( + explicitCastExpr().bind("enclosingCastExpr")); + + // Implicit/explicit casting from/to |raw_ptr| matches. + // Both casting direction is unsafe. + // https://godbolt.org/z/zqKMzcKfo + // |__bit/bit_cast.h| header is configured to bypass exclusions to perform + // checking on |std::bit_cast|. + auto cast_matcher = + castExpr( + allOf(anyOf(hasSourceExpression(hasType(src_type)), + implicitCastExpr(hasImplicitDestinationType(dst_type)), + explicitCastExpr(hasDestinationType(dst_type))), + cast_kind, optionally(enclosingCastExpr), + anyOf(isInStdBitCastHeader(), unless(exclusions)))) + .bind("castExpr"); + return cast_matcher; +} + +// If |field_decl| declares a field in an implicit template specialization, then +// finds and returns the corresponding FieldDecl from the template definition. +// Otherwise, just returns the original |field_decl| argument. +const clang::FieldDecl* GetExplicitDecl(const clang::FieldDecl* field_decl) { + if (field_decl->isAnonymousStructOrUnion()) { + return field_decl; // Safe fallback - |field_decl| is not a pointer field. + } + + const clang::CXXRecordDecl* record_decl = + clang::dyn_cast(field_decl->getParent()); + if (!record_decl) { + return field_decl; // Non-C++ records are never template instantiations. + } + + const clang::CXXRecordDecl* pattern_decl = + record_decl->getTemplateInstantiationPattern(); + if (!pattern_decl) { + return field_decl; // |pattern_decl| is not a template instantiation. + } + + if (record_decl->getTemplateSpecializationKind() != + clang::TemplateSpecializationKind::TSK_ImplicitInstantiation) { + return field_decl; // |field_decl| was in an *explicit* specialization. + } + + // Find the field decl with the same name in |pattern_decl|. + clang::DeclContextLookupResult lookup_result = + pattern_decl->lookup(field_decl->getDeclName()); + assert(!lookup_result.empty()); + const clang::NamedDecl* found_decl = lookup_result.front(); + assert(found_decl); + field_decl = clang::dyn_cast(found_decl); + assert(field_decl); + return field_decl; +} + +// If |original_param| declares a parameter in an implicit template +// specialization of a function or method, then finds and returns the +// corresponding ParmVarDecl from the template definition. Otherwise, just +// returns the |original_param| argument. +// +// Note: nullptr may be returned in rare, unimplemented cases. +const clang::ParmVarDecl* GetExplicitDecl( + const clang::ParmVarDecl* original_param) { + const clang::FunctionDecl* original_func = + clang::dyn_cast(original_param->getDeclContext()); + if (!original_func) { + // |!original_func| may happen when the ParmVarDecl is part of a + // FunctionType, but not part of a FunctionDecl: + // base::RepeatingCallback + // + // In theory, |parm_var_decl_here| can also represent an implicit template + // specialization in this scenario. OTOH, it should be rare + shouldn't + // matter for this rewriter, so for now let's just return the + // |original_param|. + // + // TODO: Implement support for this scenario. + return nullptr; + } + + const clang::FunctionDecl* pattern_func = + original_func->getTemplateInstantiationPattern(); + if (!pattern_func) { + // |original_func| is not a template instantiation - return the + // |original_param|. + return original_param; + } + + // See if |pattern_func| has a parameter that is a template parameter pack. + bool has_param_pack = false; + unsigned int index_of_param_pack = std::numeric_limits::max(); + for (unsigned int i = 0; i < pattern_func->getNumParams(); i++) { + const clang::ParmVarDecl* pattern_param = pattern_func->getParamDecl(i); + if (!pattern_param->isParameterPack()) { + continue; + } + + if (has_param_pack) { + // TODO: Implement support for multiple parameter packs. + return nullptr; + } + + has_param_pack = true; + index_of_param_pack = i; + } + + // Find and return the corresponding ParmVarDecl from |pattern_func|. + unsigned int original_index = original_param->getFunctionScopeIndex(); + unsigned int pattern_index = std::numeric_limits::max(); + if (!has_param_pack) { + pattern_index = original_index; + } else { + // |original_func| has parameters that look like this: + // l1, l2, l3, p1, p2, p3, t1, t2, t3 + // where + // lN is a leading, non-pack parameter + // pN is an expansion of a template parameter pack + // tN is a trailing, non-pack parameter + // Using the knowledge above, let's adjust |pattern_index| as needed. + unsigned int leading_param_num = index_of_param_pack; // How many |lN|. + unsigned int pack_expansion_num = // How many |pN| above. + original_func->getNumParams() - pattern_func->getNumParams() + 1; + if (original_index < leading_param_num) { + // |original_param| is a leading, non-pack parameter. + pattern_index = original_index; + } else if (leading_param_num <= original_index && + original_index < (leading_param_num + pack_expansion_num)) { + // |original_param| is an expansion of a template pack parameter. + pattern_index = index_of_param_pack; + } else if ((leading_param_num + pack_expansion_num) <= original_index) { + // |original_param| is a trailing, non-pack parameter. + pattern_index = original_index - pack_expansion_num + 1; + } + } + assert(pattern_index < pattern_func->getNumParams()); + return pattern_func->getParamDecl(pattern_index); +} + +} // namespace raw_ptr_plugin diff --git a/clang/raw_ptr_plugin/RawPtrHelpers.h b/clang/raw_ptr_plugin/RawPtrHelpers.h new file mode 100644 index 0000000000000000000000000000000000000000..6ef2bb08c623801c1b46c68d54aa5d49b285aea9 --- /dev/null +++ b/clang/raw_ptr_plugin/RawPtrHelpers.h @@ -0,0 +1,526 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRHELPERS_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRHELPERS_H_ + +#include + +#include "RawPtrCastingUnsafeChecker.h" +#include "StackAllocatedChecker.h" +#include "Util.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersMacros.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/CommandLine.h" + +namespace raw_ptr_plugin { + +// Represents a filter file specified via cmdline. +// +// Filter file format: +// - '#' character starts a comment (which gets ignored). +// - Blank or whitespace-only or comment-only lines are ignored. +// - Other lines are expected to contain a fully-qualified name of a field +// like: +// autofill::AddressField::address1_ # some comment +// - Templates are represented without template arguments, like: +// WTF::HashTable::table_ # some comment +class FilterFile { + public: + explicit FilterFile(const std::string& filepath, + const std::string& arg_name) { + ParseInputFile(filepath, arg_name); + } + explicit FilterFile(const std::vector& lines); + + FilterFile(const FilterFile&) = delete; + FilterFile& operator=(const FilterFile&) = delete; + + // Returns true if any of the filter file lines is exactly equal to |line|. + bool ContainsLine(llvm::StringRef line) const; + + // Returns true if |string_to_match| matches based on the filter file lines. + // Filter file lines can contain both inclusions and exclusions in the filter. + // Only returns true if |string_to_match| both matches an inclusion filter and + // is *not* matched by an exclusion filter. + bool ContainsSubstringOf(llvm::StringRef string_to_match) const; + + private: + void ParseInputFile(const std::string& filepath, const std::string& arg_name); + + // Stores all file lines (after stripping comments and blank lines). + llvm::StringSet<> file_lines_; + + // |file_lines_| is partitioned based on whether the line starts with a ! + // (exclusion line) or not (inclusion line). Inclusion lines specify things to + // be matched by the filter. The exclusion lines specify what to force exclude + // from the filter. Lazily-constructed regex that matches strings that contain + // any of the inclusion lines in |file_lines_|. + mutable std::optional inclusion_substring_regex_; + + // Lazily-constructed regex that matches strings that contain any of the + // exclusion lines in |file_lines_|. + mutable std::optional exclusion_substring_regex_; +}; + +// Represents an exclusion rules for raw pointers/references errors. +// See |PtrAndRefExclusions| for details. +struct RawPtrAndRefExclusionsOptions { + FilterFile* fields_to_exclude; + FilterFile* paths_to_exclude; + bool should_exclude_stack_allocated_records; + raw_ptr_plugin::StackAllocatedPredicate* stack_allocated_predicate; + bool should_rewrite_non_string_literals; +}; + +AST_MATCHER(clang::Type, anyCharType) { + return Node.isAnyCharacterType(); +} + +AST_POLYMORPHIC_MATCHER(isNotSpelledInSource, + AST_POLYMORPHIC_SUPPORTED_TYPES(clang::Decl, + clang::Stmt, + clang::TypeLoc)) { + const clang::SourceManager& source_manager = + Finder->getASTContext().getSourceManager(); + const auto loc = + source_manager.getSpellingLoc(getRepresentativeLocation(Node)); + // Returns true if `loc` is inside either one of followings: + // - "" + // - "" + // - "" + return source_manager.isWrittenInBuiltinFile(loc) || + source_manager.isWrittenInCommandLineFile(loc) || + source_manager.isWrittenInScratchSpace(loc); +} + +AST_POLYMORPHIC_MATCHER(isInThirdPartyLocation, + AST_POLYMORPHIC_SUPPORTED_TYPES(clang::Decl, + clang::Stmt, + clang::TypeLoc)) { + clang::SourceManager& sm = Finder->getASTContext().getSourceManager(); + std::string filename = GetFilename(sm, getRepresentativeLocation(Node), + FilenameLocationType::kSpellingLoc); + + // Blink is part of the Chromium git repo, even though it contains + // "third_party" in its path. + if (filename.find("/third_party/blink/") != std::string::npos) { + return false; + } + // Dawn repo has started using raw_ptr. + if (filename.find("/third_party/dawn/") != std::string::npos) { + return false; + } + // Otherwise, just check if the paths contains the "third_party" substring. + // We don't want to rewrite content of such paths even if they are in the main + // Chromium git repository. + return filename.find("/third_party/") != std::string::npos; +} + +AST_MATCHER(clang::Stmt, isInStdBitCastHeader) { + clang::SourceManager& sm = Finder->getASTContext().getSourceManager(); + std::string filename = GetFilename(sm, Node.getSourceRange().getBegin(), + FilenameLocationType::kSpellingLoc); + return filename.find("__bit/bit_cast.h") != std::string::npos; +} + +AST_MATCHER(clang::Stmt, isInRawPtrCastHeader) { + clang::SourceManager& sm = Finder->getASTContext().getSourceManager(); + std::string filename = GetFilename(sm, Node.getSourceRange().getBegin(), + FilenameLocationType::kSpellingLoc); + return filename.find( + "base/allocator/partition_allocator/src/partition_alloc/pointers/" + "raw_ptr_cast.h") != std::string::npos; +} + +AST_POLYMORPHIC_MATCHER(isInGeneratedLocation, + AST_POLYMORPHIC_SUPPORTED_TYPES(clang::Decl, + clang::Stmt, + clang::TypeLoc)) { + clang::SourceManager& sm = Finder->getASTContext().getSourceManager(); + std::string filename = GetFilename(sm, getRepresentativeLocation(Node), + FilenameLocationType::kSpellingLoc); + + return filename.find("/gen/") != std::string::npos || + filename.rfind("gen/", 0) == 0; +} + +AST_MATCHER_P(clang::NamedDecl, + isFieldDeclListedInFilterFile, + const FilterFile*, + Filter) { + return Filter->ContainsLine(Node.getQualifiedNameAsString()); +} + +AST_POLYMORPHIC_MATCHER_P(isInLocationListedInFilterFile, + AST_POLYMORPHIC_SUPPORTED_TYPES(clang::Decl, + clang::Stmt, + clang::TypeLoc), + const FilterFile*, + Filter) { + clang::SourceLocation loc = getRepresentativeLocation(Node); + if (loc.isInvalid()) { + return false; + } + clang::SourceManager& sm = Finder->getASTContext().getSourceManager(); + std::string file_path = + GetFilename(sm, loc, FilenameLocationType::kSpellingLoc); + return Filter->ContainsSubstringOf(file_path); +} + +AST_MATCHER(clang::Decl, isInExternCContext) { + return Node.getLexicalDeclContext()->isExternCContext(); +} + +// Given: +// template class MyTemplate {}; // Node1 and Node4 +// template class MyTemplate {}; // Node2 +// template <> class MyTemplate {}; // Node3 +// void foo() { +// // This creates implicit template specialization (Node4) out of the +// // explicit template definition (Node1). +// MyTemplate v; +// } +// with the following AST nodes: +// ClassTemplateDecl MyTemplate - Node1 +// | |-CXXRecordDecl class MyTemplate definition +// | `-ClassTemplateSpecializationDecl class MyTemplate definition - Node4 +// ClassTemplatePartialSpecializationDecl class MyTemplate definition - Node2 +// ClassTemplateSpecializationDecl class MyTemplate definition - Node3 +// +// Matches AST node 4, but not AST node2 nor node3. +AST_MATCHER(clang::ClassTemplateSpecializationDecl, + isImplicitClassTemplateSpecialization) { + return !Node.isExplicitSpecialization(); +} + +static bool IsAnnotated(const clang::Decl* decl, + const std::string& expected_annotation) { + clang::AnnotateAttr* attr = decl->getAttr(); + return attr && (attr->getAnnotation() == expected_annotation); +} + +AST_MATCHER(clang::Decl, isRawPtrExclusionAnnotated) { + return IsAnnotated(&Node, "raw_ptr_exclusion"); +} + +AST_MATCHER(clang::CXXRecordDecl, isAnonymousStructOrUnion) { + return Node.getName().empty(); +} + +// Given: +// template void foo(T t, T2 t2) {}; // N1 and N4 +// template void foo(int t, T2 t) {}; // N2 +// template <> void foo(int t, char t2) {}; // N3 +// void foo() { +// // This creates implicit template specialization (N4) out of the +// // explicit template definition (N1). +// foo(true, 1.23); +// } +// with the following AST nodes: +// FunctionTemplateDecl foo +// |-FunctionDecl 0x191da68 foo 'void (T, T2)' // N1 +// `-FunctionDecl 0x194bf08 foo 'void (bool, double)' // N4 +// FunctionTemplateDecl foo +// `-FunctionDecl foo 'void (int, T2)' // N2 +// FunctionDecl foo 'void (int, char)' // N3 +// +// Matches AST node N4, but not AST nodes N1, N2 nor N3. +AST_MATCHER(clang::FunctionDecl, isImplicitFunctionTemplateSpecialization) { + switch (Node.getTemplateSpecializationKind()) { + case clang::TSK_ImplicitInstantiation: + return true; + case clang::TSK_Undeclared: + case clang::TSK_ExplicitSpecialization: + case clang::TSK_ExplicitInstantiationDeclaration: + case clang::TSK_ExplicitInstantiationDefinition: + return false; + } +} + +// Matches Objective-C @synthesize field declaration. +AST_MATCHER(clang::Decl, isObjCSynthesize) { + const auto* ivar_decl = clang::dyn_cast(&Node); + return ivar_decl && ivar_decl->getSynthesize(); +} + +// Matches field declarations that do not explicitly appear in the source +// code: +// 1. fields of classes generated by the compiler to back capturing lambdas, +// 2. fields within an implicit class or function template specialization +// (e.g. when a template is instantiated by a bit of code and there's no +// explicit specialization for it). +clang::ast_matchers::internal::Matcher ImplicitFieldDeclaration(); + +// Matches raw pointer field declarations that is a candidate for raw_ptr +// conversion. +clang::ast_matchers::internal::Matcher AffectedRawPtrFieldDecl( + const RawPtrAndRefExclusionsOptions& options); + +// Matches raw reference field declarations that are candidates for raw_ref +// conversion. +clang::ast_matchers::internal::Matcher AffectedRawRefFieldDecl( + const RawPtrAndRefExclusionsOptions& options); + +// Matches (raw_ptr|raw_ref) (variable|field) declarations pointing to +// |STACK_ALLOCATED| object. +clang::ast_matchers::internal::Matcher +RawPtrToStackAllocatedTypeLoc( + const raw_ptr_plugin::StackAllocatedPredicate* predicate); + +clang::ast_matchers::internal::Matcher BadRawPtrCastExpr( + const CastingUnsafePredicate& casting_unsafe_predicate, + const FilterFile& exclude_files, + const FilterFile& exclude_functions); + +// If `field_decl` declares a field in an implicit template specialization, then +// finds and returns the corresponding FieldDecl from the template definition. +// Otherwise, just returns the original `field_decl` argument. +const clang::FieldDecl* GetExplicitDecl(const clang::FieldDecl* field_decl); + +// Given: +// template +// class MyTemplate { +// T field; // This is an explicit field declaration. +// }; +// void foo() { +// // This creates implicit template specialization for MyTemplate, +// // including an implicit `field` declaration. +// MyTemplate v; +// v.field = 123; +// } +// and +// innerMatcher that will match the explicit `T field` declaration (but not +// necessarily the implicit template declarations), +// hasExplicitFieldDecl(innerMatcher) will match both explicit and implicit +// field declarations. +// +// For example, `member_expr_matcher` below will match `v.field` in the example +// above, even though the type of `v.field` is `int`, rather than `T` (matched +// by substTemplateTypeParmType()): +// auto explicit_field_decl_matcher = +// fieldDecl(hasType(substTemplateTypeParmType())); +// auto member_expr_matcher = memberExpr(member(fieldDecl( +// hasExplicitFieldDecl(explicit_field_decl_matcher)))) +AST_MATCHER_P(clang::FieldDecl, + hasExplicitFieldDecl, + clang::ast_matchers::internal::Matcher, + InnerMatcher) { + const clang::FieldDecl* explicit_field_decl = GetExplicitDecl(&Node); + return InnerMatcher.matches(*explicit_field_decl, Finder, Builder); +} + +// If `original_param` declares a parameter in an implicit template +// specialization of a function or method, then finds and returns the +// corresponding ParmVarDecl from the template definition. Otherwise, just +// returns the `original_param` argument. +// +// Note: nullptr may be returned in rare, unimplemented cases. +const clang::ParmVarDecl* GetExplicitDecl( + const clang::ParmVarDecl* original_param); + +AST_MATCHER_P(clang::ParmVarDecl, + hasExplicitParmVarDecl, + clang::ast_matchers::internal::Matcher, + InnerMatcher) { + const clang::ParmVarDecl* explicit_param = GetExplicitDecl(&Node); + if (!explicit_param) { + // Rare, unimplemented case - fall back to returning "no match". + return false; + } + + return InnerMatcher.matches(*explicit_param, Finder, Builder); +} + +// forEachInitExprWithFieldDecl matches InitListExpr if it +// 1) evaluates to a RecordType +// 2) has a InitListExpr + FieldDecl pair that matches the submatcher args. +// +// forEachInitExprWithFieldDecl is based on and very similar to the builtin +// forEachArgumentWithParam matcher. +AST_MATCHER_P2(clang::InitListExpr, + forEachInitExprWithFieldDecl, + clang::ast_matchers::internal::Matcher, + init_expr_matcher, + clang::ast_matchers::internal::Matcher, + field_decl_matcher) { + const clang::InitListExpr& init_list_expr = Node; + const clang::Type* type = init_list_expr.getType() + .getDesugaredType(Finder->getASTContext()) + .getTypePtrOrNull(); + if (!type) { + return false; + } + const clang::CXXRecordDecl* record_decl = type->getAsCXXRecordDecl(); + if (!record_decl) { + return false; + } + + bool is_matching = false; + clang::ast_matchers::internal::BoundNodesTreeBuilder result; + const llvm::SmallVector field_decls( + record_decl->fields()); + for (unsigned i = 0; i < init_list_expr.getNumInits(); i++) { + const clang::Expr* expr = init_list_expr.getInit(i); + + const clang::FieldDecl* field_decl = nullptr; + if (const clang::ImplicitValueInitExpr* implicit_value_init_expr = + clang::dyn_cast(expr)) { + continue; // Do not match implicit value initializers. + } else if (const clang::DesignatedInitExpr* designated_init_expr = + clang::dyn_cast(expr)) { + // Nested designators are unsupported by C++. + if (designated_init_expr->size() != 1) { + break; + } + expr = designated_init_expr->getInit(); + field_decl = designated_init_expr->getDesignator(0)->getFieldDecl(); + } else { + if (i >= field_decls.size()) { + break; + } + field_decl = field_decls[i]; + } + + // `field_decl` might be equal to `nullptr`. In the case, we should not + // run `field_decl_matcher`. + if (field_decl) { + clang::ast_matchers::internal::BoundNodesTreeBuilder field_matches( + *Builder); + if (field_decl_matcher.matches(*field_decl, Finder, &field_matches)) { + clang::ast_matchers::internal::BoundNodesTreeBuilder expr_matches( + field_matches); + if (init_expr_matcher.matches(*expr, Finder, &expr_matches)) { + result.addMatch(expr_matches); + is_matching = true; + } + } + } + } + + *Builder = std::move(result); + return is_matching; +} + +AST_POLYMORPHIC_MATCHER(isInMacroLocation, + AST_POLYMORPHIC_SUPPORTED_TYPES(clang::Decl, + clang::Stmt, + clang::TypeLoc)) { + return Node.getBeginLoc().isMacroID(); +} + +// Matches AST nodes that were spelled within system-header-files. +// Unlike clang's `isExpansionInSystemHeader`, this is based on: +// - spelling location +// - `getRepresentativeLocation(Node)`, not `Node.getBeginLoc()` +AST_POLYMORPHIC_MATCHER(isSpellingInSystemHeader, + AST_POLYMORPHIC_SUPPORTED_TYPES(clang::Decl, + clang::Stmt, + clang::TypeLoc)) { + auto& source_manager = Finder->getASTContext().getSourceManager(); + auto spelling_loc = + source_manager.getSpellingLoc(getRepresentativeLocation(Node)); + if (spelling_loc.isInvalid()) { + return false; + } + return source_manager.isInSystemHeader(spelling_loc); +} + +AST_MATCHER_P(clang::CXXRecordDecl, + isStackAllocated, + raw_ptr_plugin::StackAllocatedPredicate, + checker) { + return checker.IsStackAllocated(&Node); +} + +AST_MATCHER_P(clang::Decl, + isDeclaredInStackAllocated, + raw_ptr_plugin::StackAllocatedPredicate, + checker) { + const auto* ctx = llvm::dyn_cast(Node.getDeclContext()); + if (ctx == nullptr) { + return false; + } + return checker.IsStackAllocated(ctx); +} + +AST_MATCHER_P(clang::Type, isCastingUnsafe, CastingUnsafePredicate, checker) { + return checker.Matches(&Node); +} + +// Matches outermost explicit cast, traversing ancestors. +// +// (void*) static_cast(&v); +// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CStyleCastExpr 'void *' +// ^~~~~~~~~~~~~~~~~~~~~~ CXXStaticCastExpr 'void *' +// ^~ ImplicitCastExpr 'void *' +// [part_of_explicit_cast = true] +// +// This won't match neither |CStyleCastExpr| nor |CXXStaticCastExpr| +// as they are explicit casts. +// Given |ImplicitCastExpr|, this runs |InnerMatcher| to |CXXStaticCastExpr|. +AST_MATCHER_P(clang::CastExpr, + hasEnclosingExplicitCastExpr, + clang::ast_matchers::internal::Matcher, + InnerMatcher) { + auto* cast_expr = &Node; + auto& context = Finder->getASTContext(); + while (true) { + const auto* implicit_cast = + llvm::dyn_cast(cast_expr); + if (!implicit_cast || !implicit_cast->isPartOfExplicitCast()) { + break; + } + const auto parents = context.getParents(*implicit_cast); + // AST is a tree and |ASTContext::getParents| returns exactly one node, + // at least for |clang::CastExpr|. + assert(parents.size() == 1); + cast_expr = parents[0].get(); + assert(cast_expr); + } + const auto* explicit_cast_expr = + llvm::dyn_cast(cast_expr); + if (cast_expr == &Node || !explicit_cast_expr) { + // No enclosing explicit cast. + return false; + } + return InnerMatcher.matches(*explicit_cast_expr, Finder, Builder); +} + +// Matches the pointer types supported by the rewriters. +// These exclude: function, member and array type pointers. +clang::ast_matchers::internal::Matcher supported_pointer_type(); + +// Matches const char pointers. +clang::ast_matchers::internal::Matcher const_char_pointer_type( + bool should_rewrite_non_string_literals); + +// These represent the common conditions to skip the rewrite for reference and +// pointer decls. This includes decls that are: +// - listed in the --exclude-fields cmdline param or located in paths +// matched by --exclude-paths cmdline param +// - "implicit" (i.e. field decls that are not explicitly present in +// the source code) +// - located in Extern C context, in generated code or annotated with +// RAW_PTR_EXCLUSION +// - located under third_party/ except under third_party/blink as Blink +// is part of chromium git repo. +// +// Additionally, if |options.should_exclude_stack_allocated_records|, +// - Pointer pointing to a STACK_ALLOCATED() object. +// - Pointer that are a member of STACK_ALLOCATED() object. +// struct Foo { +// STACK_ALLOCATED(); +// int* ptr2; // isDeclaredInStackAllocated(...) +// } +// struct Bar { +// Foo* ptr2; // hasDescendant(StackAllocatedQualType(...)) +// } +clang::ast_matchers::internal::Matcher PtrAndRefExclusions( + const RawPtrAndRefExclusionsOptions& options); + +} // namespace raw_ptr_plugin + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRHELPERS_H_ diff --git a/clang/raw_ptr_plugin/RawPtrManualPathsToIgnore.h b/clang/raw_ptr_plugin/RawPtrManualPathsToIgnore.h new file mode 100644 index 0000000000000000000000000000000000000000..0186e5461371ceda23d3aab326ac99121e1d3ff1 --- /dev/null +++ b/clang/raw_ptr_plugin/RawPtrManualPathsToIgnore.h @@ -0,0 +1,106 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRMANUALPATHSTOIGNORE_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRMANUALPATHSTOIGNORE_H_ + +// Array listing regular expressions of paths that should be ignored when +// running the rewrite_raw_ptr_fields tool on Chromium sources. +// +// If a source file path contains any of the lines in the filter file below, +// then such source file will not be rewritten. +// +// Lines prefixed with "!" can be used to force include files that matched a +// file path to be ignored. +// +// Note that the rewriter has a hardcoded logic for a handful of path-based +// exclusions that cannot be expressed as substring matches: +// - Excluding paths containing "third_party/", but still covering +// "third_party/blink/" +// (see the isInThirdPartyLocation AST matcher in RewriteRawPtrFields.cpp). +// - Excluding paths _starting_ with "gen/" or containing "/gen/" +// (i.e. hopefully just the paths under out/.../gen/... directory) +// via the isInGeneratedLocation AST matcher in RewriteRawPtrFields.cpp. +constexpr const char* const kRawPtrManualPathsToIgnore[] = { + // Exclude to prevent PartitionAlloc<->raw_ptr cyclical dependency. + "base/allocator/", + + // win:pe_image target that uses this file does not depend on base/. + "base/no_destructor.h", + + // Can't depend on //base, pointers/references under this directory can't be + // rewritten. + "testing/rust_gtest_interop/", + + // Exclude - deprecated and contains legacy C++ and pre-C++11 code. + "ppapi/", + + // Exclude tools that do not ship in the Chrome binary. Can't depend on + // //base. + "base/android/linker/", + "/tools/", // catches subdirs e.g. /net/tools, but not devtools/ etc. + "chrome/chrome_elf/", + "chrome/installer/mini_installer/", + "testing/platform_test.h", + + // DEPS prohibits includes from base/ + "chrome/install_static", + "sandbox/mac/", + + // Exclude pocdll.dll as it doesn't depend on //base and only used for + // testing. + "sandbox/win/sandbox_poc/pocdll", + + // Exclude internal definitions of undocumented Windows structures. + "sandbox/win/src/nt_internals.h", + + // Exclude directories that don't depend on //base, because nothing there + // uses + // anything from /base. + "sandbox/linux/system_headers/", + "components/history_clusters/core/", + "ui/qt/", + + // The folder holds headers that are duplicated in the Android source and + // need to + // provide a stable C ABI. Can't depend on //base. + "android_webview/public/", + + // Exclude dir that should hold C headers. + "mojo/public/c/", + + // Renderer-only code is generally allowed to use MiraclePtr. These + // directories, however, are specifically disallowed, for perf reasons. + // + // Note that some renderer-only directories are already excluded + // elsewhere - for example "v8/" is excluded, because it's in another + // repository. + // + // Also, note that isInThirdPartyLocation AST matcher in + // RewriteRawPtrFields.cpp explicitly allows third_party/blink + "third_party/blink/renderer/core/", + "third_party/blink/renderer/platform/heap/", + "third_party/blink/renderer/platform/wtf/", + "third_party/blink/renderer/platform/fonts/", + + // The below paths are an explicitly listed subset of Renderer-only code, + // because the plan is to Oilpanize it. + // TODO(crbug.com/330759291): Remove once Oilpanization is completed or + // abandoned. + "third_party/blink/renderer/core/paint/", + "third_party/blink/renderer/platform/graphics/compositing/", + "third_party/blink/renderer/platform/graphics/paint/", + + // Contains sysroot dirs like debian_bullseye_amd64-sysroot/ that are not + // part of the repository. + "build/linux/", + + // glslang_tab.cpp.h uses #line directive and modifies the file path to + // "MachineIndependent/glslang.y" so the isInThirdPartyLocation() filter + // cannot + // catch it even though glslang_tab.cpp.h is in third_party/ + "MachineIndependent/", +}; + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRMANUALPATHSTOIGNORE_H_ diff --git a/clang/raw_ptr_plugin/RawPtrPlugin.cpp b/clang/raw_ptr_plugin/RawPtrPlugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e85bc698f25c7a4fc0f1db16014797d3050c07c5 --- /dev/null +++ b/clang/raw_ptr_plugin/RawPtrPlugin.cpp @@ -0,0 +1,136 @@ +// 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 "FindBadRawPtrPatterns.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "llvm/Support/TimeProfiler.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. +// +// 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 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 ASTConsumer { + public: + PluginConsumer(CompilerInstance* instance, const Options& options) + : options_(options), instance_(*instance) {} + + void HandleTranslationUnit(clang::ASTContext& context) override { + llvm::TimeTraceScope TimeScope("HandleTranslationUnit for raw-ptr plugin"); + if (options_.check_bad_raw_ptr_cast || options_.check_raw_ptr_fields || + options_.check_raw_ref_fields || + (options_.check_raw_ptr_to_stack_allocated && + !options_.disable_check_raw_ptr_to_stack_allocated_error) || + options_.check_span_fields) { + FindBadRawPtrPatterns(options_, context, instance_); + } + } + + private: + // Options. + const Options options_; + + clang::CompilerInstance& instance_; +}; + +} // namespace + +RawPtrPlugin::RawPtrPlugin() {} + +std::unique_ptr RawPtrPlugin::CreateASTConsumer( + CompilerInstance& instance, + llvm::StringRef ref) { + return std::make_unique(&instance, options_); +} + +bool RawPtrPlugin::ParseArgs(const CompilerInstance& instance, + const std::vector& args) { + 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(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 { + llvm::errs() << "Unknown clang plugin argument: " << arg << "\n"; + return false; + } + } + + return true; +} + +} // namespace raw_ptr_plugin + +static FrontendPluginRegistry::Add X( + "raw-ptr-plugin", + "Check pointers for safety"); diff --git a/clang/raw_ptr_plugin/RawPtrPlugin.h b/clang/raw_ptr_plugin/RawPtrPlugin.h new file mode 100644 index 0000000000000000000000000000000000000000..bc4d5dfae6280d3928c4a6c0ced600e1bcd4fdf0 --- /dev/null +++ b/clang/raw_ptr_plugin/RawPtrPlugin.h @@ -0,0 +1,33 @@ +// 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. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRPLUGIN_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRPLUGIN_H_ + +#include "Options.h" +#include "clang/Frontend/FrontendAction.h" + +namespace raw_ptr_plugin { + +class RawPtrPlugin : public clang::PluginASTAction { + public: + RawPtrPlugin(); + + protected: + std::unique_ptr CreateASTConsumer( + clang::CompilerInstance& instance, + llvm::StringRef ref) override; + PluginASTAction::ActionType getActionType() override { + return CmdlineBeforeMainAction; + } + bool ParseArgs(const clang::CompilerInstance& instance, + const std::vector& args) override; + + private: + Options options_; +}; + +} // namespace raw_ptr_plugin + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_RAWPTRPLUGIN_H_ diff --git a/clang/raw_ptr_plugin/SeparateRepositoryPaths.h b/clang/raw_ptr_plugin/SeparateRepositoryPaths.h new file mode 100644 index 0000000000000000000000000000000000000000..b5a810c72c0415680733bdda5515a7b571c1fffb --- /dev/null +++ b/clang/raw_ptr_plugin/SeparateRepositoryPaths.h @@ -0,0 +1,81 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_SEPARATEREPOSITORYPATHS_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_SEPARATEREPOSITORYPATHS_H_ + +// Paths in separate repositories - i.e. in directories that +// 1. Contain a ".git" subdirectory +// 2. And hasn't been excluded via "third_party/" substring in their path +// (see the isInThirdPartyLocation AST matcher in +// RewriteRawPtrFields.cpp). +// +// The list below has been generated with: +// +// $ find . -type d -name .git | \ +// sed -e 's/\.git$//g' | \ +// sed -e 's/\.\///g' | \ +// grep -v third_party | \ +// grep -v '^$' | \ +// sort | uniq > ~/scratch/git-paths +constexpr const char* const kSeparateRepositoryPaths[] = { + "chrome/app/theme/default_100_percent/google_chrome/", + "chrome/app/theme/default_200_percent/google_chrome/", + "chrome/app/theme/google_chrome/", + "chrome/browser/enterprise/connectors/internal/", + "chrome/browser/google/linkdoctor_internal/", + "chrome/browser/internal/", + "chrome/browser/media/engagement_internal/", + "chrome/browser/resources/chromeos/quickoffice/", + "chrome/browser/resources/downloads/internal/", + "chrome/browser/resources/preinstalled_web_apps/internal/", + "chrome/browser/resources/settings/internal/", + "chrome/browser/spellchecker/internal/", + "chrome/installer/mac/internal/", + "chromeos/assistant/internal/", + "chrome/services/speech/internal/", + "chrome/test/data/firefox3_profile/searchplugins/", + "chrome/test/data/firefox3_searchplugins/", + "chrome/test/data/gpu/vt/", + "chrome/test/data/pdf_private/", + "chrome/test/data/perf/canvas_bench/", + "chrome/test/data/perf/frame_rate/content/", + "chrome/test/data/perf/frame_rate/private/", + "chrome/test/data/perf/private/", + "chrome/test/data/xr/webvr_info/", + "chrome/test/media_router/internal/", + "chrome/test/python_tests/", + "chrome/tools/memory/", + "components/autofill/core/browser/form_parsing/internal_resources/", + "components/crash/core/app/internal/", + "components/metrics/internal/", + "components/ntp_tiles/resources/internal/", + "components/optimization_guide/internal/", + "components/resources/default_100_percent/google_chrome/", + "components/resources/default_200_percent/google_chrome/", + "components/resources/default_300_percent/google_chrome/", + "components/site_isolation/internal/", + "components/vector_icons/google_chrome/", + "content/test/data/plugin/", + "docs/website/", + "google_apis/internal/", + "/internal/", // Manually added '/' at the beginning for strictness. + "media/cdm/api/", + "native_client/", + "remoting/host/installer/linux/internal/", + "remoting/internal/", + "remoting/test/internal/", + "remoting/tools/internal/", + "remoting/webapp/app_remoting/internal/", + "tools/page_cycler/acid3/", + "tools/perf/data/", + "ui/file_manager/internal/", + "ui/webui/internal/", + "v8/", + "webkit/data/bmp_decoder/", + "webkit/data/ico_decoder/", + "webkit/data/test_shell/plugins/", +}; + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_SEPARATEREPOSITORYPATHS_H_ diff --git a/clang/raw_ptr_plugin/StackAllocatedChecker.cpp b/clang/raw_ptr_plugin/StackAllocatedChecker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a71ce05819d234b1d35c9fd4947c3a1003555379 --- /dev/null +++ b/clang/raw_ptr_plugin/StackAllocatedChecker.cpp @@ -0,0 +1,147 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#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; +} + +} // namespace + +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; + + // Check member fields + for (clang::Decl* decl : record->decls()) { + clang::TypeAliasDecl* alias = clang::dyn_cast(decl); + if (!alias) { + continue; + } + if (alias->getName() == "IsStackAllocatedTypeMarker") { + stack_allocated = true; + break; + } + } + + // Check base classes + 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); + } + } + + // If we don't create a cache record now, it's possible to get into infinite + // mutual recursion between the base class check (above) and the template + // parameter check (below). + iter = cache_.insert({record, stack_allocated}).first; + + // Check template parameters. This is aggressive and can cause false positives + // -- a templated class doesn't necessarily store instances of its type + // parameters, in which case it need not be stack-allocated. In practice, + // though, this kind of false positive is rare; and conservatively marking + // this type as stack-allocated will catch cases where a type parameter + // doesn't have a full type definition in the translation unit. + if (auto* field_record_template = + clang::dyn_cast(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 this type is stack allocated, no need to check fields. + 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()) { + 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(); + } + } +} + +} // namespace raw_ptr_plugin diff --git a/clang/raw_ptr_plugin/StackAllocatedChecker.h b/clang/raw_ptr_plugin/StackAllocatedChecker.h new file mode 100644 index 0000000000000000000000000000000000000000..f93fdfd5f50f7d6ea93d3902a7f28bbfbc7880af --- /dev/null +++ b/clang/raw_ptr_plugin/StackAllocatedChecker.h @@ -0,0 +1,48 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_STACKALLOCATEDCHECKER_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_STACKALLOCATEDCHECKER_H_ + +#include + +namespace clang { +class CompilerInstance; +class CXXRecordDecl; +class FieldDecl; +} // namespace clang + +namespace raw_ptr_plugin { + +// This determines a record (class/struct) is annotated with +// |STACK_ALLOCATED()|. Even if it is explicitly annotated with +// |STACK_ALLOCATED()|, this will consider it as "stack allocated" when its +// ancestor has the annotation. Similarly, classes with a "stack allocated" +// template type parameter is considered "stack allocated". +class StackAllocatedPredicate { + public: + bool IsStackAllocated(const clang::CXXRecordDecl* record) const; + + private: + mutable std::map cache_; +}; + +// This verifies usage of classes annotated with STACK_ALLOCATED(). +// Specifically, it ensures that an instance of such a class cannot be used as a +// member variable in a non-STACK_ALLOCATED() class. +class StackAllocatedChecker { + public: + explicit StackAllocatedChecker(clang::CompilerInstance& compiler); + + void Check(clang::CXXRecordDecl* record); + + private: + clang::CompilerInstance& compiler_; + unsigned stack_allocated_field_error_signature_; + StackAllocatedPredicate predicate_; +}; + +} // namespace raw_ptr_plugin + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_STACKALLOCATEDCHECKER_H_ diff --git a/clang/raw_ptr_plugin/SuppressibleDiagnosticBuilder.h b/clang/raw_ptr_plugin/SuppressibleDiagnosticBuilder.h new file mode 100644 index 0000000000000000000000000000000000000000..68d2d769917e8c61ee1fa256daf4f85a560451ca --- /dev/null +++ b/clang/raw_ptr_plugin/SuppressibleDiagnosticBuilder.h @@ -0,0 +1,42 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_SUPPRESSIBLEDIAGNOSTICBUILDER_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_SUPPRESSIBLEDIAGNOSTICBUILDER_H_ + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceLocation.h" + +namespace raw_ptr_plugin { + +// A simple wrapper around DiagnosticBuilder that allows a diagnostic to be +// suppressed. The intended use case is for helper functions that return a +// DiagnosticBuilder, but only want to emit the diagnostic if some conditions +// are met. +class SuppressibleDiagnosticBuilder : public clang::DiagnosticBuilder { + public: + SuppressibleDiagnosticBuilder(clang::DiagnosticsEngine* diagnostics, + clang::SourceLocation loc, + unsigned diagnostic_id, + bool suppressed) + : DiagnosticBuilder(diagnostics->Report(loc, diagnostic_id)), + diagnostics_(diagnostics), + suppressed_(suppressed) {} + + ~SuppressibleDiagnosticBuilder() { + if (suppressed_) { + // Clear the underlying data, so the base class destructor + // doesn't try to emit the diagnostic. + Clear(); + } + } + + private: + clang::DiagnosticsEngine* const diagnostics_; + const bool suppressed_; +}; + +} // namespace raw_ptr_plugin + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_SUPPRESSIBLEDIAGNOSTICBUILDER_H_ diff --git a/clang/raw_ptr_plugin/TypePredicateUtil.h b/clang/raw_ptr_plugin/TypePredicateUtil.h new file mode 100644 index 0000000000000000000000000000000000000000..816ad77cdad613d4993033575ea24074681d6498 --- /dev/null +++ b/clang/raw_ptr_plugin/TypePredicateUtil.h @@ -0,0 +1,318 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TYPEPREDICATEUTIL_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TYPEPREDICATEUTIL_H_ + +#include +#include +#include +#include +#include +#include + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Type.h" +#include "llvm/ADT/ScopeExit.h" + +enum class InductionRule : unsigned { + kNone = 0, + kPointerPointee = (1 << 0), + kObjCPointerPointee = (1 << 1), + kReferencePointee = (1 << 2), + kArrayElement = (1 << 3), + kUnqualifiedDesugaredType = (1 << 4), + kBaseClass = (1 << 5), + kVirtualBaseClass = (1 << 6), + kField = (1 << 7), + kTemplateArgument = (1 << 8), +}; + +constexpr InductionRule operator|(InductionRule a, InductionRule b) { + return static_cast(static_cast(a) | + static_cast(b)); +} +constexpr InductionRule operator&(InductionRule a, InductionRule b) { + return static_cast(static_cast(a) & + static_cast(b)); +} + +// Represents a match result |verdict_|. +// - MatchResult::kNoMatch: no match found against |type|. +// - MatchResult::kMatch: a match found against |type|. +// - MatchResult::kUndetermined: This denotes the result +// is not yet determined, due to cross references. +// Holds some additional information to tell reasons. +class MatchResult { + public: + enum Verdict { + kMatch, + kNoMatch, + // This denotes the match status is not yet determined. + kUndetermined, + }; + + explicit MatchResult(const clang::Type* type) : type_(type) {} + explicit MatchResult(const clang::Type* type, Verdict verdict) + : type_(type), verdict_(verdict) {} + + const clang::Type* type() const { return type_; } + + Verdict verdict() const { return this->verdict_; } + + std::shared_ptr source() const { return this->source_; } + + std::optional source_loc() const { + return this->source_loc_; + } + + private: + template + friend class TypePredicate; + + // Merges a sub verdict into this type's verdict. + // + // | this \ sub | kNoMatch | kUndetermined | kMatch | + // +---------------+---------------+---------------+--------+ + // | kNoMatch | kNoMatch | kUndetermined | kMatch | + // | kUndetermined | kUndetermined | kUndetermined | kMatch | + // | kMatch | kMatch | kMatch | kMatch | + Verdict MergeSubResult( + std::shared_ptr sub, + std::optional loc = std::nullopt) { + if (sub->verdict_ == kMatch && this->verdict_ != kMatch) { + this->verdict_ = kMatch; + this->source_ = std::move(sub); + this->source_loc_ = loc; + } else if (sub->verdict_ == kUndetermined && this->verdict_ == kNoMatch) { + this->verdict_ = kUndetermined; + this->source_ = std::move(sub); + this->source_loc_ = loc; + } + return this->verdict_; + } + + // |type_| is considered to be |verdict_|. + // Optionally, the result contains a reason for the verdict, |source_|. + // There can be multiple reasons (e.g. |type_| has multiple matching + // members), but only one of them is stored. The relation between |type_| + // and |source_| is optionally shown at |source_loc_|. + const clang::Type* type_; + Verdict verdict_ = kNoMatch; + std::shared_ptr source_; + std::optional source_loc_; +}; + +// Determines there is a match against |type| or not. +// A type is considered match if |IsBaseMatch| returns true or +// reach such |type| by applying InductionRule recursively. +template +class TypePredicate { + public: + virtual ~TypePredicate() = default; + bool Matches(const clang::Type* type) const { + return GetMatchResult(type)->verdict_ == MatchResult::kMatch; + } + + std::shared_ptr GetMatchResult( + const clang::Type* type, + std::set* visited = nullptr) const { + // Retrieve a "base" type to reduce recursion depth. + const clang::Type* raw_type = GetBaseType(type); + if (!raw_type || !raw_type->isRecordType()) { + // |TypePredicate| does not support followings: + // - function type + // - enum type + // - builtin type + // - complex type + // - obj-C types + // - using type + // - typeof type + return std::make_shared(type); // No match. + } + + // Use a memoized result if exists. + auto iter = cache_.find(type); + if (iter != cache_.end()) { + return iter->second; + } + + // This performs DFS on a directed graph composed of |Type*|. + // Avoid searching for visited nodes by managing |visited|, as this can lead + // to infinite loops in the presence of self-references and + // cross-references. Since finding a match for |Type* x| is equivalent to + // being able to reach from node |Type* x| to node |Type* y| where + // |IsBaseCase(y)|, there is no need to look up visited nodes again. + bool root = visited == nullptr; + if (root) { + // Will be deleted as a part of |clean_up()|. + visited = new std::set(); + } else if (visited->count(type)) { + // This type is already visited but not memoized, + // therefore this node is reached by following cross-references from + // ancestors. The verdict of this node cannot be determined without + // waiting for computation in its ancestors. + return std::make_shared(raw_type, + MatchResult::kUndetermined); + } + visited->insert(type); + + auto match = std::make_shared(raw_type); + + // Clean-up: this lambda is called automatically at the scope exit. + const auto clean_up = + llvm::make_scope_exit([this, &visited, &raw_type, &root, &match] { + if (root) { + delete visited; + } + // Memoize the result if finalized. + if (match->verdict_ != MatchResult::kUndetermined) { + this->cache_.insert({raw_type, match}); + } + }); + + // Base case. + if (IsBaseMatch(raw_type)) { + match->verdict_ = MatchResult::kMatch; + return match; + } + + const clang::RecordDecl* decl = raw_type->getAsRecordDecl(); + assert(decl); + + // Check member fields + if constexpr ((Rules & InductionRule::kField) != InductionRule::kNone) { + for (const auto& field : decl->fields()) { + match->MergeSubResult( + GetMatchResult(field->getType().getTypePtrOrNull(), visited), + field->getBeginLoc()); + + // Verdict finalized: early return. + if (match->verdict_ == MatchResult::kMatch) { + return match; + } + } + } + + const auto* cxx_decl = clang::dyn_cast(decl); + if (cxx_decl && cxx_decl->hasDefinition()) { + // Check base classes + if constexpr ((Rules & InductionRule::kBaseClass) != + InductionRule::kNone) { + for (const auto& base_specifier : cxx_decl->bases()) { + match->MergeSubResult( + GetMatchResult(base_specifier.getType().getTypePtr(), visited), + base_specifier.getBeginLoc()); + + // Verdict finalized: early return. + if (match->verdict_ == MatchResult::kMatch) { + return match; + } + } + } + + // Check virtual base classes + if constexpr ((Rules & InductionRule::kVirtualBaseClass) != + InductionRule::kNone) { + for (const auto& base_specifier : cxx_decl->vbases()) { + match->MergeSubResult( + GetMatchResult(base_specifier.getType().getTypePtr(), visited), + base_specifier.getBeginLoc()); + + // Verdict finalized: early return. + if (match->verdict_ == MatchResult::kMatch) { + return match; + } + } + } + } + + // Check template parameters. + if constexpr ((Rules & InductionRule::kTemplateArgument) != + InductionRule::kNone) { + if (auto* field_record_template = + clang::dyn_cast(decl)) { + 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) { + continue; + } + match->MergeSubResult( + GetMatchResult(template_args[i].getAsType().getTypePtrOrNull(), + visited), + field_record_template->getTemplateKeywordLoc()); + + // Verdict finalized: early return. + if (match->verdict_ == MatchResult::kMatch) { + return match; + } + } + } + } + + // All reachable types have been traversed but the root type has not + // been marked as a match; therefore it must be no match. + if (root && match->verdict_ == MatchResult::kUndetermined) { + match->verdict_ = MatchResult::kNoMatch; + } + return match; + } + + private: + const clang::Type* GetBaseType(const clang::Type* type) const { + using clang::dyn_cast; + + const clang::Type* last_type = nullptr; + while (type && type != last_type) { + last_type = type; + + // Unwrap type aliases. + if constexpr ((Rules & InductionRule::kUnqualifiedDesugaredType) != + InductionRule::kNone) { + type = type->getUnqualifiedDesugaredType(); + } + + // Unwrap pointers. + if constexpr ((Rules & InductionRule::kPointerPointee) != + InductionRule::kNone) { + while (type && type->isPointerType()) { + type = type->getPointeeType().getTypePtr(); + } + } + + // Unwrap ObjC pointers. + if constexpr ((Rules & InductionRule::kObjCPointerPointee) != + InductionRule::kNone) { + while (type && type->isObjCObjectPointerType()) { + type = type->getPointeeType().getTypePtr(); + } + } + + // Unwrap array. + if constexpr ((Rules & InductionRule::kArrayElement) != + InductionRule::kNone) { + while (const auto* array_type = dyn_cast(type)) { + type = array_type->getElementType().getTypePtr(); + } + } + + // Unwrap reference. + if constexpr ((Rules & InductionRule::kReferencePointee) != + InductionRule::kNone) { + if (const auto* ref_type = dyn_cast(type)) { + type = ref_type->getPointeeType().getTypePtrOrNull(); + } + } + } + return type; + } + + virtual bool IsBaseMatch(const clang::Type* type) const { return false; } + + // Cache to efficiently determine match. + mutable std::map> cache_; +}; + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TYPEPREDICATEUTIL_H_ diff --git a/clang/raw_ptr_plugin/Util.cpp b/clang/raw_ptr_plugin/Util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a68ff608cf3e90b291048509dbc687e7cbed54d --- /dev/null +++ b/clang/raw_ptr_plugin/Util.cpp @@ -0,0 +1,171 @@ +// Copyright 2018 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "Util.h" + +#include + +#include "clang/AST/Decl.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" + +namespace raw_ptr_plugin { + +namespace { + +// Directories which are treated as third-party code, which can be used to +// prevent emitting diagnostics in them. +// +// Each one must start and end with a `/` to be used correctly. +const char* kTreatAsThirdPartyDirs[] = { + "/breakpad/", // + "/courgette/", // + "/frameworks/", // + "/native_client/", // + "/ppapi/", // + "/testing/", // + "/v8/", // +}; + +std::string GetNamespaceImpl(const clang::DeclContext* context, + const std::string& candidate) { + switch (context->getDeclKind()) { + case clang::Decl::TranslationUnit: { + return candidate; + } + case clang::Decl::Namespace: { + const auto* decl = llvm::dyn_cast(context); + std::string name_str; + llvm::raw_string_ostream OS(name_str); + if (decl->isAnonymousNamespace()) { + OS << ""; + } else { + OS << *decl; + } + return GetNamespaceImpl(context->getParent(), OS.str()); + } + default: { + return GetNamespaceImpl(context->getParent(), candidate); + } + } +} + +} // namespace + +std::string GetNamespace(const clang::Decl* record) { + return GetNamespaceImpl(record->getDeclContext(), std::string()); +} + +std::string GetFilename(const clang::SourceManager& source_manager, + clang::SourceLocation loc, + FilenameLocationType type, + FilenamesFollowPresumed follow_presumed) { + switch (type) { + case FilenameLocationType::kExactLoc: + break; + case FilenameLocationType::kSpellingLoc: + loc = source_manager.getSpellingLoc(loc); + break; + case FilenameLocationType::kExpansionLoc: + loc = source_manager.getExpansionLoc(loc); + break; + } + std::string name; + if (follow_presumed == FilenamesFollowPresumed::kYes) { + clang::PresumedLoc ploc = source_manager.getPresumedLoc(loc); + if (ploc.isInvalid()) { + // If we're in an invalid location, we're looking at things that aren't + // actually stated in the source. + return name; + } + name = ploc.getFilename(); + } else { + name = source_manager.getFilename(loc); + } + + // File paths can have separators which differ from ones at this platform. + // Make them consistent. + std::replace(name.begin(), name.end(), '\\', '/'); + return name; +} + +LocationClassification ClassifySourceLocation( + const clang::HeaderSearchOptions& search, + const clang::SourceManager& sm, + clang::SourceLocation loc) { + if (sm.isInSystemHeader(loc)) { + return LocationClassification::kSystem; + } + + std::string filename = GetFilename(sm, loc, FilenameLocationType::kExactLoc); + if (filename.empty()) { + // If the filename cannot be determined, simply treat this as third-party + // code, where we avoid enforcing rules, instead of going through the full + // lookup process. + return LocationClassification::kThirdParty; + } + + // Files in the sysroot do not automatically get categorized as system + // headers, so we do a path comparison. The sysroot can be set to "/" when it + // was not specified, which is just the whole filesystem, but every file is + // not a system header, so this is treated as equivalent to not having a + // sysroot. + if (!search.Sysroot.empty() && search.Sysroot != "/" && + llvm::StringRef(filename).starts_with(search.Sysroot)) { + return LocationClassification::kSystem; + } + + // We need to special case scratch space; which is where clang does its macro + // expansion. We explicitly want to allow people to do otherwise bad things + // through macros that were defined due to third party libraries. + // + // TODO(danakj): We can further classify this as first/third-party code using + // a macro defined in first/third-party code. See + // https://github.com/chromium/subspace/blob/f9c481a241961a7be827d31fadb01badac6ee86a/subdoc/lib/visit.cc#L1566-L1577 + if (filename == "") { + return LocationClassification::kMacro; + } + + // Ensure that we can search for patterns of the form "/foo/" even + // if we have a relative path like "foo/bar.cc". We don't expect + // this transformed path to exist necessarily. + if (filename.front() != '/') { + filename.insert(0, 1, '/'); + } + + if (filename.find("/gen/") != std::string::npos) { + return LocationClassification::kGenerated; + } + + // While blink is inside third_party, it's not all treated like third-party + // code. + if (auto p = filename.find("/third_party/blink/"); p != std::string::npos) { + // Browser-side code is treated like first party in order to have all + // diagnostics applied. Over time we want the rest of blink code to + // converge as well. + // + // TODO(danakj): Use starts_with() when Clang is compiled with C++20. + if (!llvm::StringRef(filename).substr(p).starts_with("browser/")) { + return LocationClassification::kBlink; + } + } + + if (filename.find("/third_party/") != std::string::npos) { + return LocationClassification::kThirdParty; + } + + for (const char* dir : kTreatAsThirdPartyDirs) { + if (filename.find(dir) != std::string::npos) { + return LocationClassification::kThirdParty; + } + } + + // TODO(danakj): Designate chromium-owned code in third_party as + // kChromiumFirstParty. + return LocationClassification::kFirstParty; +} + +} // namespace raw_ptr_plugin diff --git a/clang/raw_ptr_plugin/Util.h b/clang/raw_ptr_plugin/Util.h new file mode 100644 index 0000000000000000000000000000000000000000..f7e212034c80491bc90e6f29ad2dddeee05e8b1d --- /dev/null +++ b/clang/raw_ptr_plugin/Util.h @@ -0,0 +1,128 @@ +// Copyright 2018 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_UTIL_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_UTIL_H_ + +#include +#include + +#include "clang/AST/DeclBase.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/TypeLoc.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/HeaderSearchOptions.h" + +namespace raw_ptr_plugin { + +// Utility method for subclasses to determine the namespace of the +// specified record, if any. Unnamed namespaces will be identified as +// "". +std::string GetNamespace(const clang::Decl* record); + +enum class FilenamesFollowPresumed { + kYes, + kNo, +}; + +enum class FilenameLocationType { + // Use the location as is. + kExactLoc, + // Use the spelling location of the current location. This leaves a macro if + // the token at the location came from a macro parameter. + kSpellingLoc, + // Use the expansion location of the current location. This always leaves a + // macro. + kExpansionLoc, +}; + +// Attempts to determine the filename for the given SourceLocation. Returns an +// empty string if the filename could not be determined. +// +// Most callers use FilenameLocationType::kSpellingLoc which gets the filename +// where a macro is used, if the name at the location is coming from a macro +// parameter. This would be used when a warning is being generated based on the +// macro inputs, rather than the macro's structure itself. It may be an +// incorrect approximation of kExpansionLoc, though. +std::string GetFilename( + const clang::SourceManager& instance, + clang::SourceLocation location, + FilenameLocationType type, + FilenamesFollowPresumed follow_presumed = FilenamesFollowPresumed::kYes); + +// Utility method to obtain a "representative" source location polymorphically. +// We sometimes use a source location to determine a code owner has legitimate +// justification not to fix the issue found out by the plugin (e.g. the issue +// being inside system headers). Among several options to obtain a location, +// this utility aims to provide the best location which represents the node's +// essential token. +inline clang::SourceLocation getRepresentativeLocation( + const clang::Stmt& node) { + // clang::Stmt has T::getBeginLoc() and T::getEndLoc(). + // Usually the former one does better represent the location. + // + // e.g. clang::IfStmt + // if (foo) {} else {} + // ^ ^ + // | getEndLoc() + // getBeginLoc() + // + // e.g. clang::CastExpr + // int x = static_cast(123ll); + // ^ ^ + // | getEndLoc() + // getBeginLoc() + return node.getBeginLoc(); +} +inline clang::SourceLocation getRepresentativeLocation( + const clang::TypeLoc& node) { + // clang::TypeLoc has T::getBeginLoc() and T::getEndLoc(). + // As the former may refer to modifiers, we use the latter one. + return node.getEndLoc(); +} +inline clang::SourceLocation getRepresentativeLocation( + const clang::Decl& node) { + // Unlike other nodes, clang::Decl provides T::getLocation(). + // Usually, this provides more "representative" location. + // + // e.g. clang::FieldDecl + // int* field = nullptr; + // ^ ^ ^ + // | | getEndLoc() + // | getLocation() + // getBeginLoc() + return node.getLocation(); +} + +enum LocationClassification { + // First-party Chromium code that is part of the main project repo. + kFirstParty, + // Blink is first-party but is treated differently sometimes, with different + // style rules. + kBlink, + // Third-party code that is owned by the Chromium project. + kChromiumThirdParty, + // Third-party code that is not owned by the Chromium project, imported from + // external projects. + kThirdParty, + // Generated code which is not checked in. + kGenerated, + // Code that is generated by a macro. + kMacro, + // System headers (specified to Clang by -isystem). + kSystem, +}; + +// Determines if a SourceLocation is considered part of first-party or +// third-party code, or is generated code, which can be used to determine how or +// which rules should be enforced. +LocationClassification ClassifySourceLocation( + const clang::HeaderSearchOptions& search, + const clang::SourceManager& sm, + clang::SourceLocation loc); + +} // namespace raw_ptr_plugin + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_UTIL_H_ diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast.cpp b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5eb32fd7993662470400b3742aa6696e544e54aa --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast.cpp @@ -0,0 +1,117 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/raw_ptr.h" + +struct RawPtrWrapper { + raw_ptr ptr; +}; + +struct RawPtrWrapperWrapper { + RawPtrWrapper* ptr; +}; + +class RawPtrWrapperWrapperWrapper { + public: + explicit RawPtrWrapperWrapperWrapper(RawPtrWrapperWrapper& ptr) : ptr_(ptr) {} + + RawPtrWrapperWrapper& ptr_; +}; + +struct RawPtrWrapperSub : RawPtrWrapper {}; +struct RawPtrWrapperVirtualSub : virtual RawPtrWrapper {}; + +void MyFunc(void* p) {} + +void CastFromCastingUnsafe() { + // "Casting unsafe" type variables; + raw_ptr p0; + RawPtrWrapper p1; + RawPtrWrapperWrapper p2; + RawPtrWrapperWrapperWrapper p3(p2); + RawPtrWrapper p4[10]; + RawPtrWrapperSub p5; + RawPtrWrapperVirtualSub p6; + + // CK_BitCast via |static_cast| should emit an error. + (void)static_cast(&p0); + (void)static_cast(&p1); + (void)static_cast(&p2); + (void)static_cast(&p3); + (void)static_cast(&p4); + (void)static_cast(&p5); + (void)static_cast(&p6); + + // CK_BitCast via C-style casting should emit an error. + (void)(void*)&p0; + (void)(void*)&p1; + (void)(void*)&p2; + (void)(void*)&p3; + (void)(void*)&p4; + (void)(void*)&p5; + (void)(void*)&p6; + + // CK_BitCast via |reinterpret_cast| should emit an error. + (void)reinterpret_cast(&p0); + (void)reinterpret_cast(&p1); + (void)reinterpret_cast(&p2); + (void)reinterpret_cast(&p3); + (void)reinterpret_cast(&p4); + (void)reinterpret_cast(&p5); + (void)reinterpret_cast(&p6); + + // CK_LValueToRValueBitCast via |bit_cast| should emit an error. + (void)__builtin_bit_cast(void*, &p0); + (void)__builtin_bit_cast(void*, &p1); + (void)__builtin_bit_cast(void*, &p2); + (void)__builtin_bit_cast(void*, &p3); + (void)__builtin_bit_cast(void*, &p4); + (void)__builtin_bit_cast(void*, &p5); + (void)__builtin_bit_cast(void*, &p6); + + // CK_BitCast via implicit casting should emit an error. + MyFunc(&p0); + MyFunc(&p1); + MyFunc(&p2); + MyFunc(&p3); + MyFunc(&p4); + MyFunc(&p5); + MyFunc(&p6); +} + +void CastToCastingUnsafe() { + void* p = nullptr; + + // CK_BitCast via |static_cast| should emit an error. + (void)static_cast*>(p); + (void)static_cast(p); + (void)static_cast(p); + (void)static_cast(p); + (void)static_cast(p); + (void)static_cast(p); + + // CK_BitCast via C-style casting should emit an error. + (void)(raw_ptr*)p; + (void)(RawPtrWrapper*)p; + (void)(RawPtrWrapperWrapper*)p; + (void)(RawPtrWrapperWrapperWrapper*)p; + (void)(RawPtrWrapperSub*)p; + (void)(RawPtrWrapperVirtualSub*)p; + + // CK_BitCast via |reinterpret_cast| should emit an error. + (void)reinterpret_cast*>(p); + (void)reinterpret_cast(p); + (void)reinterpret_cast(p); + (void)reinterpret_cast(p); + (void)reinterpret_cast(p); + (void)reinterpret_cast(p); + + // CK_LValueToRValueBitCast via |bit_cast| should emit an error. + (void)__builtin_bit_cast(raw_ptr*, p); + (void)__builtin_bit_cast(RawPtrWrapper*, p); + (void)__builtin_bit_cast(RawPtrWrapperWrapper*, p); + (void)__builtin_bit_cast(RawPtrWrapperWrapperWrapper*, p); + (void)__builtin_bit_cast(RawPtrWrapperSub*, p); + (void)__builtin_bit_cast(RawPtrWrapperVirtualSub*, p); +} diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast.flags b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast.flags new file mode 100644 index 0000000000000000000000000000000000000000..e4604256a97dcc108a429e382dcd7895821a9afa --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast.flags @@ -0,0 +1 @@ +-ferror-limit=0 -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-bad-raw-ptr-cast diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast.txt b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast.txt new file mode 100644 index 0000000000000000000000000000000000000000..a23d121692d2558d4aec289adf7274b5078e8cbf --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast.txt @@ -0,0 +1,522 @@ +bad_raw_ptr_cast.cpp:38:31: error: [chromium-style] casting 'raw_ptr *' to 'void * is not allowed. + (void)static_cast(&p0); + ^ +bad_raw_ptr_cast.cpp:38:31: note: [chromium-style] 'raw_ptr *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:39:31: error: [chromium-style] casting 'RawPtrWrapper *' to 'void * is not allowed. + (void)static_cast(&p1); + ^ +bad_raw_ptr_cast.cpp:39:31: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:40:31: error: [chromium-style] casting 'RawPtrWrapperWrapper *' to 'void * is not allowed. + (void)static_cast(&p2); + ^ +bad_raw_ptr_cast.cpp:40:31: note: [chromium-style] 'RawPtrWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:41:31: error: [chromium-style] casting 'RawPtrWrapperWrapperWrapper *' to 'void * is not allowed. + (void)static_cast(&p3); + ^ +bad_raw_ptr_cast.cpp:41:31: note: [chromium-style] 'RawPtrWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:19:3: note: [chromium-style] 'RawPtrWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapperWrapper& ptr_; + ^ +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:42:31: error: [chromium-style] casting 'RawPtrWrapper (*)[10]' to 'void * is not allowed. + (void)static_cast(&p4); + ^ +bad_raw_ptr_cast.cpp:42:31: note: [chromium-style] 'RawPtrWrapper (*)[10]' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:43:31: error: [chromium-style] casting 'RawPtrWrapperSub *' to 'void * is not allowed. + (void)static_cast(&p5); + ^ +bad_raw_ptr_cast.cpp:43:31: note: [chromium-style] 'RawPtrWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:22:27: note: [chromium-style] 'RawPtrWrapperSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperSub : RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:44:31: error: [chromium-style] casting 'RawPtrWrapperVirtualSub *' to 'void * is not allowed. + (void)static_cast(&p6); + ^ +bad_raw_ptr_cast.cpp:44:31: note: [chromium-style] 'RawPtrWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:23:34: note: [chromium-style] 'RawPtrWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperVirtualSub : virtual RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:47:17: error: [chromium-style] casting 'raw_ptr *' to 'void * is not allowed. + (void)(void*)&p0; + ^ +bad_raw_ptr_cast.cpp:47:17: note: [chromium-style] 'raw_ptr *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:48:17: error: [chromium-style] casting 'RawPtrWrapper *' to 'void * is not allowed. + (void)(void*)&p1; + ^ +bad_raw_ptr_cast.cpp:48:17: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:49:17: error: [chromium-style] casting 'RawPtrWrapperWrapper *' to 'void * is not allowed. + (void)(void*)&p2; + ^ +bad_raw_ptr_cast.cpp:49:17: note: [chromium-style] 'RawPtrWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:50:17: error: [chromium-style] casting 'RawPtrWrapperWrapperWrapper *' to 'void * is not allowed. + (void)(void*)&p3; + ^ +bad_raw_ptr_cast.cpp:50:17: note: [chromium-style] 'RawPtrWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:19:3: note: [chromium-style] 'RawPtrWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapperWrapper& ptr_; + ^ +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:51:17: error: [chromium-style] casting 'RawPtrWrapper (*)[10]' to 'void * is not allowed. + (void)(void*)&p4; + ^ +bad_raw_ptr_cast.cpp:51:17: note: [chromium-style] 'RawPtrWrapper (*)[10]' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:52:17: error: [chromium-style] casting 'RawPtrWrapperSub *' to 'void * is not allowed. + (void)(void*)&p5; + ^ +bad_raw_ptr_cast.cpp:52:17: note: [chromium-style] 'RawPtrWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:22:27: note: [chromium-style] 'RawPtrWrapperSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperSub : RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:53:17: error: [chromium-style] casting 'RawPtrWrapperVirtualSub *' to 'void * is not allowed. + (void)(void*)&p6; + ^ +bad_raw_ptr_cast.cpp:53:17: note: [chromium-style] 'RawPtrWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:23:34: note: [chromium-style] 'RawPtrWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperVirtualSub : virtual RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:56:36: error: [chromium-style] casting 'raw_ptr *' to 'void * is not allowed. + (void)reinterpret_cast(&p0); + ^ +bad_raw_ptr_cast.cpp:56:36: note: [chromium-style] 'raw_ptr *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:57:36: error: [chromium-style] casting 'RawPtrWrapper *' to 'void * is not allowed. + (void)reinterpret_cast(&p1); + ^ +bad_raw_ptr_cast.cpp:57:36: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:58:36: error: [chromium-style] casting 'RawPtrWrapperWrapper *' to 'void * is not allowed. + (void)reinterpret_cast(&p2); + ^ +bad_raw_ptr_cast.cpp:58:36: note: [chromium-style] 'RawPtrWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:59:36: error: [chromium-style] casting 'RawPtrWrapperWrapperWrapper *' to 'void * is not allowed. + (void)reinterpret_cast(&p3); + ^ +bad_raw_ptr_cast.cpp:59:36: note: [chromium-style] 'RawPtrWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:19:3: note: [chromium-style] 'RawPtrWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapperWrapper& ptr_; + ^ +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:60:36: error: [chromium-style] casting 'RawPtrWrapper (*)[10]' to 'void * is not allowed. + (void)reinterpret_cast(&p4); + ^ +bad_raw_ptr_cast.cpp:60:36: note: [chromium-style] 'RawPtrWrapper (*)[10]' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:61:36: error: [chromium-style] casting 'RawPtrWrapperSub *' to 'void * is not allowed. + (void)reinterpret_cast(&p5); + ^ +bad_raw_ptr_cast.cpp:61:36: note: [chromium-style] 'RawPtrWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:22:27: note: [chromium-style] 'RawPtrWrapperSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperSub : RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:62:36: error: [chromium-style] casting 'RawPtrWrapperVirtualSub *' to 'void * is not allowed. + (void)reinterpret_cast(&p6); + ^ +bad_raw_ptr_cast.cpp:62:36: note: [chromium-style] 'RawPtrWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:23:34: note: [chromium-style] 'RawPtrWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperVirtualSub : virtual RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:65:38: error: [chromium-style] casting 'raw_ptr *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p0); + ^ +bad_raw_ptr_cast.cpp:65:38: note: [chromium-style] 'raw_ptr *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:66:38: error: [chromium-style] casting 'RawPtrWrapper *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p1); + ^ +bad_raw_ptr_cast.cpp:66:38: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:67:38: error: [chromium-style] casting 'RawPtrWrapperWrapper *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p2); + ^ +bad_raw_ptr_cast.cpp:67:38: note: [chromium-style] 'RawPtrWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:68:38: error: [chromium-style] casting 'RawPtrWrapperWrapperWrapper *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p3); + ^ +bad_raw_ptr_cast.cpp:68:38: note: [chromium-style] 'RawPtrWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:19:3: note: [chromium-style] 'RawPtrWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapperWrapper& ptr_; + ^ +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:69:38: error: [chromium-style] casting 'RawPtrWrapper (*)[10]' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p4); + ^ +bad_raw_ptr_cast.cpp:69:38: note: [chromium-style] 'RawPtrWrapper (*)[10]' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:70:38: error: [chromium-style] casting 'RawPtrWrapperSub *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p5); + ^ +bad_raw_ptr_cast.cpp:70:38: note: [chromium-style] 'RawPtrWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:22:27: note: [chromium-style] 'RawPtrWrapperSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperSub : RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:71:38: error: [chromium-style] casting 'RawPtrWrapperVirtualSub *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p6); + ^ +bad_raw_ptr_cast.cpp:71:38: note: [chromium-style] 'RawPtrWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:23:34: note: [chromium-style] 'RawPtrWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperVirtualSub : virtual RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:74:11: error: [chromium-style] casting 'raw_ptr *' to 'void * is not allowed. + MyFunc(&p0); + ^ +bad_raw_ptr_cast.cpp:74:11: note: [chromium-style] 'raw_ptr *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:75:11: error: [chromium-style] casting 'RawPtrWrapper *' to 'void * is not allowed. + MyFunc(&p1); + ^ +bad_raw_ptr_cast.cpp:75:11: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:76:11: error: [chromium-style] casting 'RawPtrWrapperWrapper *' to 'void * is not allowed. + MyFunc(&p2); + ^ +bad_raw_ptr_cast.cpp:76:11: note: [chromium-style] 'RawPtrWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:77:11: error: [chromium-style] casting 'RawPtrWrapperWrapperWrapper *' to 'void * is not allowed. + MyFunc(&p3); + ^ +bad_raw_ptr_cast.cpp:77:11: note: [chromium-style] 'RawPtrWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:19:3: note: [chromium-style] 'RawPtrWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapperWrapper& ptr_; + ^ +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:78:11: error: [chromium-style] casting 'RawPtrWrapper (*)[10]' to 'void * is not allowed. + MyFunc(&p4); + ^ +bad_raw_ptr_cast.cpp:78:11: note: [chromium-style] 'RawPtrWrapper (*)[10]' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:79:11: error: [chromium-style] casting 'RawPtrWrapperSub *' to 'void * is not allowed. + MyFunc(&p5); + ^ +bad_raw_ptr_cast.cpp:79:11: note: [chromium-style] 'RawPtrWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:22:27: note: [chromium-style] 'RawPtrWrapperSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperSub : RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:80:11: error: [chromium-style] casting 'RawPtrWrapperVirtualSub *' to 'void * is not allowed. + MyFunc(&p6); + ^ +bad_raw_ptr_cast.cpp:80:11: note: [chromium-style] 'RawPtrWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:23:34: note: [chromium-style] 'RawPtrWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperVirtualSub : virtual RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:87:37: error: [chromium-style] casting 'void *' to 'raw_ptr * is not allowed. + (void)static_cast*>(p); + ^ +bad_raw_ptr_cast.cpp:87:37: note: [chromium-style] 'raw_ptr *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:88:38: error: [chromium-style] casting 'void *' to 'RawPtrWrapper * is not allowed. + (void)static_cast(p); + ^ +bad_raw_ptr_cast.cpp:88:38: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:89:45: error: [chromium-style] casting 'void *' to 'RawPtrWrapperWrapper * is not allowed. + (void)static_cast(p); + ^ +bad_raw_ptr_cast.cpp:89:45: note: [chromium-style] 'RawPtrWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:90:52: error: [chromium-style] casting 'void *' to 'RawPtrWrapperWrapperWrapper * is not allowed. + (void)static_cast(p); + ^ +bad_raw_ptr_cast.cpp:90:52: note: [chromium-style] 'RawPtrWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:19:3: note: [chromium-style] 'RawPtrWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapperWrapper& ptr_; + ^ +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:91:41: error: [chromium-style] casting 'void *' to 'RawPtrWrapperSub * is not allowed. + (void)static_cast(p); + ^ +bad_raw_ptr_cast.cpp:91:41: note: [chromium-style] 'RawPtrWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:22:27: note: [chromium-style] 'RawPtrWrapperSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperSub : RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:92:48: error: [chromium-style] casting 'void *' to 'RawPtrWrapperVirtualSub * is not allowed. + (void)static_cast(p); + ^ +bad_raw_ptr_cast.cpp:92:48: note: [chromium-style] 'RawPtrWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:23:34: note: [chromium-style] 'RawPtrWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperVirtualSub : virtual RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:95:24: error: [chromium-style] casting 'void *' to 'raw_ptr * is not allowed. + (void)(raw_ptr*)p; + ^ +bad_raw_ptr_cast.cpp:95:24: note: [chromium-style] 'raw_ptr *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:96:25: error: [chromium-style] casting 'void *' to 'RawPtrWrapper * is not allowed. + (void)(RawPtrWrapper*)p; + ^ +bad_raw_ptr_cast.cpp:96:25: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:97:32: error: [chromium-style] casting 'void *' to 'RawPtrWrapperWrapper * is not allowed. + (void)(RawPtrWrapperWrapper*)p; + ^ +bad_raw_ptr_cast.cpp:97:32: note: [chromium-style] 'RawPtrWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:98:39: error: [chromium-style] casting 'void *' to 'RawPtrWrapperWrapperWrapper * is not allowed. + (void)(RawPtrWrapperWrapperWrapper*)p; + ^ +bad_raw_ptr_cast.cpp:98:39: note: [chromium-style] 'RawPtrWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:19:3: note: [chromium-style] 'RawPtrWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapperWrapper& ptr_; + ^ +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:99:28: error: [chromium-style] casting 'void *' to 'RawPtrWrapperSub * is not allowed. + (void)(RawPtrWrapperSub*)p; + ^ +bad_raw_ptr_cast.cpp:99:28: note: [chromium-style] 'RawPtrWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:22:27: note: [chromium-style] 'RawPtrWrapperSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperSub : RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:100:35: error: [chromium-style] casting 'void *' to 'RawPtrWrapperVirtualSub * is not allowed. + (void)(RawPtrWrapperVirtualSub*)p; + ^ +bad_raw_ptr_cast.cpp:100:35: note: [chromium-style] 'RawPtrWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:23:34: note: [chromium-style] 'RawPtrWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperVirtualSub : virtual RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:103:42: error: [chromium-style] casting 'void *' to 'raw_ptr * is not allowed. + (void)reinterpret_cast*>(p); + ^ +bad_raw_ptr_cast.cpp:103:42: note: [chromium-style] 'raw_ptr *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:104:43: error: [chromium-style] casting 'void *' to 'RawPtrWrapper * is not allowed. + (void)reinterpret_cast(p); + ^ +bad_raw_ptr_cast.cpp:104:43: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:105:50: error: [chromium-style] casting 'void *' to 'RawPtrWrapperWrapper * is not allowed. + (void)reinterpret_cast(p); + ^ +bad_raw_ptr_cast.cpp:105:50: note: [chromium-style] 'RawPtrWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:106:57: error: [chromium-style] casting 'void *' to 'RawPtrWrapperWrapperWrapper * is not allowed. + (void)reinterpret_cast(p); + ^ +bad_raw_ptr_cast.cpp:106:57: note: [chromium-style] 'RawPtrWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:19:3: note: [chromium-style] 'RawPtrWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapperWrapper& ptr_; + ^ +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:107:46: error: [chromium-style] casting 'void *' to 'RawPtrWrapperSub * is not allowed. + (void)reinterpret_cast(p); + ^ +bad_raw_ptr_cast.cpp:107:46: note: [chromium-style] 'RawPtrWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:22:27: note: [chromium-style] 'RawPtrWrapperSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperSub : RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:108:53: error: [chromium-style] casting 'void *' to 'RawPtrWrapperVirtualSub * is not allowed. + (void)reinterpret_cast(p); + ^ +bad_raw_ptr_cast.cpp:108:53: note: [chromium-style] 'RawPtrWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:23:34: note: [chromium-style] 'RawPtrWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperVirtualSub : virtual RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:111:44: error: [chromium-style] casting 'void *' to 'raw_ptr * is not allowed. + (void)__builtin_bit_cast(raw_ptr*, p); + ^ +bad_raw_ptr_cast.cpp:111:44: note: [chromium-style] 'raw_ptr *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:112:45: error: [chromium-style] casting 'void *' to 'RawPtrWrapper * is not allowed. + (void)__builtin_bit_cast(RawPtrWrapper*, p); + ^ +bad_raw_ptr_cast.cpp:112:45: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:113:52: error: [chromium-style] casting 'void *' to 'RawPtrWrapperWrapper * is not allowed. + (void)__builtin_bit_cast(RawPtrWrapperWrapper*, p); + ^ +bad_raw_ptr_cast.cpp:113:52: note: [chromium-style] 'RawPtrWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:114:59: error: [chromium-style] casting 'void *' to 'RawPtrWrapperWrapperWrapper * is not allowed. + (void)__builtin_bit_cast(RawPtrWrapperWrapperWrapper*, p); + ^ +bad_raw_ptr_cast.cpp:114:59: note: [chromium-style] 'RawPtrWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:19:3: note: [chromium-style] 'RawPtrWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapperWrapper& ptr_; + ^ +bad_raw_ptr_cast.cpp:12:3: note: [chromium-style] 'RawPtrWrapperWrapper' manages BackupRefPtr or its container here. + RawPtrWrapper* ptr; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:115:48: error: [chromium-style] casting 'void *' to 'RawPtrWrapperSub * is not allowed. + (void)__builtin_bit_cast(RawPtrWrapperSub*, p); + ^ +bad_raw_ptr_cast.cpp:115:48: note: [chromium-style] 'RawPtrWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:22:27: note: [chromium-style] 'RawPtrWrapperSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperSub : RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast.cpp:116:55: error: [chromium-style] casting 'void *' to 'RawPtrWrapperVirtualSub * is not allowed. + (void)__builtin_bit_cast(RawPtrWrapperVirtualSub*, p); + ^ +bad_raw_ptr_cast.cpp:116:55: note: [chromium-style] 'RawPtrWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast.cpp:23:34: note: [chromium-style] 'RawPtrWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawPtrWrapperVirtualSub : virtual RawPtrWrapper {}; + ^ +bad_raw_ptr_cast.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +59 errors generated. diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_exclusion.cpp b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_exclusion.cpp new file mode 100644 index 0000000000000000000000000000000000000000..794981947d2595e92d594d5e425eb5358a999acd --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_exclusion.cpp @@ -0,0 +1,92 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/raw_ptr.h" +#include "base/memory/raw_ptr_cast.h" + +struct RawPtrWrapper { + raw_ptr ptr; +}; + +struct RawPtrWrapperWrapper { + RawPtrWrapper* ptr; +}; + +class RawPtrWrapperWrapperWrapper { + public: + explicit RawPtrWrapperWrapperWrapper(RawPtrWrapperWrapper& ptr) : ptr_(ptr) {} + + RawPtrWrapperWrapper& ptr_; +}; + +struct RawPtrWrapperSub : RawPtrWrapper {}; +struct RawPtrWrapperVirtualSub : virtual RawPtrWrapper {}; + +void MyFunc(void* p) {} + +// 'unsafe_raw_ptr_*_cast' should not emit errors. +void CastFromCastingUnsafeExclusion() { + // "Casting unsafe" type variables; + raw_ptr p0; + RawPtrWrapper p1; + RawPtrWrapperWrapper p2; + RawPtrWrapperWrapperWrapper p3(p2); + RawPtrWrapper p4[10]; + RawPtrWrapperSub p5; + RawPtrWrapperVirtualSub p6; + + (void)base::unsafe_raw_ptr_static_cast(&p0); + (void)base::unsafe_raw_ptr_static_cast(&p1); + (void)base::unsafe_raw_ptr_static_cast(&p2); + (void)base::unsafe_raw_ptr_static_cast(&p3); + (void)base::unsafe_raw_ptr_static_cast(&p4); + (void)base::unsafe_raw_ptr_static_cast(&p5); + (void)base::unsafe_raw_ptr_static_cast(&p6); + + (void)base::unsafe_raw_ptr_reinterpret_cast(&p0); + (void)base::unsafe_raw_ptr_reinterpret_cast(&p1); + (void)base::unsafe_raw_ptr_reinterpret_cast(&p2); + (void)base::unsafe_raw_ptr_reinterpret_cast(&p3); + (void)base::unsafe_raw_ptr_reinterpret_cast(&p4); + (void)base::unsafe_raw_ptr_reinterpret_cast(&p5); + (void)base::unsafe_raw_ptr_reinterpret_cast(&p6); + + (void)base::unsafe_raw_ptr_bit_cast(&p0); + (void)base::unsafe_raw_ptr_bit_cast(&p1); + (void)base::unsafe_raw_ptr_bit_cast(&p2); + (void)base::unsafe_raw_ptr_bit_cast(&p3); + (void)base::unsafe_raw_ptr_bit_cast(&p4); + (void)base::unsafe_raw_ptr_bit_cast(&p5); + (void)base::unsafe_raw_ptr_bit_cast(&p6); +} + +// 'unsafe_raw_ptr_*_cast' should not emit errors. +void CastToCastingUnsafeExclusion() { + void* p = nullptr; + + // CK_BitCast via |base::unsafe_raw_ptr_static_cast| should emit an error. + (void)base::unsafe_raw_ptr_static_cast*>(p); + (void)base::unsafe_raw_ptr_static_cast(p); + (void)base::unsafe_raw_ptr_static_cast(p); + (void)base::unsafe_raw_ptr_static_cast(p); + (void)base::unsafe_raw_ptr_static_cast(p); + (void)base::unsafe_raw_ptr_static_cast(p); + + // CK_BitCast via |base::unsafe_raw_ptr_reinterpret_cast| should emit an + // error. + (void)base::unsafe_raw_ptr_reinterpret_cast*>(p); + (void)base::unsafe_raw_ptr_reinterpret_cast(p); + (void)base::unsafe_raw_ptr_reinterpret_cast(p); + (void)base::unsafe_raw_ptr_reinterpret_cast(p); + (void)base::unsafe_raw_ptr_reinterpret_cast(p); + (void)base::unsafe_raw_ptr_reinterpret_cast(p); + + // CK_BitCast via |bit_cast| should emit an error. + (void)base::unsafe_raw_ptr_bit_cast*>(p); + (void)base::unsafe_raw_ptr_bit_cast(p); + (void)base::unsafe_raw_ptr_bit_cast(p); + (void)base::unsafe_raw_ptr_bit_cast(p); + (void)base::unsafe_raw_ptr_bit_cast(p); + (void)base::unsafe_raw_ptr_bit_cast(p); +} diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_exclusion.flags b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_exclusion.flags new file mode 100644 index 0000000000000000000000000000000000000000..83a67e3c7a512821a87ec6592b6140d8de225d38 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_exclusion.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-bad-raw-ptr-cast diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_exclusion.txt b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_exclusion.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_implicit_exclusion.cpp b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_implicit_exclusion.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ec6082f717c3c435f8f10b9d3ed19d20d6acbbb --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_implicit_exclusion.cpp @@ -0,0 +1,69 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/raw_ptr.h" + +struct RawPtrWrapper { + raw_ptr ptr; +}; + +void OverloadedFunction(void* p) {} +void OverloadedFunction(int v) {} + +namespace test { +void NormalFunc(void* p) {} +void AllowlistedFunc(void* p) {} +struct AllowlistedConstructor { + explicit AllowlistedConstructor(void* p) {} +}; +struct NormalConstructor { + explicit NormalConstructor(void* p) {} +}; +} // namespace test + +// 'unsafe_raw_ptr_*_cast' should not emit errors. +void CastToCastingUnsafeExclusion() { + void* p = nullptr; + RawPtrWrapper* q = nullptr; + + // Base case: should error. + (void)reinterpret_cast(p); + (void)reinterpret_cast(q); + + // Casts to const built-in typed pointers should be excluded. + (void)reinterpret_cast(q); + (void)reinterpret_cast(q); + + // Casts in allowlisted invocation context should be excluded. + test::NormalFunc(q); // Not allowlisted + test::AllowlistedFunc(q); // Allowlisted + (void)test::NormalConstructor(q); // Not allowlisted + (void)test::AllowlistedConstructor(q); // Allowlisted + + // Casts in comparison context should be excluded. + (void)(p == q); + // ^ implicit cast from |RawPtrWrapper*| to |void*| here. + + // Implicit casts in invocation inside template context should be excluded. + auto f = [](auto* x) { OverloadedFunction(x); }; + f(p); + f(q); + + // Casts that |isNotSpelledInSource()| should be excluded. +#define ANY_CAST(type) type##_cast + (void)ANY_CAST(reinterpret)(p); + // ^~~~~~~~ token "reinterpret_cast" is in . + + // Casts that |isInThirdPartyLocation()| should be excluded. +#line 1 "../../third_party/fake_loc/bad_raw_ptr_cast_implicit_exclusion.cpp" + (void)reinterpret_cast(p); + + // Casts that |isInLocationListedInFilterFile(...)| should be excluded. +#line 1 "../../internal/fake_loc/bad_raw_ptr_cast_implicit_exclusion.cpp" + (void)reinterpret_cast(p); + + // Casts in allowlisted paths should be excluded. +#line 1 "../../ppapi/fake_loc/bad_raw_ptr_cast_implicit_exclusion.cpp" + (void)reinterpret_cast(p); +} diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_implicit_exclusion.flags b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_implicit_exclusion.flags new file mode 100644 index 0000000000000000000000000000000000000000..fa34b97ff2f695ab4faf8c930a4d25aaed7c0bda --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_implicit_exclusion.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-bad-raw-ptr-cast -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-bad-raw-ptr-cast-exclude-func=test::AllowlistedFunc -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-bad-raw-ptr-cast-exclude-func=test::AllowlistedConstructor::AllowlistedConstructor -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-bad-raw-ptr-cast-exclude-path=ppapi/ diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_implicit_exclusion.txt b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_implicit_exclusion.txt new file mode 100644 index 0000000000000000000000000000000000000000..e4695078a9bc3d10d28c787adbd27e45c4ddd2c8 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_implicit_exclusion.txt @@ -0,0 +1,29 @@ +bad_raw_ptr_cast_implicit_exclusion.cpp:31:43: error: [chromium-style] casting 'void *' to 'RawPtrWrapper * is not allowed. + (void)reinterpret_cast(p); + ^ +bad_raw_ptr_cast_implicit_exclusion.cpp:31:43: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_implicit_exclusion.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_implicit_exclusion.cpp:32:34: error: [chromium-style] casting 'RawPtrWrapper *' to 'void * is not allowed. + (void)reinterpret_cast(q); + ^ +bad_raw_ptr_cast_implicit_exclusion.cpp:32:34: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_implicit_exclusion.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_implicit_exclusion.cpp:39:20: error: [chromium-style] casting 'RawPtrWrapper *' to 'void * is not allowed. + test::NormalFunc(q); // Not allowlisted + ^ +bad_raw_ptr_cast_implicit_exclusion.cpp:39:20: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_implicit_exclusion.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_implicit_exclusion.cpp:41:33: error: [chromium-style] casting 'RawPtrWrapper *' to 'void * is not allowed. + (void)test::NormalConstructor(q); // Not allowlisted + ^ +bad_raw_ptr_cast_implicit_exclusion.cpp:41:33: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_implicit_exclusion.cpp:8:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +4 errors generated. diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_in_the_wild.cpp b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_in_the_wild.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3c1a9828c098acb738e35d46942f454955a4ec3f --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_in_the_wild.cpp @@ -0,0 +1,48 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/raw_ptr.h" + +// Real examples of bad casting, cited from here. +// https://docs.google.com/document/d/14Ol_adOdNpy4Ge-XReI7CXNKMzs_LL5vucDQIERDQyg/edit?usp=sharing + +// ============================== +// Example 1. "Initialization" +// ============================== +struct A { + raw_ptr ptr; +}; + +A* ExampleOne(void* buf) { + return reinterpret_cast(buf); // Should error. +} + +// ============================== +// Example 2. "Matching Struct" +// ============================== +struct ThirdPartyA { + int* ptr; +}; + +A* ExampleTwo(ThirdPartyA* obj) { + return reinterpret_cast(obj); // Should error. +} + +// ============================== +// Example 3. "Reinterpreting as void**" +// ============================== +int** ExampleThree(raw_ptr* ptr) { + return reinterpret_cast(ptr); // Should error. +} + +// ============================== +// Example 4. "Reinterpreting pointer to embedder class as void*" +// ============================== +void* my_memset(void* s, int c, int n); + +void ExampleFour() { + A obj; + A* obj_ptr = &obj; + my_memset(obj_ptr, 0, sizeof(obj_ptr)); // Should error. +} diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_in_the_wild.flags b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_in_the_wild.flags new file mode 100644 index 0000000000000000000000000000000000000000..c42a9209adcf1ec503d878cb79fdf487a55b6fdf --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_in_the_wild.flags @@ -0,0 +1 @@ +-ferror-limit=0 -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-bad-raw-ptr-cast \ No newline at end of file diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_in_the_wild.txt b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_in_the_wild.txt new file mode 100644 index 0000000000000000000000000000000000000000..a4c5abbeab2a1f236d840b29029802fbd8dccd72 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_in_the_wild.txt @@ -0,0 +1,26 @@ +bad_raw_ptr_cast_in_the_wild.cpp:18:34: error: [chromium-style] casting 'void *' to 'A * is not allowed. + return reinterpret_cast(buf); // Should error. + ^ +bad_raw_ptr_cast_in_the_wild.cpp:18:34: note: [chromium-style] 'A *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_in_the_wild.cpp:14:3: note: [chromium-style] 'A' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_in_the_wild.cpp:29:34: error: [chromium-style] casting 'ThirdPartyA *' to 'A * is not allowed. + return reinterpret_cast(obj); // Should error. + ^ +bad_raw_ptr_cast_in_the_wild.cpp:29:34: note: [chromium-style] 'A *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_in_the_wild.cpp:14:3: note: [chromium-style] 'A' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_in_the_wild.cpp:36:37: error: [chromium-style] casting 'raw_ptr *' to 'int ** is not allowed. + return reinterpret_cast(ptr); // Should error. + ^ +bad_raw_ptr_cast_in_the_wild.cpp:36:37: note: [chromium-style] 'raw_ptr *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_in_the_wild.cpp:47:13: error: [chromium-style] casting 'A *' to 'void * is not allowed. + my_memset(obj_ptr, 0, sizeof(obj_ptr)); // Should error. + ^ +bad_raw_ptr_cast_in_the_wild.cpp:47:13: note: [chromium-style] 'A *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_in_the_wild.cpp:14:3: note: [chromium-style] 'A' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +4 errors generated. diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_kinds.cpp b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_kinds.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1795dd902dbc48e22c78b2a4dc235e27813ba94 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_kinds.cpp @@ -0,0 +1,63 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/memory/raw_ptr.h" + +struct RawPtrWrapper { + raw_ptr ptr; +}; +struct RawPtrWrapperSub : RawPtrWrapper {}; + +void VariousCasting() { + raw_ptr ptr; + RawPtrWrapper wrapped; + RawPtrWrapper arr[10]; + void* void_ptr = nullptr; + + // CK_BitCast should emit an error. + (void)reinterpret_cast(void_ptr); + + // CK_LValueBitCast should emit an error. + RawPtrWrapper& ref = wrapped; + ref = reinterpret_cast(void_ptr); + + // CK_LValueToRValueBitCast should emit an error. + (void)__builtin_bit_cast(int*, ptr); + (void)__builtin_bit_cast(raw_ptr, wrapped); + (void)__builtin_bit_cast(int*, wrapped); + + // CK_PointerToIntegral should emit an error. + uintptr_t i = reinterpret_cast(&wrapped); + + // CK_IntegralToPointer should emit an error. + wrapped = *reinterpret_cast(i); + + // CK_ArrayToPointerDecay should be safe. + (void)static_cast(arr); + + // This line has two casts, CK_ArrayToPointerDecay and CK_BitCast. + // q = (void*) (RawPtrWrapper*) arr; + // The latter should emit an error. + void_ptr = arr; + + // CK_BaseToDerived should be safe. + RawPtrWrapperSub* sub = static_cast(&wrapped); + + // CK_DerivedToBase should be safe. + wrapped = *static_cast(sub); + + // CK_ToVoid should be safe. + (void)&wrapped; + + // Illegal casts: should be disallowed by the compiler. + (void)static_cast(ptr); + (void)static_cast>(wrapped); + (void)static_cast(wrapped); + + (void)reinterpret_cast(ptr); + (void)reinterpret_cast>(wrapped); + (void)reinterpret_cast(wrapped); +} diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_kinds.flags b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_kinds.flags new file mode 100644 index 0000000000000000000000000000000000000000..e4604256a97dcc108a429e382dcd7895821a9afa --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_kinds.flags @@ -0,0 +1 @@ +-ferror-limit=0 -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-bad-raw-ptr-cast diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_kinds.txt b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_kinds.txt new file mode 100644 index 0000000000000000000000000000000000000000..bd1495aeb4dccbd90e76c27c75135d28219641b9 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_kinds.txt @@ -0,0 +1,79 @@ +bad_raw_ptr_cast_kinds.cpp:56:9: error: cannot cast from type 'raw_ptr' to pointer type 'int *' + (void)static_cast(ptr); + ^~~~~~~~~~~~~~~~~~~~~~ +bad_raw_ptr_cast_kinds.cpp:57:9: error: no matching conversion for static_cast from 'RawPtrWrapper' to 'raw_ptr' + (void)static_cast>(wrapped); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +./base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h:11:7: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'RawPtrWrapper' to 'const raw_ptr' for 1st argument +class raw_ptr { + ^~~~~~~ +./base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h:11:7: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'RawPtrWrapper' to 'raw_ptr' for 1st argument +class raw_ptr { + ^~~~~~~ +./base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h:11:7: note: candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided +bad_raw_ptr_cast_kinds.cpp:58:9: error: cannot cast from type 'RawPtrWrapper' to pointer type 'int *' + (void)static_cast(wrapped); + ^~~~~~~~~~~~~~~~~~~~~~~~~~ +bad_raw_ptr_cast_kinds.cpp:60:9: error: reinterpret_cast from 'raw_ptr' to 'int *' is not allowed + (void)reinterpret_cast(ptr); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +bad_raw_ptr_cast_kinds.cpp:61:9: error: reinterpret_cast from 'RawPtrWrapper' to 'raw_ptr' is not allowed + (void)reinterpret_cast>(wrapped); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bad_raw_ptr_cast_kinds.cpp:62:9: error: reinterpret_cast from 'RawPtrWrapper' to 'int *' is not allowed + (void)reinterpret_cast(wrapped); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +bad_raw_ptr_cast_kinds.cpp:21:50: error: [chromium-style] casting 'void *' to 'RawPtrWrapper * is not allowed. + (void)reinterpret_cast(void_ptr); + ^ +bad_raw_ptr_cast_kinds.cpp:21:50: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_kinds.cpp:10:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_kinds.cpp:25:50: error: [chromium-style] casting 'void *' to 'RawPtrWrapper is not allowed. + ref = reinterpret_cast(void_ptr); + ^ +bad_raw_ptr_cast_kinds.cpp:25:50: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_kinds.cpp:10:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_kinds.cpp:28:37: error: [chromium-style] casting 'raw_ptr' to 'int * is not allowed. + (void)__builtin_bit_cast(int*, ptr); + ^ +bad_raw_ptr_cast_kinds.cpp:28:37: note: [chromium-style] 'raw_ptr' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_kinds.cpp:29:49: error: [chromium-style] casting 'RawPtrWrapper' to 'raw_ptr is not allowed. + (void)__builtin_bit_cast(raw_ptr, wrapped); + ^ +bad_raw_ptr_cast_kinds.cpp:29:49: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_kinds.cpp:10:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_kinds.cpp:30:41: error: [chromium-style] casting 'RawPtrWrapper' to 'int * is not allowed. + (void)__builtin_bit_cast(int*, wrapped); + ^ +bad_raw_ptr_cast_kinds.cpp:30:41: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_kinds.cpp:10:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_kinds.cpp:33:53: error: [chromium-style] casting 'RawPtrWrapper *' to 'uintptr_t is not allowed. + uintptr_t i = reinterpret_cast(&wrapped); + ^ +bad_raw_ptr_cast_kinds.cpp:33:53: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_kinds.cpp:10:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_kinds.cpp:36:48: error: [chromium-style] casting 'uintptr_t' to 'RawPtrWrapper * is not allowed. + wrapped = *reinterpret_cast(i); + ^ +bad_raw_ptr_cast_kinds.cpp:36:48: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_kinds.cpp:10:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_kinds.cpp:44:14: error: [chromium-style] casting 'RawPtrWrapper *' to 'void * is not allowed. + void_ptr = arr; + ^ +bad_raw_ptr_cast_kinds.cpp:44:14: note: [chromium-style] 'RawPtrWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_kinds.cpp:10:3: note: [chromium-style] 'RawPtrWrapper' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +14 errors generated. diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_recursive.cpp b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_recursive.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d2e333a470c032975c5eb310897c59d71f102851 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_recursive.cpp @@ -0,0 +1,40 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/raw_ptr.h" + +struct A; +struct B; +struct C; +struct D; + +// raw_ptr <-- A <-> B --> C +// ^ +// | +// D +// A, B and D are casting-unsafe. +struct A { + B* b; + raw_ptr ptr; +}; + +struct B { + A* a; + C* c; +}; + +struct C {}; + +struct D { + B* b; +}; + +void CastToCastingUnsafe() { + void* p = nullptr; + + (void)static_cast(p); // Error. + (void)static_cast(p); // Error. + (void)static_cast(p); // OK. + (void)static_cast(p); // Error. +} diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_recursive.flags b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_recursive.flags new file mode 100644 index 0000000000000000000000000000000000000000..e4604256a97dcc108a429e382dcd7895821a9afa --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_recursive.flags @@ -0,0 +1 @@ +-ferror-limit=0 -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-bad-raw-ptr-cast diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_recursive.txt b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_recursive.txt new file mode 100644 index 0000000000000000000000000000000000000000..d6efc16a1988214ecdf10f8381cfe61255603fa9 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ptr_cast_recursive.txt @@ -0,0 +1,31 @@ +bad_raw_ptr_cast_recursive.cpp:36:26: error: [chromium-style] casting 'void *' to 'A * is not allowed. + (void)static_cast(p); // Error. + ^ +bad_raw_ptr_cast_recursive.cpp:36:26: note: [chromium-style] 'A *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_recursive.cpp:19:3: note: [chromium-style] 'A' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_recursive.cpp:37:26: error: [chromium-style] casting 'void *' to 'B * is not allowed. + (void)static_cast(p); // Error. + ^ +bad_raw_ptr_cast_recursive.cpp:37:26: note: [chromium-style] 'B *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_recursive.cpp:23:3: note: [chromium-style] 'B' manages BackupRefPtr or its container here. + A* a; + ^ +bad_raw_ptr_cast_recursive.cpp:19:3: note: [chromium-style] 'A' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +bad_raw_ptr_cast_recursive.cpp:39:26: error: [chromium-style] casting 'void *' to 'D * is not allowed. + (void)static_cast(p); // Error. + ^ +bad_raw_ptr_cast_recursive.cpp:39:26: note: [chromium-style] 'D *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ptr_cast_recursive.cpp:30:3: note: [chromium-style] 'D' manages BackupRefPtr or its container here. + B* b; + ^ +bad_raw_ptr_cast_recursive.cpp:23:3: note: [chromium-style] 'B' manages BackupRefPtr or its container here. + A* a; + ^ +bad_raw_ptr_cast_recursive.cpp:19:3: note: [chromium-style] 'A' manages BackupRefPtr or its container here. + raw_ptr ptr; + ^ +3 errors generated. diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ref_cast.cpp b/clang/raw_ptr_plugin/tests/bad_raw_ref_cast.cpp new file mode 100644 index 0000000000000000000000000000000000000000..61228639f85b0081417a424061aaa0aa641ef71b --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ref_cast.cpp @@ -0,0 +1,117 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/raw_ptr.h" + +struct RawRefWrapper { + raw_ref ref; +}; + +struct RawRefWrapperWrapper { + RawRefWrapper* ref; +}; + +class RawRefWrapperWrapperWrapper { + public: + explicit RawRefWrapperWrapperWrapper(RawRefWrapperWrapper& ref) : ref_(ref) {} + + RawRefWrapperWrapper& ref_; +}; + +struct RawRefWrapperSub : RawRefWrapper {}; +struct RawRefWrapperVirtualSub : virtual RawRefWrapper {}; + +void MyFunc(void* p) {} + +void CastFromCastingUnsafe() { + // "Casting unsafe" type variables; + raw_ref p0; + RawRefWrapper p1; + RawRefWrapperWrapper p2; + RawRefWrapperWrapperWrapper p3(p2); + RawRefWrapper p4[10]; + RawRefWrapperSub p5; + RawRefWrapperVirtualSub p6; + + // CK_BitCast via |static_cast| should emit an error. + (void)static_cast(&p0); + (void)static_cast(&p1); + (void)static_cast(&p2); + (void)static_cast(&p3); + (void)static_cast(&p4); + (void)static_cast(&p5); + (void)static_cast(&p6); + + // CK_BitCast via C-style casting should emit an error. + (void)(void*)&p0; + (void)(void*)&p1; + (void)(void*)&p2; + (void)(void*)&p3; + (void)(void*)&p4; + (void)(void*)&p5; + (void)(void*)&p6; + + // CK_BitCast via |reinterpret_cast| should emit an error. + (void)reinterpret_cast(&p0); + (void)reinterpret_cast(&p1); + (void)reinterpret_cast(&p2); + (void)reinterpret_cast(&p3); + (void)reinterpret_cast(&p4); + (void)reinterpret_cast(&p5); + (void)reinterpret_cast(&p6); + + // CK_LValueToRValueBitCast via |bit_cast| should emit an error. + (void)__builtin_bit_cast(void*, &p0); + (void)__builtin_bit_cast(void*, &p1); + (void)__builtin_bit_cast(void*, &p2); + (void)__builtin_bit_cast(void*, &p3); + (void)__builtin_bit_cast(void*, &p4); + (void)__builtin_bit_cast(void*, &p5); + (void)__builtin_bit_cast(void*, &p6); + + // CK_BitCast via implicit casting should emit an error. + MyFunc(&p0); + MyFunc(&p1); + MyFunc(&p2); + MyFunc(&p3); + MyFunc(&p4); + MyFunc(&p5); + MyFunc(&p6); +} + +void CastToCastingUnsafe() { + void* p = nullptr; + + // CK_BitCast via |static_cast| should emit an error. + (void)static_cast*>(p); + (void)static_cast(p); + (void)static_cast(p); + (void)static_cast(p); + (void)static_cast(p); + (void)static_cast(p); + + // CK_BitCast via C-style casting should emit an error. + (void)(raw_ptr*)p; + (void)(RawRefWrapper*)p; + (void)(RawRefWrapperWrapper*)p; + (void)(RawRefWrapperWrapperWrapper*)p; + (void)(RawRefWrapperSub*)p; + (void)(RawRefWrapperVirtualSub*)p; + + // CK_BitCast via |reinterpret_cast| should emit an error. + (void)reinterpret_cast*>(p); + (void)reinterpret_cast(p); + (void)reinterpret_cast(p); + (void)reinterpret_cast(p); + (void)reinterpret_cast(p); + (void)reinterpret_cast(p); + + // CK_LValueToRValueBitCast via |bit_cast| should emit an error. + (void)__builtin_bit_cast(raw_ref*, p); + (void)__builtin_bit_cast(RawRefWrapper*, p); + (void)__builtin_bit_cast(RawRefWrapperWrapper*, p); + (void)__builtin_bit_cast(RawRefWrapperWrapperWrapper*, p); + (void)__builtin_bit_cast(RawRefWrapperSub*, p); + (void)__builtin_bit_cast(RawRefWrapperVirtualSub*, p); +} diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ref_cast.flags b/clang/raw_ptr_plugin/tests/bad_raw_ref_cast.flags new file mode 100644 index 0000000000000000000000000000000000000000..e4604256a97dcc108a429e382dcd7895821a9afa --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ref_cast.flags @@ -0,0 +1 @@ +-ferror-limit=0 -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-bad-raw-ptr-cast diff --git a/clang/raw_ptr_plugin/tests/bad_raw_ref_cast.txt b/clang/raw_ptr_plugin/tests/bad_raw_ref_cast.txt new file mode 100644 index 0000000000000000000000000000000000000000..1368a3391feed9aa6c84328047128e46f289c6d6 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/bad_raw_ref_cast.txt @@ -0,0 +1,522 @@ +bad_raw_ref_cast.cpp:38:31: error: [chromium-style] casting 'raw_ref *' to 'void * is not allowed. + (void)static_cast(&p0); + ^ +bad_raw_ref_cast.cpp:38:31: note: [chromium-style] 'raw_ref *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:39:31: error: [chromium-style] casting 'RawRefWrapper *' to 'void * is not allowed. + (void)static_cast(&p1); + ^ +bad_raw_ref_cast.cpp:39:31: note: [chromium-style] 'RawRefWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:40:31: error: [chromium-style] casting 'RawRefWrapperWrapper *' to 'void * is not allowed. + (void)static_cast(&p2); + ^ +bad_raw_ref_cast.cpp:40:31: note: [chromium-style] 'RawRefWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:41:31: error: [chromium-style] casting 'RawRefWrapperWrapperWrapper *' to 'void * is not allowed. + (void)static_cast(&p3); + ^ +bad_raw_ref_cast.cpp:41:31: note: [chromium-style] 'RawRefWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:19:3: note: [chromium-style] 'RawRefWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapperWrapper& ref_; + ^ +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:42:31: error: [chromium-style] casting 'RawRefWrapper (*)[10]' to 'void * is not allowed. + (void)static_cast(&p4); + ^ +bad_raw_ref_cast.cpp:42:31: note: [chromium-style] 'RawRefWrapper (*)[10]' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:43:31: error: [chromium-style] casting 'RawRefWrapperSub *' to 'void * is not allowed. + (void)static_cast(&p5); + ^ +bad_raw_ref_cast.cpp:43:31: note: [chromium-style] 'RawRefWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:22:27: note: [chromium-style] 'RawRefWrapperSub' manages BackupRefPtr or its container here. +struct RawRefWrapperSub : RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:44:31: error: [chromium-style] casting 'RawRefWrapperVirtualSub *' to 'void * is not allowed. + (void)static_cast(&p6); + ^ +bad_raw_ref_cast.cpp:44:31: note: [chromium-style] 'RawRefWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:23:34: note: [chromium-style] 'RawRefWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawRefWrapperVirtualSub : virtual RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:47:17: error: [chromium-style] casting 'raw_ref *' to 'void * is not allowed. + (void)(void*)&p0; + ^ +bad_raw_ref_cast.cpp:47:17: note: [chromium-style] 'raw_ref *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:48:17: error: [chromium-style] casting 'RawRefWrapper *' to 'void * is not allowed. + (void)(void*)&p1; + ^ +bad_raw_ref_cast.cpp:48:17: note: [chromium-style] 'RawRefWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:49:17: error: [chromium-style] casting 'RawRefWrapperWrapper *' to 'void * is not allowed. + (void)(void*)&p2; + ^ +bad_raw_ref_cast.cpp:49:17: note: [chromium-style] 'RawRefWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:50:17: error: [chromium-style] casting 'RawRefWrapperWrapperWrapper *' to 'void * is not allowed. + (void)(void*)&p3; + ^ +bad_raw_ref_cast.cpp:50:17: note: [chromium-style] 'RawRefWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:19:3: note: [chromium-style] 'RawRefWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapperWrapper& ref_; + ^ +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:51:17: error: [chromium-style] casting 'RawRefWrapper (*)[10]' to 'void * is not allowed. + (void)(void*)&p4; + ^ +bad_raw_ref_cast.cpp:51:17: note: [chromium-style] 'RawRefWrapper (*)[10]' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:52:17: error: [chromium-style] casting 'RawRefWrapperSub *' to 'void * is not allowed. + (void)(void*)&p5; + ^ +bad_raw_ref_cast.cpp:52:17: note: [chromium-style] 'RawRefWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:22:27: note: [chromium-style] 'RawRefWrapperSub' manages BackupRefPtr or its container here. +struct RawRefWrapperSub : RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:53:17: error: [chromium-style] casting 'RawRefWrapperVirtualSub *' to 'void * is not allowed. + (void)(void*)&p6; + ^ +bad_raw_ref_cast.cpp:53:17: note: [chromium-style] 'RawRefWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:23:34: note: [chromium-style] 'RawRefWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawRefWrapperVirtualSub : virtual RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:56:36: error: [chromium-style] casting 'raw_ref *' to 'void * is not allowed. + (void)reinterpret_cast(&p0); + ^ +bad_raw_ref_cast.cpp:56:36: note: [chromium-style] 'raw_ref *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:57:36: error: [chromium-style] casting 'RawRefWrapper *' to 'void * is not allowed. + (void)reinterpret_cast(&p1); + ^ +bad_raw_ref_cast.cpp:57:36: note: [chromium-style] 'RawRefWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:58:36: error: [chromium-style] casting 'RawRefWrapperWrapper *' to 'void * is not allowed. + (void)reinterpret_cast(&p2); + ^ +bad_raw_ref_cast.cpp:58:36: note: [chromium-style] 'RawRefWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:59:36: error: [chromium-style] casting 'RawRefWrapperWrapperWrapper *' to 'void * is not allowed. + (void)reinterpret_cast(&p3); + ^ +bad_raw_ref_cast.cpp:59:36: note: [chromium-style] 'RawRefWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:19:3: note: [chromium-style] 'RawRefWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapperWrapper& ref_; + ^ +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:60:36: error: [chromium-style] casting 'RawRefWrapper (*)[10]' to 'void * is not allowed. + (void)reinterpret_cast(&p4); + ^ +bad_raw_ref_cast.cpp:60:36: note: [chromium-style] 'RawRefWrapper (*)[10]' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:61:36: error: [chromium-style] casting 'RawRefWrapperSub *' to 'void * is not allowed. + (void)reinterpret_cast(&p5); + ^ +bad_raw_ref_cast.cpp:61:36: note: [chromium-style] 'RawRefWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:22:27: note: [chromium-style] 'RawRefWrapperSub' manages BackupRefPtr or its container here. +struct RawRefWrapperSub : RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:62:36: error: [chromium-style] casting 'RawRefWrapperVirtualSub *' to 'void * is not allowed. + (void)reinterpret_cast(&p6); + ^ +bad_raw_ref_cast.cpp:62:36: note: [chromium-style] 'RawRefWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:23:34: note: [chromium-style] 'RawRefWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawRefWrapperVirtualSub : virtual RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:65:38: error: [chromium-style] casting 'raw_ref *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p0); + ^ +bad_raw_ref_cast.cpp:65:38: note: [chromium-style] 'raw_ref *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:66:38: error: [chromium-style] casting 'RawRefWrapper *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p1); + ^ +bad_raw_ref_cast.cpp:66:38: note: [chromium-style] 'RawRefWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:67:38: error: [chromium-style] casting 'RawRefWrapperWrapper *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p2); + ^ +bad_raw_ref_cast.cpp:67:38: note: [chromium-style] 'RawRefWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:68:38: error: [chromium-style] casting 'RawRefWrapperWrapperWrapper *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p3); + ^ +bad_raw_ref_cast.cpp:68:38: note: [chromium-style] 'RawRefWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:19:3: note: [chromium-style] 'RawRefWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapperWrapper& ref_; + ^ +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:69:38: error: [chromium-style] casting 'RawRefWrapper (*)[10]' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p4); + ^ +bad_raw_ref_cast.cpp:69:38: note: [chromium-style] 'RawRefWrapper (*)[10]' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:70:38: error: [chromium-style] casting 'RawRefWrapperSub *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p5); + ^ +bad_raw_ref_cast.cpp:70:38: note: [chromium-style] 'RawRefWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:22:27: note: [chromium-style] 'RawRefWrapperSub' manages BackupRefPtr or its container here. +struct RawRefWrapperSub : RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:71:38: error: [chromium-style] casting 'RawRefWrapperVirtualSub *' to 'void * is not allowed. + (void)__builtin_bit_cast(void*, &p6); + ^ +bad_raw_ref_cast.cpp:71:38: note: [chromium-style] 'RawRefWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:23:34: note: [chromium-style] 'RawRefWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawRefWrapperVirtualSub : virtual RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:74:11: error: [chromium-style] casting 'raw_ref *' to 'void * is not allowed. + MyFunc(&p0); + ^ +bad_raw_ref_cast.cpp:74:11: note: [chromium-style] 'raw_ref *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:75:11: error: [chromium-style] casting 'RawRefWrapper *' to 'void * is not allowed. + MyFunc(&p1); + ^ +bad_raw_ref_cast.cpp:75:11: note: [chromium-style] 'RawRefWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:76:11: error: [chromium-style] casting 'RawRefWrapperWrapper *' to 'void * is not allowed. + MyFunc(&p2); + ^ +bad_raw_ref_cast.cpp:76:11: note: [chromium-style] 'RawRefWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:77:11: error: [chromium-style] casting 'RawRefWrapperWrapperWrapper *' to 'void * is not allowed. + MyFunc(&p3); + ^ +bad_raw_ref_cast.cpp:77:11: note: [chromium-style] 'RawRefWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:19:3: note: [chromium-style] 'RawRefWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapperWrapper& ref_; + ^ +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:78:11: error: [chromium-style] casting 'RawRefWrapper (*)[10]' to 'void * is not allowed. + MyFunc(&p4); + ^ +bad_raw_ref_cast.cpp:78:11: note: [chromium-style] 'RawRefWrapper (*)[10]' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:79:11: error: [chromium-style] casting 'RawRefWrapperSub *' to 'void * is not allowed. + MyFunc(&p5); + ^ +bad_raw_ref_cast.cpp:79:11: note: [chromium-style] 'RawRefWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:22:27: note: [chromium-style] 'RawRefWrapperSub' manages BackupRefPtr or its container here. +struct RawRefWrapperSub : RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:80:11: error: [chromium-style] casting 'RawRefWrapperVirtualSub *' to 'void * is not allowed. + MyFunc(&p6); + ^ +bad_raw_ref_cast.cpp:80:11: note: [chromium-style] 'RawRefWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:23:34: note: [chromium-style] 'RawRefWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawRefWrapperVirtualSub : virtual RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:87:37: error: [chromium-style] casting 'void *' to 'raw_ref * is not allowed. + (void)static_cast*>(p); + ^ +bad_raw_ref_cast.cpp:87:37: note: [chromium-style] 'raw_ref *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:88:38: error: [chromium-style] casting 'void *' to 'RawRefWrapper * is not allowed. + (void)static_cast(p); + ^ +bad_raw_ref_cast.cpp:88:38: note: [chromium-style] 'RawRefWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:89:45: error: [chromium-style] casting 'void *' to 'RawRefWrapperWrapper * is not allowed. + (void)static_cast(p); + ^ +bad_raw_ref_cast.cpp:89:45: note: [chromium-style] 'RawRefWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:90:52: error: [chromium-style] casting 'void *' to 'RawRefWrapperWrapperWrapper * is not allowed. + (void)static_cast(p); + ^ +bad_raw_ref_cast.cpp:90:52: note: [chromium-style] 'RawRefWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:19:3: note: [chromium-style] 'RawRefWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapperWrapper& ref_; + ^ +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:91:41: error: [chromium-style] casting 'void *' to 'RawRefWrapperSub * is not allowed. + (void)static_cast(p); + ^ +bad_raw_ref_cast.cpp:91:41: note: [chromium-style] 'RawRefWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:22:27: note: [chromium-style] 'RawRefWrapperSub' manages BackupRefPtr or its container here. +struct RawRefWrapperSub : RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:92:48: error: [chromium-style] casting 'void *' to 'RawRefWrapperVirtualSub * is not allowed. + (void)static_cast(p); + ^ +bad_raw_ref_cast.cpp:92:48: note: [chromium-style] 'RawRefWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:23:34: note: [chromium-style] 'RawRefWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawRefWrapperVirtualSub : virtual RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:95:24: error: [chromium-style] casting 'void *' to 'raw_ptr * is not allowed. + (void)(raw_ptr*)p; + ^ +bad_raw_ref_cast.cpp:95:24: note: [chromium-style] 'raw_ptr *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:96:25: error: [chromium-style] casting 'void *' to 'RawRefWrapper * is not allowed. + (void)(RawRefWrapper*)p; + ^ +bad_raw_ref_cast.cpp:96:25: note: [chromium-style] 'RawRefWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:97:32: error: [chromium-style] casting 'void *' to 'RawRefWrapperWrapper * is not allowed. + (void)(RawRefWrapperWrapper*)p; + ^ +bad_raw_ref_cast.cpp:97:32: note: [chromium-style] 'RawRefWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:98:39: error: [chromium-style] casting 'void *' to 'RawRefWrapperWrapperWrapper * is not allowed. + (void)(RawRefWrapperWrapperWrapper*)p; + ^ +bad_raw_ref_cast.cpp:98:39: note: [chromium-style] 'RawRefWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:19:3: note: [chromium-style] 'RawRefWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapperWrapper& ref_; + ^ +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:99:28: error: [chromium-style] casting 'void *' to 'RawRefWrapperSub * is not allowed. + (void)(RawRefWrapperSub*)p; + ^ +bad_raw_ref_cast.cpp:99:28: note: [chromium-style] 'RawRefWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:22:27: note: [chromium-style] 'RawRefWrapperSub' manages BackupRefPtr or its container here. +struct RawRefWrapperSub : RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:100:35: error: [chromium-style] casting 'void *' to 'RawRefWrapperVirtualSub * is not allowed. + (void)(RawRefWrapperVirtualSub*)p; + ^ +bad_raw_ref_cast.cpp:100:35: note: [chromium-style] 'RawRefWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:23:34: note: [chromium-style] 'RawRefWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawRefWrapperVirtualSub : virtual RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:103:42: error: [chromium-style] casting 'void *' to 'raw_ref * is not allowed. + (void)reinterpret_cast*>(p); + ^ +bad_raw_ref_cast.cpp:103:42: note: [chromium-style] 'raw_ref *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:104:43: error: [chromium-style] casting 'void *' to 'RawRefWrapper * is not allowed. + (void)reinterpret_cast(p); + ^ +bad_raw_ref_cast.cpp:104:43: note: [chromium-style] 'RawRefWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:105:50: error: [chromium-style] casting 'void *' to 'RawRefWrapperWrapper * is not allowed. + (void)reinterpret_cast(p); + ^ +bad_raw_ref_cast.cpp:105:50: note: [chromium-style] 'RawRefWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:106:57: error: [chromium-style] casting 'void *' to 'RawRefWrapperWrapperWrapper * is not allowed. + (void)reinterpret_cast(p); + ^ +bad_raw_ref_cast.cpp:106:57: note: [chromium-style] 'RawRefWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:19:3: note: [chromium-style] 'RawRefWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapperWrapper& ref_; + ^ +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:107:46: error: [chromium-style] casting 'void *' to 'RawRefWrapperSub * is not allowed. + (void)reinterpret_cast(p); + ^ +bad_raw_ref_cast.cpp:107:46: note: [chromium-style] 'RawRefWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:22:27: note: [chromium-style] 'RawRefWrapperSub' manages BackupRefPtr or its container here. +struct RawRefWrapperSub : RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:108:53: error: [chromium-style] casting 'void *' to 'RawRefWrapperVirtualSub * is not allowed. + (void)reinterpret_cast(p); + ^ +bad_raw_ref_cast.cpp:108:53: note: [chromium-style] 'RawRefWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:23:34: note: [chromium-style] 'RawRefWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawRefWrapperVirtualSub : virtual RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:111:44: error: [chromium-style] casting 'void *' to 'raw_ref * is not allowed. + (void)__builtin_bit_cast(raw_ref*, p); + ^ +bad_raw_ref_cast.cpp:111:44: note: [chromium-style] 'raw_ref *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:112:45: error: [chromium-style] casting 'void *' to 'RawRefWrapper * is not allowed. + (void)__builtin_bit_cast(RawRefWrapper*, p); + ^ +bad_raw_ref_cast.cpp:112:45: note: [chromium-style] 'RawRefWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:113:52: error: [chromium-style] casting 'void *' to 'RawRefWrapperWrapper * is not allowed. + (void)__builtin_bit_cast(RawRefWrapperWrapper*, p); + ^ +bad_raw_ref_cast.cpp:113:52: note: [chromium-style] 'RawRefWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:114:59: error: [chromium-style] casting 'void *' to 'RawRefWrapperWrapperWrapper * is not allowed. + (void)__builtin_bit_cast(RawRefWrapperWrapperWrapper*, p); + ^ +bad_raw_ref_cast.cpp:114:59: note: [chromium-style] 'RawRefWrapperWrapperWrapper *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:19:3: note: [chromium-style] 'RawRefWrapperWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapperWrapper& ref_; + ^ +bad_raw_ref_cast.cpp:12:3: note: [chromium-style] 'RawRefWrapperWrapper' manages BackupRefPtr or its container here. + RawRefWrapper* ref; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:115:48: error: [chromium-style] casting 'void *' to 'RawRefWrapperSub * is not allowed. + (void)__builtin_bit_cast(RawRefWrapperSub*, p); + ^ +bad_raw_ref_cast.cpp:115:48: note: [chromium-style] 'RawRefWrapperSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:22:27: note: [chromium-style] 'RawRefWrapperSub' manages BackupRefPtr or its container here. +struct RawRefWrapperSub : RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +bad_raw_ref_cast.cpp:116:55: error: [chromium-style] casting 'void *' to 'RawRefWrapperVirtualSub * is not allowed. + (void)__builtin_bit_cast(RawRefWrapperVirtualSub*, p); + ^ +bad_raw_ref_cast.cpp:116:55: note: [chromium-style] 'RawRefWrapperVirtualSub *' manages BackupRefPtr refcounts; bypassing its C++ interface or treating it as a POD will lead to memory safety errors. +bad_raw_ref_cast.cpp:23:34: note: [chromium-style] 'RawRefWrapperVirtualSub' manages BackupRefPtr or its container here. +struct RawRefWrapperVirtualSub : virtual RawRefWrapper {}; + ^ +bad_raw_ref_cast.cpp:8:3: note: [chromium-style] 'RawRefWrapper' manages BackupRefPtr or its container here. + raw_ref ref; + ^ +59 errors generated. diff --git a/clang/raw_ptr_plugin/tests/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h b/clang/raw_ptr_plugin/tests/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h new file mode 100644 index 0000000000000000000000000000000000000000..f0c2f4ba2f0cd3dbab53f102f867a0c0a5f1208b --- /dev/null +++ b/clang/raw_ptr_plugin/tests/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h @@ -0,0 +1,25 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_H_ + +namespace base { + +template +class raw_ptr { + T* ptr; +}; + +template +class raw_ref { + T* ref; +}; + +} // namespace base + +using base::raw_ptr; +using base::raw_ref; + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_H_ diff --git a/clang/raw_ptr_plugin/tests/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_cast.h b/clang/raw_ptr_plugin/tests/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_cast.h new file mode 100644 index 0000000000000000000000000000000000000000..d304d0d53e9b5fe2c3da2a0a6a713efae4e9d74c --- /dev/null +++ b/clang/raw_ptr_plugin/tests/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_cast.h @@ -0,0 +1,27 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_CAST_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_CAST_H_ + +namespace base { + +template +inline constexpr Dest unsafe_raw_ptr_static_cast(Source&& source) { + return static_cast(source); +} + +template +inline constexpr Dest unsafe_raw_ptr_reinterpret_cast(Source&& source) { + return reinterpret_cast(source); +} + +template +inline constexpr Dest unsafe_raw_ptr_bit_cast(Source source) { + return __builtin_bit_cast(Dest, source); +} + +} // namespace base + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_CAST_H_ diff --git a/clang/raw_ptr_plugin/tests/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_exclusion.h b/clang/raw_ptr_plugin/tests/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_exclusion.h new file mode 100644 index 0000000000000000000000000000000000000000..ef29bd33dc9171796b9d4a14bf0efc3a40261f75 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_exclusion.h @@ -0,0 +1,12 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_EXCLUSION_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_EXCLUSION_H_ + +// Marks a field as excluded from the raw_ptr usage enforcement clang plugin. +// Example: RAW_PTR_EXCLUSION Foo* foo_; +#define RAW_PTR_EXCLUSION __attribute__((annotate("raw_ptr_exclusion"))) + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_EXCLUSION_H_ diff --git a/clang/raw_ptr_plugin/tests/base/containers/span.h b/clang/raw_ptr_plugin/tests/base/containers/span.h new file mode 100644 index 0000000000000000000000000000000000000000..f9c5697590c62c0f0d5148f91b521052ad4bf6e3 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/base/containers/span.h @@ -0,0 +1,22 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_CONTAINERS_SPAN_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_CONTAINERS_SPAN_H_ + +#include "base/memory/raw_ptr.h" + +namespace base { + +template +class span { + public: + InternalPtrType data_; +}; + +template +using raw_span = span>; +} // namespace base + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_CONTAINERS_SPAN_H_ diff --git a/clang/raw_ptr_plugin/tests/base/memory/checked_ptr.h b/clang/raw_ptr_plugin/tests/base/memory/checked_ptr.h new file mode 100644 index 0000000000000000000000000000000000000000..6dba972e6e35433bd79417507fb6d51a73e0fc59 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/base/memory/checked_ptr.h @@ -0,0 +1,17 @@ +// Copyright 2020 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_CHECKED_PTR_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_CHECKED_PTR_H_ + +namespace base { + +template +class CheckedPtr {}; + +} // namespace base + +using base::CheckedPtr; + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_CHECKED_PTR_H_ diff --git a/clang/raw_ptr_plugin/tests/base/memory/raw_ptr.h b/clang/raw_ptr_plugin/tests/base/memory/raw_ptr.h new file mode 100644 index 0000000000000000000000000000000000000000..dd7aef9bb9a6664ffbdcf54c7fd47ee40a448ae4 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/base/memory/raw_ptr.h @@ -0,0 +1,13 @@ +// Copyright 2021 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_RAW_PTR_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_RAW_PTR_H_ + +// Although `raw_ptr` is part of the standalone PA distribution, it is +// easier to use the shorter path in `//base/memory`. We retain this +// facade header for ease of typing. +#include "base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h" // IWYU pragma: export + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_RAW_PTR_H_ diff --git a/clang/raw_ptr_plugin/tests/base/memory/raw_ptr_cast.h b/clang/raw_ptr_plugin/tests/base/memory/raw_ptr_cast.h new file mode 100644 index 0000000000000000000000000000000000000000..bd6b9e0742c9d24b98f88aeb00e9f565132e9091 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/base/memory/raw_ptr_cast.h @@ -0,0 +1,13 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_RAW_PTR_CAST_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_RAW_PTR_CAST_H_ + +// Although `raw_ptr` is part of the standalone PA distribution, it is +// easier to use the shorter path in `//base/memory`. We retain this +// facade header for ease of typing. +#include "base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_cast.h" // IWYU pragma: export + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_RAW_PTR_CAST_H_ diff --git a/clang/raw_ptr_plugin/tests/base/memory/raw_ptr_exclusion.h b/clang/raw_ptr_plugin/tests/base/memory/raw_ptr_exclusion.h new file mode 100644 index 0000000000000000000000000000000000000000..4709629d0784dce70ebe8fea8a3974c07306f368 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/base/memory/raw_ptr_exclusion.h @@ -0,0 +1,13 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_RAW_PTR_EXCLUSION_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_RAW_PTR_EXCLUSION_H_ + +// Although `raw_ptr` is part of the standalone PA distribution, it is +// easier to use the shorter path in `//base/memory`. We retain this +// facade header for ease of typing. +#include "base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_exclusion.h" // IWYU pragma: export + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_BASE_MEMORY_RAW_PTR_EXCLUSION_H_ diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.cpp b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.cpp new file mode 100644 index 0000000000000000000000000000000000000000..52c5821f4debfba55380c62b2576e88ec2ad9234 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.cpp @@ -0,0 +1,19 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +class SomeClass {}; + +class MyClass { + public: + // Error expected. A raw_ptr should be used instead of a raw pointer. + SomeClass* raw_ptr_field; + // No error expected because of exclude-fields file, + // raw_ptr_exclude_fields.exclude. + SomeClass* excluded_ptr_field; + // Error expected. A raw_ref should be used instead of a native reference. + SomeClass& raw_ref_field; + // No error expected because of exclude-fields file, + // raw_ref_exclude_fields.exclude. + SomeClass& excluded_ref_field; +}; diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.exclude b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.exclude new file mode 100644 index 0000000000000000000000000000000000000000..42a1830545906c71e34c46aa82ccc7bd3f51a47f --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.exclude @@ -0,0 +1,2 @@ +MyClass::excluded_ptr_field +MyClass::excluded_ref_field diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.flags b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.flags new file mode 100644 index 0000000000000000000000000000000000000000..d44a94a0520e9a35fd373b97bb3ff619251160d2 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ref-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang exclude-fields=raw_ptr_exclude_fields.exclude diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.txt b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.txt new file mode 100644 index 0000000000000000000000000000000000000000..9c6601ce447ad51400bf333926bcea183eea8003 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_fields.txt @@ -0,0 +1,7 @@ +raw_ptr_exclude_fields.cpp:10:14: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SomeClass* raw_ptr_field; + ^ +raw_ptr_exclude_fields.cpp:15:14: error: [chromium-rawref] Use raw_ref instead of a native reference. + SomeClass& raw_ref_field; + ^ +2 errors generated. diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclude_paths.cpp b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_paths.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc8cc44a4524650f41ce6f0be2d099473b55cab4 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_paths.cpp @@ -0,0 +1,14 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +class SomeClass {}; + +class MyClass { + public: + // No error expected because of raw-ptr-exclude-path arg. + SomeClass* raw_ptr_field1; + // No error expected because of exclude-paths file, + // raw_ptr_exclude_paths.exclude. + SomeClass& raw_ref_field; +}; diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclude_paths.flags b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_paths.flags new file mode 100644 index 0000000000000000000000000000000000000000..e62c15ec9edb95c732595afec79854c56ea684bc --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_paths.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ref-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang raw-ptr-exclude-path=foo -Xclang -plugin-arg-raw-ptr-plugin -Xclang raw-ptr-exclude-path=raw_ptr_exclude_paths diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclude_paths.txt b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_paths.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclude_stack_allocated.cpp b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_stack_allocated.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e65474991d7a214e52a68fbb1b07a51488907e2 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_stack_allocated.cpp @@ -0,0 +1,28 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +struct StackAllocatedType { + using IsStackAllocatedTypeMarker [[maybe_unused]] = int; +}; +struct StackAllocatedSubType : public StackAllocatedType {}; +struct NonStackAllocatedType {}; + +struct MyStruct { + StackAllocatedType* excluded_a; + StackAllocatedSubType* excluded_b; + std::vector* excluded_c; + std::vector* excluded_d; + + StackAllocatedType& excluded_ref_a; + StackAllocatedSubType& excluded_ref_b; + std::vector& excluded_ref_c; + std::vector& excluded_ref_d; + + NonStackAllocatedType* err_a; + std::vector* err_b; + NonStackAllocatedType& err_ref_a; + std::vector& err_ref_b; +}; diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclude_stack_allocated.flags b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_stack_allocated.flags new file mode 100644 index 0000000000000000000000000000000000000000..974099b9553996968ae7c760c7bf31e2893ebe0c --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_stack_allocated.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ref-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-to-stack-allocated diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclude_stack_allocated.txt b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_stack_allocated.txt new file mode 100644 index 0000000000000000000000000000000000000000..ec008764f70edc25608f40e41d82724bda69ac83 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclude_stack_allocated.txt @@ -0,0 +1,13 @@ +raw_ptr_exclude_stack_allocated.cpp:24:26: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + NonStackAllocatedType* err_a; + ^ +raw_ptr_exclude_stack_allocated.cpp:25:39: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + std::vector* err_b; + ^ +raw_ptr_exclude_stack_allocated.cpp:26:26: error: [chromium-rawref] Use raw_ref instead of a native reference. + NonStackAllocatedType& err_ref_a; + ^ +raw_ptr_exclude_stack_allocated.cpp:27:39: error: [chromium-rawref] Use raw_ref instead of a native reference. + std::vector& err_ref_b; + ^ +4 errors generated. diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclusion.cpp b/clang/raw_ptr_plugin/tests/raw_ptr_exclusion.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7c5f90bc3399ccf480cadeeb0cbd6b4d6b45baf2 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclusion.cpp @@ -0,0 +1,32 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "base/memory/raw_ptr_exclusion.h" + +#include "base/containers/span.h" + +class SomeClass {}; + +class MyClass { + public: + // Error expected. + SomeClass* raw_ptr_field1; + // No error expected. Fields can be excluded due to performance reasons. + RAW_PTR_EXCLUSION SomeClass* ignored_ptr_field1; + // Error expected. + SomeClass* raw_ptr_field2; + // No error expected. Fields can be excluded due to performance reasons. + RAW_PTR_EXCLUSION SomeClass* ignored_ptr_field2; + // Error expected. + SomeClass& raw_ref_field1; + // No error expected. Fields can be excluded due to performance reasons. + RAW_PTR_EXCLUSION SomeClass& ignored_ref_field1; + // Error expected. + SomeClass& raw_ref_field2; + // No error expected. Fields can be excluded due to performance reasons. + RAW_PTR_EXCLUSION SomeClass& ignored_ref_field2; + // Error expected. + base::span span_field; + // No error expected. Fields can be excluded due to performance reasons. + RAW_PTR_EXCLUSION base::span ignored_span_field; +}; diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclusion.flags b/clang/raw_ptr_plugin/tests/raw_ptr_exclusion.flags new file mode 100644 index 0000000000000000000000000000000000000000..b3750d2053a00e17714c7641dbafd1e8886e41e0 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclusion.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ref-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-span-fields diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_exclusion.txt b/clang/raw_ptr_plugin/tests/raw_ptr_exclusion.txt new file mode 100644 index 0000000000000000000000000000000000000000..8ea81ca8ab1f36b3a17bf2fc726c9cb9e22db911 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_exclusion.txt @@ -0,0 +1,16 @@ +raw_ptr_exclusion.cpp:13:14: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SomeClass* raw_ptr_field1; + ^ +raw_ptr_exclusion.cpp:17:14: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SomeClass* raw_ptr_field2; + ^ +raw_ptr_exclusion.cpp:21:14: error: [chromium-rawref] Use raw_ref instead of a native reference. + SomeClass& raw_ref_field1; + ^ +raw_ptr_exclusion.cpp:25:14: error: [chromium-rawref] Use raw_ref instead of a native reference. + SomeClass& raw_ref_field2; + ^ +raw_ptr_exclusion.cpp:29:25: error: [chromium-rawptr] Use raw_span instead of a span. + base::span span_field; + ^ +5 errors generated. diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_basic.cpp b/clang/raw_ptr_plugin/tests/raw_ptr_fields_basic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5fd3ee161b6ffaa2813dd2202cf57b73aea315b8 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_fields_basic.cpp @@ -0,0 +1,78 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +class SomeClass {}; + +class MyClass { + public: + // Error expected. + SomeClass* raw_ptr_field; + + // No error expected. + int int_field; + + // Error expected. + SomeClass& raw_ref_field; +}; + +struct MyStruct { + // Error expected. + SomeClass* raw_ptr_field; + + // No error expected. + int int_field; + + // "*" next to the field name. This is non-standard formatting, so + // "clang-format off" is used to make sure |git cl format| won't change this + // testcase. + // + // Error expected. + // clang-format off + SomeClass *raw_ptr_field2; + // clang-format on + + // Error expected. + SomeClass& raw_ref_field; + + // No error expected. + int int_field2; + + // "&" next to the field name. This is non-standard formatting, so + // "clang-format off" is used to make sure |git cl format| won't change this + // testcase. + // + // Error expected. + // clang-format off + SomeClass &raw_ref_field2; + // clang-format on +}; + +template +class MyTemplate { + public: + // Error expected. + T* raw_ptr_field; + + // No error expected. + int int_field; + + // Error expected. + T& raw_ref_field; +}; + +// The field below won't compile without the |typename| keyword (because +// at this point we don't know if MaybeProvidesType::Type is a type, +// value or something else). +template +struct MaybeProvidesType; +template +struct DependentNameTest { + // Error expected. Even though MaybeProvidesType::Type is an unknown type, + // MaybeProvidesType::Type* field; + + // Error expected. Even though MaybeProvidesType::Type is an unknown type, + // MaybeProvidesType::Type& ref_field; +}; diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_basic.flags b/clang/raw_ptr_plugin/tests/raw_ptr_fields_basic.flags new file mode 100644 index 0000000000000000000000000000000000000000..bbcf26ed859c4db011ddbfa5564e3812f909bbde --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_fields_basic.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ref-fields diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_basic.txt b/clang/raw_ptr_plugin/tests/raw_ptr_fields_basic.txt new file mode 100644 index 0000000000000000000000000000000000000000..daa42104f7893e9aac4d22147e861833f346f444 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_fields_basic.txt @@ -0,0 +1,31 @@ +raw_ptr_fields_basic.cpp:10:14: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SomeClass* raw_ptr_field; + ^ +raw_ptr_fields_basic.cpp:16:14: error: [chromium-rawref] Use raw_ref instead of a native reference. + SomeClass& raw_ref_field; + ^ +raw_ptr_fields_basic.cpp:21:14: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SomeClass* raw_ptr_field; + ^ +raw_ptr_fields_basic.cpp:32:14: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SomeClass *raw_ptr_field2; + ^ +raw_ptr_fields_basic.cpp:36:14: error: [chromium-rawref] Use raw_ref instead of a native reference. + SomeClass& raw_ref_field; + ^ +raw_ptr_fields_basic.cpp:47:14: error: [chromium-rawref] Use raw_ref instead of a native reference. + SomeClass &raw_ref_field2; + ^ +raw_ptr_fields_basic.cpp:55:6: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + T* raw_ptr_field; + ^ +raw_ptr_fields_basic.cpp:61:6: error: [chromium-rawref] Use raw_ref instead of a native reference. + T& raw_ref_field; + ^ +raw_ptr_fields_basic.cpp:73:40: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + typename MaybeProvidesType::Type* field; + ^ +raw_ptr_fields_basic.cpp:77:40: error: [chromium-rawref] Use raw_ref instead of a native reference. + typename MaybeProvidesType::Type& ref_field; + ^ +10 errors generated. diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_macro.cpp b/clang/raw_ptr_plugin/tests/raw_ptr_fields_macro.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aeadc9994bce69620f48a9bb3b0b0be4c332e2ed --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_fields_macro.cpp @@ -0,0 +1,465 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Testing normal case +#line 6 "raw_ptr_fields_macro.cpp" +#define USR_INT int +#define USR_INTP int* +#define USR_CONST const +#define USR_ATTR [[fake_attribute]] +#define USR_INTP_FIELD() int* macro_ptr +#define USR_TYPE_WITH_SUFFIX(TYP) TYP##Suffix +#define USR_SYMBOL(SYM) SYM +#define USR_SYMBOL_WITH_SUFFIX(SYM) SYM##_suffix +#define USR_EQ = +#define USR_NULLPTR nullptr + +// Testing isInThirdPartyLocation() +#line 19 "/src/tools/clang/plugins/tests/third_party/fake_location.cpp" +#define TP_INT int +#define TP_INTP int* +#define TP_CONST const +#define TP_ATTR [[fake_attribute]] +#define TP_INTP_FIELD() int* macro_ptr +#define TP_TYPE_WITH_SUFFIX(TYP) TYP##Suffix +#define TP_SYMBOL(SYM) SYM +#define TP_SYMBOL_WITH_SUFFIX(SYM) SYM##_suffix +#define TP_EQ = +#define TP_NULLPTR nullptr + +// Testing isInLocationListedInFilterFile() +#line 32 "/src/tools/clang/plugins/tests/internal/fake_location.cpp" +#define IG_INT int +#define IG_INTP int* +#define IG_CONST const +#define IG_ATTR [[fake_attribute]] +#define IG_INTP_FIELD() int* macro_ptr +#define IG_TYPE_WITH_SUFFIX(TYP) TYP##Suffix +#define IG_SYMBOL(SYM) SYM +#define IG_SYMBOL_WITH_SUFFIX(SYM) SYM##_suffix +#define IG_EQ = +#define IG_NULLPTR nullptr + +// Testing isInGeneratedLocation() +#line 45 "/src/tools/clang/plugins/tests/gen/fake_location.cpp" +#define GEN_INT int +#define GEN_INTP int* +#define GEN_CONST const +#define GEN_ATTR [[fake_attribute]] +#define GEN_INTP_FIELD() int* macro_ptr +#define GEN_TYPE_WITH_SUFFIX(TYP) TYP##Suffix +#define GEN_SYMBOL(SYM) SYM +#define GEN_SYMBOL_WITH_SUFFIX(SYM) SYM##_suffix +#define GEN_EQ = +#define GEN_NULLPTR nullptr + +// Testing isSpellingInSystemHeader() +#include + +#line 60 "raw_ptr_fields_macro.cpp" +class UsrTypSuffix; + +// These `SYS_***` macro should be defined +// in `//tools/clang/plugins/tests/system/raw_ptr_system_test.h`. +struct UsrStructWithSysMacro { + // Error. + int* ptr0; + // Error: typeLoc is macro but identifier is written here. + SYS_INT* ptr1; + // Error: typeLoc is macro but identifier is written here. + SYS_INTP ptr2; + // Error: typeLoc is macro but identifier is written here. + int* SYS_CONST ptr3; + // Error: attribute is macro but identifier is written here. + int* SYS_ATTR ptr4; + // OK: code owner has no control over fieldDecl. + SYS_INTP_FIELD(); + // Error: typeLoc is macro but identifier is written here. + SYS_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; + // Error: identifier is defined with macro but it is written here. + int* SYS_SYMBOL(ptr6); + // OK: the source location for this field declaration will be "" and the real file path cannot be detected. + int* SYS_SYMBOL_WITH_SUFFIX(ptr7); + // Error: field is initialized with macro but identifier is written here. + int* ptr8 SYS_EQ nullptr; + // Error: field is initialized with macro but identifier is written here. + int* ptr9 = SYS_NULLPTR; + // OK: the source location for this field declaration will be "" and the real file path cannot be detected. + int* SYS_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; +}; + +// These `CMD_***` macro should be defined in command line arguments. +// Same as for UsrStructWithSysMacro. +struct UsrStructWithCmdMacro { + int* ptr0; // Error. + CMD_INT* ptr1; // Error. + CMD_INTP ptr2; // Error. + int* CMD_CONST ptr3; // Error. + int* CMD_ATTR ptr4; // Error. + CMD_INTP_FIELD(); // OK. + CMD_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; // Error. + int* CMD_SYMBOL(ptr6); // Error. + int* CMD_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 CMD_EQ nullptr; // Error. + int* ptr9 = CMD_NULLPTR; // Error. + int* CMD_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct UsrStructWithUsrMacro { + // Error. + int* ptr0; + // Error: typeLoc is macro but identifier is written here. + USR_INT* ptr1; + // Error: typeLoc is macro but identifier is written here. + USR_INTP ptr2; + // Error: typeLoc is macro but identifier is written here. + int* USR_CONST ptr3; + // Error: attribute is macro but identifier is written here. + int* USR_ATTR ptr4; + // Error: user has control over the macro. + USR_INTP_FIELD(); + // Error: user has control over the macro. + USR_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; + // Error: identifier is defined with macro but it is written here. + int* USR_SYMBOL(ptr6); + // OK: the source location for this field declaration will be "" and the real file path cannot be detected. + int* USR_SYMBOL_WITH_SUFFIX(ptr7); + // Error: field is initialized with macro but identifier is written here. + int* ptr8 USR_EQ nullptr; + // Error: field is initialized with macro but identifier is written here. + int* ptr9 = USR_NULLPTR; + // OK: the source location for this field declaration will be "" and the real file path cannot be detected. + int* USR_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; +}; + +// Same as for UsrStructWithSysMacro. +struct UsrStructWithThirdPartyMacro { + int* ptr0; // Error. + TP_INT* ptr1; // Error. + TP_INTP ptr2; // Error. + int* TP_CONST ptr3; // Error. + int* TP_ATTR ptr4; // Error. + TP_INTP_FIELD(); // OK. + TP_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; // Error. + int* TP_SYMBOL(ptr6); // Error. + int* TP_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 TP_EQ nullptr; // Error. + int* ptr9 = TP_NULLPTR; // Error. + int* TP_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +// Same as for UsrStructWithSysMacro. +struct UsrStructWithManuallyIgnoredMacro { + int* ptr0; // Error. + IG_INT* ptr1; // Error. + IG_INTP ptr2; // Error. + int* IG_CONST ptr3; // Error. + int* IG_ATTR ptr4; // Error. + IG_INTP_FIELD(); // OK. + IG_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; // Error. + int* IG_SYMBOL(ptr6); // Error. + int* IG_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 IG_EQ nullptr; // Error. + int* ptr9 = IG_NULLPTR; // Error. + int* IG_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +// Same as for UsrStructWithSysMacro. +struct UsrStructWithGeneratedMacro { + int* ptr0; // Error. + GEN_INT* ptr1; // Error. + GEN_INTP ptr2; // Error. + int* GEN_CONST ptr3; // Error. + int* GEN_ATTR ptr4; // Error. + GEN_INTP_FIELD(); // OK. + GEN_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; // Error. + int* GEN_SYMBOL(ptr6); // Error. + int* GEN_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 GEN_EQ nullptr; // Error. + int* ptr9 = GEN_NULLPTR; // Error. + int* GEN_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +#line 188 "/src/tools/clang/plugins/tests/third_party/fake_location.cpp" +class ThirdPartyTypSuffix; + +struct ThirdPartyStructWithSysMacro { + int* ptr0; // OK. + SYS_INT* ptr1; // OK. + SYS_INTP ptr2; // OK. + int* SYS_CONST ptr3; // OK. + int* SYS_ATTR ptr4; // OK. + SYS_INTP_FIELD(); // OK. + SYS_TYPE_WITH_SUFFIX(ThirdPartyTyp) * ptr5; // OK. + int* SYS_SYMBOL(ptr6); // OK. + int* SYS_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 SYS_EQ nullptr; // OK. + int* ptr9 = SYS_NULLPTR; // OK. + int* SYS_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct ThirdPartyStructWithCmdMacro { + int* ptr0; // OK. + CMD_INT* ptr1; // OK. + CMD_INTP ptr2; // OK. + int* CMD_CONST ptr3; // OK. + int* CMD_ATTR ptr4; // OK. + CMD_INTP_FIELD(); // OK. + CMD_TYPE_WITH_SUFFIX(ThirdPartyTyp) * ptr5; // OK. + int* CMD_SYMBOL(ptr6); // OK. + int* CMD_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 CMD_EQ nullptr; // OK. + int* ptr9 = CMD_NULLPTR; // OK. + int* CMD_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct ThirdPartyStructWithUsrMacro { + int* ptr0; // OK. + USR_INT* ptr1; // OK. + USR_INTP ptr2; // OK. + int* USR_CONST ptr3; // OK. + int* USR_ATTR ptr4; // OK. + USR_INTP_FIELD(); // Error. + USR_TYPE_WITH_SUFFIX(ThirdPartyTyp) * ptr5; // OK. + int* USR_SYMBOL(ptr6); // OK. + int* USR_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 USR_EQ nullptr; // OK. + int* ptr9 = USR_NULLPTR; // OK. + int* USR_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct ThirdPartyStructWithThirdPartyMacro { + int* ptr0; // OK. + TP_INT* ptr1; // OK. + TP_INTP ptr2; // OK. + int* TP_CONST ptr3; // OK. + int* TP_ATTR ptr4; // OK. + TP_INTP_FIELD(); // OK. + TP_TYPE_WITH_SUFFIX(ThirdPartyTyp) * ptr5; // OK. + int* TP_SYMBOL(ptr6); // OK. + int* TP_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 TP_EQ nullptr; // OK. + int* ptr9 = TP_NULLPTR; // OK. + int* TP_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct ThirdPartyStructWithManuallyIgnoredMacro { + int* ptr0; // OK. + IG_INT* ptr1; // OK. + IG_INTP ptr2; // OK. + int* IG_CONST ptr3; // OK. + int* IG_ATTR ptr4; // OK. + IG_INTP_FIELD(); // OK. + IG_TYPE_WITH_SUFFIX(ThirdPartyTyp) * ptr5; // OK. + int* IG_SYMBOL(ptr6); // OK. + int* IG_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 IG_EQ nullptr; // OK. + int* ptr9 = IG_NULLPTR; // OK. + int* IG_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct ThirdPartyStructWithGeneratedMacro { + int* ptr0; // OK. + GEN_INT* ptr1; // OK. + GEN_INTP ptr2; // OK. + int* GEN_CONST ptr3; // OK. + int* GEN_ATTR ptr4; // OK. + GEN_INTP_FIELD(); // OK. + GEN_TYPE_WITH_SUFFIX(ThirdPartyTyp) * ptr5; // OK. + int* GEN_SYMBOL(ptr6); // OK. + int* GEN_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 GEN_EQ nullptr; // OK. + int* ptr9 = GEN_NULLPTR; // OK. + int* GEN_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +#line 281 "/src/tools/clang/plugins/tests/internal/fake_location.cpp" +class ManuallyIgnoredTypSuffix; + +struct ManuallyIgnoredStructWithSysMacro { + int* ptr0; // OK. + SYS_INT* ptr1; // OK. + SYS_INTP ptr2; // OK. + int* SYS_CONST ptr3; // OK. + int* SYS_ATTR ptr4; // OK. + SYS_INTP_FIELD(); // OK. + SYS_TYPE_WITH_SUFFIX(ManuallyIgnoredTyp) * ptr5; // OK. + int* SYS_SYMBOL(ptr6); // OK. + int* SYS_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 SYS_EQ nullptr; // OK. + int* ptr9 = SYS_NULLPTR; // OK. + int* SYS_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct ManuallyIgnoredStructWithCmdMacro { + int* ptr0; // OK. + CMD_INT* ptr1; // OK. + CMD_INTP ptr2; // OK. + int* CMD_CONST ptr3; // OK. + int* CMD_ATTR ptr4; // OK. + CMD_INTP_FIELD(); // OK. + CMD_TYPE_WITH_SUFFIX(ManuallyIgnoredTyp) * ptr5; // OK. + int* CMD_SYMBOL(ptr6); // OK. + int* CMD_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 CMD_EQ nullptr; // OK. + int* ptr9 = CMD_NULLPTR; // OK. + int* CMD_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct ManuallyIgnoredStructWithUsrMacro { + int* ptr0; // OK. + USR_INT* ptr1; // OK. + USR_INTP ptr2; // OK. + int* USR_CONST ptr3; // OK. + int* USR_ATTR ptr4; // OK. + USR_INTP_FIELD(); // Error. + USR_TYPE_WITH_SUFFIX(ManuallyIgnoredTyp) * ptr5; // OK. + int* USR_SYMBOL(ptr6); // OK. + int* USR_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 USR_EQ nullptr; // OK. + int* ptr9 = USR_NULLPTR; // OK. + int* USR_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct ManuallyIgnoredStructWithThirdPartyMacro { + int* ptr0; // OK. + TP_INT* ptr1; // OK. + TP_INTP ptr2; // OK. + int* TP_CONST ptr3; // OK. + int* TP_ATTR ptr4; // OK. + TP_INTP_FIELD(); // OK. + TP_TYPE_WITH_SUFFIX(ManuallyIgnoredTyp) * ptr5; // OK. + int* TP_SYMBOL(ptr6); // OK. + int* TP_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 TP_EQ nullptr; // OK. + int* ptr9 = TP_NULLPTR; // OK. + int* TP_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct ManuallyIgnoredStructWithManuallyIgnoredMacro { + int* ptr0; // OK. + IG_INT* ptr1; // OK. + IG_INTP ptr2; // OK. + int* IG_CONST ptr3; // OK. + int* IG_ATTR ptr4; // OK. + IG_INTP_FIELD(); // OK. + IG_TYPE_WITH_SUFFIX(ManuallyIgnoredTyp) * ptr5; // OK. + int* IG_SYMBOL(ptr6); // OK. + int* IG_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 IG_EQ nullptr; // OK. + int* ptr9 = IG_NULLPTR; // OK. + int* IG_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct ManuallyIgnoredStructWithGeneratedMacro { + int* ptr0; // OK. + GEN_INT* ptr1; // OK. + GEN_INTP ptr2; // OK. + int* GEN_CONST ptr3; // OK. + int* GEN_ATTR ptr4; // OK. + GEN_INTP_FIELD(); // OK. + GEN_TYPE_WITH_SUFFIX(ManuallyIgnoredTyp) * ptr5; // OK. + int* GEN_SYMBOL(ptr6); // OK. + int* GEN_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 GEN_EQ nullptr; // OK. + int* ptr9 = GEN_NULLPTR; // OK. + int* GEN_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +#line 374 "/src/tools/clang/plugins/tests/gen/fake_location.cpp" +class GeneratedTypSuffix; + +struct GeneratedStructWithSysMacro { + int* ptr0; // OK. + SYS_INT* ptr1; // OK. + SYS_INTP ptr2; // OK. + int* SYS_CONST ptr3; // OK. + int* SYS_ATTR ptr4; // OK. + SYS_INTP_FIELD(); // OK. + SYS_TYPE_WITH_SUFFIX(GeneratedTyp) * ptr5; // OK. + int* SYS_SYMBOL(ptr6); // OK. + int* SYS_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 SYS_EQ nullptr; // OK. + int* ptr9 = SYS_NULLPTR; // OK. + int* SYS_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct GeneratedStructWithCmdMacro { + int* ptr0; // OK. + CMD_INT* ptr1; // OK. + CMD_INTP ptr2; // OK. + int* CMD_CONST ptr3; // OK. + int* CMD_ATTR ptr4; // OK. + CMD_INTP_FIELD(); // OK. + CMD_TYPE_WITH_SUFFIX(GeneratedTyp) * ptr5; // OK. + int* CMD_SYMBOL(ptr6); // OK. + int* CMD_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 CMD_EQ nullptr; // OK. + int* ptr9 = CMD_NULLPTR; // OK. + int* CMD_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct GeneratedStructWithUsrMacro { + int* ptr0; // OK. + USR_INT* ptr1; // OK. + USR_INTP ptr2; // OK. + int* USR_CONST ptr3; // OK. + int* USR_ATTR ptr4; // OK. + USR_INTP_FIELD(); // Error. + USR_TYPE_WITH_SUFFIX(GeneratedTyp) * ptr5; // OK. + int* USR_SYMBOL(ptr6); // OK. + int* USR_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 USR_EQ nullptr; // OK. + int* ptr9 = USR_NULLPTR; // OK. + int* USR_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct GeneratedStructWithThirdPartyMacro { + int* ptr0; // OK. + TP_INT* ptr1; // OK. + TP_INTP ptr2; // OK. + int* TP_CONST ptr3; // OK. + int* TP_ATTR ptr4; // OK. + TP_INTP_FIELD(); // OK. + TP_TYPE_WITH_SUFFIX(GeneratedTyp) * ptr5; // OK. + int* TP_SYMBOL(ptr6); // OK. + int* TP_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 TP_EQ nullptr; // OK. + int* ptr9 = TP_NULLPTR; // OK. + int* TP_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct GeneratedStructWithManuallyIgnoredMacro { + int* ptr0; // OK. + IG_INT* ptr1; // OK. + IG_INTP ptr2; // OK. + int* IG_CONST ptr3; // OK. + int* IG_ATTR ptr4; // OK. + IG_INTP_FIELD(); // OK. + IG_TYPE_WITH_SUFFIX(GeneratedTyp) * ptr5; // OK. + int* IG_SYMBOL(ptr6); // OK. + int* IG_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 IG_EQ nullptr; // OK. + int* ptr9 = IG_NULLPTR; // OK. + int* IG_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +struct GeneratedStructWithGeneratedMacro { + int* ptr0; // OK. + GEN_INT* ptr1; // OK. + GEN_INTP ptr2; // OK. + int* GEN_CONST ptr3; // OK. + int* GEN_ATTR ptr4; // OK. + GEN_INTP_FIELD(); // OK. + GEN_TYPE_WITH_SUFFIX(GeneratedTyp) * ptr5; // OK. + int* GEN_SYMBOL(ptr6); // OK. + int* GEN_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 GEN_EQ nullptr; // OK. + int* ptr9 = GEN_NULLPTR; // OK. + int* GEN_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_macro.flags b/clang/raw_ptr_plugin/tests/raw_ptr_fields_macro.flags new file mode 100644 index 0000000000000000000000000000000000000000..a45cfeb026e0ea0e8c35a3978c4f4f8bce05c344 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_fields_macro.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-fields -Wno-unknown-attributes -ferror-limit=0 -DCMD_INT=int -DCMD_INTP=int* -DCMD_CONST=const -DCMD_ATTR=[[fake_attribute]] -DCMD_INTP_FIELD()=int*macro_ptr -DCMD_TYPE_WITH_SUFFIX(TYP)=TYP##Suffix -DCMD_SYMBOL(SYM)=SYM -DCMD_SYMBOL_WITH_SUFFIX(SYM)=SYM##_suffix -DCMD_EQ== -DCMD_NULLPTR=nullptr diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_macro.txt b/clang/raw_ptr_plugin/tests/raw_ptr_fields_macro.txt new file mode 100644 index 0000000000000000000000000000000000000000..dfed75591810075cd284ec7faa086703078ae8e8 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_fields_macro.txt @@ -0,0 +1,194 @@ +In file included from /src/tools/clang/plugins/tests/gen/fake_location.cpp:57: +/src/tools/clang/plugins/tests/system/raw_ptr_system_test.h:66:3: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + USR_INTP_FIELD(); + ^ +raw_ptr_fields_macro.cpp:10:31: note: expanded from macro 'USR_INTP_FIELD' +#define USR_INTP_FIELD() int* macro_ptr + ^ +raw_ptr_fields_macro.cpp:66:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr0; + ^ +raw_ptr_fields_macro.cpp:68:12: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SYS_INT* ptr1; + ^ +raw_ptr_fields_macro.cpp:70:12: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SYS_INTP ptr2; + ^ +raw_ptr_fields_macro.cpp:72:18: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* SYS_CONST ptr3; + ^ +raw_ptr_fields_macro.cpp:74:17: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* SYS_ATTR ptr4; + ^ +raw_ptr_fields_macro.cpp:78:34: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SYS_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; + ^ +raw_ptr_fields_macro.cpp:80:19: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* SYS_SYMBOL(ptr6); + ^ +raw_ptr_fields_macro.cpp:85:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr8 SYS_EQ nullptr; + ^ +raw_ptr_fields_macro.cpp:87:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr9 = SYS_NULLPTR; + ^ +raw_ptr_fields_macro.cpp:96:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr0; // Error. + ^ +raw_ptr_fields_macro.cpp:97:12: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + CMD_INT* ptr1; // Error. + ^ +raw_ptr_fields_macro.cpp:98:12: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + CMD_INTP ptr2; // Error. + ^ +raw_ptr_fields_macro.cpp:99:18: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* CMD_CONST ptr3; // Error. + ^ +raw_ptr_fields_macro.cpp:100:17: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* CMD_ATTR ptr4; // Error. + ^ +raw_ptr_fields_macro.cpp:102:34: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + CMD_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; // Error. + ^ +raw_ptr_fields_macro.cpp:103:19: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* CMD_SYMBOL(ptr6); // Error. + ^ +raw_ptr_fields_macro.cpp:105:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr8 CMD_EQ nullptr; // Error. + ^ +raw_ptr_fields_macro.cpp:106:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr9 = CMD_NULLPTR; // Error. + ^ +raw_ptr_fields_macro.cpp:112:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr0; + ^ +raw_ptr_fields_macro.cpp:114:12: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + USR_INT* ptr1; + ^ +raw_ptr_fields_macro.cpp:116:12: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + USR_INTP ptr2; + ^ +raw_ptr_fields_macro.cpp:118:18: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* USR_CONST ptr3; + ^ +raw_ptr_fields_macro.cpp:120:17: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* USR_ATTR ptr4; + ^ +raw_ptr_fields_macro.cpp:122:3: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + USR_INTP_FIELD(); + ^ +raw_ptr_fields_macro.cpp:10:31: note: expanded from macro 'USR_INTP_FIELD' +#define USR_INTP_FIELD() int* macro_ptr + ^ +raw_ptr_fields_macro.cpp:124:34: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + USR_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; + ^ +raw_ptr_fields_macro.cpp:126:19: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* USR_SYMBOL(ptr6); + ^ +raw_ptr_fields_macro.cpp:131:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr8 USR_EQ nullptr; + ^ +raw_ptr_fields_macro.cpp:133:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr9 = USR_NULLPTR; + ^ +raw_ptr_fields_macro.cpp:141:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr0; // Error. + ^ +raw_ptr_fields_macro.cpp:142:11: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + TP_INT* ptr1; // Error. + ^ +raw_ptr_fields_macro.cpp:143:11: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + TP_INTP ptr2; // Error. + ^ +raw_ptr_fields_macro.cpp:144:17: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* TP_CONST ptr3; // Error. + ^ +raw_ptr_fields_macro.cpp:145:16: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* TP_ATTR ptr4; // Error. + ^ +raw_ptr_fields_macro.cpp:147:33: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + TP_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; // Error. + ^ +raw_ptr_fields_macro.cpp:148:18: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* TP_SYMBOL(ptr6); // Error. + ^ +raw_ptr_fields_macro.cpp:150:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr8 TP_EQ nullptr; // Error. + ^ +raw_ptr_fields_macro.cpp:151:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr9 = TP_NULLPTR; // Error. + ^ +raw_ptr_fields_macro.cpp:157:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr0; // Error. + ^ +raw_ptr_fields_macro.cpp:158:11: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + IG_INT* ptr1; // Error. + ^ +raw_ptr_fields_macro.cpp:159:11: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + IG_INTP ptr2; // Error. + ^ +raw_ptr_fields_macro.cpp:160:17: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* IG_CONST ptr3; // Error. + ^ +raw_ptr_fields_macro.cpp:161:16: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* IG_ATTR ptr4; // Error. + ^ +raw_ptr_fields_macro.cpp:163:33: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + IG_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; // Error. + ^ +raw_ptr_fields_macro.cpp:164:18: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* IG_SYMBOL(ptr6); // Error. + ^ +raw_ptr_fields_macro.cpp:166:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr8 IG_EQ nullptr; // Error. + ^ +raw_ptr_fields_macro.cpp:167:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr9 = IG_NULLPTR; // Error. + ^ +raw_ptr_fields_macro.cpp:173:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr0; // Error. + ^ +raw_ptr_fields_macro.cpp:174:12: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + GEN_INT* ptr1; // Error. + ^ +raw_ptr_fields_macro.cpp:175:12: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + GEN_INTP ptr2; // Error. + ^ +raw_ptr_fields_macro.cpp:176:18: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* GEN_CONST ptr3; // Error. + ^ +raw_ptr_fields_macro.cpp:177:17: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* GEN_ATTR ptr4; // Error. + ^ +raw_ptr_fields_macro.cpp:179:34: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + GEN_TYPE_WITH_SUFFIX(UsrTyp) * ptr5; // Error. + ^ +raw_ptr_fields_macro.cpp:180:19: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* GEN_SYMBOL(ptr6); // Error. + ^ +raw_ptr_fields_macro.cpp:182:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr8 GEN_EQ nullptr; // Error. + ^ +raw_ptr_fields_macro.cpp:183:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* ptr9 = GEN_NULLPTR; // Error. + ^ +/src/tools/clang/plugins/tests/third_party/fake_location.cpp:226:3: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + USR_INTP_FIELD(); // Error. + ^ +raw_ptr_fields_macro.cpp:10:31: note: expanded from macro 'USR_INTP_FIELD' +#define USR_INTP_FIELD() int* macro_ptr + ^ +/src/tools/clang/plugins/tests/internal/fake_location.cpp:319:3: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + USR_INTP_FIELD(); // Error. + ^ +raw_ptr_fields_macro.cpp:10:31: note: expanded from macro 'USR_INTP_FIELD' +#define USR_INTP_FIELD() int* macro_ptr + ^ +/src/tools/clang/plugins/tests/gen/fake_location.cpp:412:3: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + USR_INTP_FIELD(); // Error. + ^ +raw_ptr_fields_macro.cpp:10:31: note: expanded from macro 'USR_INTP_FIELD' +#define USR_INTP_FIELD() int* macro_ptr + ^ +59 errors generated. diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_objc.flags b/clang/raw_ptr_plugin/tests/raw_ptr_fields_objc.flags new file mode 100644 index 0000000000000000000000000000000000000000..d146d1576c9899060c682a5d755ee2e9b50ca161 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_fields_objc.flags @@ -0,0 +1 @@ +--target=arm64-apple-macosx -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-fields -Wobjc-root-class diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_objc.mm b/clang/raw_ptr_plugin/tests/raw_ptr_fields_objc.mm new file mode 100644 index 0000000000000000000000000000000000000000..87e95a9b7a6c8f8e9135f3b86d41162e812104c7 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_fields_objc.mm @@ -0,0 +1,14 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +__attribute__((objc_root_class)) +@interface MyInterface {} +@property(readonly) int* p; +@end + +@implementation MyInterface +// @synthesize will automatically declares `int* _p`. +// The plugin should explicitly exclude a synthesized field from the check. +@synthesize p; +@end diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_objc.txt b/clang/raw_ptr_plugin/tests/raw_ptr_fields_objc.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_various_types.cpp b/clang/raw_ptr_plugin/tests/raw_ptr_fields_various_types.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b66127c0458024a5c9d615b9f58bc5341a174447 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_fields_various_types.cpp @@ -0,0 +1,152 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include + +namespace my_namespace { + +class SomeClass { + public: + void Method(char) {} + // No error expected. + int data_member; +}; + +template +struct SomeTemplate { + // No error expected. + T t; +}; + +struct MyStruct { + // Error expected. + SomeClass** double_ptr; + + // Error expected. + void* void_ptr; + + // Error expected. + bool* bool_ptr; + const bool* const_bool_ptr; + + // Pointers to templates. + // Error expected. + std::string* string_ptr; + std::vector* vector_ptr; + SomeTemplate* template_ptr; + + // + // Various integer types. + // + // Error expected. + int* int_spelling1; + signed int* int_spelling2; + long int* int_spelling3; + unsigned* int_spelling4; + + // + // Various structs and classes. + // + // Error expected. + SomeClass* class_spelling1; + class SomeClass* class_spelling2; + my_namespace::SomeClass* class_spelling3; + + // Function pointers. + // No error expected. Because they won't ever be allocated by PartitionAlloc. + int (*func_ptr)(); + void (SomeClass::*member_func_ptr)(char); // ~ pointer to SomeClass::Method + int SomeClass::*member_data_ptr; // ~ pointer to SomeClass::data_member + typedef void (*func_ptr_typedef)(char); + func_ptr_typedef func_ptr_typedef_field; + + // Typedef-ed or type-aliased pointees. + typedef SomeClass SomeClassTypedef; + using SomeClassAlias = SomeClass; + typedef void (*func_ptr_typedef2)(char); + // Error expected. + SomeClassTypedef* typedef_ptr; + // Error expected. + SomeClassAlias* alias_ptr; + // Error expected. + func_ptr_typedef2* ptr_to_function_ptr; + // Error expected. + SomeClassTypedef& typedef_ref; + // Error expected. + SomeClassAlias& alias_ref; + // Error expected. + func_ptr_typedef2& ref_to_function_ptr; + + // Char pointer fields. + // + // Error expected. + char* char_ptr; + // No error expected. crbug.com/1381955 + const char* const_char_ptr; + // Error expected. + wchar_t* wide_char_ptr; + // No error expected. crbug.com/1381955 + const wchar_t* const_wide_char_ptr; + // Error expected. + char8_t* char8_ptr; + // No error expected. crbug.com/1381955 + const char8_t* const_char8_ptr; + // Error expected. + char16_t* char16_ptr; + // No error expected. crbug.com/1381955 + const char16_t* const_char16_ptr; + char32_t* char32_ptr; + // No error expected. crbug.com/1381955 + const char32_t* const_char32_ptr; + + // Error expected. + const uint8_t* const_unsigned_char_ptr; + // Error expected. + const int8_t* const_signed_char_ptr; + // Error expected. + const unsigned char* const_unsigned_char_ptr2; + // Error expected. + const signed char* const_signed_char_ptr2; + + // |array_of_ptrs| is an array 123 of pointer to SomeClass. + // No error expected. (this is not a pointer - this is an array). + SomeClass* ptr_array[123]; + + // |ptr_to_array| is a pointer to array 123 of const SomeClass. + // + // This test is based on EqualsFramesMatcher from + // //net/websockets/websocket_channel_test.cc + // + // No error expected. Because this rewrite was tricky and not supported by the + // rewriter. + // crbug.com/1381969 + const SomeClass (*ptr_to_array)[123]; + + // References to templates. + // Error expected. + std::string& string_ref; + std::vector& vector_ref; + SomeTemplate& template_ref; + + // + // Various integer types. + // + // Error expected. + int& int_ref_spelling1; + signed int& int_ref_spelling2; + long int& int_ref_spelling3; + unsigned& int_ref_spelling4; + + // + // Various structs and classes. + // + // Error expected. + SomeClass& class_ref_spelling1; + class SomeClass& class_ref_spelling2; + my_namespace::SomeClass& class_ref_spelling3; +}; + +} // namespace my_namespace diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_various_types.flags b/clang/raw_ptr_plugin/tests/raw_ptr_fields_various_types.flags new file mode 100644 index 0000000000000000000000000000000000000000..66f625557f8783b45a65d4e798dec96f4362c5b1 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_fields_various_types.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ref-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang check-ptrs-to-non-string-literals -ferror-limit=50 diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_fields_various_types.txt b/clang/raw_ptr_plugin/tests/raw_ptr_fields_various_types.txt new file mode 100644 index 0000000000000000000000000000000000000000..2e784ec92c43235d45b441c3f34cbc13270265e9 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_fields_various_types.txt @@ -0,0 +1,118 @@ +raw_ptr_fields_various_types.cpp:26:15: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SomeClass** double_ptr; + ^ +raw_ptr_fields_various_types.cpp:29:9: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + void* void_ptr; + ^ +raw_ptr_fields_various_types.cpp:32:9: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + bool* bool_ptr; + ^ +raw_ptr_fields_various_types.cpp:33:15: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + const bool* const_bool_ptr; + ^ +raw_ptr_fields_various_types.cpp:37:16: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + std::string* string_ptr; + ^ +raw_ptr_fields_various_types.cpp:38:22: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + std::vector* vector_ptr; + ^ +raw_ptr_fields_various_types.cpp:39:23: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SomeTemplate* template_ptr; + ^ +raw_ptr_fields_various_types.cpp:45:8: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + int* int_spelling1; + ^ +raw_ptr_fields_various_types.cpp:46:15: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + signed int* int_spelling2; + ^ +raw_ptr_fields_various_types.cpp:47:13: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + long int* int_spelling3; + ^ +raw_ptr_fields_various_types.cpp:48:13: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + unsigned* int_spelling4; + ^ +raw_ptr_fields_various_types.cpp:54:14: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SomeClass* class_spelling1; + ^ +raw_ptr_fields_various_types.cpp:55:20: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + class SomeClass* class_spelling2; + ^ +raw_ptr_fields_various_types.cpp:56:28: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + my_namespace::SomeClass* class_spelling3; + ^ +raw_ptr_fields_various_types.cpp:71:21: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SomeClassTypedef* typedef_ptr; + ^ +raw_ptr_fields_various_types.cpp:73:19: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + SomeClassAlias* alias_ptr; + ^ +raw_ptr_fields_various_types.cpp:75:22: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + func_ptr_typedef2* ptr_to_function_ptr; + ^ +raw_ptr_fields_various_types.cpp:77:21: error: [chromium-rawref] Use raw_ref instead of a native reference. + SomeClassTypedef& typedef_ref; + ^ +raw_ptr_fields_various_types.cpp:79:19: error: [chromium-rawref] Use raw_ref instead of a native reference. + SomeClassAlias& alias_ref; + ^ +raw_ptr_fields_various_types.cpp:81:22: error: [chromium-rawref] Use raw_ref instead of a native reference. + func_ptr_typedef2& ref_to_function_ptr; + ^ +raw_ptr_fields_various_types.cpp:86:9: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + char* char_ptr; + ^ +raw_ptr_fields_various_types.cpp:90:12: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + wchar_t* wide_char_ptr; + ^ +raw_ptr_fields_various_types.cpp:94:12: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + char8_t* char8_ptr; + ^ +raw_ptr_fields_various_types.cpp:98:13: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + char16_t* char16_ptr; + ^ +raw_ptr_fields_various_types.cpp:101:13: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + char32_t* char32_ptr; + ^ +raw_ptr_fields_various_types.cpp:106:18: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + const uint8_t* const_unsigned_char_ptr; + ^ +raw_ptr_fields_various_types.cpp:108:17: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + const int8_t* const_signed_char_ptr; + ^ +raw_ptr_fields_various_types.cpp:110:24: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + const unsigned char* const_unsigned_char_ptr2; + ^ +raw_ptr_fields_various_types.cpp:112:22: error: [chromium-rawptr] Use raw_ptr instead of a raw pointer. + const signed char* const_signed_char_ptr2; + ^ +raw_ptr_fields_various_types.cpp:130:16: error: [chromium-rawref] Use raw_ref instead of a native reference. + std::string& string_ref; + ^ +raw_ptr_fields_various_types.cpp:131:22: error: [chromium-rawref] Use raw_ref instead of a native reference. + std::vector& vector_ref; + ^ +raw_ptr_fields_various_types.cpp:132:23: error: [chromium-rawref] Use raw_ref instead of a native reference. + SomeTemplate& template_ref; + ^ +raw_ptr_fields_various_types.cpp:138:8: error: [chromium-rawref] Use raw_ref instead of a native reference. + int& int_ref_spelling1; + ^ +raw_ptr_fields_various_types.cpp:139:15: error: [chromium-rawref] Use raw_ref instead of a native reference. + signed int& int_ref_spelling2; + ^ +raw_ptr_fields_various_types.cpp:140:13: error: [chromium-rawref] Use raw_ref instead of a native reference. + long int& int_ref_spelling3; + ^ +raw_ptr_fields_various_types.cpp:141:13: error: [chromium-rawref] Use raw_ref instead of a native reference. + unsigned& int_ref_spelling4; + ^ +raw_ptr_fields_various_types.cpp:147:14: error: [chromium-rawref] Use raw_ref instead of a native reference. + SomeClass& class_ref_spelling1; + ^ +raw_ptr_fields_various_types.cpp:148:20: error: [chromium-rawref] Use raw_ref instead of a native reference. + class SomeClass& class_ref_spelling2; + ^ +raw_ptr_fields_various_types.cpp:149:28: error: [chromium-rawref] Use raw_ref instead of a native reference. + my_namespace::SomeClass& class_ref_spelling3; + ^ +39 errors generated. diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_in_stack_allocated.cpp b/clang/raw_ptr_plugin/tests/raw_ptr_in_stack_allocated.cpp new file mode 100644 index 0000000000000000000000000000000000000000..04bf9fef8b3883398551668574dc5c710ffe1d73 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_in_stack_allocated.cpp @@ -0,0 +1,14 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// fields should be checked +struct FooStruct { + using IsStackAllocatedTypeMarker [[maybe_unused]] = int; + + int* ptr1; // OK: inside STACK_ALLOCATED(); +}; + +struct BarStruct : FooStruct { + int* ptr2; // OK: inside inherited STACK_ALLOCATED(); +}; diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_in_stack_allocated.flags b/clang/raw_ptr_plugin/tests/raw_ptr_in_stack_allocated.flags new file mode 100644 index 0000000000000000000000000000000000000000..a044e58b5b89ac9a576ef164eb1590eef29b4584 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_in_stack_allocated.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-to-stack-allocated diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_in_stack_allocated.txt b/clang/raw_ptr_plugin/tests/raw_ptr_in_stack_allocated.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated.cpp b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated.cpp new file mode 100644 index 0000000000000000000000000000000000000000..729ac907996097f86980bec234e674758d544fc7 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated.cpp @@ -0,0 +1,61 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/memory/raw_ptr.h" + +struct StackAllocatedType { + using IsStackAllocatedTypeMarker [[maybe_unused]] = int; +}; +struct StackAllocatedSubType : public StackAllocatedType {}; +struct NonStackAllocatedType {}; + +// typedefs should be checked +typedef raw_ptr ErrTypeA; +typedef raw_ptr ErrTypeB; +typedef raw_ptr OkTypeC; +typedef raw_ptr> ErrTypeD; +typedef raw_ptr> ErrTypeE; +typedef raw_ptr> OkTypeF; +typedef std::vector> ErrTypeG; +typedef std::vector> ErrTypeH; +typedef std::vector> OkTypeI; + +// fields should be checked +struct MyStruct { + raw_ptr err_a; + raw_ptr err_b; + raw_ptr ok_c; + raw_ptr> err_d; + raw_ptr> err_e; + raw_ptr> ok_f; + std::vector> err_g; + std::vector> err_h; + std::vector> ok_i; +}; + +// variables should be checked +void MyFunc() { + raw_ptr err_a; + raw_ptr err_b; + raw_ptr ok_c; + raw_ptr> err_d; + raw_ptr> err_e; + raw_ptr> ok_f; + std::vector> err_g; + std::vector> err_h; + std::vector> ok_i; + + raw_ref err_raw_ref; + raw_ref ok_raw_ref; +} + +// TODO(mikt@google.com): Currently, this rule cannot detect bad usage wrapped +// with a template type parameter. +template +struct RawPtrWrapper { + raw_ptr p; +}; +typedef RawPtrWrapper UndesiredButNotDetectedType; diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated.flags b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated.flags new file mode 100644 index 0000000000000000000000000000000000000000..a044e58b5b89ac9a576ef164eb1590eef29b4584 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-to-stack-allocated diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated.txt b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated.txt new file mode 100644 index 0000000000000000000000000000000000000000..0b7ca1136eb72c29707c12e0815884dbaefae720 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated.txt @@ -0,0 +1,76 @@ +raw_ptr_to_stack_allocated.cpp:16:35: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedType'. +typedef raw_ptr ErrTypeA; + ^ +raw_ptr_to_stack_allocated.cpp:17:38: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedSubType'. +typedef raw_ptr ErrTypeB; + ^ +raw_ptr_to_stack_allocated.cpp:19:48: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'std::vector'. +typedef raw_ptr> ErrTypeD; + ^ +raw_ptr_to_stack_allocated.cpp:20:51: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'std::vector'. +typedef raw_ptr> ErrTypeE; + ^ +raw_ptr_to_stack_allocated.cpp:22:47: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedType'. +typedef std::vector> ErrTypeG; + ^ +:7:1: note: expanded from here +> +^ +raw_ptr_to_stack_allocated.cpp:23:50: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedSubType'. +typedef std::vector> ErrTypeH; + ^ +:8:1: note: expanded from here +> +^ +raw_ptr_to_stack_allocated.cpp:28:29: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedType'. + raw_ptr err_a; + ^ +raw_ptr_to_stack_allocated.cpp:29:32: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedSubType'. + raw_ptr err_b; + ^ +raw_ptr_to_stack_allocated.cpp:31:42: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'std::vector'. + raw_ptr> err_d; + ^ +raw_ptr_to_stack_allocated.cpp:32:45: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'std::vector'. + raw_ptr> err_e; + ^ +raw_ptr_to_stack_allocated.cpp:34:41: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedType'. + std::vector> err_g; + ^ +:13:1: note: expanded from here +> +^ +raw_ptr_to_stack_allocated.cpp:35:44: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedSubType'. + std::vector> err_h; + ^ +:14:1: note: expanded from here +> +^ +raw_ptr_to_stack_allocated.cpp:41:29: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedType'. + raw_ptr err_a; + ^ +raw_ptr_to_stack_allocated.cpp:42:32: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedSubType'. + raw_ptr err_b; + ^ +raw_ptr_to_stack_allocated.cpp:44:42: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'std::vector'. + raw_ptr> err_d; + ^ +raw_ptr_to_stack_allocated.cpp:45:45: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'std::vector'. + raw_ptr> err_e; + ^ +raw_ptr_to_stack_allocated.cpp:47:41: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedType'. + std::vector> err_g; + ^ +:19:1: note: expanded from here +> +^ +raw_ptr_to_stack_allocated.cpp:48:44: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ptr' on a `STACK_ALLOCATED` object 'StackAllocatedSubType'. + std::vector> err_h; + ^ +:20:1: note: expanded from here +> +^ +raw_ptr_to_stack_allocated.cpp:51:29: error: [chromium-raw-ptr-to-stack-allocated] Do not use 'raw_ref' on a `STACK_ALLOCATED` object 'StackAllocatedType'. + raw_ref err_raw_ref; + ^ +19 errors generated. diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_disabled.cpp b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_disabled.cpp new file mode 100644 index 0000000000000000000000000000000000000000..01a24c0e8112ba9bb593e659eec2197232227218 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_disabled.cpp @@ -0,0 +1,53 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/memory/raw_ptr.h" + +struct StackAllocatedType { + using IsStackAllocatedTypeMarker [[maybe_unused]] = int; +}; +struct StackAllocatedSubType : public StackAllocatedType {}; +struct NonStackAllocatedType {}; + +// typedefs should be checked +typedef raw_ptr ErrTypeA; +typedef raw_ptr ErrTypeB; +typedef raw_ptr OkTypeC; +typedef raw_ptr> ErrTypeD; +typedef raw_ptr> ErrTypeE; +typedef raw_ptr> OkTypeF; +typedef std::vector> ErrTypeG; +typedef std::vector> ErrTypeH; +typedef std::vector> OkTypeI; + +// fields should be checked +struct MyStruct { + raw_ptr err_a; + raw_ptr err_b; + raw_ptr ok_c; + raw_ptr> err_d; + raw_ptr> err_e; + raw_ptr> ok_f; + std::vector> err_g; + std::vector> err_h; + std::vector> ok_i; +}; + +// variables should be checked +void MyFunc() { + raw_ptr err_a; + raw_ptr err_b; + raw_ptr ok_c; + raw_ptr> err_d; + raw_ptr> err_e; + raw_ptr> ok_f; + std::vector> err_g; + std::vector> err_h; + std::vector> ok_i; + + raw_ref err_raw_ref; + raw_ref ok_raw_ref; +} diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_disabled.flags b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_disabled.flags new file mode 100644 index 0000000000000000000000000000000000000000..d6e8e22b4c3c42da1e18e00cc513aa58a8863d7b --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_disabled.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-to-stack-allocated -Xclang -plugin-arg-raw-ptr-plugin -Xclang disable-check-raw-ptr-to-stack-allocated-error diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_disabled.txt b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_disabled.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_exclusion.cpp b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_exclusion.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e6df5d91f987cbfe453eb2666f928d3b5229b82b --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_exclusion.cpp @@ -0,0 +1,5 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/raw_ptr_to_stack_allocated_third_party_test.h" diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_exclusion.flags b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_exclusion.flags new file mode 100644 index 0000000000000000000000000000000000000000..a044e58b5b89ac9a576ef164eb1590eef29b4584 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_exclusion.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-raw-ptr-to-stack-allocated diff --git a/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_exclusion.txt b/clang/raw_ptr_plugin/tests/raw_ptr_to_stack_allocated_exclusion.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/raw_ptr_plugin/tests/span_exclude_fields.cpp b/clang/raw_ptr_plugin/tests/span_exclude_fields.cpp new file mode 100644 index 0000000000000000000000000000000000000000..15481aea502b6cba2788ee914d0cdf332146cc33 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/span_exclude_fields.cpp @@ -0,0 +1,15 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "base/containers/span.h" + +class SomeClass {}; + +class MyClass { + public: + // Error expected. A raw_span should be used instead of a span. + base::span span_field; + // No error expected because of exclude-fields file, + // raw_ptr_exclude_fields.exclude. + base::span excluded_span_field; +}; diff --git a/clang/raw_ptr_plugin/tests/span_exclude_fields.exclude b/clang/raw_ptr_plugin/tests/span_exclude_fields.exclude new file mode 100644 index 0000000000000000000000000000000000000000..06882f9a560a4a72580e610062440ae14bfaaf1e --- /dev/null +++ b/clang/raw_ptr_plugin/tests/span_exclude_fields.exclude @@ -0,0 +1 @@ +MyClass::excluded_span_field \ No newline at end of file diff --git a/clang/raw_ptr_plugin/tests/span_exclude_fields.flags b/clang/raw_ptr_plugin/tests/span_exclude_fields.flags new file mode 100644 index 0000000000000000000000000000000000000000..c5d668fe09fb9fea53d4b3714df355afa00686c6 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/span_exclude_fields.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-span-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang exclude-fields=span_exclude_fields.exclude diff --git a/clang/raw_ptr_plugin/tests/span_exclude_fields.txt b/clang/raw_ptr_plugin/tests/span_exclude_fields.txt new file mode 100644 index 0000000000000000000000000000000000000000..e27d587ee9c907f89223510b0e289c1a6ca82514 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/span_exclude_fields.txt @@ -0,0 +1,4 @@ +span_exclude_fields.cpp:11:25: error: [chromium-rawptr] Use raw_span instead of a span. + base::span span_field; + ^ +1 error generated. diff --git a/clang/raw_ptr_plugin/tests/span_exclude_paths.cpp b/clang/raw_ptr_plugin/tests/span_exclude_paths.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f1987bf19442a9c6f2d3e5615fddc69bc810f8b --- /dev/null +++ b/clang/raw_ptr_plugin/tests/span_exclude_paths.cpp @@ -0,0 +1,12 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "base/containers/span.h" + +class SomeClass {}; + +class MyClass { + public: + // No error expected because of span-exclude-path arg. + base::span span_field1; +}; diff --git a/clang/raw_ptr_plugin/tests/span_exclude_paths.flags b/clang/raw_ptr_plugin/tests/span_exclude_paths.flags new file mode 100644 index 0000000000000000000000000000000000000000..cf54da24d93a2cd24474be875dcc35c7b9704a19 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/span_exclude_paths.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-span-fields -Xclang -plugin-arg-raw-ptr-plugin -Xclang raw-ptr-exclude-path=span_exclude_paths diff --git a/clang/raw_ptr_plugin/tests/span_exclude_paths.txt b/clang/raw_ptr_plugin/tests/span_exclude_paths.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/clang/raw_ptr_plugin/tests/span_fields_basic.cpp b/clang/raw_ptr_plugin/tests/span_fields_basic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dd3c8c6fa2de171bf5dbf17268f2d8c64e03072d --- /dev/null +++ b/clang/raw_ptr_plugin/tests/span_fields_basic.cpp @@ -0,0 +1,158 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include +#include +#include +#include + +#include "base/containers/span.h" + +class SomeClass {}; + +// No error expected. Only class/struct members are enforced. +int get(base::span s) { + return 0; +} + +// Since base::span internally stores a pointer, base::span class/struct members +// should internally store a raw_ptr. For this reason base::raw_span should be +// used instead of base::span for class/struct members. +class MyClass { + public: + using IntSpan = base::span; + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members. + base::span member1; + + // No error expected. + int int_field; + + // Error expected. + // IntSpan is an alias for base::span, base::raw_span should be used instead + // of base::span for class/struct members. + IntSpan member2; + + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members. + std::vector> member3; + + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members (including container template arguments). + std::map> member4; + + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members (including container template arguments). + std::map, SomeClass*> member5; + + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members (including container template arguments). + std::map, base::span> member6; + + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members. + base::span member7; + + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members (including container template arguments). + std::vector> member8; + + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members. + std::optional> member9; + + // No error expected. Already using base::raw_span + base::raw_span raw_span_member; + + // No error expected. Already using base::raw_span + std::vector> raw_span_container; + + // No error expected. Already using base::raw_span + std::optional> optional_raw_span; + + // No errors expected for the following fields. + // const char*, const wchar_t*, const char8_t*, const char16_t*, const + // char32_t* are excluded from the rewrite because there are likely pointing + // to string literal, which wouldn't be protected by MiraclePtr. + base::span const_char_span_field; + base::span const_wchar_span_field; + base::span const_char8_span_field; + base::span const_char16_span_field; + base::span const_char32_span_field; + std::optional> optional_const_char_span_field; + std::optional> optional_const_wchar_span_field; + std::optional> optional_const_char8_span_field; + std::optional> optional_const_char16_span_field; + std::optional> optional_const_char32_span_field; + std::vector> vector_const_char_span_field; + std::vector> vector_const_wchar_span_field; + std::vector> vector_const_char8_span_field; + std::vector> vector_const_char16_span_field; + std::vector> vector_const_char32_span_field; + + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members. + base::span char_span; + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members. + base::span wide_char_span; + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members. + base::span char8_span; + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members. + base::span char16_span; + // Error expected. + // base::raw_span should be used instead of base::span for class/struct + // members. + base::span char32_span; + // Error expected. const uint8_t pointers don't point to string literals. + // base::raw_span should be used instead of base::span + base::span const_uint8_span; + // Error expected. const int8_t pointers don't point to string literals. + // base::raw_span should be used instead of base::span + base::span const_int8_span; + // Error expected. const unsigned char don't point to string literals. + // base::raw_span should be used instead of base::span + base::span const_unsigned_char_span; + // Error expected. const signed char pointers don't point to string literals. + // base::raw_span should be used instead of base::span + base::span const_signed_char_span; +}; + +// The field below won't compile without the |typename| keyword (because +// at this point we don't know if MaybeProvidesType::Type is a type, +// value or something else). +template +struct MaybeProvidesType; +template +struct DependentNameTest { + // Error expected. + // base::raw_span should be used instead of base::span for members. + base::span::Type> span_field; + // Error expected. + // base::raw_span should be used instead of base::span for members. + std::vector::Type>> + vector_of_span_field; + // Error expected. + // base::raw_span should be used instead of base::span for members. + std::optional::Type>> + optional_span_field; + + // Error expected. + // base::raw_span should be used instead of base::span for members. + std::vector::Type>>> + vector_of_optional_span_field; +}; diff --git a/clang/raw_ptr_plugin/tests/span_fields_basic.flags b/clang/raw_ptr_plugin/tests/span_fields_basic.flags new file mode 100644 index 0000000000000000000000000000000000000000..f867b88c3a2ef301c3ff518567f68bcc35493639 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/span_fields_basic.flags @@ -0,0 +1 @@ +-Xclang -plugin-arg-raw-ptr-plugin -Xclang check-span-fields -ferror-limit=30 \ No newline at end of file diff --git a/clang/raw_ptr_plugin/tests/span_fields_basic.txt b/clang/raw_ptr_plugin/tests/span_fields_basic.txt new file mode 100644 index 0000000000000000000000000000000000000000..916c670e423058f35142d46055862f1f392b26d3 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/span_fields_basic.txt @@ -0,0 +1,67 @@ +span_fields_basic.cpp:27:25: error: [chromium-rawptr] Use raw_span instead of a span. + base::span member1; + ^ +span_fields_basic.cpp:35:11: error: [chromium-rawptr] Use raw_span instead of a span. + IntSpan member2; + ^ +span_fields_basic.cpp:40:38: error: [chromium-rawptr] Use raw_span instead of a span in the field type's template arguments. + std::vector> member3; + ^ +span_fields_basic.cpp:45:40: error: [chromium-rawptr] Use raw_span instead of a span in the field type's template arguments. + std::map> member4; + ^ +span_fields_basic.cpp:50:47: error: [chromium-rawptr] Use raw_span instead of a span in the field type's template arguments. + std::map, SomeClass*> member5; + ^ +span_fields_basic.cpp:55:58: error: [chromium-rawptr] Use raw_span instead of a span in the field type's template arguments. + std::map, base::span> member6; + ^ +span_fields_basic.cpp:60:25: error: [chromium-rawptr] Use raw_span instead of a span. + base::span member7; + ^ +span_fields_basic.cpp:65:38: error: [chromium-rawptr] Use raw_span instead of a span in the field type's template arguments. + std::vector> member8; + ^ +span_fields_basic.cpp:70:40: error: [chromium-rawptr] Use raw_span instead of a span. + std::optional> member9; + ^ +span_fields_basic.cpp:104:20: error: [chromium-rawptr] Use raw_span instead of a span. + base::span char_span; + ^ +span_fields_basic.cpp:108:23: error: [chromium-rawptr] Use raw_span instead of a span. + base::span wide_char_span; + ^ +span_fields_basic.cpp:112:23: error: [chromium-rawptr] Use raw_span instead of a span. + base::span char8_span; + ^ +span_fields_basic.cpp:116:24: error: [chromium-rawptr] Use raw_span instead of a span. + base::span char16_span; + ^ +span_fields_basic.cpp:120:24: error: [chromium-rawptr] Use raw_span instead of a span. + base::span char32_span; + ^ +span_fields_basic.cpp:123:29: error: [chromium-rawptr] Use raw_span instead of a span. + base::span const_uint8_span; + ^ +span_fields_basic.cpp:126:28: error: [chromium-rawptr] Use raw_span instead of a span. + base::span const_int8_span; + ^ +span_fields_basic.cpp:129:35: error: [chromium-rawptr] Use raw_span instead of a span. + base::span const_unsigned_char_span; + ^ +span_fields_basic.cpp:132:33: error: [chromium-rawptr] Use raw_span instead of a span. + base::span const_signed_char_span; + ^ +span_fields_basic.cpp:144:51: error: [chromium-rawptr] Use raw_span instead of a span. + base::span::Type> span_field; + ^ +span_fields_basic.cpp:148:7: error: [chromium-rawptr] Use raw_span instead of a span in the field type's template arguments. + vector_of_span_field; + ^ +span_fields_basic.cpp:152:7: error: [chromium-rawptr] Use raw_span instead of a span. + optional_span_field; + ^ +span_fields_basic.cpp:157:7: error: [chromium-rawptr] Use raw_span instead of a span in the field type's template arguments. + vector_of_optional_span_field; + ^ +22 errors generated. diff --git a/clang/raw_ptr_plugin/tests/system/atomic b/clang/raw_ptr_plugin/tests/system/atomic new file mode 100644 index 0000000000000000000000000000000000000000..c67bf1faf45716301607c65c77444fa032243dc1 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/system/atomic @@ -0,0 +1,44 @@ +// Copyright 2021 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_ATOMIC_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_ATOMIC_ + +namespace std { + +// Faux-implementation of std::atomic. +// The standard requires that the following conditions are met for `T`: +// std::is_copy_constructible::value == true +// std::is_move_constructible::value == true +// std::is_copy_assignable::value == true +// std::is_move_assignable::value == true +// std::is_trivially_copyable::value == true +// +// An exception is made to the last condition for +// std::atomic> and std::atomic>, by +// defining partial-specializations in +// +// The standard also says that std::atomic (where `Integral` is a +// built-in integral type, including raw pointers) is a standard-layout struct, +// and has a trivial destructor: +// https://eel.is/c++draft/atomics.types.generic#atomics.types.int-2 +template +struct atomic { + T i; + + atomic(); + ~atomic() = default; + + // std::atomic is never copyable or moveable + atomic(const atomic&) = delete; + atomic(atomic&&) = delete; + atomic& operator=(const atomic&) = delete; + atomic& operator=(atomic&&) = delete; +}; + +using atomic_int = std::atomic; + +} // namespace std + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_ATOMIC_ diff --git a/clang/raw_ptr_plugin/tests/system/cstdint b/clang/raw_ptr_plugin/tests/system/cstdint new file mode 100644 index 0000000000000000000000000000000000000000..e38217d3c5c2d8fee515adb6d9e08afdbd96ae93 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/system/cstdint @@ -0,0 +1,14 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_CSTDINT_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_CSTDINT_ + +using uintptr_t = __UINTPTR_TYPE__; + +using uint8_t = unsigned char; + +using int8_t = signed char; + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_CSTDINT_ diff --git a/clang/raw_ptr_plugin/tests/system/map b/clang/raw_ptr_plugin/tests/system/map new file mode 100644 index 0000000000000000000000000000000000000000..de489d83fb8595bdddda4549b4fc8d054c881f8e --- /dev/null +++ b/clang/raw_ptr_plugin/tests/system/map @@ -0,0 +1,25 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_MAP_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_MAP_ + +namespace std { + +template +struct map { + map(); + map(const map&); + map(map&&); + ~map(); + map& operator=(const map&); + map& operator=(map&&); + unsigned int size() { return 0; } + void insert() {} + void erase() {} +}; + +} // namespace std + +#endif diff --git a/clang/raw_ptr_plugin/tests/system/memory b/clang/raw_ptr_plugin/tests/system/memory new file mode 100644 index 0000000000000000000000000000000000000000..a92bae4e98e11242507684f9bd2167815d1c1525 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/system/memory @@ -0,0 +1,42 @@ +// Copyright 2021 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_MEMORY_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_MEMORY_ + +#include + +namespace std { + +template +struct shared_ptr { + shared_ptr(); + ~shared_ptr(); + shared_ptr(const shared_ptr&); + shared_ptr(shared_ptr&&); + shared_ptr& operator=(const shared_ptr&); + shared_ptr& operator=(shared_ptr&&); + + private: + T* ptr_ = nullptr; +}; + +// The STL has this specialization defined through . +// Normally template parameters to std::atomic must be trivially copyable, +// but exceptions are made for std::shared_ptr and std::weak_ptr. +template +struct atomic > { + shared_ptr i; + + atomic(); + ~atomic(); + atomic(const atomic&) = delete; + atomic(atomic&&) = delete; + atomic& operator=(const atomic&) = delete; + atomic& operator=(atomic&&) = delete; +}; + +} // namespace std + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_MEMORY_ diff --git a/clang/raw_ptr_plugin/tests/system/optional b/clang/raw_ptr_plugin/tests/system/optional new file mode 100644 index 0000000000000000000000000000000000000000..94fe2eddc5f348c63c26e0ae896edfe86e3620a2 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/system/optional @@ -0,0 +1,22 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_OPTIONAL_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_OPTIONAL_ + +namespace std { + +template +struct optional { + optional(); + optional(const optional&); + optional(optional&&); + ~optional(); + optional& operator=(const optional&); + optional& operator=(optional&&); +}; + +} // namespace std + +#endif diff --git a/clang/raw_ptr_plugin/tests/system/raw_ptr_system_test.h b/clang/raw_ptr_plugin/tests/system/raw_ptr_system_test.h new file mode 100644 index 0000000000000000000000000000000000000000..0066b91c50636b131f5ffa66489e794af44c37bf --- /dev/null +++ b/clang/raw_ptr_plugin/tests/system/raw_ptr_system_test.h @@ -0,0 +1,132 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_RAW_PTR_SYSTEM_TEST_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_RAW_PTR_SYSTEM_TEST_H_ +#line 7 "/src/tools/clang/plugins/tests/system/raw_ptr_system_test.h" + +#define SYS_INT int +#define SYS_INTP int* +#define SYS_CONST const +#define SYS_ATTR [[fake_attribute]] +#define SYS_INTP_FIELD() int* macro_ptr +#define SYS_TYPE_WITH_SUFFIX(TYP) TYP##Suffix +#define SYS_SYMBOL(SYM) SYM +#define SYS_SYMBOL_WITH_SUFFIX(SYM) SYM##_Suffix +#define SYS_EQ = +#define SYS_NULLPTR nullptr +class SysTypSuffix; + +// OK: code owner has no control over system header. +struct SysStructWithSysMacro { + int* ptr0; // OK. + SYS_INT* ptr1; // OK. + SYS_INTP ptr2; // OK. + int* SYS_CONST ptr3; // OK. + int* SYS_ATTR ptr4; // OK. + SYS_INTP_FIELD(); // OK. + SYS_TYPE_WITH_SUFFIX(SysTyp) * ptr5; // OK. + int* SYS_SYMBOL(ptr6); // OK. + int* SYS_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 SYS_EQ nullptr; // OK. + int* ptr9 = SYS_NULLPTR; // OK. + int* SYS_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +// OK: code owner has no control over system header and command line. +struct SysStructWithCmdMacro { + int* ptr0; // OK. + CMD_INT* ptr1; // OK. + CMD_INTP ptr2; // OK. + int* CMD_CONST ptr3; // OK. + int* CMD_ATTR ptr4; // OK. + CMD_INTP_FIELD(); // OK. + CMD_TYPE_WITH_SUFFIX(SysTyp) * ptr5; // OK. + int* CMD_SYMBOL(ptr6); // OK. + int* CMD_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 CMD_EQ nullptr; // OK. + int* ptr9 = CMD_NULLPTR; // OK. + int* CMD_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +// These `USR_***` macro should be defined before including this header, +// in `//tools/clang/plugins/tests/raw_ptr_fields_macro.cpp`. +struct SysStructWithUsrMacro { + // OK: code owner has no control over system header. + int* ptr0; + // OK: code owner has no control over system header. + USR_INT* ptr1; + // OK: code owner has no control over system header. + USR_INTP ptr2; + // OK: code owner has no control over system header. + int* USR_CONST ptr3; + // OK: code owner has no control over system header. + int* USR_ATTR ptr4; + // Error: user has control over the macro. + USR_INTP_FIELD(); + // OK: code owner has no control over system header. + USR_TYPE_WITH_SUFFIX(SysTyp) * ptr5; + // OK: code owner has no control over system header. + int* USR_SYMBOL(ptr6); + // OK: the source location for this field declaration will be "" and the real file path cannot be detected. + int* USR_SYMBOL_WITH_SUFFIX(ptr7); + // OK: code owner has no control over system header. + int* ptr8 USR_EQ nullptr; + // OK: code owner has no control over system header. + int* ptr9 = USR_NULLPTR; + // OK: the source location for this field declaration will be "" and the real file path cannot be detected. + int* USR_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; +}; + +// Same as for SysStructWithSysMacro. +struct SysStructWithThirdPartyMacro { + int* ptr0; // OK. + TP_INT* ptr1; // OK. + TP_INTP ptr2; // OK. + int* TP_CONST ptr3; // OK. + int* TP_ATTR ptr4; // OK. + TP_INTP_FIELD(); // OK. + TP_TYPE_WITH_SUFFIX(SysTyp) * ptr5; // OK. + int* TP_SYMBOL(ptr6); // OK. + int* TP_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 TP_EQ nullptr; // OK. + int* ptr9 = TP_NULLPTR; // OK. + int* TP_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +// Same as for SysStructWithSysMacro. +struct SysStructWithManuallyIgnoredMacro { + int* ptr0; // OK. + IG_INT* ptr1; // OK. + IG_INTP ptr2; // OK. + int* IG_CONST ptr3; // OK. + int* IG_ATTR ptr4; // OK. + IG_INTP_FIELD(); // OK. + IG_TYPE_WITH_SUFFIX(SysTyp) * ptr5; // OK. + int* IG_SYMBOL(ptr6); // OK. + int* IG_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 IG_EQ nullptr; // OK. + int* ptr9 = IG_NULLPTR; // OK. + int* IG_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +// Same as for SysStructWithSysMacro. +struct SysStructWithGeneratedMacro { + int* ptr0; // OK. + GEN_INT* ptr1; // OK. + GEN_INTP ptr2; // OK. + int* GEN_CONST ptr3; // OK. + int* GEN_ATTR ptr4; // OK. + GEN_INTP_FIELD(); // OK. + GEN_TYPE_WITH_SUFFIX(SysTyp) * ptr5; // OK. + int* GEN_SYMBOL(ptr6); // OK. + int* GEN_SYMBOL_WITH_SUFFIX(ptr7); // OK. + int* ptr8 GEN_EQ nullptr; // OK. + int* ptr9 = GEN_NULLPTR; // OK. + int* GEN_SYMBOL_WITH_SUFFIX(ptr10) = nullptr; // OK. +}; + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_RAW_PTR_SYSTEM_TEST_H_ diff --git a/clang/raw_ptr_plugin/tests/system/string b/clang/raw_ptr_plugin/tests/system/string new file mode 100644 index 0000000000000000000000000000000000000000..46a7e0167733f1cbc0d424178a174f81ff0f6be1 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/system/string @@ -0,0 +1,22 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_STRING_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_STRING_ + +namespace std { + +template +struct basic_string { + basic_string(); + basic_string(const basic_string&); + basic_string(basic_string&&); + ~basic_string(); +}; + +using string = basic_string; + +} // namespace std + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_STRING_ diff --git a/clang/raw_ptr_plugin/tests/system/system_unsafe_buffers.h b/clang/raw_ptr_plugin/tests/system/system_unsafe_buffers.h new file mode 100644 index 0000000000000000000000000000000000000000..7053f5c8e95151c7458d7835becd150233294501 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/system/system_unsafe_buffers.h @@ -0,0 +1,12 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_SYSTEM_UNSAFE_BUFFERS_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_SYSTEM_UNSAFE_BUFFERS_H_ + +inline int system_bad_stuff(int* i, unsigned s) { + return i[s]; // This is in a system header, so no warning is emitted. +} + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_SYSTEM_UNSAFE_BUFFERS_H_ diff --git a/clang/raw_ptr_plugin/tests/system/vector b/clang/raw_ptr_plugin/tests/system/vector new file mode 100644 index 0000000000000000000000000000000000000000..b6726d51709837c64a653ad1242b2a2657cd6df3 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/system/vector @@ -0,0 +1,25 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_VECTOR_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_VECTOR_ + +namespace std { + +template +struct vector { + vector(); + vector(const vector&); + vector(vector&&); + ~vector(); + vector& operator=(const vector&); + vector& operator=(vector&&); + unsigned int size() { return 0; } + void insert() {} + void erase() {} +}; + +} // namespace std + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_VECTOR_ diff --git a/clang/raw_ptr_plugin/tests/system/windows.h b/clang/raw_ptr_plugin/tests/system/windows.h new file mode 100644 index 0000000000000000000000000000000000000000..ebefa86dc50d59678b49ee9bc17862b0d112ba8f --- /dev/null +++ b/clang/raw_ptr_plugin/tests/system/windows.h @@ -0,0 +1,20 @@ +// Copyright 2015 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_WINDOWS_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_WINDOWS_H_ + +#define STDMETHOD(x) virtual void x + +#define END_COM_MAP() virtual void AddRef() = 0; + +#define SYSTEM_REDUNDANT1 virtual void NonVirtualFinal() final +#define SYSTEM_REDUNDANT2 virtual void Virtual() override final + +#define SYSTEM_INLINE_VIRTUAL \ + virtual int Foo() { \ + return 4; \ + } + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_SYSTEM_WINDOWS_H_ diff --git a/clang/raw_ptr_plugin/tests/test.py b/clang/raw_ptr_plugin/tests/test.py new file mode 100644 index 0000000000000000000000000000000000000000..9f4c5dd78523d223e306194a6bcaf8d6361257da --- /dev/null +++ b/clang/raw_ptr_plugin/tests/test.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# Copyright 2016 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import os +import subprocess +import sys + +script_dir = os.path.dirname(os.path.realpath(__file__)) +tool_dir = os.path.abspath(os.path.join(script_dir, '../../pylib')) +sys.path.insert(0, tool_dir) + +from clang import plugin_testing + + +class ChromeStylePluginTest(plugin_testing.ClangPluginTest): + """Test harness for the Chrome style plugin.""" + + def AdjustClangArguments(self, clang_cmd): + clang_cmd.extend([ + # Skip code generation + '-fsyntax-only', + # Fake system directory for tests + '-isystem', + os.path.join(os.getcwd(), 'system'), + '-Wno-inconsistent-missing-override', + '--include-directory', + '.', + ]) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--reset-results', + action='store_true', + help='If specified, overwrites the expected results in place.') + parser.add_argument('clang_path', help='The path to the clang binary.') + parser.add_argument('--filter', + action='store', + help='Filter to test files that match a regex') + args = parser.parse_args() + + return ChromeStylePluginTest(os.path.dirname(os.path.realpath(__file__)), + args.clang_path, ['raw-ptr-plugin'], + args.reset_results, + filename_regex=args.filter).Run() + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/clang/raw_ptr_plugin/tests/third_party/blink/public/public.h b/clang/raw_ptr_plugin/tests/third_party/blink/public/public.h new file mode 100644 index 0000000000000000000000000000000000000000..08d71210d5dd72de4231287698bb65974856c3e5 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/third_party/blink/public/public.h @@ -0,0 +1,18 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_PUBLIC_PUBLIC_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_PUBLIC_PUBLIC_H_ + +#include + +namespace blink { + +// Blink public API is allowed to use type aliases that resolve to STL +// containers. +using BlinkPublicType = std::vector; + +} // namespace blink + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_RENDERER_PUBLIC_PUBLIC_H_ diff --git a/clang/raw_ptr_plugin/tests/third_party/blink/renderer/discouraged_type.h b/clang/raw_ptr_plugin/tests/third_party/blink/renderer/discouraged_type.h new file mode 100644 index 0000000000000000000000000000000000000000..7fbf2a0e9ea296fdc055af7dcc903f5d42237638 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/third_party/blink/renderer/discouraged_type.h @@ -0,0 +1,89 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_RENDERER_DISCOURAGED_TYPE_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_RENDERER_DISCOURAGED_TYPE_H_ + +#include + +#include "third_party/blink/public/public.h" + +namespace cc { +// Allowed, since type aliases are defined outside of blink:: namespace. +using CcVector = std::vector; +typedef std::vector CcVector2; +} // namespace cc + +namespace blink { + +namespace nested { +// Not allowed. Will report error when this type is used for a data member. +using IntVector = std::vector; +// Allowed, since this is a type alias of an allowed type. +using CcVector = cc::CcVector; +} // namespace nested + +struct Foo { + Foo(std::vector v); + + // Not allowed. + std::vector v1; + + using FloatVector = std::vector; + typedef std::vector FloatVector2; + // Not allowed. + nested::IntVector v2a; + FloatVector v2b; + FloatVector2 v2c; + + // Not allowed. + std::vector v_array[4][4]; + + // cc::CcVector is not under the blink:: namespace, the checker should + // ignore it and allow the use. In real world, this will be OK as long as + // audit_non_blink_usages.py allows cc::CcVector. + cc::CcVector v3a; + cc::CcVector2 v3b; + + // This is a type alias that ultimately refers to cc::CcVector. Since the + // underlying type is not under the blink:: namespace, the checker should + // ignore it and allow the use. + nested::CcVector v3c; + + // This is a type alias defined in third_party/blink/public/public.h which + // should not be checked. + BlinkPublicType v4; + + std::vector v5 __attribute__((annotate("other1"))) + __attribute__((annotate("other2"), annotate("allow_discouraged_type"))); + + using VectorAllowed __attribute__((annotate("allow_discouraged_type"))) = + std::vector; + VectorAllowed v6; +}; + +// Function params of discouraged types are allowed. +inline Foo::Foo(std::vector v) { + // This is OK. + std::vector vv(v); + + struct { + // Not allowed. + std::vector v; + } sv = {vv}; + + v1 = sv.v; +} + +template +class Template { + // Not allowed. + std::vector v1; + + std::vector v2 __attribute__((annotate("allow_discouraged_type"))); +}; + +} // namespace blink + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_RENDERER_DISCOURAGED_TYPE_H_ diff --git a/clang/raw_ptr_plugin/tests/third_party/blink/renderer/discouraged_type_test.h b/clang/raw_ptr_plugin/tests/third_party/blink/renderer/discouraged_type_test.h new file mode 100644 index 0000000000000000000000000000000000000000..ce102482d8b5c6705aaca703b25d1b02fe962ac4 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/third_party/blink/renderer/discouraged_type_test.h @@ -0,0 +1,25 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_RENDERER_DISCOURAGED_TYPE_TEST_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_RENDERER_DISCOURAGED_TYPE_TEST_H_ + +// The plugin should ignore this file because it has "_test." in its filename. + +#include + +namespace blink { + +struct FooTest { + std::vector v1; + + using FloatVector = std::vector; + FloatVector v2; + + std::vector v_array[4][4]; +}; + +} // namespace blink + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_RENDERER_DISCOURAGED_TYPE_TEST_H_ diff --git a/clang/raw_ptr_plugin/tests/third_party/blink/renderer/testing/discouraged_type.h b/clang/raw_ptr_plugin/tests/third_party/blink/renderer/testing/discouraged_type.h new file mode 100644 index 0000000000000000000000000000000000000000..9dd5d08a74286041ca1342a899cdfc5a64e08523 --- /dev/null +++ b/clang/raw_ptr_plugin/tests/third_party/blink/renderer/testing/discouraged_type.h @@ -0,0 +1,25 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_RENDERER_TESTING_DISCOURAGED_TYPE_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_RENDERER_TESTING_DISCOURAGED_TYPE_H_ + +// The plugin should ignore this file because it is under "testing" directory. + +#include + +namespace blink { + +struct FooTest1 { + std::vector v1; + + using FloatVector = std::vector; + FloatVector v2; + + std::vector v_array[4][4]; +}; + +} // namespace blink + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_BLINK_RENDERER_TESTING_DISCOURAGED_TYPE_H_ diff --git a/clang/raw_ptr_plugin/tests/third_party/raw_ptr_to_stack_allocated_third_party_test.h b/clang/raw_ptr_plugin/tests/third_party/raw_ptr_to_stack_allocated_third_party_test.h new file mode 100644 index 0000000000000000000000000000000000000000..9a931494e2003b5218c692f91f1005e6c9074e5f --- /dev/null +++ b/clang/raw_ptr_plugin/tests/third_party/raw_ptr_to_stack_allocated_third_party_test.h @@ -0,0 +1,35 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_RAW_PTR_TO_STACK_ALLOCATED_THIRD_PARTY_TEST_H_ +#define TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_RAW_PTR_TO_STACK_ALLOCATED_THIRD_PARTY_TEST_H_ + +#include "base/memory/raw_ptr.h" + +struct StackAllocatedType { + using IsStackAllocatedTypeMarker [[maybe_unused]] = int; +}; +struct StackAllocatedSubType : public StackAllocatedType {}; +struct NonStackAllocatedType {}; + +// typedefs should be checked but excluded here +typedef raw_ptr ErrTypeA; +typedef raw_ptr ErrTypeB; +typedef raw_ptr OkTypeC; + +// fields should be checked but excluded here +struct MyStruct { + raw_ptr err_a; + raw_ptr err_b; + raw_ptr ok_c; +}; + +// variables should be checked but excluded here +void MyFunc() { + raw_ptr err_a; + raw_ptr err_b; + raw_ptr ok_c; +} + +#endif // TOOLS_CLANG_RAW_PTR_PLUGIN_TESTS_THIRD_PARTY_RAW_PTR_TO_STACK_ALLOCATED_THIRD_PARTY_TEST_H_