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