| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // A general interface for filtering and only acting on classes in Chromium C++ |
| // code. |
| |
| #include"ChromeClassTester.h" |
| |
| #include"Util.h" |
| #include"clang/AST/AST.h" |
| #include"clang/Basic/FileManager.h" |
| #include"clang/Basic/SourceManager.h" |
| |
| #ifdef LLVM_ON_UNIX |
| #include<sys/param.h> |
| #endif |
| #if defined(_WIN32) |
| #include<windows.h> |
| #endif |
| |
| usingnamespace clang; |
| using chrome_checker::Options; |
| |
| namespace{ |
| |
| bool ends_with(const std::string& one,const std::string& two){ |
| if(two.size()> one.size()) |
| returnfalse; |
| |
| return one.compare(one.size()- two.size(), two.size(), two)==0; |
| } |
| |
| }// namespace |
| |
| ChromeClassTester::ChromeClassTester(CompilerInstance& instance, |
| constOptions& options) |
| : options_(options), |
| instance_(instance), |
| diagnostic_(instance.getDiagnostics()){ |
| BuildBannedLists(); |
| } |
| |
| ChromeClassTester::~ChromeClassTester(){} |
| |
| voidChromeClassTester::CheckTag(TagDecl* tag){ |
| // We handle class types here where we have semantic information. We can only |
| // check structs/classes/enums here, but we get a bunch of nice semantic |
| // information instead of just parsing information. |
| SourceLocation location= tag->getInnerLocStart(); |
| LocationType location_type=ClassifyLocation(location); |
| if(location_type==LocationType::kThirdParty) |
| return; |
| |
| if(CXXRecordDecl* record= dyn_cast<CXXRecordDecl>(tag)){ |
| // We sadly need to maintain a blocklist of types that violate these |
| // rules, but do so for good reason or due to limitations of this |
| // checker (i.e., we don't handle extern templates very well). |
| std::string base_name= record->getNameAsString(); |
| if(IsIgnoredType(base_name)) |
| return; |
| |
| CheckChromeClass(location_type, location, record); |
| } |
| } |
| |
| ChromeClassTester::LocationTypeChromeClassTester::ClassifyLocation( |
| SourceLocation loc){ |
| auto classification= chrome_checker::ClassifySourceLocation( |
| instance().getHeaderSearchOpts(), instance().getSourceManager(), loc); |
| |
| // Convert to a less granular legacy classificatoin. |
| switch(classification){ |
| case chrome_checker::LocationClassification::kFirstParty: |
| returnLocationType::kChrome; |
| case chrome_checker::LocationClassification::kBlink: |
| returnLocationType::kBlink; |
| case chrome_checker::LocationClassification::kChromiumThirdParty: |
| returnLocationType::kThirdParty; |
| case chrome_checker::LocationClassification::kThirdParty: |
| returnLocationType::kThirdParty; |
| case chrome_checker::LocationClassification::kGenerated: |
| returnLocationType::kThirdParty; |
| case chrome_checker::LocationClassification::kMacro: |
| returnLocationType::kThirdParty; |
| case chrome_checker::LocationClassification::kSystem: |
| returnLocationType::kThirdParty; |
| } |
| assert(false); |
| } |
| |
| boolChromeClassTester::HasIgnoredBases(constCXXRecordDecl* record){ |
| for(constauto& base: record->bases()){ |
| CXXRecordDecl* base_record= base.getType()->getAsCXXRecordDecl(); |
| if(!base_record) |
| continue; |
| |
| const std::string& base_name= base_record->getQualifiedNameAsString(); |
| if(ignored_base_classes_.count(base_name)>0) |
| returntrue; |
| if(HasIgnoredBases(base_record)) |
| returntrue; |
| } |
| returnfalse; |
| } |
| |
| boolChromeClassTester::InImplementationFile(SourceLocation record_location){ |
| std::string filename; |
| |
| // If |record_location| is a macro, check the whole chain of expansions. |
| constSourceManager& source_manager= instance_.getSourceManager(); |
| while(true){ |
| filename=GetFilename(instance().getSourceManager(), record_location, |
| FilenameLocationType::kSpellingLoc); |
| if(ends_with(filename,".cc")|| ends_with(filename,".cpp")|| |
| ends_with(filename,".mm")){ |
| returntrue; |
| } |
| if(!record_location.isMacroID()){ |
| break; |
| } |
| record_location= |
| source_manager.getImmediateExpansionRange(record_location).getBegin(); |
| } |
| |
| returnfalse; |
| } |
| |
| voidChromeClassTester::BuildBannedLists(){ |
| // A complicated pickle derived struct that is all packed integers. |
| ignored_record_names_.emplace("Header"); |
| |
| // Part of the GPU system that uses multiple included header |
| // weirdness. Never getting this right. |
| ignored_record_names_.emplace("Validators"); |
| |
| // Has a UNIT_TEST only constructor. Isn't *terribly* complex... |
| ignored_record_names_.emplace("AutocompleteController"); |
| ignored_record_names_.emplace("HistoryURLProvider"); |
| |
| // Used over in the net unittests. A large enough bundle of integers with 1 |
| // non-pod class member. Probably harmless. |
| ignored_record_names_.emplace("MockTransaction"); |
| |
| // Used heavily in ui_base_unittests and once in views_unittests. Fixing this |
| // isn't worth the overhead of an additional library. |
| ignored_record_names_.emplace("TestAnimationDelegate"); |
| |
| // Part of our public interface that nacl and friends use. (Arguably, this |
| // should mean that this is a higher priority but fixing this looks hard.) |
| ignored_record_names_.emplace("PluginVersionInfo"); |
| |
| // Measured performance improvement on cc_perftests. See |
| // https://codereview.chromium.org/11299290/ |
| ignored_record_names_.emplace("QuadF"); |
| |
| // Ignore IPC::NoParams bases, since these structs are generated via |
| // macros and it makes it difficult to add explicit ctors. |
| ignored_base_classes_.emplace("IPC::NoParams"); |
| } |
| |
| boolChromeClassTester::IsIgnoredType(std::string_view base_name){ |
| return ignored_record_names_.count(base_name)!=0u; |
| } |
| |
| DiagnosticsEngine::LevelChromeClassTester::getErrorLevel(){ |
| return diagnostic().getWarningsAsErrors()?DiagnosticsEngine::Error |
| :DiagnosticsEngine::Warning; |
| } |