1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "CustomAttributes.h"
6 #include "plugin.h"
7 #include "clang/Frontend/FrontendPluginRegistry.h"
8 #include <algorithm>
9
10 /* Having annotations in the AST unexpectedly impacts codegen.
11 * Ideally, we'd avoid having annotations at all, by using an API such as
12 * the one from https://reviews.llvm.org/D31338, and storing the attributes
13 * data separately from the AST on our own. Unfortunately, there is no such
14 * API currently in clang, so we must do without.
15 * We can do something similar, though, where we go through the AST before
16 * running the checks, create a mapping of AST nodes to attributes, and
17 * remove the attributes/annotations from the AST nodes.
18 * Not all declarations can be reached from the decl() AST matcher, though,
19 * so we do our best effort (getting the other declarations we look at in
20 * checks). We emit a warning when checks look at a note that still has
21 * annotations attached (aka, hasn't been seen during our first pass),
22 * so that those don't go unnoticed. (-Werror should then take care of
23 * making that an error)
24 */
25
26 using namespace clang;
27 using namespace llvm;
28
29 static DenseMap<const Decl *, CustomAttributesSet> AttributesCache;
30
CacheAttributes(const Decl * D)31 static CustomAttributesSet CacheAttributes(const Decl *D) {
32 CustomAttributesSet attrs = {};
33 for (auto Attr : D->specific_attrs<AnnotateAttr>()) {
34 auto annotation = Attr->getAnnotation();
35 #define ATTR(a) \
36 if (annotation == #a) { \
37 attrs.has_##a = true; \
38 } else
39 #include "CustomAttributes.inc"
40 #include "external/CustomAttributes.inc"
41 #undef ATTR
42 {}
43 }
44 const_cast<Decl *>(D)->dropAttr<AnnotateAttr>();
45 AttributesCache.insert(std::make_pair(D, attrs));
46 return attrs;
47 }
48
Report(const Decl * D,const char * message)49 static void Report(const Decl *D, const char *message) {
50 ASTContext &Context = D->getASTContext();
51 DiagnosticsEngine &Diag = Context.getDiagnostics();
52 unsigned ID =
53 Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Warning, message);
54 Diag.Report(D->getBeginLoc(), ID);
55 }
56
GetAttributes(const Decl * D)57 CustomAttributesSet GetAttributes(const Decl *D) {
58 CustomAttributesSet attrs = {};
59 if (D->hasAttr<AnnotateAttr>()) {
60 Report(D, "Declaration has unhandled annotations.");
61 attrs = CacheAttributes(D);
62 } else {
63 auto attributes = AttributesCache.find(D);
64 if (attributes != AttributesCache.end()) {
65 attrs = attributes->second;
66 }
67 }
68 return attrs;
69 }
70
hasCustomAttribute(const clang::Decl * D,CustomAttributes A)71 bool hasCustomAttribute(const clang::Decl *D, CustomAttributes A) {
72 CustomAttributesSet attrs = GetAttributes(D);
73 switch (A) {
74 #define ATTR(a) \
75 case a: \
76 return attrs.has_##a;
77 #include "CustomAttributes.inc"
78 #include "external/CustomAttributes.inc"
79 #undef ATTR
80 }
81 return false;
82 }
83
84 class CustomAttributesMatcher
85 : public ast_matchers::MatchFinder::MatchCallback {
86 public:
run(const ast_matchers::MatchFinder::MatchResult & Result)87 void run(const ast_matchers::MatchFinder::MatchResult &Result) final {
88 if (auto D = Result.Nodes.getNodeAs<Decl>("decl")) {
89 CacheAttributes(D);
90 } else if (auto L = Result.Nodes.getNodeAs<LambdaExpr>("lambda")) {
91 CacheAttributes(L->getCallOperator());
92 CacheAttributes(L->getLambdaClass());
93 }
94 }
95 };
96
97 class CustomAttributesAction : public PluginASTAction {
98 public:
CreateASTConsumer(CompilerInstance & CI,StringRef FileName)99 ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI,
100 StringRef FileName) override {
101 auto &Context = CI.getASTContext();
102 auto AstMatcher = new (Context.Allocate<MatchFinder>()) MatchFinder();
103 auto Matcher = new (Context.Allocate<CustomAttributesMatcher>())
104 CustomAttributesMatcher();
105 AstMatcher->addMatcher(decl().bind("decl"), Matcher);
106 AstMatcher->addMatcher(lambdaExpr().bind("lambda"), Matcher);
107 return AstMatcher->newASTConsumer();
108 }
109
ParseArgs(const CompilerInstance & CI,const std::vector<std::string> & Args)110 bool ParseArgs(const CompilerInstance &CI,
111 const std::vector<std::string> &Args) override {
112 return true;
113 }
114
getActionType()115 ActionType getActionType() override { return AddBeforeMainAction; }
116 };
117
118 static FrontendPluginRegistry::Add<CustomAttributesAction>
119 X("moz-custom-attributes", "prepare custom attributes for moz-check");
120