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