// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "BadPatternFinder.h" #include "DiagnosticsReporter.h" #include #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" using namespace clang::ast_matchers; namespace { TypeMatcher GarbageCollectedType() { auto has_gc_base = hasCanonicalType(hasDeclaration( cxxRecordDecl(isDerivedFrom(hasAnyName("::blink::GarbageCollected", "::blink::GarbageCollectedMixin"))) .bind("gctype"))); 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) { 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_; }; class OptionalGarbageCollectedMatcher : public MatchFinder::MatchCallback { public: explicit OptionalGarbageCollectedMatcher(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 optional_construction = cxxConstructExpr(hasDeclaration(cxxConstructorDecl(ofClass( classTemplateSpecializationDecl( hasName("::base::Optional"), hasTemplateArgument( 0, refersToType(GarbageCollectedType()))) .bind("optional"))))) .bind("bad"); match_finder.addDynamicMatcher(optional_construction, this); } void run(const MatchFinder::MatchResult& result) { auto* bad_use = result.Nodes.getNodeAs("bad"); auto* optional = result.Nodes.getNodeAs("optional"); auto* gc_type = result.Nodes.getNodeAs("gctype"); diagnostics_.OptionalUsedWithGC(bad_use, optional, gc_type); } private: DiagnosticsReporter& diagnostics_; }; class MissingMixinMarker : public MatchFinder::MatchCallback { public: explicit MissingMixinMarker(DiagnosticsReporter& diagnostics) : diagnostics_(diagnostics) {} void Register(MatchFinder& match_finder) { auto class_missing_mixin_marker = cxxRecordDecl( decl().bind("bad_class"), // Definition of a garbage-collected class isDefinition(), isDerivedFrom(cxxRecordDecl(decl().bind("gc_base_class"), hasName("::blink::GarbageCollected"))), // ...which derives some mixin... isDerivedFrom(cxxRecordDecl(decl().bind("mixin_base_class"), hasName("::blink::GarbageCollectedMixin"))), // ...and doesn't use USING_GARBAGE_COLLECTED_MIXIN unless(anyOf(isSameOrDerivedFrom(has(typedefNameDecl( hasName("HasUsingGarbageCollectedMixinMacro")))), isSameOrDerivedFrom(has( fieldDecl(hasName("mixin_constructor_marker_")))))), // ...and might end up actually being constructed unless(hasMethod(isPure())), unless(matchesName("::SameSizeAs"))); match_finder.addDynamicMatcher(class_missing_mixin_marker, this); } void run(const MatchFinder::MatchResult& result) { auto* bad_class = result.Nodes.getNodeAs("bad_class"); auto* gc_base_class = result.Nodes.getNodeAs("gc_base_class"); auto* mixin_base_class = result.Nodes.getNodeAs("mixin_base_class"); clang::CXXBasePaths paths; if (!bad_class->isDerivedFrom(mixin_base_class, paths)) return; const auto& path = paths.front(); // It's most useful to describe the most derived "mixin" class (i.e. which // does not derive the concrete GarbageCollected base). auto mixin_it = std::find_if( path.begin(), path.end(), [gc_base_class](const clang::CXXBasePathElement& path_element) { return !path_element.Class->isDerivedFrom(gc_base_class); }); const clang::CXXRecordDecl* mixin_class = mixin_it->Class; diagnostics_.MissingMixinMarker(bad_class, mixin_class, path.begin()->Base); ++mixin_it; for (auto it = path.begin() + 1; it != mixin_it; ++it) diagnostics_.MissingMixinMarkerNote(it->Base); } private: DiagnosticsReporter& diagnostics_; }; } // namespace void FindBadPatterns(clang::ASTContext& ast_context, DiagnosticsReporter& diagnostics) { MatchFinder match_finder; UniquePtrGarbageCollectedMatcher unique_ptr_gc(diagnostics); unique_ptr_gc.Register(match_finder); OptionalGarbageCollectedMatcher optional_gc(diagnostics); optional_gc.Register(match_finder); MissingMixinMarker missing_mixin_marker(diagnostics); missing_mixin_marker.Register(match_finder); match_finder.matchAST(ast_context); }