blob: 2992d8078a27aa7debe74e40818cc52cf227c69f [file] [log] [blame]
// 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 <algorithm>
#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<clang::Expr>("bad");
auto* bad_function = result.Nodes.getNodeAs<clang::FunctionDecl>("badfunc");
auto* gc_type = result.Nodes.getNodeAs<clang::CXXRecordDecl>("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<clang::Expr>("bad");
auto* optional = result.Nodes.getNodeAs<clang::CXXRecordDecl>("optional");
auto* gc_type = result.Nodes.getNodeAs<clang::CXXRecordDecl>("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<clang::CXXRecordDecl>("bad_class");
auto* gc_base_class =
result.Nodes.getNodeAs<clang::CXXRecordDecl>("gc_base_class");
auto* mixin_base_class =
result.Nodes.getNodeAs<clang::CXXRecordDecl>("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);
}