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 != "") { |
| 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 |