Bug 1487622 - Refactor the clang plugin wrt attributes r=andi

- We forcefully remove annotations from the AST so that they don't end
up impacting codegen.
- We change the API such that we use identifiers instead of strings,
reducing the chances of typo errors.

Differential Revision: https://phabricator.services.mozilla.com/D5493

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Mike Hommey 2018-09-18 13:03:33 +00:00
parent cdd1898856
commit f8798bbeb2
17 changed files with 230 additions and 51 deletions

View File

@ -110,7 +110,7 @@ void FuncSetCallback::run(const MatchFinder::MatchResult &Result) {
const FunctionDecl *Func;
if (auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda")) {
Func = Lambda->getCallOperator();
if (!Func || !hasCustomAnnotation(Func, "moz_can_run_script"))
if (!Func || !hasCustomAttribute<moz_can_run_script>(Func))
return;
} else {
Func = Result.Nodes.getNodeAs<FunctionDecl>("canRunScriptFunction");
@ -211,7 +211,7 @@ void CanRunScriptChecker::check(const MatchFinder::MatchResult &Result) {
// Bindings.
if (ParentFunction &&
(CanRunScriptFuncs.count(ParentFunction) ||
hasCustomAnnotation(ParentFunction, "moz_can_run_script_boundary"))) {
hasCustomAttribute<moz_can_run_script_boundary>(ParentFunction))) {
ParentFunction = nullptr;
}
@ -236,7 +236,7 @@ void CanRunScriptChecker::check(const MatchFinder::MatchResult &Result) {
// If the parent function is not marked as MOZ_CAN_RUN_SCRIPT, we emit an
// error and a not indicating it.
if (ParentFunction) {
assert(!hasCustomAnnotation(ParentFunction, "moz_can_run_script") &&
assert(!hasCustomAttribute<moz_can_run_script>(ParentFunction) &&
"Matcher missed something");
diag(CallRange.getBegin(), ErrorNonCanRunScriptParent, DiagnosticIDs::Error)

View File

@ -0,0 +1,122 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <algorithm>
#include "CustomAttributes.h"
#include "plugin.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
/* Having annotations in the AST unexpectedly impacts codegen.
* Ideally, we'd avoid having annotations at all, by using an API such as
* the one from https://reviews.llvm.org/D31338, and storing the attributes
* data separately from the AST on our own. Unfortunately, there is no such
* API currently in clang, so we must do without.
* We can do something similar, though, where we go through the AST before
* running the checks, create a mapping of AST nodes to attributes, and
* remove the attributes/annotations from the AST nodes.
* Not all declarations can be reached from the decl() AST matcher, though,
* so we do our best effort (getting the other declarations we look at in
* checks). We emit a warning when checks look at a note that still has
* annotations attached (aka, hasn't been seen during our first pass),
* so that those don't go unnoticed. (-Werror should then take care of
* making that an error)
*/
using namespace clang;
using namespace llvm;
static DenseMap<const Decl*, CustomAttributesSet> AttributesCache;
static CustomAttributesSet CacheAttributes(const Decl* D)
{
CustomAttributesSet attrs = {};
for (auto Attr : D->specific_attrs<AnnotateAttr>()) {
auto annotation = Attr->getAnnotation();
#define ATTR(a) \
if (annotation == #a) { \
attrs.has_ ## a = true; \
} else
#include "CustomAttributes.inc"
#undef ATTR
{}
}
const_cast<Decl*>(D)->dropAttr<AnnotateAttr>();
AttributesCache.insert(std::make_pair(D, attrs));
return attrs;
}
static void Report(const Decl* D, const char* message)
{
ASTContext& Context = D->getASTContext();
DiagnosticsEngine& Diag = Context.getDiagnostics();
unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Warning, message);
Diag.Report(D->getLocStart(), ID);
}
CustomAttributesSet GetAttributes(const Decl* D)
{
CustomAttributesSet attrs = {};
if (D->hasAttr<AnnotateAttr>()) {
Report(D, "Declaration has unhandled annotations.");
attrs = CacheAttributes(D);
} else {
auto attributes = AttributesCache.find(D);
if (attributes != AttributesCache.end()) {
attrs = attributes->second;
}
}
return attrs;
}
bool hasCustomAttribute(const clang::Decl* D, CustomAttributes A)
{
CustomAttributesSet attrs = GetAttributes(D);
switch (A) {
#define ATTR(a) case a: return attrs.has_ ## a;
#include "CustomAttributes.inc"
#undef ATTR
}
return false;
}
class CustomAttributesMatcher : public ast_matchers::MatchFinder::MatchCallback {
public:
virtual void
run(const ast_matchers::MatchFinder::MatchResult &Result) final
{
if (auto D = Result.Nodes.getNodeAs<Decl>("decl")) {
CacheAttributes(D);
} else if (auto L = Result.Nodes.getNodeAs<LambdaExpr>("lambda")) {
CacheAttributes(L->getCallOperator());
CacheAttributes(L->getLambdaClass());
}
}
};
class CustomAttributesAction : public PluginASTAction {
public:
ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI,
StringRef FileName) override {
auto& Context = CI.getASTContext();
auto AstMatcher = new (Context.Allocate<MatchFinder>()) MatchFinder();
auto Matcher = new (Context.Allocate<CustomAttributesMatcher>()) CustomAttributesMatcher();
AstMatcher->addMatcher(decl().bind("decl"), Matcher);
AstMatcher->addMatcher(lambdaExpr().bind("lambda"), Matcher);
return AstMatcher->newASTConsumer();
}
bool ParseArgs(const CompilerInstance &CI,
const std::vector<std::string> &Args) override {
return true;
}
ActionType getActionType() override {
return AddBeforeMainAction;
}
};
static FrontendPluginRegistry::Add<CustomAttributesAction> X(
"moz-custom-attributes",
"prepare custom attributes for moz-check");

View File

@ -0,0 +1,40 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CustomAttributes_h__
#define CustomAttributes_h__
#include "clang/AST/DeclBase.h"
#include "llvm/ADT/StringRef.h"
enum CustomAttributes {
#define ATTR(a) a,
#include "CustomAttributes.inc"
#undef ATTR
};
struct CustomAttributesSet {
#define ATTR(a) bool has_ ## a: 1;
#include "CustomAttributes.inc"
#undef ATTR
};
template<CustomAttributes A>
bool hasCustomAttribute(const clang::Decl* D) {
return false;
}
extern CustomAttributesSet GetAttributes(const clang::Decl* D);
#define ATTR(name) \
template<> \
inline bool hasCustomAttribute<name>(const clang::Decl* D) { \
return GetAttributes(D).has_ ## name; \
}
#include "CustomAttributes.inc"
#undef ATTR
extern bool hasCustomAttribute(const clang::Decl* D, CustomAttributes A);
#endif /* CustomAttributes_h__ */

View File

@ -0,0 +1,27 @@
ATTR(moz_can_run_script)
ATTR(moz_can_run_script_boundary)
ATTR(moz_global_class)
ATTR(moz_heap_allocator)
ATTR(moz_heap_class)
ATTR(moz_implicit)
ATTR(moz_inherit_type_annotations_from_template_args)
ATTR(moz_is_smartptr_to_refcounted)
ATTR(moz_may_call_after_must_return)
ATTR(moz_must_override)
ATTR(moz_must_return_from_caller)
ATTR(moz_must_use_type)
ATTR(moz_needs_memmovable_members)
ATTR(moz_needs_memmovable_type)
ATTR(moz_needs_no_vtable_type)
ATTR(moz_no_addref_release_on_return)
ATTR(moz_no_arith_expr_in_arg)
ATTR(moz_no_dangling_on_temporaries)
ATTR(moz_non_autoable)
ATTR(moz_non_memmovable)
ATTR(moz_non_param)
ATTR(moz_non_temporary_class)
ATTR(moz_nonheap_class)
ATTR(moz_required_base_method)
ATTR(moz_stack_class)
ATTR(moz_temporary_class)
ATTR(moz_trivial_ctor_dtor)

View File

@ -14,19 +14,19 @@ namespace ast_matchers {
/// This matcher will match any function declaration that is declared as a heap
/// allocator.
AST_MATCHER(FunctionDecl, heapAllocator) {
return hasCustomAnnotation(&Node, "moz_heap_allocator");
return hasCustomAttribute<moz_heap_allocator>(&Node);
}
/// This matcher will match any declaration that is marked as not accepting
/// arithmetic expressions in its arguments.
AST_MATCHER(Decl, noArithmeticExprInArgs) {
return hasCustomAnnotation(&Node, "moz_no_arith_expr_in_arg");
return hasCustomAttribute<moz_no_arith_expr_in_arg>(&Node);
}
/// This matcher will match any C++ class that is marked as having a trivial
/// constructor and destructor.
AST_MATCHER(CXXRecordDecl, hasTrivialCtorDtor) {
return hasCustomAnnotation(&Node, "moz_trivial_ctor_dtor");
return hasCustomAttribute<moz_trivial_ctor_dtor>(&Node);
}
/// This matcher will match lvalue-ref-qualified methods.
@ -56,19 +56,19 @@ AST_MATCHER(Expr, isTemporary) {
/// This matcher will match any method declaration that is marked as returning
/// a pointer deleted by the destructor of the class.
AST_MATCHER(CXXMethodDecl, noDanglingOnTemporaries) {
return hasCustomAnnotation(&Node, "moz_no_dangling_on_temporaries");
return hasCustomAttribute<moz_no_dangling_on_temporaries>(&Node);
}
/// This matcher will match any function declaration that is marked to prohibit
/// calling AddRef or Release on its return value.
AST_MATCHER(FunctionDecl, hasNoAddRefReleaseOnReturnAttr) {
return hasCustomAnnotation(&Node, "moz_no_addref_release_on_return");
return hasCustomAttribute<moz_no_addref_release_on_return>(&Node);
}
/// This matcher will match any function declaration that is marked as being
/// allowed to run script.
AST_MATCHER(FunctionDecl, hasCanRunScriptAnnotation) {
return hasCustomAnnotation(&Node, "moz_can_run_script");
return hasCustomAttribute<moz_can_run_script>(&Node);
}
/// This matcher will match all arithmetic binary operators.
@ -156,7 +156,7 @@ AST_MATCHER(CXXRecordDecl, isRefCounted) { return isClassRefCounted(&Node); }
AST_MATCHER(QualType, hasVTable) { return typeHasVTable(Node); }
AST_MATCHER(CXXRecordDecl, hasNeedsNoVTableTypeAttr) {
return hasCustomAnnotation(&Node, "moz_needs_no_vtable_type");
return hasCustomAttribute<moz_needs_no_vtable_type>(&Node);
}
/// This matcher will select classes which are non-memmovable
@ -166,12 +166,12 @@ AST_MATCHER(QualType, isNonMemMovable) {
/// This matcher will select classes which require a memmovable template arg
AST_MATCHER(CXXRecordDecl, needsMemMovableTemplateArg) {
return hasCustomAnnotation(&Node, "moz_needs_memmovable_type");
return hasCustomAttribute<moz_needs_memmovable_type>(&Node);
}
/// This matcher will select classes which require all members to be memmovable
AST_MATCHER(CXXRecordDecl, needsMemMovableMembers) {
return hasCustomAnnotation(&Node, "moz_needs_memmovable_members");
return hasCustomAttribute<moz_needs_memmovable_members>(&Node);
}
AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) {
@ -198,7 +198,7 @@ AST_MATCHER_P(Expr, ignoreTrivials, internal::Matcher<Expr>, InnerMatcher) {
// We can't call this "isImplicit" since it clashes with an existing matcher in
// clang.
AST_MATCHER(CXXConstructorDecl, isMarkedImplicit) {
return hasCustomAnnotation(&Node, "moz_implicit");
return hasCustomAttribute<moz_implicit>(&Node);
}
AST_MATCHER(CXXRecordDecl, isConcreteClass) { return !Node.isAbstract(); }
@ -206,7 +206,7 @@ AST_MATCHER(CXXRecordDecl, isConcreteClass) { return !Node.isAbstract(); }
AST_MATCHER(QualType, autoNonAutoableType) {
if (const AutoType *T = Node->getContainedAutoType()) {
if (const CXXRecordDecl *Rec = T->getAsCXXRecordDecl()) {
return hasCustomAnnotation(Rec, "moz_non_autoable");
return hasCustomAttribute<moz_non_autoable>(Rec);
}
}
return false;
@ -258,7 +258,7 @@ AST_MATCHER(QualType, isSmartPtrToRefCounted) {
D = D->getCanonicalDecl();
return D && hasCustomAnnotation(D, "moz_is_smartptr_to_refcounted");
return D && hasCustomAttribute<moz_is_smartptr_to_refcounted>(D);
}
AST_MATCHER(CXXRecordDecl, hasBaseClasses) {
@ -270,7 +270,7 @@ AST_MATCHER(CXXRecordDecl, hasBaseClasses) {
AST_MATCHER(CXXMethodDecl, isRequiredBaseMethod) {
const CXXMethodDecl *Decl = Node.getCanonicalDecl();
return Decl && hasCustomAnnotation(Decl, "moz_required_base_method");
return Decl && hasCustomAttribute<moz_required_base_method>(Decl);
}
AST_MATCHER(CXXMethodDecl, isNonVirtual) {
@ -280,7 +280,7 @@ AST_MATCHER(CXXMethodDecl, isNonVirtual) {
AST_MATCHER(FunctionDecl, isMozMustReturnFromCaller) {
const FunctionDecl *Decl = Node.getCanonicalDecl();
return Decl && hasCustomAnnotation(Decl, "moz_must_return_from_caller");
return Decl && hasCustomAttribute<moz_must_return_from_caller>(Decl);
}
#if CLANG_VERSION_FULL < 309

View File

@ -6,16 +6,16 @@
#include "Utils.h"
CustomTypeAnnotation StackClass =
CustomTypeAnnotation("moz_stack_class", "stack");
CustomTypeAnnotation(moz_stack_class, "stack");
CustomTypeAnnotation GlobalClass =
CustomTypeAnnotation("moz_global_class", "global");
CustomTypeAnnotation(moz_global_class, "global");
CustomTypeAnnotation NonHeapClass =
CustomTypeAnnotation("moz_nonheap_class", "non-heap");
CustomTypeAnnotation HeapClass = CustomTypeAnnotation("moz_heap_class", "heap");
CustomTypeAnnotation(moz_nonheap_class, "non-heap");
CustomTypeAnnotation HeapClass = CustomTypeAnnotation(moz_heap_class, "heap");
CustomTypeAnnotation NonTemporaryClass =
CustomTypeAnnotation("moz_non_temporary_class", "non-temporary");
CustomTypeAnnotation(moz_non_temporary_class, "non-temporary");
CustomTypeAnnotation TemporaryClass =
CustomTypeAnnotation("moz_temporary_class", "temporary");
CustomTypeAnnotation(moz_temporary_class, "temporary");
void CustomTypeAnnotation::dumpAnnotationReason(BaseCheck &Check, QualType T,
SourceLocation Loc) {
@ -74,7 +74,7 @@ void CustomTypeAnnotation::dumpAnnotationReason(BaseCheck &Check, QualType T,
CustomTypeAnnotation::AnnotationReason
CustomTypeAnnotation::directAnnotationReason(QualType T) {
if (const TagDecl *D = T->getAsTagDecl()) {
if (hasCustomAnnotation(D, Spelling)) {
if (hasCustomAttribute(D, Attribute)) {
AnnotationReason Reason = {T, RK_Direct, nullptr, ""};
return Reason;
}
@ -127,8 +127,8 @@ CustomTypeAnnotation::directAnnotationReason(QualType T) {
// Recurse into template arguments if the annotation
// MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS is present
if (hasCustomAnnotation(
Declaration, "moz_inherit_type_annotations_from_template_args")) {
if (hasCustomAttribute<moz_inherit_type_annotations_from_template_args>(
Declaration)) {
const ClassTemplateSpecializationDecl *Spec =
dyn_cast<ClassTemplateSpecializationDecl>(Declaration);
if (Spec) {

View File

@ -5,6 +5,7 @@
#ifndef CustomTypeAnnotation_h__
#define CustomTypeAnnotation_h__
#include "CustomAttributes.h"
#include "plugin.h"
class CustomTypeAnnotation {
@ -27,13 +28,13 @@ class CustomTypeAnnotation {
};
typedef DenseMap<void *, AnnotationReason> ReasonCache;
const char *Spelling;
CustomAttributes Attribute;
const char *Pretty;
ReasonCache Cache;
public:
CustomTypeAnnotation(const char *Spelling, const char *Pretty)
: Spelling(Spelling), Pretty(Pretty){};
CustomTypeAnnotation(CustomAttributes Attribute, const char *Pretty)
: Attribute(Attribute), Pretty(Pretty){};
virtual ~CustomTypeAnnotation() {}

View File

@ -22,7 +22,7 @@ void ExplicitOperatorBoolChecker::check(
const CXXRecordDecl *Clazz = Method->getParent();
if (!Method->isExplicitSpecified() &&
!hasCustomAnnotation(Method, "moz_implicit") &&
!hasCustomAttribute<moz_implicit>(Method) &&
!ASTIsInSystemHeader(Method->getASTContext(), *Method) &&
isInterestingDeclForImplicitConversion(Method)) {
diag(Method->getLocStart(), "bad implicit conversion operator for %0",

View File

@ -12,7 +12,7 @@
class MemMoveAnnotation final : public CustomTypeAnnotation {
public:
MemMoveAnnotation()
: CustomTypeAnnotation("moz_non_memmovable", "non-memmove()able") {}
: CustomTypeAnnotation(moz_non_memmovable, "non-memmove()able") {}
virtual ~MemMoveAnnotation() {}

View File

@ -34,7 +34,7 @@ void MustOverrideChecker::check(const MatchFinder::MatchResult &Result) {
}
Parent = Parent->getDefinition();
for (const auto &M : Parent->methods()) {
if (hasCustomAnnotation(M, "moz_must_override"))
if (hasCustomAttribute<moz_must_override>(M))
MustOverrides.push_back(M);
}
}

View File

@ -84,7 +84,7 @@ bool MustReturnFromCallerChecker::immediatelyReturns(
if (auto CE = dyn_cast<CallExpr>(AfterTrivials)) {
auto Callee = CE->getDirectCallee();
if (Callee &&
hasCustomAnnotation(Callee, "moz_may_call_after_must_return")) {
hasCustomAttribute<moz_may_call_after_must_return>(Callee)) {
continue;
}

View File

@ -7,7 +7,7 @@
#include "CustomTypeAnnotation.h"
CustomTypeAnnotation MustUse =
CustomTypeAnnotation("moz_must_use_type", "must-use");
CustomTypeAnnotation(moz_must_use_type, "must-use");
void MustUseChecker::registerMatchers(MatchFinder *AstMatcher) {
AstMatcher->addMatcher(switchCase().bind("switchcase"), this);

View File

@ -21,7 +21,7 @@ void NoAddRefReleaseOnReturnChecker::check(
// to a MOZ_NO_ADDREF_RELEASE_ON_RETURN function or method.
if (auto *Call = dyn_cast<CallExpr>(Base)) {
if (auto *Callee = Call->getDirectCallee()) {
if (hasCustomAnnotation(Callee, "moz_no_addref_release_on_return")) {
if (hasCustomAttribute<moz_no_addref_release_on_return>(Callee)) {
diag(Call->getLocStart(),
"%1 cannot be called on the return value of %0",
DiagnosticIDs::Error)

View File

@ -7,7 +7,7 @@
class NonParamAnnotation : public CustomTypeAnnotation {
public:
NonParamAnnotation() : CustomTypeAnnotation("moz_non_param", "non-param"){};
NonParamAnnotation() : CustomTypeAnnotation(moz_non_param, "non-param"){};
protected:
// Adding alignas(_) on a struct implicitly marks it as MOZ_NON_PARAM, due to

View File

@ -11,7 +11,7 @@ void OverrideBaseCallChecker::registerMatchers(MatchFinder *AstMatcher) {
bool OverrideBaseCallChecker::isRequiredBaseMethod(
const CXXMethodDecl *Method) {
return hasCustomAnnotation(Method, "moz_required_base_method");
return hasCustomAttribute<moz_required_base_method>(Method);
}
void OverrideBaseCallChecker::evaluateExpression(

View File

@ -5,6 +5,7 @@
#ifndef Utils_h__
#define Utils_h__
#include "CustomAttributes.h"
#include "ThirdPartyPaths.h"
#include "plugin.h"
@ -333,25 +334,12 @@ inline const FieldDecl *getBaseRefCntMember(QualType T) {
return Clazz ? getBaseRefCntMember(Clazz) : 0;
}
inline bool hasCustomAnnotation(const Decl *D, StringRef Spelling) {
iterator_range<specific_attr_iterator<AnnotateAttr>> Attrs =
D->specific_attrs<AnnotateAttr>();
for (AnnotateAttr *Attr : Attrs) {
if (Attr->getAnnotation() == Spelling) {
return true;
}
}
return false;
}
inline bool isPlacementNew(const CXXNewExpr *Expression) {
// Regular new expressions aren't placement new
if (Expression->getNumPlacementArgs() == 0)
return false;
const FunctionDecl *Declaration = Expression->getOperatorNew();
if (Declaration && hasCustomAnnotation(Declaration, "moz_heap_allocator")) {
if (Declaration && hasCustomAttribute<moz_heap_allocator>(Declaration)) {
return false;
}
return true;

View File

@ -12,6 +12,7 @@ HOST_SOURCES += [
'ArithmeticArgChecker.cpp',
'AssertAssignmentChecker.cpp',
'CanRunScriptChecker.cpp',
'CustomAttributes.cpp',
'CustomTypeAnnotation.cpp',
'DanglingOnTemporaryChecker.cpp',
'DiagnosticsMatcher.cpp',