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