BadPatternFinder.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "BadPatternFinder.h"
  5. #include <clang/AST/Decl.h>
  6. #include "BlinkGCPluginOptions.h"
  7. #include "Config.h"
  8. #include "DiagnosticsReporter.h"
  9. #include <algorithm>
  10. #include "clang/AST/ASTContext.h"
  11. #include "clang/ASTMatchers/ASTMatchFinder.h"
  12. #include "clang/ASTMatchers/ASTMatchers.h"
  13. #include "clang/ASTMatchers/ASTMatchersMacros.h"
  14. using namespace clang::ast_matchers;
  15. namespace {
  16. TypeMatcher GarbageCollectedType() {
  17. auto has_gc_base = hasCanonicalType(hasDeclaration(
  18. cxxRecordDecl(isDerivedFrom(hasAnyName("::blink::GarbageCollected",
  19. "::blink::GarbageCollectedMixin",
  20. "::cppgc::GarbageCollected",
  21. "::cppgc::GarbageCollectedMixin")))
  22. .bind("gctype")));
  23. return anyOf(has_gc_base,
  24. hasCanonicalType(arrayType(hasElementType(has_gc_base))));
  25. }
  26. class UniquePtrGarbageCollectedMatcher : public MatchFinder::MatchCallback {
  27. public:
  28. explicit UniquePtrGarbageCollectedMatcher(DiagnosticsReporter& diagnostics)
  29. : diagnostics_(diagnostics) {}
  30. void Register(MatchFinder& match_finder) {
  31. // Matches any application of make_unique where the template argument is
  32. // known to refer to a garbage-collected type.
  33. auto make_unique_matcher =
  34. callExpr(
  35. callee(functionDecl(
  36. hasAnyName("::std::make_unique", "::base::WrapUnique"),
  37. hasTemplateArgument(
  38. 0, refersToType(GarbageCollectedType())))
  39. .bind("badfunc")))
  40. .bind("bad");
  41. match_finder.addDynamicMatcher(make_unique_matcher, this);
  42. }
  43. void run(const MatchFinder::MatchResult& result) override {
  44. auto* bad_use = result.Nodes.getNodeAs<clang::Expr>("bad");
  45. auto* bad_function = result.Nodes.getNodeAs<clang::FunctionDecl>("badfunc");
  46. auto* gc_type = result.Nodes.getNodeAs<clang::CXXRecordDecl>("gctype");
  47. diagnostics_.UniquePtrUsedWithGC(bad_use, bad_function, gc_type);
  48. }
  49. private:
  50. DiagnosticsReporter& diagnostics_;
  51. };
  52. class OptionalGarbageCollectedMatcher : public MatchFinder::MatchCallback {
  53. public:
  54. explicit OptionalGarbageCollectedMatcher(DiagnosticsReporter& diagnostics)
  55. : diagnostics_(diagnostics) {}
  56. void Register(MatchFinder& match_finder) {
  57. // Matches fields and new-expressions of type absl::optional where the
  58. // template argument is known to refer to a garbage-collected type.
  59. auto optional_type = hasType(
  60. classTemplateSpecializationDecl(
  61. hasName("::absl::optional"),
  62. hasTemplateArgument(0, refersToType(GarbageCollectedType())))
  63. .bind("optional"));
  64. auto optional_field = fieldDecl(optional_type).bind("bad_field");
  65. auto optional_new_expression =
  66. cxxNewExpr(has(cxxConstructExpr(optional_type))).bind("bad_new");
  67. match_finder.addDynamicMatcher(optional_field, this);
  68. match_finder.addDynamicMatcher(optional_new_expression, this);
  69. }
  70. void run(const MatchFinder::MatchResult& result) override {
  71. auto* optional = result.Nodes.getNodeAs<clang::CXXRecordDecl>("optional");
  72. auto* gc_type = result.Nodes.getNodeAs<clang::CXXRecordDecl>("gctype");
  73. if (auto* bad_field =
  74. result.Nodes.getNodeAs<clang::FieldDecl>("bad_field")) {
  75. diagnostics_.OptionalFieldUsedWithGC(bad_field, optional, gc_type);
  76. } else {
  77. auto* bad_new = result.Nodes.getNodeAs<clang::Expr>("bad_new");
  78. diagnostics_.OptionalNewExprUsedWithGC(bad_new, optional, gc_type);
  79. }
  80. }
  81. private:
  82. DiagnosticsReporter& diagnostics_;
  83. };
  84. // For the absl::variant checker, we need to match the inside of a variadic
  85. // template class, which doesn't seem easy with the built-in matchers: define a
  86. // custom matcher to go through the template parameter list.
  87. AST_MATCHER_P(clang::TemplateArgument,
  88. parameterPackHasAnyElement,
  89. // Clang exports other instantiations of Matcher via
  90. // using-declarations in public headers, e.g. `using TypeMatcher =
  91. // Matcher<QualType>`.
  92. //
  93. // Once https://reviews.llvm.org/D89920, a Clang patch adding a
  94. // similar alias for template arguments, lands, this can be
  95. // changed to TemplateArgumentMatcher and won't need to use the
  96. // internal namespace any longer.
  97. clang::ast_matchers::internal::Matcher<clang::TemplateArgument>,
  98. InnerMatcher) {
  99. if (Node.getKind() != clang::TemplateArgument::Pack)
  100. return false;
  101. return llvm::any_of(Node.pack_elements(),
  102. [&](const clang::TemplateArgument& Arg) {
  103. return InnerMatcher.matches(Arg, Finder, Builder);
  104. });
  105. }
  106. class VariantGarbageCollectedMatcher : public MatchFinder::MatchCallback {
  107. public:
  108. explicit VariantGarbageCollectedMatcher(DiagnosticsReporter& diagnostics)
  109. : diagnostics_(diagnostics) {}
  110. void Register(MatchFinder& match_finder) {
  111. // Matches any constructed absl::variant where a template argument is
  112. // known to refer to a garbage-collected type.
  113. auto variant_construction =
  114. cxxConstructExpr(
  115. hasDeclaration(cxxConstructorDecl(
  116. ofClass(classTemplateSpecializationDecl(
  117. hasName("::absl::variant"),
  118. hasAnyTemplateArgument(parameterPackHasAnyElement(
  119. refersToType(GarbageCollectedType()))))
  120. .bind("variant")))))
  121. .bind("bad");
  122. match_finder.addDynamicMatcher(variant_construction, this);
  123. }
  124. void run(const MatchFinder::MatchResult& result) override {
  125. auto* bad_use = result.Nodes.getNodeAs<clang::Expr>("bad");
  126. auto* variant = result.Nodes.getNodeAs<clang::CXXRecordDecl>("variant");
  127. auto* gc_type = result.Nodes.getNodeAs<clang::CXXRecordDecl>("gctype");
  128. diagnostics_.VariantUsedWithGC(bad_use, variant, gc_type);
  129. }
  130. private:
  131. DiagnosticsReporter& diagnostics_;
  132. };
  133. class MemberOnStackMatcher : public MatchFinder::MatchCallback {
  134. public:
  135. explicit MemberOnStackMatcher(DiagnosticsReporter& diagnostics)
  136. : diagnostics_(diagnostics) {}
  137. void Register(MatchFinder& match_finder) {
  138. auto class_member_variable_matcher =
  139. varDecl(hasType(recordDecl(
  140. hasAnyName("::blink::Member", "::blink::WeakMember",
  141. "::cppgc::internal::BasicMember"))))
  142. .bind("member");
  143. match_finder.addDynamicMatcher(class_member_variable_matcher, this);
  144. auto alias_member_variable_matcher =
  145. varDecl(hasType(typeAliasTemplateDecl(
  146. hasAnyName("::blink::Member", "::blink::WeakMember",
  147. "::cppgc::Member", "::cppgc::WeakMember"))))
  148. .bind("member");
  149. match_finder.addDynamicMatcher(alias_member_variable_matcher, this);
  150. }
  151. void run(const MatchFinder::MatchResult& result) override {
  152. auto* member = result.Nodes.getNodeAs<clang::VarDecl>("member");
  153. if (Config::IsIgnoreAnnotated(member))
  154. return;
  155. diagnostics_.MemberOnStack(member);
  156. }
  157. private:
  158. DiagnosticsReporter& diagnostics_;
  159. };
  160. } // namespace
  161. void FindBadPatterns(clang::ASTContext& ast_context,
  162. DiagnosticsReporter& diagnostics,
  163. const BlinkGCPluginOptions& options) {
  164. MatchFinder match_finder;
  165. UniquePtrGarbageCollectedMatcher unique_ptr_gc(diagnostics);
  166. unique_ptr_gc.Register(match_finder);
  167. OptionalGarbageCollectedMatcher optional_gc(diagnostics);
  168. optional_gc.Register(match_finder);
  169. VariantGarbageCollectedMatcher variant_gc(diagnostics);
  170. variant_gc.Register(match_finder);
  171. MemberOnStackMatcher member_on_stack(diagnostics);
  172. if (options.enable_members_on_stack_check) {
  173. member_on_stack.Register(match_finder);
  174. }
  175. match_finder.matchAST(ast_context);
  176. }