11d77d6caf647424f9c1c481145be0465e96c9e3eBrian// Copyright 2014 The Chromium Authors. All rights reserved. 21d77d6caf647424f9c1c481145be0465e96c9e3eBrian// Use of this source code is governed by a BSD-style license that can be 31d77d6caf647424f9c1c481145be0465e96c9e3eBrian// found in the LICENSE file. 41d77d6caf647424f9c1c481145be0465e96c9e3eBrian 51d77d6caf647424f9c1c481145be0465e96c9e3eBrian#include "tools/gn/c_include_iterator.h" 61d77d6caf647424f9c1c481145be0465e96c9e3eBrian 71d77d6caf647424f9c1c481145be0465e96c9e3eBrian#include "base/logging.h" 81d77d6caf647424f9c1c481145be0465e96c9e3eBrian#include "base/strings/string_util.h" 91d77d6caf647424f9c1c481145be0465e96c9e3eBrian#include "tools/gn/input_file.h" 101d77d6caf647424f9c1c481145be0465e96c9e3eBrian#include "tools/gn/location.h" 111d77d6caf647424f9c1c481145be0465e96c9e3eBrian 121d77d6caf647424f9c1c481145be0465e96c9e3eBriannamespace { 131d77d6caf647424f9c1c481145be0465e96c9e3eBrian 141d77d6caf647424f9c1c481145be0465e96c9e3eBrianenum IncludeType { 151d77d6caf647424f9c1c481145be0465e96c9e3eBrian INCLUDE_NONE, 161d77d6caf647424f9c1c481145be0465e96c9e3eBrian INCLUDE_SYSTEM, // #include <...> 171d77d6caf647424f9c1c481145be0465e96c9e3eBrian INCLUDE_USER // #include "..." 181d77d6caf647424f9c1c481145be0465e96c9e3eBrian}; 191d77d6caf647424f9c1c481145be0465e96c9e3eBrian 201d77d6caf647424f9c1c481145be0465e96c9e3eBrian// Returns true if str starts with the prefix. 211d77d6caf647424f9c1c481145be0465e96c9e3eBrianbool StartsWith(const base::StringPiece& str, const base::StringPiece& prefix) { 221d77d6caf647424f9c1c481145be0465e96c9e3eBrian base::StringPiece extracted = str.substr(0, prefix.size()); 231d77d6caf647424f9c1c481145be0465e96c9e3eBrian return extracted == prefix; 241d77d6caf647424f9c1c481145be0465e96c9e3eBrian} 251d77d6caf647424f9c1c481145be0465e96c9e3eBrian 261d77d6caf647424f9c1c481145be0465e96c9e3eBrian// Returns a new string piece referencing the same buffer as the argument, but 271d77d6caf647424f9c1c481145be0465e96c9e3eBrian// with leading space trimmed. This only checks for space and tab characters 281d77d6caf647424f9c1c481145be0465e96c9e3eBrian// since we're dealing with lines in C source files. 291d77d6caf647424f9c1c481145be0465e96c9e3eBrianbase::StringPiece TrimLeadingWhitespace(const base::StringPiece& str) { 301d77d6caf647424f9c1c481145be0465e96c9e3eBrian size_t new_begin = 0; 311d77d6caf647424f9c1c481145be0465e96c9e3eBrian while (new_begin < str.size() && 32679b6cf0a0e662513c8d7732049c44916e0e9e86Brian (str[new_begin] == ' ' || str[new_begin] == '\t')) 331410b7bb509ef37c41043b173bc1047257483af0Brian new_begin++; 341d77d6caf647424f9c1c481145be0465e96c9e3eBrian return str.substr(new_begin); 351d77d6caf647424f9c1c481145be0465e96c9e3eBrian} 361d77d6caf647424f9c1c481145be0465e96c9e3eBrian 371d77d6caf647424f9c1c481145be0465e96c9e3eBrian// We don't want to count comment lines and preprocessor lines toward our 381d77d6caf647424f9c1c481145be0465e96c9e3eBrian// "max lines to look at before giving up" since the beginnings of some files 391d77d6caf647424f9c1c481145be0465e96c9e3eBrian// may have a lot of comments. 401d77d6caf647424f9c1c481145be0465e96c9e3eBrian// 411410b7bb509ef37c41043b173bc1047257483af0Brian// We only handle C-style "//" comments since this is the normal commenting 421410b7bb509ef37c41043b173bc1047257483af0Brian// style used in Chrome, and do so pretty stupidly. We don't want to write a 431410b7bb509ef37c41043b173bc1047257483af0Brian// full C++ parser here, we're just trying to get a good heuristic for checking 441410b7bb509ef37c41043b173bc1047257483af0Brian// the file. 451410b7bb509ef37c41043b173bc1047257483af0Brian// 461410b7bb509ef37c41043b173bc1047257483af0Brian// We assume the line has leading whitespace trimmed. We also assume that empty 475f0b49e7a956291842c7ad3a597570cf0db50cb6Michal Krol// lines have already been filtered out. 4807a30e3d18a528a2dc8a247af5c43e7428be1743Dave Airliebool ShouldCountTowardNonIncludeLines(const base::StringPiece& line) { 49abe4f3d1aa68aec70d329447abc890b3eaaba9cbJosé Fonseca if (StartsWith(line, "//")) 50607a830fe281bb042740ef5cd9ae99df73e19090Michal Krol return false; // Don't count comments. 511410b7bb509ef37c41043b173bc1047257483af0Brian if (StartsWith(line, "#")) 521410b7bb509ef37c41043b173bc1047257483af0Brian return false; // Don't count preprocessor. 531410b7bb509ef37c41043b173bc1047257483af0Brian if (base::ContainsOnlyChars(line, base::kWhitespaceASCII)) 54b550d8d76b42ef5ba5e8293dcc24220d5b683369Brian Paul return false; // Don't count whitespace lines. 55b550d8d76b42ef5ba5e8293dcc24220d5b683369Brian Paul return true; // Count everything else. 56b550d8d76b42ef5ba5e8293dcc24220d5b683369Brian Paul} 571d77d6caf647424f9c1c481145be0465e96c9e3eBrian 581d77d6caf647424f9c1c481145be0465e96c9e3eBrian// Given a line, checks to see if it looks like an include or import and 593197ad5a56ee94773f974ac727b316c5adfe1b6fBrian// extract the path. The type of include is returned. Returns INCLUDE_NONE on 601d77d6caf647424f9c1c481145be0465e96c9e3eBrian// error or if this is not an include line. 611d77d6caf647424f9c1c481145be0465e96c9e3eBrian// 626080e567f0ca1fdcce21e76271d4239c33a50db3Brian Paul// The 1-based character number on the line that the include was found at 631d77d6caf647424f9c1c481145be0465e96c9e3eBrian// will be filled into *begin_char. 641d77d6caf647424f9c1c481145be0465e96c9e3eBrianIncludeType ExtractInclude(const base::StringPiece& line, 651d77d6caf647424f9c1c481145be0465e96c9e3eBrian base::StringPiece* path, 66c8c2fc9a7a029bb61520973e55fb3cec18f13e20Jakob Bornecrantz int* begin_char) { 67c8c2fc9a7a029bb61520973e55fb3cec18f13e20Jakob Bornecrantz static const char kInclude[] = "#include"; 681d77d6caf647424f9c1c481145be0465e96c9e3eBrian static const size_t kIncludeLen = arraysize(kInclude) - 1; // No null. 694ecb2c105da590abf79421a06234b636cd1afcd6Dave Airlie static const char kImport[] = "#import"; 70ff5b0c72db20be099f9fc7dee22aeebbda75ab42Roland Scheidegger static const size_t kImportLen = arraysize(kImport) - 1; // No null. 71fb40c5a9c7dc91c03f80780e0a09be0cade98705Brian 72b550d8d76b42ef5ba5e8293dcc24220d5b683369Brian Paul base::StringPiece trimmed = TrimLeadingWhitespace(line); 73e7ccd703a28e14431b90f29540cec0bf67be1e0fChristoph Bumiller if (trimmed.empty()) 742253906da3c506bb5378a8f2fa203ed0c9021171Brian Paul return INCLUDE_NONE; 752253906da3c506bb5378a8f2fa203ed0c9021171Brian Paul 762253906da3c506bb5378a8f2fa203ed0c9021171Brian Paul base::StringPiece contents; 771d77d6caf647424f9c1c481145be0465e96c9e3eBrian if (StartsWith(trimmed, base::StringPiece(kInclude, kIncludeLen))) 78f7e3e46f72fffe4b83cd3f922173ff28e9ab9c7cDave Airlie contents = TrimLeadingWhitespace(trimmed.substr(kIncludeLen)); 7985206e56a1c3400be47229d4a8c6a1cd7a2f476eBrian Paul else if (StartsWith(trimmed, base::StringPiece(kImport, kImportLen))) 8085206e56a1c3400be47229d4a8c6a1cd7a2f476eBrian Paul contents = TrimLeadingWhitespace(trimmed.substr(kImportLen)); 8185206e56a1c3400be47229d4a8c6a1cd7a2f476eBrian Paul 8285206e56a1c3400be47229d4a8c6a1cd7a2f476eBrian Paul if (contents.empty()) 8385206e56a1c3400be47229d4a8c6a1cd7a2f476eBrian Paul return INCLUDE_NONE; 8485206e56a1c3400be47229d4a8c6a1cd7a2f476eBrian Paul 853ff688ea299581e60caf5d6e1a464f68c717fe83Zack Rusin IncludeType type = INCLUDE_NONE; 863ff688ea299581e60caf5d6e1a464f68c717fe83Zack Rusin char terminating_char = 0; 873ff688ea299581e60caf5d6e1a464f68c717fe83Zack Rusin if (contents[0] == '"') { 883ff688ea299581e60caf5d6e1a464f68c717fe83Zack Rusin type = INCLUDE_USER; 893ff688ea299581e60caf5d6e1a464f68c717fe83Zack Rusin terminating_char = '"'; 903ff688ea299581e60caf5d6e1a464f68c717fe83Zack Rusin } else if (contents[0] == '<') { 911d77d6caf647424f9c1c481145be0465e96c9e3eBrian type = INCLUDE_SYSTEM; 921d77d6caf647424f9c1c481145be0465e96c9e3eBrian terminating_char = '>'; 931d77d6caf647424f9c1c481145be0465e96c9e3eBrian } else { 941d77d6caf647424f9c1c481145be0465e96c9e3eBrian return INCLUDE_NONE; 951d77d6caf647424f9c1c481145be0465e96c9e3eBrian } 961d77d6caf647424f9c1c481145be0465e96c9e3eBrian 978223add3304451d5e75737a6d1be1739e4517943Brian Paul // Count everything to next "/> as the contents. 988223add3304451d5e75737a6d1be1739e4517943Brian Paul size_t terminator_index = contents.find(terminating_char, 1); 998223add3304451d5e75737a6d1be1739e4517943Brian Paul if (terminator_index == base::StringPiece::npos) 1008223add3304451d5e75737a6d1be1739e4517943Brian Paul return INCLUDE_NONE; 1011d77d6caf647424f9c1c481145be0465e96c9e3eBrian 102 *path = contents.substr(1, terminator_index - 1); 103 // Note: one based so we do "+ 1". 104 *begin_char = static_cast<int>(path->data() - line.data()) + 1; 105 return type; 106} 107 108} // namespace 109 110const int CIncludeIterator::kMaxNonIncludeLines = 10; 111 112CIncludeIterator::CIncludeIterator(const InputFile* input) 113 : input_file_(input), 114 file_(input->contents()), 115 offset_(0), 116 line_number_(0), 117 lines_since_last_include_(0) { 118} 119 120CIncludeIterator::~CIncludeIterator() { 121} 122 123bool CIncludeIterator::GetNextIncludeString(base::StringPiece* out, 124 LocationRange* location) { 125 base::StringPiece line; 126 int cur_line_number = 0; 127 while (lines_since_last_include_ <= kMaxNonIncludeLines && 128 GetNextLine(&line, &cur_line_number)) { 129 base::StringPiece include_contents; 130 int begin_char; 131 IncludeType type = ExtractInclude(line, &include_contents, &begin_char); 132 if (type == INCLUDE_USER) { 133 // Only count user includes for now. 134 *out = include_contents; 135 *location = LocationRange( 136 Location(input_file_, 137 cur_line_number, 138 begin_char, 139 -1 /* TODO(scottmg): Is this important? */), 140 Location(input_file_, 141 cur_line_number, 142 begin_char + static_cast<int>(include_contents.size()), 143 -1 /* TODO(scottmg): Is this important? */)); 144 145 lines_since_last_include_ = 0; 146 return true; 147 } 148 149 if (ShouldCountTowardNonIncludeLines(line)) 150 lines_since_last_include_++; 151 } 152 return false; 153} 154 155bool CIncludeIterator::GetNextLine(base::StringPiece* line, int* line_number) { 156 if (offset_ == file_.size()) 157 return false; 158 159 size_t begin = offset_; 160 while (offset_ < file_.size() && file_[offset_] != '\n') 161 offset_++; 162 line_number_++; 163 164 *line = file_.substr(begin, offset_ - begin); 165 *line_number = line_number_; 166 167 // If we didn't hit EOF, skip past the newline for the next one. 168 if (offset_ < file_.size()) 169 offset_++; 170 return true; 171} 172