1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/build/clang-plugin/clang-plugin.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,393 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +#include "clang/AST/ASTConsumer.h" 1.8 +#include "clang/AST/ASTContext.h" 1.9 +#include "clang/AST/RecursiveASTVisitor.h" 1.10 +#include "clang/ASTMatchers/ASTMatchers.h" 1.11 +#include "clang/ASTMatchers/ASTMatchFinder.h" 1.12 +#include "clang/Basic/Version.h" 1.13 +#include "clang/Frontend/CompilerInstance.h" 1.14 +#include "clang/Frontend/FrontendPluginRegistry.h" 1.15 +#include "clang/Frontend/MultiplexConsumer.h" 1.16 +#include "clang/Sema/Sema.h" 1.17 +#include "llvm/ADT/DenseMap.h" 1.18 + 1.19 +#define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR) 1.20 + 1.21 +using namespace llvm; 1.22 +using namespace clang; 1.23 + 1.24 +namespace { 1.25 + 1.26 +using namespace clang::ast_matchers; 1.27 +class DiagnosticsMatcher { 1.28 +public: 1.29 + DiagnosticsMatcher(); 1.30 + 1.31 + ASTConsumer *makeASTConsumer() { 1.32 + return astMatcher.newASTConsumer(); 1.33 + } 1.34 + 1.35 +private: 1.36 + class StackClassChecker : public MatchFinder::MatchCallback { 1.37 + public: 1.38 + virtual void run(const MatchFinder::MatchResult &Result); 1.39 + void noteInferred(QualType T, DiagnosticsEngine &Diag); 1.40 + }; 1.41 + 1.42 + class NonHeapClassChecker : public MatchFinder::MatchCallback { 1.43 + public: 1.44 + virtual void run(const MatchFinder::MatchResult &Result); 1.45 + void noteInferred(QualType T, DiagnosticsEngine &Diag); 1.46 + }; 1.47 + 1.48 + StackClassChecker stackClassChecker; 1.49 + NonHeapClassChecker nonheapClassChecker; 1.50 + MatchFinder astMatcher; 1.51 +}; 1.52 + 1.53 +class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> { 1.54 + DiagnosticsEngine &Diag; 1.55 + const CompilerInstance &CI; 1.56 + DiagnosticsMatcher matcher; 1.57 +public: 1.58 + MozChecker(const CompilerInstance &CI) : Diag(CI.getDiagnostics()), CI(CI) {} 1.59 + 1.60 + ASTConsumer *getOtherConsumer() { 1.61 + return matcher.makeASTConsumer(); 1.62 + } 1.63 + 1.64 + virtual void HandleTranslationUnit(ASTContext &ctx) { 1.65 + TraverseDecl(ctx.getTranslationUnitDecl()); 1.66 + } 1.67 + 1.68 + static bool hasCustomAnnotation(const Decl *d, const char *spelling) { 1.69 + AnnotateAttr *attr = d->getAttr<AnnotateAttr>(); 1.70 + if (!attr) 1.71 + return false; 1.72 + 1.73 + return attr->getAnnotation() == spelling; 1.74 + } 1.75 + 1.76 + bool VisitCXXRecordDecl(CXXRecordDecl *d) { 1.77 + // We need definitions, not declarations 1.78 + if (!d->isThisDeclarationADefinition()) return true; 1.79 + 1.80 + // Look through all of our immediate bases to find methods that need to be 1.81 + // overridden 1.82 + typedef std::vector<CXXMethodDecl *> OverridesVector; 1.83 + OverridesVector must_overrides; 1.84 + for (CXXRecordDecl::base_class_iterator base = d->bases_begin(), 1.85 + e = d->bases_end(); base != e; ++base) { 1.86 + // The base is either a class (CXXRecordDecl) or it's a templated class... 1.87 + CXXRecordDecl *parent = base->getType() 1.88 + .getDesugaredType(d->getASTContext())->getAsCXXRecordDecl(); 1.89 + // The parent might not be resolved to a type yet. In this case, we can't 1.90 + // do any checking here. For complete correctness, we should visit 1.91 + // template instantiations, but this case is likely to be rare, so we will 1.92 + // ignore it until it becomes important. 1.93 + if (!parent) { 1.94 + continue; 1.95 + } 1.96 + parent = parent->getDefinition(); 1.97 + for (CXXRecordDecl::method_iterator M = parent->method_begin(); 1.98 + M != parent->method_end(); ++M) { 1.99 + if (hasCustomAnnotation(*M, "moz_must_override")) 1.100 + must_overrides.push_back(*M); 1.101 + } 1.102 + } 1.103 + 1.104 + for (OverridesVector::iterator it = must_overrides.begin(); 1.105 + it != must_overrides.end(); ++it) { 1.106 + bool overridden = false; 1.107 + for (CXXRecordDecl::method_iterator M = d->method_begin(); 1.108 + !overridden && M != d->method_end(); ++M) { 1.109 + // The way that Clang checks if a method M overrides its parent method 1.110 + // is if the method has the same name but would not overload. 1.111 + if (M->getName() == (*it)->getName() && 1.112 + !CI.getSema().IsOverload(*M, (*it), false)) 1.113 + overridden = true; 1.114 + } 1.115 + if (!overridden) { 1.116 + unsigned overrideID = Diag.getDiagnosticIDs()->getCustomDiagID( 1.117 + DiagnosticIDs::Error, "%0 must override %1"); 1.118 + unsigned overrideNote = Diag.getDiagnosticIDs()->getCustomDiagID( 1.119 + DiagnosticIDs::Note, "function to override is here"); 1.120 + Diag.Report(d->getLocation(), overrideID) << d->getDeclName() << 1.121 + (*it)->getDeclName(); 1.122 + Diag.Report((*it)->getLocation(), overrideNote); 1.123 + } 1.124 + } 1.125 + return true; 1.126 + } 1.127 +}; 1.128 + 1.129 +/** 1.130 + * Where classes may be allocated. Regular classes can be allocated anywhere, 1.131 + * non-heap classes on the stack or as static variables, and stack classes only 1.132 + * on the stack. Note that stack classes subsumes non-heap classes. 1.133 + */ 1.134 +enum ClassAllocationNature { 1.135 + RegularClass = 0, 1.136 + NonHeapClass = 1, 1.137 + StackClass = 2 1.138 +}; 1.139 + 1.140 +/// A cached data of whether classes are stack classes, non-heap classes, or 1.141 +/// neither. 1.142 +DenseMap<const CXXRecordDecl *, 1.143 + std::pair<const Decl *, ClassAllocationNature> > inferredAllocCauses; 1.144 + 1.145 +ClassAllocationNature getClassAttrs(QualType T); 1.146 + 1.147 +ClassAllocationNature getClassAttrs(CXXRecordDecl *D) { 1.148 + // Normalize so that D points to the definition if it exists. If it doesn't, 1.149 + // then we can't allocate it anyways. 1.150 + if (!D->hasDefinition()) 1.151 + return RegularClass; 1.152 + D = D->getDefinition(); 1.153 + // Base class: anyone with this annotation is obviously a stack class 1.154 + if (MozChecker::hasCustomAnnotation(D, "moz_stack_class")) 1.155 + return StackClass; 1.156 + 1.157 + // See if we cached the result. 1.158 + DenseMap<const CXXRecordDecl *, 1.159 + std::pair<const Decl *, ClassAllocationNature> >::iterator it = 1.160 + inferredAllocCauses.find(D); 1.161 + if (it != inferredAllocCauses.end()) { 1.162 + return it->second.second; 1.163 + } 1.164 + 1.165 + // Continue looking, we might be a stack class yet. Even if we're a nonheap 1.166 + // class, it might be possible that we've inferred to be a stack class. 1.167 + ClassAllocationNature type = RegularClass; 1.168 + if (MozChecker::hasCustomAnnotation(D, "moz_nonheap_class")) { 1.169 + type = NonHeapClass; 1.170 + } 1.171 + inferredAllocCauses.insert(std::make_pair(D, 1.172 + std::make_pair((const Decl *)0, type))); 1.173 + 1.174 + // Look through all base cases to figure out if the parent is a stack class or 1.175 + // a non-heap class. Since we might later infer to also be a stack class, keep 1.176 + // going. 1.177 + for (CXXRecordDecl::base_class_iterator base = D->bases_begin(), 1.178 + e = D->bases_end(); base != e; ++base) { 1.179 + ClassAllocationNature super = getClassAttrs(base->getType()); 1.180 + if (super == StackClass) { 1.181 + inferredAllocCauses[D] = std::make_pair( 1.182 + base->getType()->getAsCXXRecordDecl(), StackClass); 1.183 + return StackClass; 1.184 + } else if (super == NonHeapClass) { 1.185 + inferredAllocCauses[D] = std::make_pair( 1.186 + base->getType()->getAsCXXRecordDecl(), NonHeapClass); 1.187 + type = NonHeapClass; 1.188 + } 1.189 + } 1.190 + 1.191 + // Maybe it has a member which is a stack class. 1.192 + for (RecordDecl::field_iterator field = D->field_begin(), e = D->field_end(); 1.193 + field != e; ++field) { 1.194 + ClassAllocationNature fieldType = getClassAttrs(field->getType()); 1.195 + if (fieldType == StackClass) { 1.196 + inferredAllocCauses[D] = std::make_pair(*field, StackClass); 1.197 + return StackClass; 1.198 + } else if (fieldType == NonHeapClass) { 1.199 + inferredAllocCauses[D] = std::make_pair(*field, NonHeapClass); 1.200 + type = NonHeapClass; 1.201 + } 1.202 + } 1.203 + 1.204 + return type; 1.205 +} 1.206 + 1.207 +ClassAllocationNature getClassAttrs(QualType T) { 1.208 + while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe()) 1.209 + T = arrTy->getElementType(); 1.210 + CXXRecordDecl *clazz = T->getAsCXXRecordDecl(); 1.211 + return clazz ? getClassAttrs(clazz) : RegularClass; 1.212 +} 1.213 + 1.214 +} 1.215 + 1.216 +namespace clang { 1.217 +namespace ast_matchers { 1.218 + 1.219 +/// This matcher will match any class with the stack class assertion or an 1.220 +/// array of such classes. 1.221 +AST_MATCHER(QualType, stackClassAggregate) { 1.222 + return getClassAttrs(Node) == StackClass; 1.223 +} 1.224 + 1.225 +/// This matcher will match any class with the stack class assertion or an 1.226 +/// array of such classes. 1.227 +AST_MATCHER(QualType, nonheapClassAggregate) { 1.228 + return getClassAttrs(Node) == NonHeapClass; 1.229 +} 1.230 + 1.231 +/// This matcher will match any function declaration that is declared as a heap 1.232 +/// allocator. 1.233 +AST_MATCHER(FunctionDecl, heapAllocator) { 1.234 + return MozChecker::hasCustomAnnotation(&Node, "moz_heap_allocator"); 1.235 +} 1.236 +} 1.237 +} 1.238 + 1.239 +namespace { 1.240 + 1.241 +bool isPlacementNew(const CXXNewExpr *expr) { 1.242 + // Regular new expressions aren't placement new 1.243 + if (expr->getNumPlacementArgs() == 0) 1.244 + return false; 1.245 + if (MozChecker::hasCustomAnnotation(expr->getOperatorNew(), 1.246 + "moz_heap_allocator")) 1.247 + return false; 1.248 + return true; 1.249 +} 1.250 + 1.251 +DiagnosticsMatcher::DiagnosticsMatcher() { 1.252 + // Stack class assertion: non-local variables of a stack class are forbidden 1.253 + // (non-localness checked in the callback) 1.254 + astMatcher.addMatcher(varDecl(hasType(stackClassAggregate())).bind("node"), 1.255 + &stackClassChecker); 1.256 + // Stack class assertion: new stack class is forbidden (unless placement new) 1.257 + astMatcher.addMatcher(newExpr(hasType(pointerType( 1.258 + pointee(stackClassAggregate()) 1.259 + ))).bind("node"), &stackClassChecker); 1.260 + // Non-heap class assertion: new non-heap class is forbidden (unless placement 1.261 + // new) 1.262 + astMatcher.addMatcher(newExpr(hasType(pointerType( 1.263 + pointee(nonheapClassAggregate()) 1.264 + ))).bind("node"), &nonheapClassChecker); 1.265 + 1.266 + // Any heap allocation function that returns a non-heap or a stack class is 1.267 + // definitely doing something wrong 1.268 + astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(), 1.269 + returns(pointerType(pointee(nonheapClassAggregate()))))))).bind("node"), 1.270 + &nonheapClassChecker); 1.271 + astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(), 1.272 + returns(pointerType(pointee(stackClassAggregate()))))))).bind("node"), 1.273 + &stackClassChecker); 1.274 +} 1.275 + 1.276 +void DiagnosticsMatcher::StackClassChecker::run( 1.277 + const MatchFinder::MatchResult &Result) { 1.278 + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); 1.279 + unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID( 1.280 + DiagnosticIDs::Error, "variable of type %0 only valid on the stack"); 1.281 + if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) { 1.282 + // Ignore the match if it's a local variable. 1.283 + if (d->hasLocalStorage()) 1.284 + return; 1.285 + 1.286 + Diag.Report(d->getLocation(), stackID) << d->getType(); 1.287 + noteInferred(d->getType(), Diag); 1.288 + } else if (const CXXNewExpr *expr = 1.289 + Result.Nodes.getNodeAs<CXXNewExpr>("node")) { 1.290 + // If it's placement new, then this match doesn't count. 1.291 + if (isPlacementNew(expr)) 1.292 + return; 1.293 + Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType(); 1.294 + noteInferred(expr->getAllocatedType(), Diag); 1.295 + } else if (const CallExpr *expr = 1.296 + Result.Nodes.getNodeAs<CallExpr>("node")) { 1.297 + QualType badType = expr->getCallReturnType()->getPointeeType(); 1.298 + Diag.Report(expr->getLocStart(), stackID) << badType; 1.299 + noteInferred(badType, Diag); 1.300 + } 1.301 +} 1.302 + 1.303 +void DiagnosticsMatcher::StackClassChecker::noteInferred(QualType T, 1.304 + DiagnosticsEngine &Diag) { 1.305 + unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID( 1.306 + DiagnosticIDs::Note, 1.307 + "%0 is a stack class because it inherits from a stack class %1"); 1.308 + unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID( 1.309 + DiagnosticIDs::Note, 1.310 + "%0 is a stack class because member %1 is a stack class %2"); 1.311 + 1.312 + // Find the CXXRecordDecl that is the stack class of interest 1.313 + while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe()) 1.314 + T = arrTy->getElementType(); 1.315 + CXXRecordDecl *clazz = T->getAsCXXRecordDecl(); 1.316 + 1.317 + // Direct result, we're done. 1.318 + if (MozChecker::hasCustomAnnotation(clazz, "moz_stack_class")) 1.319 + return; 1.320 + 1.321 + const Decl *cause = inferredAllocCauses[clazz].first; 1.322 + if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) { 1.323 + Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName(); 1.324 + } else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) { 1.325 + Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType(); 1.326 + } 1.327 + 1.328 + // Recursively follow this back. 1.329 + noteInferred(cast<ValueDecl>(cause)->getType(), Diag); 1.330 +} 1.331 + 1.332 +void DiagnosticsMatcher::NonHeapClassChecker::run( 1.333 + const MatchFinder::MatchResult &Result) { 1.334 + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); 1.335 + unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID( 1.336 + DiagnosticIDs::Error, "variable of type %0 is not valid on the heap"); 1.337 + if (const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node")) { 1.338 + // If it's placement new, then this match doesn't count. 1.339 + if (isPlacementNew(expr)) 1.340 + return; 1.341 + Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType(); 1.342 + noteInferred(expr->getAllocatedType(), Diag); 1.343 + } else if (const CallExpr *expr = Result.Nodes.getNodeAs<CallExpr>("node")) { 1.344 + QualType badType = expr->getCallReturnType()->getPointeeType(); 1.345 + Diag.Report(expr->getLocStart(), stackID) << badType; 1.346 + noteInferred(badType, Diag); 1.347 + } 1.348 +} 1.349 + 1.350 +void DiagnosticsMatcher::NonHeapClassChecker::noteInferred(QualType T, 1.351 + DiagnosticsEngine &Diag) { 1.352 + unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID( 1.353 + DiagnosticIDs::Note, 1.354 + "%0 is a non-heap class because it inherits from a non-heap class %1"); 1.355 + unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID( 1.356 + DiagnosticIDs::Note, 1.357 + "%0 is a non-heap class because member %1 is a non-heap class %2"); 1.358 + 1.359 + // Find the CXXRecordDecl that is the stack class of interest 1.360 + while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe()) 1.361 + T = arrTy->getElementType(); 1.362 + CXXRecordDecl *clazz = T->getAsCXXRecordDecl(); 1.363 + 1.364 + // Direct result, we're done. 1.365 + if (MozChecker::hasCustomAnnotation(clazz, "moz_nonheap_class")) 1.366 + return; 1.367 + 1.368 + const Decl *cause = inferredAllocCauses[clazz].first; 1.369 + if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) { 1.370 + Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName(); 1.371 + } else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) { 1.372 + Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType(); 1.373 + } 1.374 + 1.375 + // Recursively follow this back. 1.376 + noteInferred(cast<ValueDecl>(cause)->getType(), Diag); 1.377 +} 1.378 + 1.379 +class MozCheckAction : public PluginASTAction { 1.380 +public: 1.381 + ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef fileName) { 1.382 + MozChecker *checker = new MozChecker(CI); 1.383 + 1.384 + ASTConsumer *consumers[] = { checker, checker->getOtherConsumer() }; 1.385 + return new MultiplexConsumer(consumers); 1.386 + } 1.387 + 1.388 + bool ParseArgs(const CompilerInstance &CI, 1.389 + const std::vector<std::string> &args) { 1.390 + return true; 1.391 + } 1.392 +}; 1.393 +} 1.394 + 1.395 +static FrontendPluginRegistry::Add<MozCheckAction> 1.396 +X("moz-check", "check moz action");