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 |