OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "tools/gn/c_include_iterator.h" | 5 #include "tools/gn/c_include_iterator.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/strings/string_util.h" | |
9 #include "tools/gn/input_file.h" | |
10 #include "tools/gn/location.h" | |
11 | 8 |
12 namespace { | 9 namespace { |
13 | 10 |
14 enum IncludeType { | 11 enum IncludeType { |
15 INCLUDE_NONE, | 12 INCLUDE_NONE, |
16 INCLUDE_SYSTEM, // #include <...> | 13 INCLUDE_SYSTEM, // #include <...> |
17 INCLUDE_USER // #include "..." | 14 INCLUDE_USER // #include "..." |
18 }; | 15 }; |
19 | 16 |
20 // Returns true if str starts with the prefix. | 17 // Returns true if str starts with the prefix. |
(...skipping 22 matching lines...) Expand all Loading... |
43 // full C++ parser here, we're just trying to get a good heuristic for checking | 40 // full C++ parser here, we're just trying to get a good heuristic for checking |
44 // the file. | 41 // the file. |
45 // | 42 // |
46 // We assume the line has leading whitespace trimmed. We also assume that empty | 43 // We assume the line has leading whitespace trimmed. We also assume that empty |
47 // lines have already been filtered out. | 44 // lines have already been filtered out. |
48 bool ShouldCountTowardNonIncludeLines(const base::StringPiece& line) { | 45 bool ShouldCountTowardNonIncludeLines(const base::StringPiece& line) { |
49 if (StartsWith(line, "//")) | 46 if (StartsWith(line, "//")) |
50 return false; // Don't count comments. | 47 return false; // Don't count comments. |
51 if (StartsWith(line, "#")) | 48 if (StartsWith(line, "#")) |
52 return false; // Don't count preprocessor. | 49 return false; // Don't count preprocessor. |
53 if (base::ContainsOnlyChars(line, base::kWhitespaceASCII)) | |
54 return false; // Don't count whitespace lines. | |
55 return true; // Count everything else. | 50 return true; // Count everything else. |
56 } | 51 } |
57 | 52 |
58 // Given a line, checks to see if it looks like an include or import and | 53 // Given a line, checks to see if it looks like an include or import and |
59 // extract the path. The type of include is returned. Returns INCLUDE_NONE on | 54 // extract the path. The type of include is returned. Returns INCLUDE_NONE on |
60 // error or if this is not an include line. | 55 // error or if this is not an include line. |
61 // | |
62 // The 1-based character number on the line that the include was found at | |
63 // will be filled into *begin_char. | |
64 IncludeType ExtractInclude(const base::StringPiece& line, | 56 IncludeType ExtractInclude(const base::StringPiece& line, |
65 base::StringPiece* path, | 57 base::StringPiece* path) { |
66 int* begin_char) { | |
67 static const char kInclude[] = "#include"; | 58 static const char kInclude[] = "#include"; |
68 static const size_t kIncludeLen = arraysize(kInclude) - 1; // No null. | 59 static const size_t kIncludeLen = arraysize(kInclude) - 1; // No null. |
69 static const char kImport[] = "#import"; | 60 static const char kImport[] = "#import"; |
70 static const size_t kImportLen = arraysize(kImport) - 1; // No null. | 61 static const size_t kImportLen = arraysize(kImport) - 1; // No null. |
71 | 62 |
72 base::StringPiece trimmed = TrimLeadingWhitespace(line); | |
73 if (trimmed.empty()) | |
74 return INCLUDE_NONE; | |
75 | |
76 base::StringPiece contents; | 63 base::StringPiece contents; |
77 if (StartsWith(trimmed, base::StringPiece(kInclude, kIncludeLen))) | 64 if (StartsWith(line, base::StringPiece(kInclude, kIncludeLen))) |
78 contents = TrimLeadingWhitespace(trimmed.substr(kIncludeLen)); | 65 contents = TrimLeadingWhitespace(line.substr(kIncludeLen)); |
79 else if (StartsWith(trimmed, base::StringPiece(kImport, kImportLen))) | 66 else if (StartsWith(line, base::StringPiece(kImport, kImportLen))) |
80 contents = TrimLeadingWhitespace(trimmed.substr(kImportLen)); | 67 contents = TrimLeadingWhitespace(line.substr(kImportLen)); |
81 | 68 |
82 if (contents.empty()) | 69 if (contents.empty()) |
83 return INCLUDE_NONE; | 70 return INCLUDE_NONE; |
84 | 71 |
85 IncludeType type = INCLUDE_NONE; | 72 IncludeType type = INCLUDE_NONE; |
86 char terminating_char = 0; | 73 char terminating_char = 0; |
87 if (contents[0] == '"') { | 74 if (contents[0] == '"') { |
88 type = INCLUDE_USER; | 75 type = INCLUDE_USER; |
89 terminating_char = '"'; | 76 terminating_char = '"'; |
90 } else if (contents[0] == '<') { | 77 } else if (contents[0] == '<') { |
91 type = INCLUDE_SYSTEM; | 78 type = INCLUDE_SYSTEM; |
92 terminating_char = '>'; | 79 terminating_char = '>'; |
93 } else { | 80 } else { |
94 return INCLUDE_NONE; | 81 return INCLUDE_NONE; |
95 } | 82 } |
96 | 83 |
97 // Count everything to next "/> as the contents. | 84 // Count everything to next "/> as the contents. |
98 size_t terminator_index = contents.find(terminating_char, 1); | 85 size_t terminator_index = contents.find(terminating_char, 1); |
99 if (terminator_index == base::StringPiece::npos) | 86 if (terminator_index == base::StringPiece::npos) |
100 return INCLUDE_NONE; | 87 return INCLUDE_NONE; |
101 | 88 |
102 *path = contents.substr(1, terminator_index - 1); | 89 *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; | 90 return type; |
106 } | 91 } |
107 | 92 |
108 } // namespace | 93 } // namespace |
109 | 94 |
110 const int CIncludeIterator::kMaxNonIncludeLines = 10; | 95 const int CIncludeIterator::kMaxNonIncludeLines = 10; |
111 | 96 |
112 CIncludeIterator::CIncludeIterator(const InputFile* input) | 97 CIncludeIterator::CIncludeIterator(const base::StringPiece& file) |
113 : input_file_(input), | 98 : file_(file), |
114 file_(input->contents()), | |
115 offset_(0), | 99 offset_(0), |
116 line_number_(0), | |
117 lines_since_last_include_(0) { | 100 lines_since_last_include_(0) { |
118 } | 101 } |
119 | 102 |
120 CIncludeIterator::~CIncludeIterator() { | 103 CIncludeIterator::~CIncludeIterator() { |
121 } | 104 } |
122 | 105 |
123 bool CIncludeIterator::GetNextIncludeString(base::StringPiece* out, | 106 bool CIncludeIterator::GetNextIncludeString(base::StringPiece* out) { |
124 LocationRange* location) { | |
125 base::StringPiece line; | 107 base::StringPiece line; |
126 int cur_line_number = 0; | |
127 while (lines_since_last_include_ <= kMaxNonIncludeLines && | 108 while (lines_since_last_include_ <= kMaxNonIncludeLines && |
128 GetNextLine(&line, &cur_line_number)) { | 109 GetNextLine(&line)) { |
| 110 base::StringPiece trimmed = TrimLeadingWhitespace(line); |
| 111 if (trimmed.empty()) |
| 112 continue; // Just ignore all empty lines. |
| 113 |
129 base::StringPiece include_contents; | 114 base::StringPiece include_contents; |
130 int begin_char; | 115 IncludeType type = ExtractInclude(trimmed, &include_contents); |
131 IncludeType type = ExtractInclude(line, &include_contents, &begin_char); | |
132 if (type == INCLUDE_USER) { | 116 if (type == INCLUDE_USER) { |
133 // Only count user includes for now. | 117 // Only count user includes for now. |
134 *out = include_contents; | 118 *out = include_contents; |
135 *location = LocationRange( | |
136 Location(input_file_, cur_line_number, begin_char), | |
137 Location(input_file_, cur_line_number, | |
138 begin_char + include_contents.size())); | |
139 | |
140 lines_since_last_include_ = 0; | 119 lines_since_last_include_ = 0; |
141 return true; | 120 return true; |
142 } | 121 } |
143 | 122 |
144 if (ShouldCountTowardNonIncludeLines(line)) | 123 if (ShouldCountTowardNonIncludeLines(trimmed)) |
145 lines_since_last_include_++; | 124 lines_since_last_include_++; |
146 } | 125 } |
147 return false; | 126 return false; |
148 } | 127 } |
149 | 128 |
150 bool CIncludeIterator::GetNextLine(base::StringPiece* line, int* line_number) { | 129 bool CIncludeIterator::GetNextLine(base::StringPiece* line) { |
151 if (offset_ == file_.size()) | 130 if (offset_ == file_.size()) |
152 return false; | 131 return false; |
153 | 132 |
154 size_t begin = offset_; | 133 size_t begin = offset_; |
155 while (offset_ < file_.size() && file_[offset_] != '\n') | 134 while (offset_ < file_.size() && file_[offset_] != '\n') |
156 offset_++; | 135 offset_++; |
157 line_number_++; | |
158 | 136 |
159 *line = file_.substr(begin, offset_ - begin); | 137 *line = file_.substr(begin, offset_ - begin); |
160 *line_number = line_number_; | |
161 | 138 |
162 // If we didn't hit EOF, skip past the newline for the next one. | 139 // If we didn't hit EOF, skip past the newline for the next one. |
163 if (offset_ < file_.size()) | 140 if (offset_ < file_.size()) |
164 offset_++; | 141 offset_++; |
165 return true; | 142 return true; |
166 } | 143 } |
OLD | NEW |