OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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 #include "tools/gn/c_include_iterator.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 |
| 9 namespace { |
| 10 |
| 11 enum IncludeType { |
| 12 INCLUDE_NONE, |
| 13 INCLUDE_SYSTEM, // #include <...> |
| 14 INCLUDE_USER // #include "..." |
| 15 }; |
| 16 |
| 17 // Returns true if str starts with the prefix. |
| 18 bool StartsWith(const base::StringPiece& str, const base::StringPiece& prefix) { |
| 19 base::StringPiece extracted = str.substr(0, prefix.size()); |
| 20 return extracted == prefix; |
| 21 } |
| 22 |
| 23 // Returns a new string piece referencing the same buffer as the argument, but |
| 24 // with leading space trimmed. This only checks for space and tab characters |
| 25 // since we're dealing with lines in C source files. |
| 26 base::StringPiece TrimLeadingWhitespace(const base::StringPiece& str) { |
| 27 size_t new_begin = 0; |
| 28 while (new_begin < str.size() && |
| 29 (str[new_begin] == ' ' || str[new_begin] == '\t')) |
| 30 new_begin++; |
| 31 return str.substr(new_begin); |
| 32 } |
| 33 |
| 34 // We don't want to count comment lines and preprocessor lines toward our |
| 35 // "max lines to look at before giving up" since the beginnings of some files |
| 36 // may have a lot of comments. |
| 37 // |
| 38 // We only handle C-style "//" comments since this is the normal commenting |
| 39 // style used in Chrome, and do so pretty stupidly. We don't want to write a |
| 40 // full C++ parser here, we're just trying to get a good heuristic for checking |
| 41 // the file. |
| 42 // |
| 43 // We assume the line has leading whitespace trimmed. We also assume that empty |
| 44 // lines have already been filtered out. |
| 45 bool ShouldCountTowardNonIncludeLines(const base::StringPiece& line) { |
| 46 if (StartsWith(line, "//")) |
| 47 return false; // Don't count comments. |
| 48 if (StartsWith(line, "#")) |
| 49 return false; // Don't count preprocessor. |
| 50 return true; // Count everything else. |
| 51 } |
| 52 |
| 53 // Given a line, checks to see if it looks like an include or import and |
| 54 // extract the path. The type of include is returned. Returns INCLUDE_NONE on |
| 55 // error or if this is not an include line. |
| 56 IncludeType ExtractInclude(const base::StringPiece& line, |
| 57 base::StringPiece* path) { |
| 58 static const char kInclude[] = "#include"; |
| 59 static const size_t kIncludeLen = arraysize(kInclude) - 1; // No null. |
| 60 static const char kImport[] = "#import"; |
| 61 static const size_t kImportLen = arraysize(kImport) - 1; // No null. |
| 62 |
| 63 base::StringPiece contents; |
| 64 if (StartsWith(line, base::StringPiece(kInclude, kIncludeLen))) |
| 65 contents = TrimLeadingWhitespace(line.substr(kIncludeLen)); |
| 66 else if (StartsWith(line, base::StringPiece(kImport, kImportLen))) |
| 67 contents = TrimLeadingWhitespace(line.substr(kImportLen)); |
| 68 |
| 69 if (contents.empty()) |
| 70 return INCLUDE_NONE; |
| 71 |
| 72 IncludeType type = INCLUDE_NONE; |
| 73 char terminating_char = 0; |
| 74 if (contents[0] == '"') { |
| 75 type = INCLUDE_USER; |
| 76 terminating_char = '"'; |
| 77 } else if (contents[0] == '<') { |
| 78 type = INCLUDE_SYSTEM; |
| 79 terminating_char = '>'; |
| 80 } else { |
| 81 return INCLUDE_NONE; |
| 82 } |
| 83 |
| 84 // Count everything to next "/> as the contents. |
| 85 size_t terminator_index = contents.find(terminating_char, 1); |
| 86 if (terminator_index == base::StringPiece::npos) |
| 87 return INCLUDE_NONE; |
| 88 |
| 89 *path = contents.substr(1, terminator_index - 1); |
| 90 return type; |
| 91 } |
| 92 |
| 93 } // namespace |
| 94 |
| 95 const int CIncludeIterator::kMaxNonIncludeLines = 10; |
| 96 |
| 97 CIncludeIterator::CIncludeIterator(const base::StringPiece& file) |
| 98 : file_(file), |
| 99 offset_(0), |
| 100 lines_since_last_include_(0) { |
| 101 } |
| 102 |
| 103 CIncludeIterator::~CIncludeIterator() { |
| 104 } |
| 105 |
| 106 bool CIncludeIterator::GetNextIncludeString(base::StringPiece* out) { |
| 107 base::StringPiece line; |
| 108 while (lines_since_last_include_ <= kMaxNonIncludeLines && |
| 109 GetNextLine(&line)) { |
| 110 base::StringPiece trimmed = TrimLeadingWhitespace(line); |
| 111 if (trimmed.empty()) |
| 112 continue; // Just ignore all empty lines. |
| 113 |
| 114 base::StringPiece include_contents; |
| 115 IncludeType type = ExtractInclude(trimmed, &include_contents); |
| 116 if (type == INCLUDE_USER) { |
| 117 // Only count user includes for now. |
| 118 *out = include_contents; |
| 119 lines_since_last_include_ = 0; |
| 120 return true; |
| 121 } |
| 122 |
| 123 if (ShouldCountTowardNonIncludeLines(trimmed)) |
| 124 lines_since_last_include_++; |
| 125 } |
| 126 return false; |
| 127 } |
| 128 |
| 129 bool CIncludeIterator::GetNextLine(base::StringPiece* line) { |
| 130 if (offset_ == file_.size()) |
| 131 return false; |
| 132 |
| 133 size_t begin = offset_; |
| 134 while (offset_ < file_.size() && file_[offset_] != '\n') |
| 135 offset_++; |
| 136 |
| 137 *line = file_.substr(begin, offset_ - begin); |
| 138 |
| 139 // If we didn't hit EOF, skip past the newline for the next one. |
| 140 if (offset_ < file_.size()) |
| 141 offset_++; |
| 142 return true; |
| 143 } |
OLD | NEW |