media/webrtc/trunk/tools/clang/plugins/ChromeClassTester.cpp

changeset 0
6474c204b198
     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 +}

mercurial