Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // A general interface for filtering and only acting on classes in Chromium C++ | |
| 6 // code. | |
| 7 | |
| 8 #include "ChromeClassTester.h" | |
| 9 | |
| 10 #include "clang/Basic/FileManager.h" | |
| 11 | |
| 12 using namespace clang; | |
| 13 | |
| 14 namespace { | |
| 15 | |
| 16 bool starts_with(const std::string& one, const std::string& two) { | |
| 17 return one.substr(0, two.size()) == two; | |
| 18 } | |
| 19 | |
| 20 bool ends_with(const std::string& one, const std::string& two) { | |
| 21 return one.substr(one.size() - two.size(), two.size()) == two; | |
| 22 } | |
| 23 | |
| 24 } // namespace | |
| 25 | |
| 26 ChromeClassTester::ChromeClassTester(CompilerInstance& instance) | |
| 27 : instance_(instance), | |
| 28 diagnostic_(instance.getDiagnostics()) { | |
| 29 banned_namespaces_.push_back("std"); | |
| 30 banned_namespaces_.push_back("__gnu_cxx"); | |
| 31 | |
| 32 banned_directories_.push_back("third_party"); | |
| 33 banned_directories_.push_back("native_client"); | |
| 34 banned_directories_.push_back("breakpad"); | |
| 35 banned_directories_.push_back("courgette"); | |
| 36 banned_directories_.push_back("ppapi"); | |
| 37 banned_directories_.push_back("/usr"); | |
| 38 banned_directories_.push_back("testing"); | |
| 39 banned_directories_.push_back("googleurl"); | |
| 40 banned_directories_.push_back("v8"); | |
| 41 banned_directories_.push_back("sdch"); | |
| 42 | |
| 43 // You are standing in a mazy of twisty dependencies, all resolved by | |
| 44 // putting everything in the header. | |
| 45 banned_directories_.push_back("chrome/test/automation"); | |
| 46 | |
| 47 // Used in really low level threading code that probably shouldn't be out of | |
| 48 // lined. | |
| 49 ignored_record_names_.push_back("ThreadLocalBoolean"); | |
| 50 | |
| 51 // A complicated pickle derived struct that is all packed integers. | |
| 52 ignored_record_names_.push_back("Header"); | |
| 53 | |
| 54 // Part of the GPU system that uses multiple included header | |
| 55 // weirdness. Never getting this right. | |
| 56 ignored_record_names_.push_back("Validators"); | |
| 57 | |
| 58 // RAII class that's simple enough (media/base/callback.h). | |
| 59 ignored_record_names_.push_back("AutoTaskRunner"); | |
| 60 ignored_record_names_.push_back("AutoCallbackRunner"); | |
| 61 | |
| 62 // Part of our public interface that nacl and friends use. (Arguably, this | |
| 63 // should mean that this is a higher priority but fixing this looks hard.) | |
| 64 ignored_record_names_.push_back("PluginVersionInfo"); | |
| 65 } | |
| 66 | |
| 67 ChromeClassTester::~ChromeClassTester() {} | |
| 68 | |
| 69 void ChromeClassTester::HandleTagDeclDefinition(TagDecl* tag) { | |
| 70 if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) { | |
| 71 // If this is a POD or a class template or a type dependent on a | |
| 72 // templated class, assume there's no ctor/dtor/virtual method | |
| 73 // optimization that we can do. | |
| 74 if (record->isPOD() || | |
| 75 record->getDescribedClassTemplate() || | |
| 76 record->getTemplateSpecializationKind() || | |
| 77 record->isDependentType()) | |
| 78 return; | |
| 79 | |
| 80 if (InBannedNamespace(record)) | |
| 81 return; | |
| 82 | |
| 83 SourceLocation record_location = record->getInnerLocStart(); | |
| 84 if (InBannedDirectory(record_location)) | |
| 85 return; | |
| 86 | |
| 87 // For now, due to large amounts of inlining in our unit test code, there's | |
| 88 // no way in hell we'll pass our checks but we want to deploy clang plugins | |
| 89 // now. | |
| 90 // | |
| 91 // TODO(erg): Deinline all our test code, and only perform something like | |
| 92 // this check for the "virtual" inlining tests on GMOCK code (since all of | |
| 93 // that is autogenerated and doesn't specify the "virtual" keyword). | |
| 94 if (IsTestCode(record)) { | |
| 95 return; | |
| 96 } | |
| 97 | |
| 98 // We sadly need to maintain a blacklist of types that violate these | |
| 99 // rules, but do so for good reason or due to limitations of this | |
| 100 // checker (i.e., we don't handle extern templates very well). | |
| 101 if (IsIgnoredType(record)) | |
| 102 return; | |
| 103 | |
| 104 CheckChromeClass(record_location, record); | |
| 105 } | |
| 106 } | |
| 107 | |
| 108 void ChromeClassTester::emitWarning(SourceLocation loc, const char* raw_error) { | |
| 109 FullSourceLoc full(loc, instance().getSourceManager()); | |
| 110 std::string err; | |
| 111 err = "[chrome-style] "; | |
| 112 err += raw_error; | |
| 113 unsigned id = diagnostic().getCustomDiagID(Diagnostic::Warning, err); | |
| 114 DiagnosticBuilder B = diagnostic().Report(full, id); | |
| 115 } | |
| 116 | |
| 117 bool ChromeClassTester::IsTestCode(Decl* record) { | |
| 118 if (instance_.hasSourceManager()) { | |
| 119 SourceManager& m = instance_.getSourceManager(); | |
| 120 std::string name = m.getFileEntryForID(m.getMainFileID())->getName(); | |
| 121 return name.find("test") != name.npos || | |
| 122 name.find("mock") != name.npos; | |
| 123 } | |
| 124 return false; | |
| 125 } | |
| 126 | |
| 127 bool ChromeClassTester::InBannedNamespace(Decl* record) { | |
| 128 std::string n = GetNamespace(record); | |
| 129 if (n != "") { | |
|
tfarina
2011/02/03 02:47:53
!n.empty() ?
| |
| 130 return std::find(banned_namespaces_.begin(), banned_namespaces_.end(), n) | |
| 131 != banned_namespaces_.end(); | |
| 132 } | |
| 133 | |
| 134 return false; | |
| 135 } | |
| 136 | |
| 137 std::string ChromeClassTester::GetNamespace(Decl* record) { | |
| 138 return GetNamespaceImpl(record->getDeclContext(), ""); | |
| 139 } | |
| 140 | |
| 141 std::string ChromeClassTester::GetNamespaceImpl(const DeclContext* context, | |
| 142 std::string candidate) { | |
| 143 switch (context->getDeclKind()) { | |
| 144 case Decl::TranslationUnit: { | |
| 145 return candidate; | |
| 146 } | |
| 147 case Decl::Namespace: { | |
| 148 const NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context); | |
| 149 std::string name_str; | |
| 150 llvm::raw_string_ostream OS(name_str); | |
| 151 if (decl->isAnonymousNamespace()) | |
| 152 OS << "<anonymous namespace>"; | |
| 153 else | |
| 154 OS << decl; | |
| 155 return GetNamespaceImpl(context->getParent(), | |
| 156 OS.str()); | |
| 157 } | |
| 158 default: { | |
| 159 return GetNamespaceImpl(context->getParent(), candidate); | |
| 160 } | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 bool ChromeClassTester::InBannedDirectory(const SourceLocation& loc) { | |
| 165 if (loc.isFileID() && loc.isValid()) { | |
| 166 bool invalid = false; | |
| 167 const char* buffer_name = instance_.getSourceManager().getBufferName( | |
| 168 loc, &invalid); | |
| 169 if (!invalid && buffer_name) { | |
| 170 std::string b(buffer_name); | |
| 171 | |
| 172 // Don't complain about these things in implementation files. | |
| 173 if (ends_with(b, ".cc") || ends_with(b, ".cpp")) { | |
| 174 return true; | |
| 175 } | |
| 176 | |
| 177 // Don't complain about autogenerated protobuf files. | |
| 178 if (ends_with(b, ".pb.h")) { | |
| 179 return true; | |
| 180 } | |
| 181 | |
| 182 // Strip preceding path crap. | |
| 183 if (starts_with(b, "./")) | |
| 184 b = b.substr(2); | |
| 185 | |
| 186 for (std::vector<std::string>::const_iterator it = | |
| 187 banned_directories_.begin(); | |
| 188 it != banned_directories_.end(); ++it) { | |
| 189 if (starts_with(b, *it)) | |
| 190 return true; | |
| 191 } | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 return false; | |
| 196 } | |
| 197 | |
| 198 bool ChromeClassTester::IsIgnoredType(RecordDecl* record) { | |
| 199 std::string base_name = record->getNameAsString(); | |
| 200 for (std::vector<std::string>::const_iterator it = | |
| 201 ignored_record_names_.begin(); | |
| 202 it != ignored_record_names_.end(); ++it) { | |
| 203 if (base_name == *it) | |
| 204 return true; | |
| 205 } | |
| 206 | |
| 207 return false; | |
| 208 } | |
| OLD | NEW |