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 d_(instance.getDiagnostics()) { | |
| 29 // TODO: Ignore a user configurable list of toplevel namespaces, not just | |
| 30 // these hardcoded ones. | |
| 31 banned_namespaces_.push_back("std"); | |
| 32 banned_namespaces_.push_back("__gnu_cxx"); | |
| 33 | |
| 34 // TODO: Let the user configure this list of directories. | |
|
Nico
2011/02/02 23:13:06
i think this todo and the one before can just be d
| |
| 35 banned_directories_.push_back("third_party"); | |
| 36 banned_directories_.push_back("native_client"); | |
| 37 banned_directories_.push_back("breakpad"); | |
| 38 banned_directories_.push_back("courgette"); | |
| 39 banned_directories_.push_back("ppapi"); | |
| 40 banned_directories_.push_back("/usr"); | |
| 41 banned_directories_.push_back("testing"); | |
| 42 banned_directories_.push_back("googleurl"); | |
| 43 banned_directories_.push_back("v8"); | |
| 44 banned_directories_.push_back("sdch"); | |
| 45 | |
| 46 // You are standing in a mazy of twisty dependencies, all resolved by | |
| 47 // putting everything in the header. | |
| 48 banned_directories_.push_back("chrome/test/automation"); | |
| 49 | |
| 50 // TODO: Confurgre this some other way. | |
|
Nico
2011/02/02 23:13:06
"Configure"?
| |
| 51 ignored_record_names_.push_back("ThreadLocalBoolean"); | |
| 52 | |
| 53 // A complicated pickle derived struct that is all packed integers. | |
| 54 ignored_record_names_.push_back("Header"); | |
| 55 | |
| 56 // Part of the GPU system that uses multiple included header | |
| 57 // weirdness. Never getting this right. | |
| 58 ignored_record_names_.push_back("Validators"); | |
| 59 | |
| 60 // RAII class that's simple enough (media/base/callback.h). | |
| 61 ignored_record_names_.push_back("AutoTaskRunner"); | |
| 62 ignored_record_names_.push_back("AutoCallbackRunner"); | |
| 63 | |
| 64 // Part of our public interface that nacl and friends use. (Arguably, this | |
| 65 // should mean that this is a higher priority but fixing this looks hard.) | |
| 66 ignored_record_names_.push_back("PluginVersionInfo"); | |
| 67 } | |
| 68 | |
| 69 ChromeClassTester::~ChromeClassTester() {} | |
| 70 | |
| 71 void ChromeClassTester::HandleTagDeclDefinition(TagDecl* tag) { | |
| 72 if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) { | |
| 73 // If this is a POD or a class template or a type dependent on a | |
| 74 // templated class, assume there's no ctor/dtor/virtual method | |
| 75 // optimization that we can do. | |
| 76 if (record->isPOD() || | |
| 77 record->getDescribedClassTemplate() || | |
| 78 record->getTemplateSpecializationKind() || | |
| 79 record->isDependentType()) | |
| 80 return; | |
| 81 | |
| 82 if (InBannedNamespace(record)) | |
| 83 return; | |
| 84 | |
| 85 SourceLocation record_location = record->getInnerLocStart(); | |
| 86 if (InBannedDirectory(record_location)) | |
| 87 return; | |
| 88 | |
| 89 // For now, due to large amounts of inlining in our unit test code, there's | |
| 90 // no way in hell we'll pass our checks but we want to deploy clang plugins | |
| 91 // now. | |
|
Nico
2011/02/02 23:13:06
TODO? Seems like inlining in test code is what's s
| |
| 92 if (IsTestCode(record)) { | |
| 93 return; | |
| 94 } | |
| 95 | |
| 96 // We sadly need to maintain a blacklist of types that violate these | |
| 97 // rules, but do so for good reason or due to limitations of this | |
| 98 // checker (i.e., we don't handle extern templates very well). | |
| 99 if (IsIgnoredType(record)) | |
| 100 return; | |
| 101 | |
| 102 CheckChromeClass(record_location, record); | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 void ChromeClassTester::emitWarning(SourceLocation loc, const char* raw_error) { | |
| 107 FullSourceLoc full(loc, instance().getSourceManager()); | |
| 108 std::string err; | |
| 109 err = "[chrome-style] "; | |
| 110 err += raw_error; | |
| 111 unsigned id = diagnostic().getCustomDiagID(Diagnostic::Warning, err); | |
| 112 DiagnosticBuilder B = diagnostic().Report(full, id); | |
| 113 } | |
| 114 | |
| 115 bool ChromeClassTester::IsTestCode(Decl* record) { | |
| 116 if (instance_.hasSourceManager()) { | |
| 117 SourceManager& m = instance_.getSourceManager(); | |
| 118 std::string name = m.getFileEntryForID(m.getMainFileID())->getName(); | |
| 119 return name.find("test") != name.npos || | |
| 120 name.find("mock") != name.npos; | |
| 121 } | |
| 122 return false; | |
| 123 } | |
| 124 | |
| 125 bool ChromeClassTester::InBannedNamespace(Decl* record) { | |
| 126 std::string n = GetNamespace(record); | |
| 127 if (n != "") { | |
| 128 return std::find(banned_namespaces_.begin(), banned_namespaces_.end(), n) | |
| 129 != banned_namespaces_.end(); | |
| 130 } | |
| 131 | |
| 132 return false; | |
| 133 } | |
| 134 | |
| 135 std::string ChromeClassTester::GetNamespace(Decl* record) { | |
| 136 return GetNamespaceImpl(record->getDeclContext(), ""); | |
| 137 } | |
| 138 | |
| 139 std::string ChromeClassTester::GetNamespaceImpl(const DeclContext* context, | |
| 140 std::string candidate) { | |
| 141 switch (context->getDeclKind()) { | |
| 142 case Decl::TranslationUnit: { | |
| 143 return candidate; | |
| 144 } | |
| 145 case Decl::Namespace: { | |
| 146 const NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context); | |
| 147 std::string name_str; | |
| 148 llvm::raw_string_ostream OS(name_str); | |
| 149 if (decl->isAnonymousNamespace()) | |
| 150 OS << "<anonymous namespace>"; | |
| 151 else | |
| 152 OS << decl; | |
| 153 return GetNamespaceImpl(context->getParent(), | |
| 154 OS.str()); | |
| 155 } | |
| 156 default: { | |
| 157 return GetNamespaceImpl(context->getParent(), candidate); | |
| 158 } | |
| 159 } | |
| 160 } | |
| 161 | |
| 162 bool ChromeClassTester::InBannedDirectory(const SourceLocation& loc) { | |
| 163 if (loc.isFileID() && loc.isValid()) { | |
| 164 bool invalid = false; | |
| 165 const char* buffer_name = instance_.getSourceManager().getBufferName( | |
| 166 loc, &invalid); | |
| 167 if (!invalid && buffer_name) { | |
| 168 // TODO: Un-hard code these values and make them specifiable on the | |
| 169 // command line. | |
|
Nico
2011/02/02 23:13:06
why?
| |
| 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 |