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