// 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 "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_;
};
} // 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);
match_finder.matchAST(ast_context);
}