blob: cc3cb35d27f1c0120b979409841a94d2b0c7cff6 [file] [log] [blame]
[email protected]126d8b52014-04-07 22:17:351// 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"
avicab0f832015-12-26 07:00:398#include "base/macros.h"
[email protected]fce5c3fe2014-04-10 21:13:059#include "base/strings/string_util.h"
10#include "tools/gn/input_file.h"
11#include "tools/gn/location.h"
[email protected]126d8b52014-04-07 22:17:3512
13namespace {
14
15enum IncludeType {
16 INCLUDE_NONE,
17 INCLUDE_SYSTEM, // #include <...>
18 INCLUDE_USER // #include "..."
19};
20
[email protected]126d8b52014-04-07 22:17:3521// Returns a new string piece referencing the same buffer as the argument, but
22// with leading space trimmed. This only checks for space and tab characters
23// since we're dealing with lines in C source files.
24base::StringPiece TrimLeadingWhitespace(const base::StringPiece& str) {
25 size_t new_begin = 0;
26 while (new_begin < str.size() &&
27 (str[new_begin] == ' ' || str[new_begin] == '\t'))
28 new_begin++;
29 return str.substr(new_begin);
30}
31
32// We don't want to count comment lines and preprocessor lines toward our
33// "max lines to look at before giving up" since the beginnings of some files
34// may have a lot of comments.
35//
36// We only handle C-style "//" comments since this is the normal commenting
37// style used in Chrome, and do so pretty stupidly. We don't want to write a
38// full C++ parser here, we're just trying to get a good heuristic for checking
39// the file.
40//
41// We assume the line has leading whitespace trimmed. We also assume that empty
42// lines have already been filtered out.
43bool ShouldCountTowardNonIncludeLines(const base::StringPiece& line) {
tfarina934d88d2015-12-01 17:41:0544 if (base::StartsWith(line, "//", base::CompareCase::SENSITIVE))
[email protected]126d8b52014-04-07 22:17:3545 return false; // Don't count comments.
tfarina934d88d2015-12-01 17:41:0546 if (base::StartsWith(line, "/*", base::CompareCase::SENSITIVE) ||
47 base::StartsWith(line, " *", base::CompareCase::SENSITIVE))
brettwa2d721a2015-10-16 05:34:2448 return false; // C-style comment blocks with stars along the left side.
tfarina934d88d2015-12-01 17:41:0549 if (base::StartsWith(line, "#", base::CompareCase::SENSITIVE))
[email protected]126d8b52014-04-07 22:17:3550 return false; // Don't count preprocessor.
[email protected]fce5c3fe2014-04-10 21:13:0551 if (base::ContainsOnlyChars(line, base::kWhitespaceASCII))
52 return false; // Don't count whitespace lines.
[email protected]126d8b52014-04-07 22:17:3553 return true; // Count everything else.
54}
55
56// Given a line, checks to see if it looks like an include or import and
57// extract the path. The type of include is returned. Returns INCLUDE_NONE on
58// error or if this is not an include line.
[email protected]fce5c3fe2014-04-10 21:13:0559//
60// The 1-based character number on the line that the include was found at
61// will be filled into *begin_char.
[email protected]126d8b52014-04-07 22:17:3562IncludeType ExtractInclude(const base::StringPiece& line,
[email protected]fce5c3fe2014-04-10 21:13:0563 base::StringPiece* path,
64 int* begin_char) {
[email protected]126d8b52014-04-07 22:17:3565 static const char kInclude[] = "#include";
66 static const size_t kIncludeLen = arraysize(kInclude) - 1; // No null.
67 static const char kImport[] = "#import";
68 static const size_t kImportLen = arraysize(kImport) - 1; // No null.
69
[email protected]fce5c3fe2014-04-10 21:13:0570 base::StringPiece trimmed = TrimLeadingWhitespace(line);
71 if (trimmed.empty())
72 return INCLUDE_NONE;
73
[email protected]126d8b52014-04-07 22:17:3574 base::StringPiece contents;
tfarina934d88d2015-12-01 17:41:0575 if (base::StartsWith(trimmed, base::StringPiece(kInclude, kIncludeLen),
76 base::CompareCase::SENSITIVE))
[email protected]fce5c3fe2014-04-10 21:13:0577 contents = TrimLeadingWhitespace(trimmed.substr(kIncludeLen));
tfarina934d88d2015-12-01 17:41:0578 else if (base::StartsWith(trimmed, base::StringPiece(kImport, kImportLen),
79 base::CompareCase::SENSITIVE))
[email protected]fce5c3fe2014-04-10 21:13:0580 contents = TrimLeadingWhitespace(trimmed.substr(kImportLen));
[email protected]126d8b52014-04-07 22:17:3581
82 if (contents.empty())
83 return INCLUDE_NONE;
84
85 IncludeType type = INCLUDE_NONE;
86 char terminating_char = 0;
87 if (contents[0] == '"') {
88 type = INCLUDE_USER;
89 terminating_char = '"';
90 } else if (contents[0] == '<') {
91 type = INCLUDE_SYSTEM;
92 terminating_char = '>';
93 } else {
94 return INCLUDE_NONE;
95 }
96
97 // Count everything to next "/> as the contents.
98 size_t terminator_index = contents.find(terminating_char, 1);
99 if (terminator_index == base::StringPiece::npos)
100 return INCLUDE_NONE;
101
102 *path = contents.substr(1, terminator_index - 1);
[email protected]fce5c3fe2014-04-10 21:13:05103 // Note: one based so we do "+ 1".
104 *begin_char = static_cast<int>(path->data() - line.data()) + 1;
[email protected]126d8b52014-04-07 22:17:35105 return type;
106}
107
brettw20982dc2015-07-06 22:37:06108// Returns true if this line has a "nogncheck" comment associated with it.
109bool HasNoCheckAnnotation(const base::StringPiece& line) {
110 return line.find("nogncheck") != base::StringPiece::npos;
111}
112
[email protected]126d8b52014-04-07 22:17:35113} // namespace
114
115const int CIncludeIterator::kMaxNonIncludeLines = 10;
116
[email protected]fce5c3fe2014-04-10 21:13:05117CIncludeIterator::CIncludeIterator(const InputFile* input)
118 : input_file_(input),
119 file_(input->contents()),
[email protected]126d8b52014-04-07 22:17:35120 offset_(0),
[email protected]fce5c3fe2014-04-10 21:13:05121 line_number_(0),
[email protected]126d8b52014-04-07 22:17:35122 lines_since_last_include_(0) {
123}
124
125CIncludeIterator::~CIncludeIterator() {
126}
127
[email protected]fce5c3fe2014-04-10 21:13:05128bool CIncludeIterator::GetNextIncludeString(base::StringPiece* out,
129 LocationRange* location) {
[email protected]126d8b52014-04-07 22:17:35130 base::StringPiece line;
[email protected]fce5c3fe2014-04-10 21:13:05131 int cur_line_number = 0;
[email protected]126d8b52014-04-07 22:17:35132 while (lines_since_last_include_ <= kMaxNonIncludeLines &&
[email protected]fce5c3fe2014-04-10 21:13:05133 GetNextLine(&line, &cur_line_number)) {
[email protected]126d8b52014-04-07 22:17:35134 base::StringPiece include_contents;
[email protected]fce5c3fe2014-04-10 21:13:05135 int begin_char;
136 IncludeType type = ExtractInclude(line, &include_contents, &begin_char);
brettw20982dc2015-07-06 22:37:06137 if (type == INCLUDE_USER && !HasNoCheckAnnotation(line)) {
[email protected]126d8b52014-04-07 22:17:35138 // Only count user includes for now.
139 *out = include_contents;
[email protected]fce5c3fe2014-04-10 21:13:05140 *location = LocationRange(
scottmgbe5d4512014-09-24 02:29:12141 Location(input_file_,
142 cur_line_number,
143 begin_char,
144 -1 /* TODO(scottmg): Is this important? */),
145 Location(input_file_,
146 cur_line_number,
147 begin_char + static_cast<int>(include_contents.size()),
148 -1 /* TODO(scottmg): Is this important? */));
[email protected]fce5c3fe2014-04-10 21:13:05149
[email protected]126d8b52014-04-07 22:17:35150 lines_since_last_include_ = 0;
151 return true;
152 }
153
[email protected]fce5c3fe2014-04-10 21:13:05154 if (ShouldCountTowardNonIncludeLines(line))
[email protected]126d8b52014-04-07 22:17:35155 lines_since_last_include_++;
156 }
157 return false;
158}
159
[email protected]fce5c3fe2014-04-10 21:13:05160bool CIncludeIterator::GetNextLine(base::StringPiece* line, int* line_number) {
[email protected]126d8b52014-04-07 22:17:35161 if (offset_ == file_.size())
162 return false;
163
164 size_t begin = offset_;
165 while (offset_ < file_.size() && file_[offset_] != '\n')
166 offset_++;
[email protected]fce5c3fe2014-04-10 21:13:05167 line_number_++;
[email protected]126d8b52014-04-07 22:17:35168
169 *line = file_.substr(begin, offset_ - begin);
[email protected]fce5c3fe2014-04-10 21:13:05170 *line_number = line_number_;
[email protected]126d8b52014-04-07 22:17:35171
172 // If we didn't hit EOF, skip past the newline for the next one.
173 if (offset_ < file_.size())
174 offset_++;
175 return true;
176}