1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/webrtc/trunk/tools/clang/plugins/ChromeClassTester.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,294 @@ 1.4 +// Copyright (c) 2012 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +// A general interface for filtering and only acting on classes in Chromium C++ 1.9 +// code. 1.10 + 1.11 +#include "ChromeClassTester.h" 1.12 + 1.13 +#include <sys/param.h> 1.14 + 1.15 +#include "clang/AST/AST.h" 1.16 +#include "clang/Basic/FileManager.h" 1.17 +#include "clang/Basic/SourceManager.h" 1.18 + 1.19 +using namespace clang; 1.20 + 1.21 +namespace { 1.22 + 1.23 +bool starts_with(const std::string& one, const std::string& two) { 1.24 + return one.compare(0, two.size(), two) == 0; 1.25 +} 1.26 + 1.27 +std::string lstrip(const std::string& one, const std::string& two) { 1.28 + if (starts_with(one, two)) 1.29 + return one.substr(two.size()); 1.30 + return one; 1.31 +} 1.32 + 1.33 +bool ends_with(const std::string& one, const std::string& two) { 1.34 + if (two.size() > one.size()) 1.35 + return false; 1.36 + 1.37 + return one.compare(one.size() - two.size(), two.size(), two) == 0; 1.38 +} 1.39 + 1.40 +} // namespace 1.41 + 1.42 +ChromeClassTester::ChromeClassTester(CompilerInstance& instance) 1.43 + : instance_(instance), 1.44 + diagnostic_(instance.getDiagnostics()) { 1.45 + BuildBannedLists(); 1.46 +} 1.47 + 1.48 +ChromeClassTester::~ChromeClassTester() {} 1.49 + 1.50 +void ChromeClassTester::HandleTagDeclDefinition(TagDecl* tag) { 1.51 + pending_class_decls_.push_back(tag); 1.52 +} 1.53 + 1.54 +bool ChromeClassTester::HandleTopLevelDecl(DeclGroupRef group_ref) { 1.55 + for (size_t i = 0; i < pending_class_decls_.size(); ++i) 1.56 + CheckTag(pending_class_decls_[i]); 1.57 + pending_class_decls_.clear(); 1.58 + 1.59 + return true; // true means continue parsing. 1.60 +} 1.61 + 1.62 +void ChromeClassTester::CheckTag(TagDecl* tag) { 1.63 + // We handle class types here where we have semantic information. We can only 1.64 + // check structs/classes/enums here, but we get a bunch of nice semantic 1.65 + // information instead of just parsing information. 1.66 + 1.67 + if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) { 1.68 + // If this is a POD or a class template or a type dependent on a 1.69 + // templated class, assume there's no ctor/dtor/virtual method 1.70 + // optimization that we can do. 1.71 + if (record->isPOD() || 1.72 + record->getDescribedClassTemplate() || 1.73 + record->getTemplateSpecializationKind() || 1.74 + record->isDependentType()) 1.75 + return; 1.76 + 1.77 + if (InBannedNamespace(record)) 1.78 + return; 1.79 + 1.80 + SourceLocation record_location = record->getInnerLocStart(); 1.81 + if (InBannedDirectory(record_location)) 1.82 + return; 1.83 + 1.84 + // We sadly need to maintain a blacklist of types that violate these 1.85 + // rules, but do so for good reason or due to limitations of this 1.86 + // checker (i.e., we don't handle extern templates very well). 1.87 + std::string base_name = record->getNameAsString(); 1.88 + if (IsIgnoredType(base_name)) 1.89 + return; 1.90 + 1.91 + // We ignore all classes that end with "Matcher" because they're probably 1.92 + // GMock artifacts. 1.93 + if (ends_with(base_name, "Matcher")) 1.94 + return; 1.95 + 1.96 + CheckChromeClass(record_location, record); 1.97 + } 1.98 +} 1.99 + 1.100 +void ChromeClassTester::emitWarning(SourceLocation loc, 1.101 + const char* raw_error) { 1.102 + FullSourceLoc full(loc, instance().getSourceManager()); 1.103 + std::string err; 1.104 + err = "[chromium-style] "; 1.105 + err += raw_error; 1.106 + DiagnosticsEngine::Level level = 1.107 + diagnostic().getWarningsAsErrors() ? 1.108 + DiagnosticsEngine::Error : 1.109 + DiagnosticsEngine::Warning; 1.110 + unsigned id = diagnostic().getCustomDiagID(level, err); 1.111 + DiagnosticBuilder builder = diagnostic().Report(full, id); 1.112 +} 1.113 + 1.114 +bool ChromeClassTester::InBannedNamespace(const Decl* record) { 1.115 + std::string n = GetNamespace(record); 1.116 + if (!n.empty()) { 1.117 + return std::find(banned_namespaces_.begin(), banned_namespaces_.end(), n) 1.118 + != banned_namespaces_.end(); 1.119 + } 1.120 + 1.121 + return false; 1.122 +} 1.123 + 1.124 +std::string ChromeClassTester::GetNamespace(const Decl* record) { 1.125 + return GetNamespaceImpl(record->getDeclContext(), ""); 1.126 +} 1.127 + 1.128 +bool ChromeClassTester::InImplementationFile(SourceLocation record_location) { 1.129 + std::string filename; 1.130 + if (!GetFilename(record_location, &filename)) 1.131 + return false; 1.132 + 1.133 + if (ends_with(filename, ".cc") || ends_with(filename, ".cpp") || 1.134 + ends_with(filename, ".mm")) { 1.135 + return true; 1.136 + } 1.137 + 1.138 + return false; 1.139 +} 1.140 + 1.141 +void ChromeClassTester::BuildBannedLists() { 1.142 + banned_namespaces_.push_back("std"); 1.143 + banned_namespaces_.push_back("__gnu_cxx"); 1.144 + banned_namespaces_.push_back("WebKit"); 1.145 + 1.146 + banned_directories_.push_back("third_party/"); 1.147 + banned_directories_.push_back("native_client/"); 1.148 + banned_directories_.push_back("breakpad/"); 1.149 + banned_directories_.push_back("courgette/"); 1.150 + banned_directories_.push_back("pdf/"); 1.151 + banned_directories_.push_back("ppapi/"); 1.152 + banned_directories_.push_back("usr/"); 1.153 + banned_directories_.push_back("testing/"); 1.154 + banned_directories_.push_back("googleurl/"); 1.155 + banned_directories_.push_back("v8/"); 1.156 + banned_directories_.push_back("dart/"); 1.157 + banned_directories_.push_back("sdch/"); 1.158 + banned_directories_.push_back("icu4c/"); 1.159 + banned_directories_.push_back("frameworks/"); 1.160 + 1.161 + // Don't check autogenerated headers. 1.162 + // Make puts them below $(builddir_name)/.../gen and geni. 1.163 + // Ninja puts them below OUTPUT_DIR/.../gen 1.164 + // Xcode has a fixed output directory for everything. 1.165 + banned_directories_.push_back("gen/"); 1.166 + banned_directories_.push_back("geni/"); 1.167 + banned_directories_.push_back("xcodebuild/"); 1.168 + 1.169 + // You are standing in a mazy of twisty dependencies, all resolved by 1.170 + // putting everything in the header. 1.171 + banned_directories_.push_back("automation/"); 1.172 + 1.173 + // Don't check system headers. 1.174 + banned_directories_.push_back("/Developer/"); 1.175 + 1.176 + // Used in really low level threading code that probably shouldn't be out of 1.177 + // lined. 1.178 + ignored_record_names_.insert("ThreadLocalBoolean"); 1.179 + 1.180 + // A complicated pickle derived struct that is all packed integers. 1.181 + ignored_record_names_.insert("Header"); 1.182 + 1.183 + // Part of the GPU system that uses multiple included header 1.184 + // weirdness. Never getting this right. 1.185 + ignored_record_names_.insert("Validators"); 1.186 + 1.187 + // Has a UNIT_TEST only constructor. Isn't *terribly* complex... 1.188 + ignored_record_names_.insert("AutocompleteController"); 1.189 + ignored_record_names_.insert("HistoryURLProvider"); 1.190 + 1.191 + // Because of chrome frame 1.192 + ignored_record_names_.insert("ReliabilityTestSuite"); 1.193 + 1.194 + // Used over in the net unittests. A large enough bundle of integers with 1 1.195 + // non-pod class member. Probably harmless. 1.196 + ignored_record_names_.insert("MockTransaction"); 1.197 + 1.198 + // Used heavily in ui_unittests and once in views_unittests. Fixing this 1.199 + // isn't worth the overhead of an additional library. 1.200 + ignored_record_names_.insert("TestAnimationDelegate"); 1.201 + 1.202 + // Part of our public interface that nacl and friends use. (Arguably, this 1.203 + // should mean that this is a higher priority but fixing this looks hard.) 1.204 + ignored_record_names_.insert("PluginVersionInfo"); 1.205 +} 1.206 + 1.207 +std::string ChromeClassTester::GetNamespaceImpl(const DeclContext* context, 1.208 + const std::string& candidate) { 1.209 + switch (context->getDeclKind()) { 1.210 + case Decl::TranslationUnit: { 1.211 + return candidate; 1.212 + } 1.213 + case Decl::Namespace: { 1.214 + const NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context); 1.215 + std::string name_str; 1.216 + llvm::raw_string_ostream OS(name_str); 1.217 + if (decl->isAnonymousNamespace()) 1.218 + OS << "<anonymous namespace>"; 1.219 + else 1.220 + OS << *decl; 1.221 + return GetNamespaceImpl(context->getParent(), 1.222 + OS.str()); 1.223 + } 1.224 + default: { 1.225 + return GetNamespaceImpl(context->getParent(), candidate); 1.226 + } 1.227 + } 1.228 +} 1.229 + 1.230 +bool ChromeClassTester::InBannedDirectory(SourceLocation loc) { 1.231 + std::string filename; 1.232 + if (!GetFilename(loc, &filename)) { 1.233 + // If the filename cannot be determined, simply treat this as a banned 1.234 + // location, instead of going through the full lookup process. 1.235 + return true; 1.236 + } 1.237 + 1.238 + // We need to special case scratch space; which is where clang does its 1.239 + // macro expansion. We explicitly want to allow people to do otherwise bad 1.240 + // things through macros that were defined due to third party libraries. 1.241 + if (filename == "<scratch space>") 1.242 + return true; 1.243 + 1.244 + // Don't complain about autogenerated protobuf files. 1.245 + if (ends_with(filename, ".pb.h")) { 1.246 + return true; 1.247 + } 1.248 + 1.249 + // We need to munge the paths so that they are relative to the repository 1.250 + // srcroot. We first resolve the symlinktastic relative path and then 1.251 + // remove our known srcroot from it if needed. 1.252 + char resolvedPath[MAXPATHLEN]; 1.253 + if (realpath(filename.c_str(), resolvedPath)) { 1.254 + filename = resolvedPath; 1.255 + } 1.256 + 1.257 + // On linux, chrome is often checked out to /usr/local/google. Due to the 1.258 + // "usr" rule in banned_directories_, all diagnostics would be suppressed 1.259 + // in that case. As a workaround, strip that prefix. 1.260 + filename = lstrip(filename, "/usr/local/google"); 1.261 + 1.262 + for (std::vector<std::string>::const_iterator it = 1.263 + banned_directories_.begin(); 1.264 + it != banned_directories_.end(); ++it) { 1.265 + // If we can find any of the banned path components in this path, then 1.266 + // this file is rejected. 1.267 + size_t index = filename.find(*it); 1.268 + if (index != std::string::npos) { 1.269 + bool matches_full_dir_name = index == 0 || filename[index - 1] == '/'; 1.270 + if ((*it)[0] == '/') 1.271 + matches_full_dir_name = true; 1.272 + if (matches_full_dir_name) 1.273 + return true; 1.274 + } 1.275 + } 1.276 + 1.277 + return false; 1.278 +} 1.279 + 1.280 +bool ChromeClassTester::IsIgnoredType(const std::string& base_name) { 1.281 + return ignored_record_names_.find(base_name) != ignored_record_names_.end(); 1.282 +} 1.283 + 1.284 +bool ChromeClassTester::GetFilename(SourceLocation loc, 1.285 + std::string* filename) { 1.286 + const SourceManager& source_manager = instance_.getSourceManager(); 1.287 + SourceLocation spelling_location = source_manager.getSpellingLoc(loc); 1.288 + PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location); 1.289 + if (ploc.isInvalid()) { 1.290 + // If we're in an invalid location, we're looking at things that aren't 1.291 + // actually stated in the source. 1.292 + return false; 1.293 + } 1.294 + 1.295 + *filename = ploc.getFilename(); 1.296 + return true; 1.297 +}