blob: 48e7c5fa5ac09eff692fef88d2f570418060e9f7 [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Cary Clark2dc84ad2018-01-26 12:56:22 -05008#include "SkOSFile.h"
9#include "SkOSPath.h"
Cary Clark8032b982017-07-28 11:04:54 -040010
Cary Clark2da9fb82018-11-01 09:29:36 -040011#include "bmhParser.h"
12#include "includeParser.h"
Brian Osman5c11d5c2019-02-06 09:41:58 -050013#include <map>
Cary Clark2da9fb82018-11-01 09:29:36 -040014
Cary Clark8032b982017-07-28 11:04:54 -040015const IncludeKey kKeyWords[] = {
16 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040017 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040018 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clarkd2ca79c2018-08-10 13:09:13 -040019 { "alignas", KeyWord::kAlignAs, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040020 { "bool", KeyWord::kBool, KeyProperty::kNumber },
21 { "char", KeyWord::kChar, KeyProperty::kNumber },
22 { "class", KeyWord::kClass, KeyProperty::kObject },
23 { "const", KeyWord::kConst, KeyProperty::kModifier },
24 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
25 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
26 { "double", KeyWord::kDouble, KeyProperty::kNumber },
27 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
28 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
29 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
30 { "enum", KeyWord::kEnum, KeyProperty::kObject },
Cary Clark2dc84ad2018-01-26 12:56:22 -050031 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
Cary Clark8032b982017-07-28 11:04:54 -040032 { "float", KeyWord::kFloat, KeyProperty::kNumber },
33 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
34 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
35 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
36 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
37 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
38 { "inline", KeyWord::kInline, KeyProperty::kModifier },
39 { "int", KeyWord::kInt, KeyProperty::kNumber },
40 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
41 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
42 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
43 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
44 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
45 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
46 { "static", KeyWord::kStatic, KeyProperty::kModifier },
47 { "struct", KeyWord::kStruct, KeyProperty::kObject },
48 { "template", KeyWord::kTemplate, KeyProperty::kObject },
49 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clark0d225392018-06-07 09:59:07 -040050 { "typename", KeyWord::kTypename, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040051 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040052 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040053 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
54 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark4dc5a452018-05-21 11:56:57 -040055 { "uintptr_t", KeyWord::kUintPtr_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040056 { "union", KeyWord::kUnion, KeyProperty::kObject },
57 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
Cary Clark61313f32018-10-08 14:57:48 -040058 { "using", KeyWord::kUsing, KeyProperty::kObject },
Cary Clark8032b982017-07-28 11:04:54 -040059 { "void", KeyWord::kVoid, KeyProperty::kNumber },
60};
61
62const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
63
64KeyWord IncludeParser::FindKey(const char* start, const char* end) {
65 int ch = 0;
66 for (size_t index = 0; index < kKeyWordCount; ) {
67 if (start[ch] > kKeyWords[index].fName[ch]) {
68 ++index;
Cary Clark61313f32018-10-08 14:57:48 -040069 if (ch > 0 && (index == kKeyWordCount ||
70 kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1])) {
Cary Clark8032b982017-07-28 11:04:54 -040071 return KeyWord::kNone;
72 }
73 continue;
74 }
75 if (start[ch] < kKeyWords[index].fName[ch]) {
76 return KeyWord::kNone;
77 }
78 ++ch;
79 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040080 if (end - start < (int) strlen(kKeyWords[index].fName)) {
81 return KeyWord::kNone;
82 }
Cary Clark8032b982017-07-28 11:04:54 -040083 return kKeyWords[index].fKeyWord;
84 }
85 }
86 return KeyWord::kNone;
87}
88
89void IncludeParser::ValidateKeyWords() {
90 for (size_t index = 1; index < kKeyWordCount; ++index) {
91 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
92 == (int) kKeyWords[index].fKeyWord);
93 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
94 }
95}
96
97void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050098 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040099 fIncludeWord = nullptr;
100 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
101 Definition* def = &fParent->fTokens.back();
102 this->addDefinition(def);
103 if (KeyWord::kEnum == fParent->fKeyWord) {
104 fInEnum = true;
105 }
106 }
107}
108
Cary Clark61313f32018-10-08 14:57:48 -0400109static bool looks_like_method(const TextParser& tp) {
110 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
111 t.skipSpace();
112 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
113 && !t.skipExact("enum")) {
114 return true;
115 }
116 t.skipSpace();
117 if (t.skipExact("SK_API")) {
118 t.skipSpace();
119 }
120 if (!isupper(t.peek())) {
121 return true;
122 }
123 return nullptr != t.strnchr('(', t.fEnd);
124}
125
126static bool looks_like_forward_declaration(const TextParser& tp) {
127 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
128 t.skipSpace();
129 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
130 && !t.skipExact("enum")) {
131 return false;
132 }
133 t.skipSpace();
134 if (t.skipExact("SK_API")) {
135 t.skipSpace();
136 }
137 if (!isupper(t.peek())) {
138 return false;
139 }
140 t.skipToNonAlphaNum();
141 if (t.eof() || ';' != t.next()) {
142 return false;
143 }
144 if (t.eof() || '\n' != t.next()) {
145 return false;
146 }
147 return t.eof();
148}
149
150static bool looks_like_constructor(const TextParser& tp) {
151 TextParser t(tp.fFileName, tp.fLine, tp.lineEnd(), tp.fLineCount);
152 t.skipSpace();
153 if (!isupper(t.peek())) {
154 if (':' == t.next() && ' ' >= t.peek()) {
155 return true;
156 }
157 return false;
158 }
159 t.skipToNonAlphaNum();
160 if ('(' != t.peek()) {
161 return false;
162 }
163 if (!t.skipToEndBracket(')')) {
164 return false;
165 }
166 SkAssertResult(')' == t.next());
167 t.skipSpace();
168 return tp.fChar == t.fChar;
169}
170
171static bool looks_like_class_decl(const TextParser& tp) {
172 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
173 t.skipSpace();
174 if (!t.skipExact("class")) {
175 return false;
176 }
177 t.skipSpace();
178 if (t.skipExact("SK_API")) {
179 t.skipSpace();
180 }
181 if (!isupper(t.peek())) {
182 return false;
183 }
184 t.skipToNonAlphaNum();
185 return !t.skipToEndBracket('(');
186}
187
188static bool looks_like_const(const TextParser& tp) {
189 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
190 if (!t.startsWith("static constexpr ")) {
191 return false;
192 }
193 if (t.skipToEndBracket(" k")) {
194 SkAssertResult(t.skipExact(" k"));
195 } else if (t.skipToEndBracket(" SK_")) {
196 SkAssertResult(t.skipExact(" SK_"));
197 } else {
198 return false;
199 }
200 if (!isupper(t.peek())) {
201 return false;
202 }
203 return t.skipToEndBracket(" = ");
204}
205
206static bool looks_like_member(const TextParser& tp) {
207 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
208 const char* end = t.anyOf("(;");
209 if (!end || '(' == *end) {
210 return false;
211 }
212 bool foundMember = false;
213 do {
214 const char* next = t.anyOf(" ;");
215 if (';' == *next) {
216 break;
217 }
218 t.skipTo(next);
219 t.skipSpace();
220 foundMember = 'f' == t.fChar[0] && isupper(t.fChar[1]);
221 } while (true);
222 return foundMember;
223}
224
225static void skip_constructor_initializers(TextParser& t) {
226 SkAssertResult(':' == t.next());
227 do {
228 t.skipWhiteSpace();
229 t.skipToNonAlphaNum();
230 t.skipWhiteSpace();
231 if ('{' == t.peek()) {
232 t.skipToBalancedEndBracket('{', '}');
233 }
234 do {
235 const char* limiter = t.anyOf("(,{");
236 t.skipTo(limiter);
237 if ('(' != t.peek()) {
238 break;
239 }
240 t.skipToBalancedEndBracket('(', ')');
241 } while (true);
242 if ('{' == t.peek()) {
243 return;
244 }
245 SkAssertResult(',' == t.next());
246 } while (true);
247}
248
249static const char kInline[] = "inline ";
250static const char kSK_API[] = "SK_API ";
251static const char kSK_WARN_UNUSED_RESULT[] = "SK_WARN_UNUSED_RESULT ";
252
253bool IncludeParser::advanceInclude(TextParser& i) {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500254 if (!i.skipWhiteSpace(&fCheck.fIndent, &fCheck.fWriteReturn)) {
255 return false;
256 }
Cary Clark61313f32018-10-08 14:57:48 -0400257 if (fCheck.fPrivateBrace) {
258 if (i.startsWith("};")) {
259 if (fCheck.fPrivateBrace == fCheck.fBraceCount) {
260 fCheck.fPrivateBrace = 0;
261 fCheck.fDoubleReturn = true;
262 } else {
263 i.skipExact("};");
264 fCheck.fBraceCount -= 1;
265 }
266 return false;
267 }
268 if (i.startsWith("public:")) {
269 if (fCheck.fBraceCount <= fCheck.fPrivateBrace) {
270 fCheck.fPrivateBrace = 0;
271 if (fCheck.fPrivateProtected) {
272 i.skipExact("public:");
273 }
274 } else {
275 i.skipExact("public:");
276 }
277 } else {
278 fCheck.fBraceCount += i.skipToLineBalance('{', '}');
279 }
280 return false;
281 } else if (i.startsWith("};")) {
282 fCheck.fDoubleReturn = 2;
283 }
284 if (i.skipExact(kInline)) {
285 fCheck.fSkipInline = true;
286 return false;
287 }
288 if (i.skipExact(kSK_API)) {
289 fCheck.fSkipAPI = true;
290 return false;
291 }
292 if (i.skipExact(kSK_WARN_UNUSED_RESULT)) {
293 fCheck.fSkipWarnUnused = true;
294 return false;
295 }
296 if (i.skipExact("SK_ATTR_DEPRECATED")) {
297 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
298 return false;
299 }
300 if (i.skipExact("SkDEBUGCODE")) {
301 i.skipWhiteSpace();
302 if ('(' != i.peek()) {
303 i.reportError("expected open paren");
304 }
305 TextParserSave save(&i);
306 SkAssertResult(i.skipToBalancedEndBracket('(', ')'));
307 fCheck.fInDebugCode = i.fChar - 1;
308 save.restore();
309 SkAssertResult('(' == i.next());
310 }
311 if ('{' == i.peek()) {
312 if (looks_like_method(i)) {
313 fCheck.fState = CheckCode::State::kMethod;
Cary Clark26d173f2019-01-07 15:16:56 -0500314 bool inBalance = false;
315 TextParser paren(i.fFileName, i.fStart, i.fEnd, i.fLineCount);
316 paren.skipToEndBracket('(');
317 paren.skipToBalancedEndBracket('(', ')');
318 inBalance = i.fChar < paren.fChar;
319 if (!inBalance) {
320 if (!i.skipToBalancedEndBracket('{', '}')) {
321 i.reportError("unbalanced open brace");
322 }
323 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
324 return false;
Cary Clark61313f32018-10-08 14:57:48 -0400325 }
Cary Clark61313f32018-10-08 14:57:48 -0400326 } else if (looks_like_class_decl(i)) {
327 fCheck.fState = CheckCode::State::kClassDeclaration;
328 fCheck.fPrivateBrace = fCheck.fBraceCount + 1;
329 fCheck.fPrivateProtected = false;
330 }
331 }
332 if (':' == i.peek() && looks_like_constructor(i)) {
333 fCheck.fState = CheckCode::State::kConstructor;
334 skip_constructor_initializers(i);
335 return false;
336 }
337 if ('#' == i.peek()) {
338 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
339 return false;
340 }
341 if (i.startsWith("//")) {
342 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
343 return false;
344 }
345 if (i.startsWith("/*")) {
346 i.skipToEndBracket("*/");
347 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
348 return false;
349 }
350 if (looks_like_forward_declaration(i)) {
351 fCheck.fState = CheckCode::State::kForwardDeclaration;
352 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
353 return false;
354 }
355 if (i.skipExact("private:") || i.skipExact("protected:")) {
356 if (!fCheck.fBraceCount) {
357 i.reportError("expect private in brace");
358 }
359 fCheck.fPrivateBrace = fCheck.fBraceCount;
360 fCheck.fPrivateProtected = true;
361 return false;
362 }
363 const char* funcEnd = i.anyOf("(\n");
364 if (funcEnd && '(' == funcEnd[0] && '_' == *i.anyOf("_(")
365 && (i.contains("internal_", funcEnd, nullptr)
366 || i.contains("private_", funcEnd, nullptr)
367 || i.contains("legacy_", funcEnd, nullptr)
368 || i.contains("temporary_", funcEnd, nullptr))) {
369 i.skipTo(funcEnd);
370 if (!i.skipToBalancedEndBracket('(', ')')) {
371 i.reportError("unbalanced open parent");
372 }
373 i.skipSpace();
374 i.skipExact("const ");
375 i.skipSpace();
376 if (';' == i.peek()) {
377 i.next();
378 }
379 fCheck.fState = CheckCode::State::kNone;
380 return false;
381 }
382 return true;
383}
384
Cary Clarkcb6bef02018-11-29 12:05:25 -0500385void IncludeParser::codeBlockAppend(string& result, string s) const {
386 for (char c : s) {
387 this->codeBlockAppend(result, c);
388 }
389}
390
Cary Clark61313f32018-10-08 14:57:48 -0400391void IncludeParser::codeBlockAppend(string& result, char ch) const {
392 if (Elided::kYes == fElided && fCheck.fBraceCount) {
393 return;
394 }
395 this->stringAppend(result, ch);
396}
397
398void IncludeParser::codeBlockSpaces(string& result, int indent) const {
399 if (!indent) {
400 return;
401 }
402 if (Elided::kYes == fElided && fCheck.fBraceCount) {
403 return;
404 }
405 SkASSERT(indent > 0);
406 if (fDebugWriteCodeBlock) {
407 SkDebugf("%*c", indent, ' ');
408 }
409 result.append(indent, ' ');
410}
411
Cary Clarkcb6bef02018-11-29 12:05:25 -0500412string IncludeParser::writeCodeBlock(const Definition& iDef) {
413 if (MarkType::kComment == iDef.fMarkType) {
414 return "";
415 }
416 if (iDef.fUndocumented) {
417 return "";
418 }
Cary Clark61313f32018-10-08 14:57:48 -0400419 TextParser i(&iDef);
Cary Clarkcb6bef02018-11-29 12:05:25 -0500420 (void) i.skipExact("SkDEBUGCODE(");
421 if (MarkType::kConst == iDef.fMarkType && !i.fEnd) {
422 // TODO: end should have been set earlier
423 auto iter = iDef.fParent->fTokens.begin();
424 std::advance(iter, iDef.fParentIndex + 1);
425 SkASSERT(iter != iDef.fParent->fTokens.end());
426 i.fEnd = iter->fContentStart;
427 }
428 const char* loc;
429 if (MarkType::kMember == iDef.fMarkType) {
430 const char* parentEnd = iDef.fParent->fContentEnd;
431 TextParser newEnd(&iDef);
432 newEnd.fEnd = parentEnd;
433 const char* memberEnd = newEnd.anyOf(",};");
434 if (memberEnd && (';' == memberEnd[0] || ',' == memberEnd[0])) {
435 i.fEnd = memberEnd + 1;
436 }
437 }
438 if (i.contains("//", i.fEnd, &loc)) {
439 i.fEnd = loc;
440 }
441 if (i.contains("/*", i.fEnd, &loc)) {
442 i.fEnd = loc;
443 }
444 if (i.contains("{", i.fEnd, &loc)) {
Cary Clark26d173f2019-01-07 15:16:56 -0500445 bool inBalance = false;
446 if (MarkType::kMethod == iDef.fMarkType) {
447 TextParser paren(&iDef);
448 paren.skipToEndBracket('(');
449 paren.skipToBalancedEndBracket('(', ')');
450 inBalance = loc < paren.fChar;
451 }
452 if (!inBalance) {
453 i.fEnd = loc + 1;
454 while (i.fEnd < iDef.fContentEnd && ' ' >= i.fEnd[0]) {
455 ++i.fEnd;
456 }
Cary Clarkcb6bef02018-11-29 12:05:25 -0500457 }
458 }
459 while (i.fEnd > i.fStart && ' ' == i.fEnd[-1]) {
460 --i.fEnd;
461 }
Cary Clark61313f32018-10-08 14:57:48 -0400462 const char* before = iDef.fContentStart;
463 while (' ' == *--before)
464 ;
465 int startIndent = iDef.fContentStart - before - 1;
Cary Clarkcb6bef02018-11-29 12:05:25 -0500466 bool saveDebugWriteBlock = fDebugWriteCodeBlock;
467 fDebugWriteCodeBlock = false;
468 string result = writeCodeBlock(i, iDef.fMarkType, startIndent);
469 fDebugWriteCodeBlock = saveDebugWriteBlock;
470 if (!result.empty()) {
471 if (MarkType::kNone != fPreviousMarkType && iDef.fMarkType != fPreviousMarkType
472 && ((MarkType::kEnum != fPreviousMarkType
473 && MarkType::kEnumClass != fPreviousMarkType)
474 || MarkType::kMember != iDef.fMarkType)
475 && (MarkType::kMember != fPreviousMarkType
476 || iDef.fParent == fPreviousDef->fParent)) {
477 result = "\n" + result;
478 }
479 if (fDebugWriteCodeBlock) {
480 SkDebugf("%s", result.c_str());
481 }
482 fPreviousDef = &iDef;
483 fPreviousMarkType = iDef.fMarkType;
484 }
485 for (auto& token : iDef.fTokens) {
486 result += this->writeCodeBlock(token);
487 }
488 if (MarkType::kEnum == iDef.fMarkType || MarkType::kEnumClass == iDef.fMarkType
489 || MarkType::kStruct == iDef.fMarkType || MarkType::kClass == iDef.fMarkType) {
490 this->codeBlockSpaces(result, startIndent);
491 this->codeBlockAppend(result, "};\n\n");
492 }
493 return result;
Cary Clark61313f32018-10-08 14:57:48 -0400494}
495
496string IncludeParser::writeCodeBlock(TextParser& i, MarkType markType, int startIndent) {
497 string result;
498 char last;
499 int lastIndent = 0;
500 bool lastDoubleMeUp = false;
501 fCheck.reset();
Cary Clarka90ea222018-10-16 10:30:28 -0400502 if (MarkType::kDefine == markType) {
503 result = "#define ";
504 } else {
505 this->codeBlockSpaces(result, startIndent);
506 }
Cary Clark61313f32018-10-08 14:57:48 -0400507 do {
508 if (!this->advanceInclude(i)) {
509 continue;
510 }
511 do {
512 last = i.peek();
513 SkASSERT(' ' < last);
514 if (fCheck.fInDebugCode == i.fChar) {
515 fCheck.fInDebugCode = nullptr;
516 i.next(); // skip close paren
517 break;
518 }
519 if (CheckCode::State::kMethod == fCheck.fState) {
520 this->codeBlockAppend(result, ';');
521 fCheck.fState = CheckCode::State::kNone;
522 }
523 if (fCheck.fWriteReturn) {
524 this->codeBlockAppend(result, '\n');
525 bool doubleMeUp = i.startsWith("typedef ") || looks_like_const(i)
526 || (!strncmp("struct ", i.fStart, 7) && looks_like_member(i));
527 if ((!--fCheck.fDoubleReturn && !i.startsWith("};")) || i.startsWith("enum ")
528 || i.startsWith("typedef ") || doubleMeUp || fCheck.fTypedefReturn
529 || (fCheck.fIndent && (i.startsWith("class ") || i.startsWith("struct ")))) {
530 if (lastIndent > 0 && (!doubleMeUp || !lastDoubleMeUp)) {
531 this->codeBlockAppend(result, '\n');
532 }
533 fCheck.fTypedefReturn = false;
534 lastDoubleMeUp = doubleMeUp;
535 }
536 if (doubleMeUp) {
537 fCheck.fTypedefReturn = true;
538 }
539 lastIndent = fCheck.fIndent;
540 }
541 if (fCheck.fIndent) {
542 size_t indent = fCheck.fIndent;
543 if (fCheck.fSkipInline && indent > sizeof(kInline)) {
544 indent -= sizeof(kInline) - 1;
545 }
546 if (fCheck.fSkipAPI && indent > sizeof(kSK_API)) {
547 indent -= sizeof(kSK_API) - 1;
548 }
549 if (fCheck.fSkipWarnUnused && indent > sizeof(kSK_WARN_UNUSED_RESULT)) {
550 indent -= sizeof(kSK_WARN_UNUSED_RESULT) - 1;
551 }
552
553 this->codeBlockSpaces(result, indent);
554 }
555 this->codeBlockAppend(result, last);
556 fCheck.fWriteReturn = false;
557 fCheck.fIndent = 0;
558 fCheck.fBraceCount += '{' == last;
559 fCheck.fBraceCount -= '}' == last;
560 if (';' == last) {
561 fCheck.fSkipInline = false;
562 fCheck.fSkipAPI = false;
563 fCheck.fSkipWarnUnused = false;
564 }
565 if (fCheck.fBraceCount < 0) {
566 i.reportError("unbalanced close brace");
567 return result;
568 }
569 i.next();
570 } while (!i.eof() && ' ' < i.peek() && !i.startsWith("//"));
571 } while (!i.eof());
572 if (CheckCode::State::kMethod == fCheck.fState) {
573 this->codeBlockAppend(result, ';');
574 }
Cary Clarkcb6bef02018-11-29 12:05:25 -0500575 bool elided = Elided::kYes == fElided;
576 bool elidedTemplate = elided && !strncmp(i.fStart, "template ", 9);
Cary Clark61313f32018-10-08 14:57:48 -0400577 bool elidedTClass = elidedTemplate && MarkType::kClass == markType;
Cary Clarkcb6bef02018-11-29 12:05:25 -0500578 bool statementEnd = !result.empty() && (MarkType::kMethod == markType
579 || MarkType::kTypedef == markType || '}' == result.back());
580 bool semiEnd = !result.empty() && (',' == result.back() || ';' == result.back());
Cary Clark61313f32018-10-08 14:57:48 -0400581 if (fCheck.fWriteReturn || elidedTClass) {
582 this->codeBlockAppend(result, '\n');
583 }
Cary Clarkcb6bef02018-11-29 12:05:25 -0500584 if (elided && ((MarkType::kFunction != markType && lastIndent > startIndent) || elidedTClass)) {
Cary Clark61313f32018-10-08 14:57:48 -0400585 this->codeBlockAppend(result, '}');
Cary Clarkcb6bef02018-11-29 12:05:25 -0500586 statementEnd = true;
Cary Clark61313f32018-10-08 14:57:48 -0400587 }
Cary Clarkcb6bef02018-11-29 12:05:25 -0500588 if (elided || statementEnd) {
589 this->codeBlockAppend(result, ";\n");
590 } else if (elidedTemplate || semiEnd) {
Cary Clark61313f32018-10-08 14:57:48 -0400591 this->codeBlockAppend(result, '\n');
592 }
593 return result;
594}
595
Ben Wagner63fd7602017-10-09 15:45:33 -0400596void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400597 const vector<string>& foundParams) {
598 for (auto& methodParam : methodParams) {
599 bool found = false;
600 for (auto& foundParam : foundParams) {
601 if (methodParam == foundParam) {
602 found = true;
603 break;
604 }
605 }
606 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400607 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400608 }
609 }
610 for (auto& foundParam : foundParams) {
611 bool found = false;
612 for (auto& methodParam : methodParams) {
613 if (methodParam == foundParam) {
614 found = true;
615 break;
616 }
617 }
618 if (!found) {
619 this->reportError("doxygen param does not match method declaration");
620 }
621 }
622}
623
624bool IncludeParser::checkForWord() {
625 if (!fIncludeWord) {
626 return true;
627 }
628 KeyWord keyWord = FindKey(fIncludeWord, fChar);
Cary Clark224c7002018-06-27 11:00:21 -0400629 if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
630 Bracket bracket = this->topBracket();
631 if (Bracket::kParen == bracket) {
632 return true;
633 }
634 }
Cary Clark8032b982017-07-28 11:04:54 -0400635 if (KeyWord::kNone != keyWord) {
636 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
637 this->addKeyword(keyWord);
638 return true;
639 }
640 } else {
641 this->addWord();
642 return true;
643 }
644 Definition* poundDef = fParent;
645 if (!fParent) {
646 return reportError<bool>("expected parent");
647 }
648 if (Definition::Type::kBracket != poundDef->fType) {
649 return reportError<bool>("expected bracket");
650 }
651 if (Bracket::kPound != poundDef->fBracket) {
652 return reportError<bool>("expected preprocessor");
653 }
654 if (KeyWord::kNone != poundDef->fKeyWord) {
655 return reportError<bool>("already found keyword");
656 }
657 poundDef->fKeyWord = keyWord;
658 fIncludeWord = nullptr;
659 switch (keyWord) {
660 // these do not link to other # directives
661 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400662 if (!fInBrace) {
663 SkASSERT(!fInDefine);
664 fInDefine = true;
665 }
Cary Clark8032b982017-07-28 11:04:54 -0400666 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500667 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400668 break;
669 // these start a # directive link
670 case KeyWord::kIf:
671 case KeyWord::kIfdef:
672 case KeyWord::kIfndef:
673 break;
674 // these continue a # directive link
675 case KeyWord::kElif:
Cary Clarkabaffd82018-11-15 08:25:12 -0500676 case KeyWord::kElse:
Cary Clark8032b982017-07-28 11:04:54 -0400677 this->popObject(); // pop elif
678 if (Bracket::kPound != fParent->fBracket) {
679 return this->reportError<bool>("expected preprocessor directive");
680 }
681 this->popBracket(); // pop if
682 poundDef->fParent = fParent;
Cary Clarkabaffd82018-11-15 08:25:12 -0500683 fParent = poundDef; // push elif back
684 break;
Cary Clark8032b982017-07-28 11:04:54 -0400685 // this ends a # directive link
686 case KeyWord::kEndif:
687 // FIXME : should this be calling popBracket() instead?
688 this->popObject(); // pop endif
689 if (Bracket::kPound != fParent->fBracket) {
690 return this->reportError<bool>("expected preprocessor directive");
691 }
692 this->popBracket(); // pop if/else
693 break;
694 default:
695 SkASSERT(0);
696 }
697 return true;
698}
699
700string IncludeParser::className() const {
701 string name(fParent->fName);
702 size_t slash = name.find_last_of("/");
703 if (string::npos == slash) {
704 slash = name.find_last_of("\\");
705 }
706 SkASSERT(string::npos != slash);
707 string result = name.substr(slash);
708 result = result.substr(1, result.size() - 3);
709 return result;
710}
711
Cary Clarka90ea222018-10-16 10:30:28 -0400712void IncludeParser::writeCodeBlock() {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500713 fElided = Elided::kNo;
Cary Clark61313f32018-10-08 14:57:48 -0400714 for (auto& classMapper : fIClassMap) {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500715 fPreviousMarkType = MarkType::kNone;
716 fPreviousDef = nullptr;
717 classMapper.second.fCode = this->writeCodeBlock(classMapper.second);
Cary Clark61313f32018-10-08 14:57:48 -0400718 }
719 for (auto& enumMapper : fIEnumMap) {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500720 fPreviousMarkType = MarkType::kNone;
721 fPreviousDef = nullptr;
722 enumMapper.second->fCode = this->writeCodeBlock(*enumMapper.second);
Cary Clarka90ea222018-10-16 10:30:28 -0400723 }
724 for (auto& typedefMapper : fITypedefMap) {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500725 fPreviousMarkType = MarkType::kNone;
726 fPreviousDef = nullptr;
727 typedefMapper.second->fCode = this->writeCodeBlock(*typedefMapper.second);
Cary Clarka90ea222018-10-16 10:30:28 -0400728 }
729 for (auto& defineMapper : fIDefineMap) {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500730 fPreviousMarkType = MarkType::kNone;
731 fPreviousDef = nullptr;
732 defineMapper.second->fCode = this->writeCodeBlock(*defineMapper.second);
733 }
734}
735
736void IncludeParser::checkName(Definition* def) {
737 SkASSERT(!def->fName.empty());
738 TextParser parser(def->fFileName, &def->fName.front(), &def->fName.back() + 1, def->fLineCount);
739 const vector<string> skipWords = { "deprecated", "experimental", "internal", "private",
740 "legacy", "temporary" };
741 if (!parser.anyWord(skipWords, 0).empty()) {
742 def->fUndocumented = true;
Cary Clark61313f32018-10-08 14:57:48 -0400743 }
744}
745
Cary Clark884dd7d2017-10-11 10:37:52 -0400746#include <sstream>
747#include <iostream>
748
Cary Clarkfd32e722018-11-16 14:36:02 -0500749void IncludeParser::checkTokens(list<Definition>& tokens, string key, string className,
750 RootDefinition* root, BmhParser& bmhParser) {
751 for (const auto& token : tokens) {
752 if (token.fPrivate) {
753 continue;
754 }
755 string fullName = key + "::" + token.fName;
756 const Definition* def = nullptr;
757 if (root) {
758 def = root->find(fullName, RootDefinition::AllowParens::kYes);
759 }
760 switch (token.fMarkType) {
761 case MarkType::kMethod: {
762 if (this->isInternalName(token)) {
763 continue;
764 }
765 if (!root) {
766 if (token.fUndocumented) {
767 break;
768 }
769 auto methIter = bmhParser.fMethodMap.find(token.fName);
770 if (bmhParser.fMethodMap.end() != methIter) {
771 def = &methIter->second;
772 if (def->crossCheck2(token)) {
773 def->fVisited = true;
774 } else {
775 this->suggestFix(Suggest::kMethodDiffers, token, root, def);
776 fFailed = true;
777 }
778 } else {
779 this->suggestFix(Suggest::kMethodMissing, token, root, nullptr);
780 fFailed = true;
781 }
782 break;
783 }
784 if (!def) {
785 string paramName = className + "::";
786 paramName += string(token.fContentStart,
787 token.fContentEnd - token.fContentStart);
788 if (string::npos != paramName.find('\n')) {
789 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
790 paramName.end());
791 }
792 def = root->find(paramName, RootDefinition::AllowParens::kYes);
793 if (!def && 0 == token.fName.find("operator")) {
794 string operatorName = className + "::";
795 TextParser oper("", token.fStart, token.fContentEnd, 0);
796 const char* start = oper.strnstr("operator", token.fContentEnd);
797 SkASSERT(start);
798 oper.skipTo(start);
799 oper.skipToEndBracket('(');
800 int parens = 0;
801 do {
802 if ('(' == oper.peek()) {
803 ++parens;
804 } else if (')' == oper.peek()) {
805 --parens;
806 }
807 } while (!oper.eof() && oper.next() && parens > 0);
808 operatorName += string(start, oper.fChar - start);
809 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
810 }
811 }
812 if (!def) {
813 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
814 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
815 const char* tokenEnd = token.methodEnd();
816 string constructorName = className + "::";
817 constructorName += string(token.fContentStart + skip,
818 tokenEnd - token.fContentStart - skip);
819 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
820 }
821 if (!def && 0 == token.fName.find("SK_")) {
822 string incName = token.fName + "()";
823 string macroName = className + "::" + incName;
824 def = root->find(macroName, RootDefinition::AllowParens::kYes);
825 if (def) {
826 if (def->fName == incName) {
827 def->fVisited = true;
828 if ("SK_TO_STRING_NONVIRT" == token.fName) {
829 def = root->find(className + "::toString",
830 RootDefinition::AllowParens::kYes);
831 if (def) {
832 def->fVisited = true;
833 } else {
834 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
835 fFailed = true;
836 }
837 }
838 break;
839 } else {
840 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
841 fFailed = true;
842 }
843 }
844 }
845 if (!def) {
846 bool allLower = true;
847 for (size_t index = 0; index < token.fName.length(); ++index) {
848 if (!islower(token.fName[index])) {
849 allLower = false;
850 break;
851 }
852 }
853 if (allLower) {
854 string lowerName = className + "::" + token.fName + "()";
855 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
856 }
857 }
858 if (!def) {
859 if (0 == token.fName.find("SkDEBUGCODE")) {
860 break;
861 }
862 }
863 if (!def) {
864 // simple method names inside nested classes have a bug and are missing trailing parens
865 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
866 def = root->find(withParens, RootDefinition::AllowParens::kNo);
867 }
868 if (!def) {
869 if (!token.fUndocumented) {
870 this->suggestFix(Suggest::kMethodMissing, token, root, nullptr);
871 fFailed = true;
872 }
873 break;
874 }
875 if (token.fUndocumented) {
876 // we can't report an error yet; if bmh documents this unnecessarily,
877 // we'll detect that later. It may be that def points to similar
878 // documented function.
879 break;
880 }
881 if (def->crossCheck2(token)) {
882 def->fVisited = true;
883 } else {
884 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
885 fFailed = true;
886 }
887 } break;
888 case MarkType::kComment:
889 break;
890 case MarkType::kEnumClass:
891 case MarkType::kEnum: {
892 if (!def) {
893 // work backwards from first word to deduce #Enum name
894 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
895 SkAssertResult(firstMember.skipName("enum"));
896 SkAssertResult(firstMember.skipToEndBracket('{'));
897 firstMember.next();
898 firstMember.skipWhiteSpace();
899 SkASSERT('k' == firstMember.peek());
900 const char* savePos = firstMember.fChar;
901 firstMember.skipToNonName();
902 const char* wordEnd = firstMember.fChar;
903 firstMember.fChar = savePos;
904 const char* lastUnderscore = nullptr;
905 do {
906 if (!firstMember.skipToEndBracket('_')) {
907 break;
908 }
909 if (firstMember.fChar > wordEnd) {
910 break;
911 }
912 lastUnderscore = firstMember.fChar;
913 } while (firstMember.next());
914 if (lastUnderscore) {
915 ++lastUnderscore;
916 string enumName(lastUnderscore, wordEnd - lastUnderscore);
917 if (root) {
918 string anonName = className + "::" + enumName + 's';
919 def = root->find(anonName, RootDefinition::AllowParens::kYes);
920 } else {
921 auto enumIter = bmhParser.fEnumMap.find(enumName);
922 if (bmhParser.fEnumMap.end() != enumIter) {
923 RootDefinition* rootDef = &enumIter->second;
924 def = rootDef;
925 }
926 }
927 }
928 if (!def && !root) {
929 auto enumIter = bmhParser.fEnumMap.find(token.fName);
930 if (bmhParser.fEnumMap.end() != enumIter) {
931 def = &enumIter->second;
932 }
933 if (!def) {
934 auto enumClassIter = bmhParser.fClassMap.find(token.fName);
935 if (bmhParser.fClassMap.end() != enumClassIter) {
936 def = &enumClassIter->second;
937 }
938 }
939 }
940 if (!def) {
941 if (!token.fUndocumented) {
942 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
943 fFailed = true;
944 }
945 break;
946 }
947 }
948 def->fVisited = true;
949 bool hasCode = false;
950 bool hasPopulate = true;
951 for (auto& child : def->fChildren) {
952 if (MarkType::kCode == child->fMarkType) {
953 hasPopulate = std::any_of(child->fChildren.begin(),
954 child->fChildren.end(), [](auto grandChild){
955 return MarkType::kPopulate == grandChild->fMarkType; });
956 if (!hasPopulate) {
957 def = child;
958 }
959 hasCode = true;
960 break;
961 }
962 }
963 if (!hasCode && !root) {
964 const Definition* topic = def->topicParent();
965 hasCode = std::any_of(topic->fChildren.begin(), topic->fChildren.end(),
966 [](Definition* def){ return MarkType::kCode == def->fMarkType
967 && def->fChildren.size() > 0 && MarkType::kPopulate ==
968 def->fChildren.front()->fMarkType; });
969 }
970 if (!hasCode) {
971 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
972 fFailed = true;
973 break;
974 }
975 if (!hasPopulate) {
976 if (def->crossCheck(token)) {
977 def->fVisited = true;
978 } else {
979 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
980 fFailed = true;
981 }
982 }
983 for (auto& member : token.fTokens) {
984 if (MarkType::kMember != member.fMarkType) {
985 continue;
986 }
987 string constName = MarkType::kEnumClass == token.fMarkType ?
988 fullName : className;
989 if (root) {
990 constName += "::" + member.fName;
991 def = root->find(constName, RootDefinition::AllowParens::kYes);
992 } else {
993 auto enumMapper = bmhParser.fEnumMap.find(token.fName);
994 if (bmhParser.fEnumMap.end() != enumMapper) {
995 auto& enumDoc = enumMapper->second;
996 auto memberIter = enumDoc.fLeaves.find(member.fName);
997 if (enumDoc.fLeaves.end() != memberIter) {
998 def = &memberIter->second;
999 }
1000 }
1001 }
1002 if (!def) {
1003 string innerName = key + "::" + member.fName;
1004 def = root->find(innerName, RootDefinition::AllowParens::kYes);
1005 }
1006 if (!def) {
1007 if (!member.fUndocumented) {
1008 SkDebugf("const missing from bmh: %s\n", constName.c_str());
1009 fFailed = true;
1010 }
1011 } else {
1012 def->fVisited = true;
1013 }
1014 }
1015 } break;
1016 case MarkType::kMember:
1017 if (def) {
1018 def->fVisited = true;
1019 } else {
1020 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
1021 fFailed = true;
1022 }
1023 break;
1024 case MarkType::kTypedef:
1025 if (!def && !root) {
1026 auto typedefIter = bmhParser.fTypedefMap.find(token.fName);
1027 if (bmhParser.fTypedefMap.end() != typedefIter) {
1028 def = &typedefIter->second;
1029 }
1030 }
1031 if (def) {
1032 def->fVisited = true;
1033 } else {
1034 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
1035 fFailed = true;
1036 }
1037 break;
1038 case MarkType::kConst:
1039 if (!def && !root) {
1040 auto constIter = bmhParser.fConstMap.find(token.fName);
1041 if (bmhParser.fConstMap.end() != constIter) {
1042 def = &constIter->second;
1043 }
1044 }
1045 if (def) {
1046 def->fVisited = true;
1047 } else {
1048 if (!token.fUndocumented) {
1049 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
1050 fFailed = true;
1051 }
1052 }
1053 break;
1054 case MarkType::kDefine:
1055 // TODO: incomplete
1056 break;
1057 default:
1058 SkASSERT(0); // unhandled
1059 break;
1060 }
1061 }
1062}
1063
Cary Clark8032b982017-07-28 11:04:54 -04001064bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -04001065 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001066 string className = classMapper.first;
1067 auto finder = bmhParser.fClassMap.find(className);
1068 if (bmhParser.fClassMap.end() == finder) {
1069 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -04001070 continue;
1071 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001072 }
1073 for (auto& classMapper : fIClassMap) {
Cary Clarkabaffd82018-11-15 08:25:12 -05001074 if (classMapper.second.fUndocumented) {
1075 continue;
1076 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001077 string className = classMapper.first;
1078 std::istringstream iss(className);
1079 string classStr;
1080 string classBase;
1081 RootDefinition* root = nullptr;
1082 while (std::getline(iss, classStr, ':')) {
1083 if (root) {
1084 if (!classStr.length()) {
1085 continue;
1086 }
1087 classBase += "::" + classStr;
1088 auto finder = root->fBranches.find(classBase);
1089 if (root->fBranches.end() != finder) {
1090 root = finder->second;
1091 } else {
1092 SkASSERT(0);
1093 }
1094 } else {
1095 classBase = classStr;
1096 auto finder = bmhParser.fClassMap.find(classBase);
1097 if (bmhParser.fClassMap.end() != finder) {
1098 root = &finder->second;
1099 } else {
1100 SkASSERT(0);
1101 }
1102 }
1103 }
Cary Clarkfd32e722018-11-16 14:36:02 -05001104 this->checkTokens(classMapper.second.fTokens, classMapper.first, className, root,
1105 bmhParser);
Cary Clark8032b982017-07-28 11:04:54 -04001106 }
Cary Clarkfd32e722018-11-16 14:36:02 -05001107 this->checkTokens(fGlobals, "", "", nullptr, bmhParser);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001108 int crossChecks = 0;
1109 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -04001110 for (auto& classMapper : fIClassMap) {
1111 string className = classMapper.first;
1112 auto finder = bmhParser.fClassMap.find(className);
1113 if (bmhParser.fClassMap.end() == finder) {
1114 continue;
1115 }
1116 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -05001117 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -05001118 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -04001119 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001120 if (crossChecks) {
1121 SkDebugf(".");
1122 } else {
1123 SkDebugf("cross-check");
1124 firstCheck = className;
1125 }
1126 ++crossChecks;
1127 }
1128 if (crossChecks) {
1129 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -05001130 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -05001131 }
1132 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -04001133 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001134 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -05001135 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -04001136}
1137
1138IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001139 string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001140 string className;
1141 const Definition* test = fParent;
1142 while (Definition::Type::kFileType != test->fType) {
1143 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
1144 className = test->fName + "::";
1145 break;
1146 }
1147 test = test->fParent;
1148 }
Ben Wagner63fd7602017-10-09 15:45:33 -04001149 className += name;
Cary Clark8032b982017-07-28 11:04:54 -04001150 unordered_map<string, IClassDefinition>& map = fIClassMap;
1151 IClassDefinition& markupDef = map[className];
1152 if (markupDef.fStart) {
1153 typedef IClassDefinition* IClassDefPtr;
1154 return INHERITED::reportError<IClassDefPtr>("class already defined");
1155 }
1156 markupDef.fFileName = fFileName;
1157 markupDef.fStart = includeDef.fStart;
1158 markupDef.fContentStart = includeDef.fStart;
1159 markupDef.fName = className;
Cary Clarkcb6bef02018-11-29 12:05:25 -05001160 this->checkName(&markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04001161 markupDef.fContentEnd = includeDef.fContentEnd;
1162 markupDef.fTerminator = includeDef.fTerminator;
1163 markupDef.fParent = fParent;
1164 markupDef.fLineCount = fLineCount;
1165 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
1166 MarkType::kStruct : MarkType::kClass;
1167 markupDef.fKeyWord = includeDef.fKeyWord;
1168 markupDef.fType = Definition::Type::kMark;
Cary Clarkabaffd82018-11-15 08:25:12 -05001169 auto tokenIter = includeDef.fParent->fTokens.begin();
1170 SkASSERT(includeDef.fParentIndex > 0);
1171 std::advance(tokenIter, includeDef.fParentIndex - 1);
1172 const Definition* priorComment = &*tokenIter;
1173 markupDef.fUndocumented = priorComment->fUndocumented;
Cary Clark8032b982017-07-28 11:04:54 -04001174 fParent = &markupDef;
1175 return &markupDef;
1176}
1177
1178void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
1179 auto& tokens = classDef.fTokens;
Cary Clarkcb6bef02018-11-29 12:05:25 -05001180 bool wroteTail = true;
Cary Clark8032b982017-07-28 11:04:54 -04001181 for (auto& token : tokens) {
1182 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
1183 continue;
1184 }
Cary Clarkcb6bef02018-11-29 12:05:25 -05001185 if (wroteTail && MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001186 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -04001187 }
1188 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -04001189 case MarkType::kConst:
1190 this->dumpConst(token, classDef.fName);
1191 break;
Cary Clark8032b982017-07-28 11:04:54 -04001192 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -05001193 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -05001194 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -04001195 break;
1196 case MarkType::kMethod:
Cary Clarkcb6bef02018-11-29 12:05:25 -05001197 if (!this->dumpMethod(token, classDef.fName)) {
1198 wroteTail = false;
1199 continue;
1200 }
Cary Clark8032b982017-07-28 11:04:54 -04001201 break;
1202 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -04001203 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -04001204 continue;
1205 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001206 case MarkType::kTypedef:
1207 this->dumpTypedef(token, classDef.fName);
1208 break;
Cary Clark8032b982017-07-28 11:04:54 -04001209 default:
1210 SkASSERT(0);
1211 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001212 this->dumpCommonTail(token);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001213 wroteTail = true;
Cary Clark8032b982017-07-28 11:04:54 -04001214 }
1215}
Cary Clark9174bda2017-09-19 17:39:32 -04001216void IncludeParser::dumpComment(const Definition& token) {
1217 fLineCount = token.fLineCount;
1218 fChar = fLine = token.fContentStart;
1219 fEnd = token.fContentEnd;
Cary Clark9174bda2017-09-19 17:39:32 -04001220 if (MarkType::kMethod == token.fMarkType) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001221 this->lf(2);
1222 this->writeTag("Populate");
1223 this->lf(2);
1224 return;
Cary Clark8032b982017-07-28 11:04:54 -04001225 }
Cary Clark9174bda2017-09-19 17:39:32 -04001226 for (const auto& child : token.fTokens) {
1227 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
1228 break;
1229 }
Cary Clark8032b982017-07-28 11:04:54 -04001230 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001231 if (child.fPrivate) {
1232 break;
1233 }
Cary Clarkcb6bef02018-11-29 12:05:25 -05001234 if (child.length() > 1) {
Cary Clark9174bda2017-09-19 17:39:32 -04001235 const char* start = child.fContentStart;
1236 ptrdiff_t length = child.fContentEnd - start;
1237 SkASSERT(length >= 0);
1238 while (length && '/' == start[0]) {
1239 start += 1;
1240 --length;
Cary Clark8032b982017-07-28 11:04:54 -04001241 }
Cary Clark9174bda2017-09-19 17:39:32 -04001242 while (length && '/' == start[length - 1]) {
1243 length -= 1;
1244 if (length && '*' == start[length - 1]) {
1245 length -= 1;
1246 }
1247 }
1248 if (length) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001249 this->lf(2);
1250 if ("!< " == string(start, length).substr(0, 3)) {
1251 return;
Cary Clark9174bda2017-09-19 17:39:32 -04001252 }
1253 this->writeBlock(length, start);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001254 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001255 }
1256 }
1257 }
1258 }
Cary Clark8032b982017-07-28 11:04:54 -04001259}
1260
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001261void IncludeParser::dumpCommonTail(const Definition& token) {
1262 this->lf(2);
1263 this->writeTag("Example");
1264 this->lf(1);
1265 this->writeString("// incomplete");
1266 this->lf(1);
1267 this->writeEndTag();
1268 this->lf(2);
1269 this->writeTag("SeeAlso");
1270 this->writeSpace();
1271 this->writeString("incomplete");
1272 this->lf(2);
1273 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
1274 this->lf(2);
1275}
1276
Cary Clark224c7002018-06-27 11:00:21 -04001277void IncludeParser::dumpConst(const Definition& token, string className) {
1278 this->writeTag("Const");
1279 this->writeSpace();
1280 this->writeString(token.fName);
1281 this->writeTagTable("Line", "incomplete");
1282 this->lf(2);
1283 this->dumpComment(token);
1284}
1285
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001286void IncludeParser::dumpDefine(const Definition& token) {
1287 this->writeTag("Define", token.fName);
1288 this->lf(2);
1289 this->writeTag("Code");
1290 this->lfAlways(1);
1291 this->writeString("###$");
1292 this->lfAlways(1);
1293 this->indentToColumn(4);
1294 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
1295 this->lf(1);
1296 this->indentToColumn(0);
1297 this->writeString("$$$#");
1298
1299 this->writeEndTag();
1300 this->lf(2);
1301 this->dumpComment(token);
1302 for (auto& child : token.fTokens) {
1303 if (MarkType::kComment == child.fMarkType) {
1304 continue;
1305 }
1306 this->writeTag("Param", child.fName);
1307 this->writeSpace();
1308 this->writeString("incomplete");
1309 this->writeSpace();
1310 this->writeString("##");
1311 this->lf(1);
1312 }
1313}
1314
1315void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001316 string tagType(MarkType::kEnum == token.fMarkType ? "Enum" : "EnumClass");
1317 this->writeTag(tagType.c_str(), token.fName);
Cary Clark9174bda2017-09-19 17:39:32 -04001318 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001319 this->writeTag("Code");
Cary Clarkcb6bef02018-11-29 12:05:25 -05001320 this->writeTag("Populate");
1321 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001322 this->lf(2);
1323 this->dumpComment(token);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001324 string prior;
1325 for (auto& child : token.fTokens) {
1326 if (MarkType::kComment == child.fMarkType) {
1327 prior = string(child.fContentStart, child.length());
1328 }
1329 if (MarkType::kMember != child.fMarkType) {
1330 continue;
1331 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001332 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -04001333 this->writeSpace();
Cary Clarkcb6bef02018-11-29 12:05:25 -05001334 this->writeString(child.fName);
1335 this->writeSpace(2);
1336 this->writeString("0 # incomplete; replace '0' with member value");
Cary Clark9174bda2017-09-19 17:39:32 -04001337 this->lf(1);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001338 this->writeTagNoLF("Line", "#");
1339 this->writeSpace();
1340 if ("/!< " == prior.substr(0, 4)) {
1341 this->writeString(prior.substr(4));
1342 } else {
1343 this->writeString("incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001344 }
Cary Clarkcb6bef02018-11-29 12:05:25 -05001345 this->writeSpace();
1346 this->writeString("##");
1347 this->lf(1);
1348 this->writeString("# incomplete; add description or delete");
Cary Clark9174bda2017-09-19 17:39:32 -04001349 this->writeEndTag();
1350 }
1351 this->lf(2);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001352 this->writeString("# incomplete; add description or delete");
1353 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001354}
1355
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001356bool IncludeParser::dumpGlobals(string* globalFileName, long int* globalTell) {
1357 bool hasGlobals = !fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1358 || !fITemplateMap.empty()|| !fITypedefMap.empty() || !fIUnionMap.empty();
1359 if (!hasGlobals) {
Cary Clark224c7002018-06-27 11:00:21 -04001360 return true;
1361 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001362 size_t lastBSlash = fFileName.rfind('\\');
1363 size_t lastSlash = fFileName.rfind('/');
1364 size_t lastDotH = fFileName.rfind(".h");
1365 SkASSERT(string::npos != lastDotH);
1366 if (string::npos != lastBSlash && (string::npos == lastSlash
1367 || lastBSlash < lastSlash)) {
1368 lastSlash = lastBSlash;
1369 } else if (string::npos == lastSlash) {
1370 lastSlash = -1;
1371 }
1372 lastSlash += 1;
1373 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
1374 string fileName = globalsName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001375 *globalFileName = fileName;
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001376 fOut = fopen(fileName.c_str(), "wb");
1377 if (!fOut) {
1378 SkDebugf("could not open output file %s\n", globalsName.c_str());
1379 return false;
1380 }
1381 string prefixName = globalsName.substr(0, 2);
1382 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
1383 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
1384 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -04001385 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001386 this->lf(2);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001387 if (!fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1388 || !fITemplateMap.empty() || !fITypedefMap.empty() || !fIUnionMap.empty()) {
1389 this->writeTag("Code");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001390 this->writeTag("Populate");
1391 this->writeEndTag();
1392 this->lf(2);
1393 }
1394 std::map<int, Definition*> sortedDefs;
1395 for (const auto& entry : fIDefineMap) {
1396 sortedDefs[entry.second->fLineCount] = entry.second;
1397 }
1398 for (const auto& entry : fIFunctionMap) {
1399 sortedDefs[entry.second->fLineCount] = entry.second;
1400 }
1401 for (const auto& entry : fIEnumMap) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001402 if (string::npos == entry.first.find("::")) {
1403 sortedDefs[entry.second->fLineCount] = entry.second;
1404 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001405 }
1406 for (const auto& entry : fITemplateMap) {
1407 sortedDefs[entry.second->fLineCount] = entry.second;
1408 }
1409 for (const auto& entry : fITypedefMap) {
1410 sortedDefs[entry.second->fLineCount] = entry.second;
1411 }
1412 for (const auto& entry : fIUnionMap) {
1413 sortedDefs[entry.second->fLineCount] = entry.second;
1414 }
1415 for (const auto& entry : sortedDefs) {
1416 const Definition* def = entry.second;
1417 this->writeBlockSeparator();
1418 switch (def->fMarkType) {
1419 case MarkType::kDefine:
1420 this->dumpDefine(*def);
1421 break;
1422 case MarkType::kMethod:
Cary Clarkcb6bef02018-11-29 12:05:25 -05001423 if (!this->dumpMethod(*def, globalsName)) {
1424 continue;
1425 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001426 break;
1427 case MarkType::kEnum:
1428 case MarkType::kEnumClass:
1429 this->dumpEnum(*def, globalsName);
1430 break;
1431 case MarkType::kTemplate:
1432 SkASSERT(0); // incomplete
1433 break;
1434 case MarkType::kTypedef: {
1435 this->writeTag("Typedef");
1436 this->writeSpace();
1437 TextParser parser(def);
1438 if (!parser.skipExact("typedef")) {
1439 return false;
1440 }
1441 if (!parser.skipSpace()) {
1442 return false;
1443 }
1444 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1445 this->lf(2);
1446 this->dumpComment(*def);
1447 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1448 this->lf(2);
1449 } continue;
1450 case MarkType::kUnion:
1451 SkASSERT(0); // incomplete
1452 break;
1453 default:
1454 SkASSERT(0);
1455 }
1456 this->dumpCommonTail(*def);
1457 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001458 *globalTell = ftell(fOut);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001459 this->writeEndTag("Topic", topicName);
1460 this->lfAlways(1);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001461// fclose(fOut); // defer closing in case class needs to be also written here
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001462 return true;
1463}
1464
1465bool IncludeParser::isClone(const Definition& token) {
1466 string name = token.fName;
1467 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1468}
1469
1470bool IncludeParser::isConstructor(const Definition& token, string className) {
1471 string name = token.fName;
1472 return 0 == name.find(className) || '~' == name[0];
1473}
1474
1475bool IncludeParser::isInternalName(const Definition& token) {
1476 string name = token.fName;
1477 // exception for this SkCanvas function .. for now
1478 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1479 return false;
1480 }
1481 return name.substr(0, 7) == "android"
1482 || 0 == token.fName.find("internal_")
1483 || 0 == token.fName.find("Internal_")
1484 || 0 == token.fName.find("legacy_")
1485 || 0 == token.fName.find("temporary_")
1486 || 0 == token.fName.find("private_");
1487}
1488
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001489bool IncludeParser::isMember(const Definition& token) const {
1490 if ('f' == token.fStart[0] && isupper(token.fStart[1])) {
1491 return true;
1492 }
1493 if (!islower(token.fStart[0])) {
1494 return false;
1495 }
1496 // make an exception for SkTextBlob::RunBuffer, sole struct with members not in fXxxx format
1497 if (string::npos != token.fFileName.find("SkTextBlob.h")) {
1498 const Definition* structToken = token.fParent;
1499 if (!structToken) {
1500 return false;
1501 }
1502 if (KeyWord::kStruct != structToken->fKeyWord) {
1503 structToken = token.fParent->fParent;
1504 if (!structToken) {
1505 return false;
1506 }
1507 if (KeyWord::kStruct != structToken->fKeyWord) {
1508 return false;
1509 }
1510 }
1511 SkASSERT(structToken->fTokens.size() > 0);
1512 const Definition& child = structToken->fTokens.front();
1513 string structName(child.fContentStart, child.length());
1514 if ("RunBuffer" != structName) {
1515 return false;
1516 }
1517 string tokenName(token.fContentStart, token.length());
1518 string allowed[] = { "glyphs", "pos", "utf8text", "clusters" };
1519 for (auto allow : allowed) {
1520 if (allow == tokenName) {
1521 return true;
1522 }
1523 }
1524 }
1525 return false;
1526}
1527
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001528bool IncludeParser::isOperator(const Definition& token) {
1529 return "operator" == token.fName.substr(0, 8);
1530}
1531
Cary Clarkcb6bef02018-11-29 12:05:25 -05001532bool IncludeParser::dumpMethod(const Definition& token, string className) {
1533 if (std::any_of(token.fTokens.begin(), token.fTokens.end(),
1534 [=](const Definition& def) { return MarkType::kComment == def.fMarkType
1535 && this->isUndocumentable(def.fFileName, def.fContentStart, def.fContentEnd,
1536 def.fLineCount); } )) {
1537 return false;
1538 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001539 this->writeTag("Method");
1540 this->writeSpace();
1541
1542 string name = string(token.fStart ? token.fStart : token.fContentStart,
1543 token.length());
Cary Clark224c7002018-06-27 11:00:21 -04001544 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001545 string inType;
1546 if (this->isConstructor(token, className)) {
1547 inType = "Constructor";
1548 } else if (this->isOperator(token)) {
1549 inType = "Operator";
1550 } else {
1551 inType = "incomplete";
1552 }
1553 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001554 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001555 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001556 this->dumpComment(token);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001557 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001558}
1559
1560void IncludeParser::dumpMember(const Definition& token) {
1561 this->writeTag("Member");
1562 this->writeSpace();
1563 this->writeDefinition(token, token.fName, 2);
1564 lf(1);
1565 for (auto child : token.fChildren) {
1566 this->writeDefinition(*child);
1567 }
1568 this->writeEndTag();
1569 lf(2);
1570}
1571
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001572bool IncludeParser::dumpTokens() {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001573 string globalFileName;
1574 long int globalTell = 0;
1575 if (!this->dumpGlobals(&globalFileName, &globalTell)) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001576 return false;
1577 }
Cary Clark9174bda2017-09-19 17:39:32 -04001578 for (const auto& member : fIClassMap) {
1579 if (string::npos != member.first.find("::")) {
1580 continue;
1581 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001582 if (!this->dumpTokens(member.first, globalFileName, &globalTell)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001583 return false;
1584 }
1585 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001586 if (globalTell) {
1587 fclose(fOut);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001588 SkDebugf("wrote %s\n", globalFileName.c_str());
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001589 }
Cary Clark9174bda2017-09-19 17:39:32 -04001590 return true;
1591}
1592
Ben Wagner63fd7602017-10-09 15:45:33 -04001593 // dump equivalent markup
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001594bool IncludeParser::dumpTokens(string skClassName, string globalFileName, long int* globalTell) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001595 string fileName = skClassName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001596 if (globalFileName != fileName) {
1597 fOut = fopen(fileName.c_str(), "wb");
1598 if (!fOut) {
1599 SkDebugf("could not open output file %s\n", fileName.c_str());
1600 return false;
1601 }
1602 } else {
1603 fseek(fOut, *globalTell, SEEK_SET);
1604 this->lf(2);
1605 this->writeBlockSeparator();
1606 *globalTell = 0;
Cary Clark8032b982017-07-28 11:04:54 -04001607 }
1608 string prefixName = skClassName.substr(0, 2);
1609 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1610 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001611 if (globalFileName != fileName) {
1612 this->writeTagNoLF("Topic", topicName);
1613 this->writeEndTag("Alias", topicName + "_Reference");
1614 this->lf(2);
1615 }
Cary Clark8032b982017-07-28 11:04:54 -04001616 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001617 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1618 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001619 this->writeTag(containerType, skClassName);
1620 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001621 auto& tokens = classMap.fTokens;
1622 for (auto& token : tokens) {
1623 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1624 continue;
1625 }
Cary Clark9174bda2017-09-19 17:39:32 -04001626 this->writeDefinition(token);
1627 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001628 }
1629 this->lf(2);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001630 this->writeTag("Code");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001631 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001632 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001633 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001634 for (auto& oneClass : fIClassMap) {
1635 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1636 continue;
1637 }
1638 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001639 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001640 KeyWord keyword = oneClass.second.fKeyWord;
1641 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1642 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001643 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001644 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001645 this->lf(2);
1646 this->writeTag("Code");
1647 this->writeEndTag("ToDo", "fill this in manually");
1648 this->writeEndTag();
1649 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001650 for (auto& token : oneClass.second.fTokens) {
1651 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1652 continue;
1653 }
Cary Clark9174bda2017-09-19 17:39:32 -04001654 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001655 }
1656 this->lf(2);
1657 this->dumpClassTokens(oneClass.second);
1658 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001659 this->writeEndTag(containerType, innerName);
1660 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001661 }
1662 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001663 this->writeEndTag(containerType, skClassName);
1664 this->lf(2);
1665 this->writeEndTag("Topic", topicName);
1666 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001667 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001668 SkDebugf("wrote %s\n", fileName.c_str());
1669 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001670}
1671
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001672void IncludeParser::dumpTypedef(const Definition& token, string className) {
1673 this->writeTag("Typedef");
1674 this->writeSpace();
1675 this->writeString(token.fName);
1676 this->writeTagTable("Line", "incomplete");
1677 this->lf(2);
1678 this->dumpComment(token);
1679}
1680
Cary Clark61313f32018-10-08 14:57:48 -04001681string IncludeParser::elidedCodeBlock(const Definition& iDef) {
1682 SkASSERT(KeyWord::kStruct == iDef.fKeyWord || KeyWord::kClass == iDef.fKeyWord
1683 || KeyWord::kTemplate == iDef.fKeyWord);
1684 TextParser i(&iDef);
1685 fElided = Elided::kYes;
1686 MarkType markType = MarkType::kClass;
1687 if (KeyWord::kTemplate == iDef.fKeyWord) { // may be function
1688 for (auto child : iDef.fChildren) {
1689 if (MarkType::kMethod == child->fMarkType) {
1690 markType = MarkType::kFunction;
1691 break;
1692 }
1693 }
1694 }
1695 return this->writeCodeBlock(i, markType, 0);
1696}
1697
Cary Clarka90ea222018-10-16 10:30:28 -04001698 string IncludeParser::filteredBlock(string inContents, string filterContents) {
1699 string result;
1700 const unordered_map<string, Definition*>* mapPtr = nullptr;
Cary Clarka90ea222018-10-16 10:30:28 -04001701 if ("Constant" == inContents) {
1702 mapPtr = &fIConstMap;
Cary Clarka90ea222018-10-16 10:30:28 -04001703 } else {
1704 SkASSERT(0); // only Constant supported for now
1705 }
Cary Clark4b076532018-10-29 14:17:22 -04001706 vector<Definition*> consts;
Cary Clarka90ea222018-10-16 10:30:28 -04001707 for (auto entry : *mapPtr) {
1708 if (string::npos == entry.first.find(filterContents)) {
1709 continue;
1710 }
Cary Clark4b076532018-10-29 14:17:22 -04001711 consts.push_back(entry.second);
1712 }
1713 std::sort(consts.begin(), consts.end(), [](Definition* def1, Definition* def2) {
1714 return def1->fLineCount < def2->fLineCount;
1715 } );
1716 for (auto oneConst : consts) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001717 result += this->writeCodeBlock(*oneConst);
Cary Clarka90ea222018-10-16 10:30:28 -04001718 }
1719 return result;
1720}
1721
Cary Clarkabaffd82018-11-15 08:25:12 -05001722bool IncludeParser::findCommentAfter(const Definition& includeDef, Definition* markupDef) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001723 this->checkName(markupDef);
Cary Clarkabaffd82018-11-15 08:25:12 -05001724 const Definition* parent = includeDef.fParent;
1725 int index = includeDef.fParentIndex;
1726 auto wordIter = parent->fTokens.begin();
1727 std::advance(wordIter, index);
1728 SkASSERT(&*wordIter == &includeDef);
1729 size_t commentLine = 0;
1730 do {
1731 wordIter = std::next(wordIter);
1732 if (parent->fTokens.end() == wordIter) {
1733 break;
1734 }
1735 commentLine = wordIter->fLineCount;
1736 } while (Punctuation::kSemicolon != wordIter->fPunctuation);
1737 wordIter = std::next(wordIter);
1738 if (parent->fTokens.end() != wordIter && Bracket::kSlashSlash == wordIter->fBracket
1739 && wordIter->fLineCount == commentLine) {
1740 return this->parseComment(wordIter->fFileName, wordIter->fContentStart,
1741 wordIter->fContentEnd, wordIter->fLineCount, markupDef, &markupDef->fUndocumented);
1742 }
1743 return true;
1744}
1745
Cary Clark8032b982017-07-28 11:04:54 -04001746bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001747 this->checkName(markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04001748 // add comment preceding class, if any
Cary Clarkabaffd82018-11-15 08:25:12 -05001749 Definition* parent = includeDef.fParent;
Cary Clark8032b982017-07-28 11:04:54 -04001750 int index = includeDef.fParentIndex;
1751 auto wordIter = parent->fTokens.begin();
1752 std::advance(wordIter, index);
1753 SkASSERT(&*wordIter == &includeDef);
1754 while (parent->fTokens.begin() != wordIter) {
1755 auto testIter = std::prev(wordIter);
1756 if (Definition::Type::kWord != testIter->fType
1757 && Definition::Type::kKeyWord != testIter->fType
1758 && (Definition::Type::kBracket != testIter->fType
1759 || Bracket::kAngle != testIter->fBracket)
1760 && (Definition::Type::kPunctuation != testIter->fType
1761 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1762 break;
1763 }
1764 wordIter = testIter;
1765 }
1766 auto commentIter = wordIter;
1767 while (parent->fTokens.begin() != commentIter) {
1768 auto testIter = std::prev(commentIter);
1769 bool isComment = Definition::Type::kBracket == testIter->fType
1770 && (Bracket::kSlashSlash == testIter->fBracket
1771 || Bracket::kSlashStar == testIter->fBracket);
1772 if (!isComment) {
1773 break;
1774 }
1775 commentIter = testIter;
1776 }
1777 while (commentIter != wordIter) {
1778 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
Cary Clarkabaffd82018-11-15 08:25:12 -05001779 commentIter->fContentEnd, commentIter->fLineCount, markupDef,
1780 &markupDef->fUndocumented)) {
Cary Clark8032b982017-07-28 11:04:54 -04001781 return false;
1782 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001783 commentIter->fUndocumented = markupDef->fUndocumented;
Cary Clark8032b982017-07-28 11:04:54 -04001784 commentIter = std::next(commentIter);
1785 }
1786 return true;
1787}
1788
Cary Clark0d225392018-06-07 09:59:07 -04001789Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1790 string typeName) {
1791 typedef Definition* DefinitionPtr;
1792 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1793 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1794 if (mapIter == fMaps.end()) {
1795 return nullptr;
1796 }
1797 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1798 return reportError<DefinitionPtr>("invalid mark type");
1799 }
1800 string name = this->uniqueName(*mapIter->fInclude, typeName);
1801 Definition& markupDef = *(*mapIter->fInclude)[name];
1802 if (markupDef.fStart) {
1803 return reportError<DefinitionPtr>("definition already defined");
1804 }
1805 markupDef.fFileName = fFileName;
1806 markupDef.fStart = includeDef.fStart;
1807 markupDef.fContentStart = includeDef.fStart;
Cary Clarkcb6bef02018-11-29 12:05:25 -05001808 this->checkName(&markupDef);
Cary Clark0d225392018-06-07 09:59:07 -04001809 markupDef.fName = name;
1810 markupDef.fContentEnd = includeDef.fContentEnd;
1811 markupDef.fTerminator = includeDef.fTerminator;
1812 markupDef.fParent = fParent;
1813 markupDef.fLineCount = includeDef.fLineCount;
1814 markupDef.fMarkType = markType;
1815 markupDef.fKeyWord = includeDef.fKeyWord;
1816 markupDef.fType = Definition::Type::kMark;
1817 return &markupDef;
1818}
1819
Cary Clarka64e4ee2018-10-18 08:30:34 -04001820Definition* IncludeParser::findMethod(const Definition& bmhDef) {
Cary Clark09d80c02018-10-31 12:14:03 -04001821 auto doubleColon = bmhDef.fName.rfind("::");
1822 if (string::npos == doubleColon) {
1823 const auto& iGlobalMethod = fIFunctionMap.find(bmhDef.fName);
1824 SkASSERT(fIFunctionMap.end() != iGlobalMethod);
1825 return iGlobalMethod->second;
1826 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001827 string className = bmhDef.fName.substr(0, doubleColon);
1828 const auto& iClass = fIClassMap.find(className);
Cary Clark77b3f3a2018-11-07 14:59:03 -05001829 if (fIClassMap.end() == iClass) {
1830 return nullptr;
1831 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001832 string methodName = bmhDef.fName.substr(doubleColon + 2);
1833 auto& iTokens = iClass->second.fTokens;
1834 const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
1835 [methodName](Definition& token) {
Cary Clark09d80c02018-10-31 12:14:03 -04001836 return MarkType::kMethod == token.fMarkType
Cary Clarkabaffd82018-11-15 08:25:12 -05001837 && !token.fUndocumented
Cary Clark09d80c02018-10-31 12:14:03 -04001838 && (methodName == token.fName
1839 || methodName == token.fName + "()"); } );
1840 if (iTokens.end() != iMethod) {
1841 return &*iMethod;
1842 }
1843 size_t subClassPos = className.rfind("::");
1844 if (string::npos != subClassPos) {
1845 className = className.substr(subClassPos + 2);
1846 }
1847 // match may be constructor; compare strings to see if this is so
Cary Clark05c1dcf2018-12-12 13:32:56 -05001848 if (string::npos == methodName.find('(')) {
1849 return nullptr;
1850 }
Cary Clark09d80c02018-10-31 12:14:03 -04001851 auto stripper = [](string s) -> string {
1852 bool last = false;
1853 string result;
1854 for (char c : s) {
1855 if (' ' >= c) {
1856 if (!last) {
1857 last = true;
1858 result += ' ';
1859 }
1860 continue;
1861 }
1862 result += c;
1863 last = false;
1864 }
1865 return result;
1866 };
1867 string strippedMethodName = stripper(methodName);
1868 if (strippedMethodName == methodName) {
1869 strippedMethodName = "";
1870 }
1871 const auto& cMethod = std::find_if(iTokens.begin(), iTokens.end(),
1872 [className, methodName, stripper, strippedMethodName](Definition& token) {
1873 if (MarkType::kMethod != token.fMarkType) {
1874 return false;
1875 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001876 if (token.fUndocumented) {
1877 return false;
1878 }
Cary Clark09d80c02018-10-31 12:14:03 -04001879 TextParser parser(&token);
1880 const char* match = parser.strnstr(className.c_str(), parser.fEnd);
1881 if (!match) {
1882 return false;
1883 }
1884 parser.skipTo(match);
1885 parser.skipExact(className.c_str());
1886 if ('(' != parser.peek()) {
1887 return false;
1888 }
1889 parser.skipToBalancedEndBracket('(', ')');
1890 string iMethodName(match, parser.fChar - match);
1891 if (methodName == iMethodName) {
1892 return true;
1893 }
Cary Clarkcb6bef02018-11-29 12:05:25 -05001894 if (strippedMethodName.empty()) {
Cary Clark09d80c02018-10-31 12:14:03 -04001895 return false;
1896 }
1897 string strippedIName = stripper(iMethodName);
1898 return strippedIName == strippedMethodName;
1899 } );
1900 SkAssertResult(iTokens.end() != cMethod);
1901 return &*cMethod;
Cary Clarka64e4ee2018-10-18 08:30:34 -04001902}
1903
Cary Clark224c7002018-06-27 11:00:21 -04001904Definition* IncludeParser::parentBracket(Definition* parent) const {
1905 while (parent && Definition::Type::kBracket != parent->fType) {
1906 parent = parent->fParent;
1907 }
1908 return parent;
1909}
1910
1911Bracket IncludeParser::grandParentBracket() const {
1912 Definition* parent = parentBracket(fParent);
1913 parent = parentBracket(parent ? parent->fParent : nullptr);
1914 return parent ? parent->fBracket : Bracket::kNone;
1915}
1916
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001917bool IncludeParser::inAlignAs() const {
1918 if (fParent->fTokens.size() < 2) {
1919 return false;
1920 }
1921 auto reverseIter = fParent->fTokens.end();
1922 bool checkForBracket = true;
1923 while (fParent->fTokens.begin() != reverseIter) {
1924 std::advance(reverseIter, -1);
1925 if (checkForBracket) {
1926 if (Definition::Type::kBracket != reverseIter->fType) {
1927 return false;
1928 }
1929 if (Bracket::kParen != reverseIter->fBracket) {
1930 return false;
1931 }
1932 checkForBracket = false;
1933 continue;
1934 }
1935 if (Definition::Type::kKeyWord != reverseIter->fType) {
1936 return false;
1937 }
1938 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1939 }
1940 return false;
1941}
1942
Cary Clark61313f32018-10-08 14:57:48 -04001943const Definition* IncludeParser::include(string match) const {
1944 for (auto& entry : fIncludeMap) {
1945 if (string::npos == entry.first.find(match)) {
1946 continue;
1947 }
1948 return &entry.second;
1949 }
1950 SkASSERT(0);
1951 return nullptr;
1952}
1953
Cary Clark137b8742018-05-30 09:21:49 -04001954// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001955bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1956 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001957 // parse class header
1958 auto iter = includeDef->fTokens.begin();
1959 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1960 // todo : documentation is ignoring this for now
1961 iter = std::next(iter);
1962 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001963 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1964 if (hasAlignAs) {
1965 iter = std::next(iter);
1966 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1967 return includeDef->reportError<bool>("expected alignas argument");
1968 }
1969 iter = std::next(iter);
1970 }
Cary Clark8032b982017-07-28 11:04:54 -04001971 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1972 includeDef->fName = nameStr;
Cary Clarkcb6bef02018-11-29 12:05:25 -05001973 this->checkName(includeDef);
Cary Clark9174bda2017-09-19 17:39:32 -04001974 iter = std::next(iter);
1975 if (iter == includeDef->fTokens.end()) {
1976 return true; // forward declaration only
1977 }
Cary Clark8032b982017-07-28 11:04:54 -04001978 do {
1979 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001980 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001981 }
1982 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1983 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001984 }
Cary Clark8032b982017-07-28 11:04:54 -04001985 } while (static_cast<void>(iter = std::next(iter)), true);
1986 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001987 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001988 }
1989 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1990 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001991 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001992 }
1993 markupDef->fStart = iter->fStart;
1994 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001995 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001996 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001997 if (markupDef->fUndocumented) {
1998 includeDef->fUndocumented = true;
1999 }
Cary Clark8032b982017-07-28 11:04:54 -04002000// if (1 != includeDef->fChildren.size()) {
2001// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
2002// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002003 auto includeDefIter = includeDef->fChildren.begin();
2004 if (hasAlignAs) {
2005 SkASSERT(includeDef->fChildren.end() != includeDefIter);
2006 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
2007 std::advance(includeDefIter, 1);
2008 }
2009 if (includeDef->fChildren.end() != includeDefIter
2010 && Bracket::kAngle == (*includeDefIter)->fBracket) {
2011 std::advance(includeDefIter, 1);
2012 }
2013 includeDef = *includeDefIter;
2014 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04002015 iter = includeDef->fTokens.begin();
2016 // skip until public
2017 int publicIndex = 0;
2018 if (IsStruct::kNo == isStruct) {
2019 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2020 size_t publicLen = strlen(publicName);
2021 while (iter != includeDef->fTokens.end()
2022 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
2023 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04002024 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002025 iter = std::next(iter);
2026 ++publicIndex;
2027 }
2028 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002029 int keyIndex = publicIndex;
2030 KeyWord currentKey = KeyWord::kPublic;
2031 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2032 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04002033 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
2034 size_t protectedLen = strlen(protectedName);
2035 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
2036 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04002037 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002038 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04002039 std::advance(childIter, 1);
2040 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002041 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04002042 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04002043 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04002044 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04002045 const char* testStart = iter->fStart;
2046 size_t testLen = (size_t) (iter->fContentEnd - testStart);
2047 iter = std::next(iter);
2048 ++keyIndex;
2049 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2050 currentKey = KeyWord::kPublic;
2051 break;
2052 }
2053 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2054 currentKey = KeyWord::kProtected;
2055 break;
2056 }
2057 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2058 currentKey = KeyWord::kPrivate;
2059 break;
2060 }
2061 }
2062 fLastObject = nullptr;
2063 if (KeyWord::kPublic == currentKey) {
2064 if (!this->parseObject(child, markupDef)) {
2065 return false;
2066 }
Cary Clark8032b982017-07-28 11:04:54 -04002067 }
Cary Clark73fa9722017-08-29 17:36:51 -04002068 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002069 childIter = std::next(childIter);
2070 }
Cary Clark137b8742018-05-30 09:21:49 -04002071 while (iter != includeDef->fTokens.end()) {
2072 iter->fPrivate = KeyWord::kPublic != currentKey;
2073 iter = std::next(iter);
2074 }
Cary Clark8032b982017-07-28 11:04:54 -04002075 SkASSERT(fParent->fParent);
2076 fParent = fParent->fParent;
2077 return true;
2078}
2079
Cary Clarkabaffd82018-11-15 08:25:12 -05002080bool IncludeParser::isUndocumentable(string filename, const char* start, const char* end,
2081 int lineCount) {
Cary Clark8032b982017-07-28 11:04:54 -04002082 TextParser parser(filename, start, end, lineCount);
Cary Clarkcb6bef02018-11-29 12:05:25 -05002083 const vector<string> skipWords = { "deprecated", "experimental", "private" };
Cary Clarkabaffd82018-11-15 08:25:12 -05002084 const vector<string> butNot = { "to be deprecated", "may be deprecated" };
2085 const vector<string> alsoNot = { "todo" };
2086 string match = parser.anyWord(skipWords, 0);
2087 if ("" != match) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05002088 if (parser.anyWord(alsoNot, 0).empty()
2089 && ("deprecated" != match || parser.anyWord(butNot, 2).empty())) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002090 return true;
2091 }
2092 }
2093 return false;
2094}
2095
2096bool IncludeParser::parseComment(string filename, const char* start, const char* end,
2097 int lineCount, Definition* markupDef, bool* undocumentedPtr) {
2098 if (this->isUndocumentable(filename, start, end, lineCount)) {
2099 *undocumentedPtr = true;
2100 }
Cary Clark8032b982017-07-28 11:04:54 -04002101 // parse doxygen if present
Cary Clarkabaffd82018-11-15 08:25:12 -05002102 TextParser parser(filename, start, end, lineCount);
Cary Clark8032b982017-07-28 11:04:54 -04002103 if (parser.startsWith("**")) {
2104 parser.next();
2105 parser.next();
2106 parser.skipWhiteSpace();
2107 if ('\\' == parser.peek()) {
2108 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002109 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2110 if (parser.skipExact("file")) {
2111 if (Definition::Type::kFileType != fParent->fType) {
2112 return reportError<bool>("expected parent is file");
2113 }
2114 string filename = markupDef->fileName();
2115 if (!parser.skipWord(filename.c_str())) {
2116 return reportError<bool>("missing object type");
2117 }
2118 } else if (parser.skipExact("fn")) {
2119 SkASSERT(0); // incomplete
2120 } else {
2121 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2122 return reportError<bool>("missing object type");
2123 }
2124 if (!parser.skipWord(markupDef->fName.c_str()) &&
2125 KeyWord::kEnum != markupDef->fKeyWord) {
2126 return reportError<bool>("missing object name");
2127 }
Cary Clark8032b982017-07-28 11:04:54 -04002128 }
Cary Clark8032b982017-07-28 11:04:54 -04002129 }
2130 }
2131 // remove leading '*' if present
2132 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2133 while (!parser.eof() && parser.skipWhiteSpace()) {
2134 while ('*' == parser.peek()) {
2135 parser.next();
2136 if (parser.eof()) {
2137 break;
2138 }
2139 parser.skipWhiteSpace();
2140 }
2141 if (parser.eof()) {
2142 break;
2143 }
2144 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002145 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002146 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002147 parser.skipToEndBracket('\n');
2148 }
2149 return true;
2150}
2151
Cary Clarkabaffd82018-11-15 08:25:12 -05002152/*
2153 find comment either in front of or after the const def and then extract if the
2154 const is undocumented
2155 */
Cary Clarkd98f78c2018-04-26 08:32:37 -04002156bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002157 if (!markupDef) {
2158 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2159 child->fLineCount, fParent, '\0');
2160 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002161 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002162 globalMarkupChild->fName = globalUniqueName;
2163 if (!this->findComments(*child, globalMarkupChild)) {
2164 return false;
2165 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002166 if (!this->findCommentAfter(*child, globalMarkupChild)) {
2167 return false;
2168 }
2169 if (globalMarkupChild->fUndocumented) {
2170 child->fUndocumented = true;
2171 } else {
2172 fIConstMap[globalUniqueName] = globalMarkupChild;
2173 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002174 return true;
2175 }
2176 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2177 child->fLineCount, markupDef, '\0');
2178 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002179 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002180 markupChild->fTerminator = markupChild->fContentEnd;
2181 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002182 classDef.fConsts[child->fName] = markupChild;
Cary Clarkabaffd82018-11-15 08:25:12 -05002183 if (!this->findComments(*child, markupChild)) {
2184 return false;
2185 }
2186 if (!this->findCommentAfter(*child, markupChild)) {
2187 return false;
2188 }
2189 if (markupChild->fUndocumented) {
2190 child->fUndocumented = true;
2191 } else {
2192 fIConstMap[child->fName] = markupChild;
2193 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002194 return true;
2195}
2196
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002197bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2198 TextParser parser(child);
2199 if (!parser.skipExact("#define")) {
2200 return false;
2201 }
2202 if (!parser.skipSpace()) {
2203 return false;
2204 }
2205 const char* nameStart = parser.fChar;
2206 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2207 if (parser.eof()) {
2208 return true; // do nothing if #define doesn't define anything
2209 }
2210 string nameStr(nameStart, parser.fChar - nameStart);
2211 struct Param {
2212 const char* fStart;
2213 const char* fEnd;
2214 };
2215 vector<Param> params;
2216 if ('(' == parser.peek()) {
2217 parser.next();
2218 if (!parser.skipSpace()) {
2219 return false;
2220 }
2221 do {
2222 const char* paramStart = parser.fChar;
2223 if (!parser.skipExact("...")) {
2224 parser.skipToNonAlphaNum();
2225 }
2226 if (parser.eof()) {
2227 return false;
2228 }
2229 params.push_back({paramStart, parser.fChar});
2230 if (!parser.skipSpace()) {
2231 return false;
2232 }
2233 if (')' == parser.peek()) {
2234 parser.next();
2235 break;
2236 }
2237 if (',' != parser.next()) {
2238 return false;
2239 }
2240 if (!parser.skipSpace()) {
2241 return false;
2242 }
2243 } while (true);
2244 }
2245 if (!parser.skipSpace()) {
2246 return false;
2247 }
2248 if (!markupDef) {
2249 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2250 child->fLineCount, fParent, '\0');
2251 Definition* globalMarkupChild = &fGlobals.back();
2252 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2253 globalMarkupChild->fName = globalUniqueName;
2254 globalMarkupChild->fTerminator = child->fContentEnd;
2255 if (!this->findComments(*child, globalMarkupChild)) {
2256 return false;
2257 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002258 if (!globalMarkupChild->fUndocumented) {
2259 fIDefineMap[globalUniqueName] = globalMarkupChild;
2260 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002261 for (Param param : params) {
2262 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2263 child->fLineCount, globalMarkupChild, '\0');
2264 Definition* paramChild = &globalMarkupChild->fTokens.back();
2265 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
Cary Clarkcb6bef02018-11-29 12:05:25 -05002266 this->checkName(paramChild);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002267 paramChild->fTerminator = param.fEnd;
2268 }
2269 return true;
2270 }
2271 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2272 child->fLineCount, markupDef, '\0');
2273 Definition* markupChild = &markupDef->fTokens.back();
2274 markupChild->fName = nameStr;
2275 markupChild->fTerminator = markupChild->fContentEnd;
2276 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2277 if (!this->findComments(*child, markupChild)) {
2278 return false;
2279 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002280 if (markupChild->fUndocumented) {
2281 child->fUndocumented = true;
2282 } else {
2283 classDef.fDefines[nameStr] = markupChild;
2284 fIDefineMap[nameStr] = markupChild;
2285 }
Cary Clark8032b982017-07-28 11:04:54 -04002286 return true;
2287}
2288
2289bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002290 if (!child->fTokens.size()) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002291 return true; // if enum is a forward declaration, do nothing
2292 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002293 bool isEnumClass = false;
2294 Definition* parent = child;
2295 auto token = parent->fTokens.begin();
2296 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2297 isEnumClass = true;
2298 parent = &*token;
2299 token = parent->fTokens.begin();
Cary Clark8032b982017-07-28 11:04:54 -04002300 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002301 SkASSERT(Definition::Type::kWord == token->fType);
2302 string nameStr = string(token->fStart, token->fContentEnd - token->fStart);
Cary Clark2dc84ad2018-01-26 12:56:22 -05002303 Definition* markupChild;
2304 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002305 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2306 child->fLineCount, fParent, '\0');
2307 markupChild = &fGlobals.back();
2308 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2309 markupChild->fName = globalUniqueName;
2310 markupChild->fTerminator = child->fContentEnd;
Cary Clarkabaffd82018-11-15 08:25:12 -05002311 if (!markupChild->fUndocumented) {
2312 fIEnumMap[globalUniqueName] = markupChild;
2313 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002314 } else {
2315 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002316 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002317 markupChild = &markupDef->fTokens.back();
2318 }
Cary Clark8032b982017-07-28 11:04:54 -04002319 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2320 markupChild->fKeyWord = KeyWord::kEnum;
Cary Clarkabaffd82018-11-15 08:25:12 -05002321 if (isEnumClass) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04002322 markupChild->fMarkType = MarkType::kEnumClass;
2323 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002324 if (markupDef) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002325 markupChild->fName = markupDef->fName + "::" + nameStr;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002326 }
Cary Clark8032b982017-07-28 11:04:54 -04002327 if (!this->findComments(*child, markupChild)) {
2328 return false;
2329 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002330 if (markupChild->fUndocumented) {
2331 child->fUndocumented = true;
2332 }
2333 if (!this->parseEnumConst(token, parent->fTokens.end(), markupChild)) {
2334 return false;
2335 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002336 for (auto outsideMember : child->fChildren) {
2337 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002338 continue;
2339 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002340 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2341 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002342 continue;
2343 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002344 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2345 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002346 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002347 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002348 member->fName = outsideMember->fName;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002349 this->checkName(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002350 // FIXME: ? add comment as well ?
2351 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002352 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002353 if (markupDef) {
2354 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2355 SkASSERT(classDef.fStart);
2356 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002357 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002358 markupChild->fName = uniqueName;
2359 classDef.fEnums[uniqueName] = markupChild;
Cary Clarkabaffd82018-11-15 08:25:12 -05002360 if (!markupChild->fUndocumented) {
2361 fIEnumMap[fullName] = markupChild;
2362 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002363 }
Cary Clark8032b982017-07-28 11:04:54 -04002364 return true;
2365}
2366
Cary Clarkabaffd82018-11-15 08:25:12 -05002367bool IncludeParser::parseOneEnumConst(list<Definition>& constList,
2368 Definition* markupChild, bool skipWord) {
2369 auto memberIter = constList.begin();
2370 const auto memberIterEnd = constList.end();
2371 if (skipWord) {
2372 SkASSERT(Definition::Type::kWord == memberIter->fType);
2373 memberIter = std::next(memberIter);
2374 SkASSERT(memberIterEnd != memberIter);
2375 }
2376 // token array has parse atoms; child array has comments
2377 bool undocumented = false;
2378 while (memberIterEnd != memberIter) {
2379 while (Bracket::kSlashStar == memberIter->fBracket) {
2380 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2381 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2382 return false;
2383 }
2384 memberIter = std::next(memberIter);
2385 if (memberIterEnd == memberIter) {
2386 return false;
2387 }
2388 }
2389 if (Bracket::kPound == memberIter->fBracket) {
2390 KeyWord keyWord = memberIter->fKeyWord;
2391 bool sawIf = KeyWord::kIfdef == keyWord || KeyWord::kIf == keyWord
2392 || KeyWord::kElif == keyWord;
2393 if (sawIf || KeyWord::kElse == keyWord) {
2394 if (!parseOneEnumConst(memberIter->fTokens, markupChild, sawIf)) {
2395 return false;
2396 }
2397 } else {
2398 SkASSERT(KeyWord::kEndif == keyWord || KeyWord::kError == keyWord);
2399 }
2400 memberIter = std::next(memberIter);
2401 if (memberIterEnd == memberIter) {
2402 break;
2403 }
2404 continue;
2405 }
2406 while (Definition::Type::kWord != memberIter->fType) {
2407 memberIter = std::next(memberIter);
2408 if (memberIterEnd == memberIter) {
2409 return false;
2410 }
2411 }
2412 auto memberStart = memberIter;
2413 Definition* memberEnd = nullptr;
2414 const char* last;
2415 do {
2416 last = memberIter->fContentEnd;
2417 memberIter = std::next(memberIter);
2418 if (memberIterEnd == memberIter) {
2419 break;
2420 }
2421 memberEnd = &*memberIter;
2422 } while (string::npos == string(last, memberIter->fContentStart).find(','));
2423 if (!memberEnd) {
2424 return false;
2425 }
2426 if (memberIterEnd != memberIter && Bracket::kSlashSlash == memberIter->fBracket) {
2427 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2428 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2429 return false;
2430 }
2431 memberIter = std::next(memberIter);
2432 }
2433 markupChild->fTokens.emplace_back(MarkType::kMember, memberStart->fContentStart,
2434 memberEnd->fContentEnd, memberStart->fLineCount, markupChild, '\0');
2435 Definition* markupMember = &markupChild->fTokens.back();
2436 string name = string(memberStart->fContentStart, memberStart->length());
2437 memberStart->fName = name;
2438 markupMember->fName = name;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002439 this->checkName(markupMember);
2440 memberStart->fUndocumented = markupMember->fUndocumented;
Cary Clarkabaffd82018-11-15 08:25:12 -05002441 memberStart->fMarkType = MarkType::kMember;
2442 undocumented = false;
2443 }
2444 return true;
2445}
2446
2447bool IncludeParser::parseEnumConst(list<Definition>::iterator& tokenIter,
2448 const list<Definition>::iterator& tokenEnd, Definition* markupChild) {
2449 SkASSERT(Definition::Type::kWord == tokenIter->fType); // should be enum name
2450 tokenIter = std::next(tokenIter);
2451 SkASSERT(tokenEnd != tokenIter);
2452 if (Definition::Type::kKeyWord == tokenIter->fType) {
2453 SkASSERT((unsigned) tokenIter->fKeyWord < SK_ARRAY_COUNT(kKeyWords));
2454 SkASSERT(KeyProperty::kNumber == kKeyWords[(int) tokenIter->fKeyWord].fProperty);
2455 tokenIter = std::next(tokenIter);
2456 SkASSERT(tokenEnd != tokenIter);
2457 }
2458 SkASSERT(Punctuation::kLeftBrace == tokenIter->fPunctuation);
2459 tokenIter = std::next(tokenIter);
2460 SkASSERT(tokenEnd != tokenIter);
2461 SkASSERT(Bracket::kBrace == tokenIter->fBracket);
2462 return parseOneEnumConst(tokenIter->fTokens, markupChild, false);
2463}
2464
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002465bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002466 fParent = &fIncludeMap[name];
2467 fParent->fName = name;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002468 this->checkName(fParent);
Cary Clark8032b982017-07-28 11:04:54 -04002469 fParent->fFileName = fFileName;
2470 fParent->fType = Definition::Type::kFileType;
2471 fParent->fContentStart = fChar;
2472 fParent->fContentEnd = fEnd;
2473 // parse include file into tree
2474 while (fChar < fEnd) {
2475 if (!this->parseChar()) {
2476 return false;
2477 }
2478 }
2479 // parse tree and add named objects to maps
2480 fParent = &fIncludeMap[name];
2481 if (!this->parseObjects(fParent, nullptr)) {
2482 return false;
2483 }
2484 return true;
2485}
2486
2487bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2488 const char* typeStart = child->fChildren[0]->fContentStart;
2489 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002490 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002491 Definition* markupChild = &markupDef->fTokens.back();
2492 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002493 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002494 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2495 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2496 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2497 markupChild->fName = uniqueName;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002498 this->checkName(markupChild);
Cary Clark9174bda2017-09-19 17:39:32 -04002499 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clarkabaffd82018-11-15 08:25:12 -05002500 if (!markupChild->fUndocumented) {
2501 classDef.fMembers[uniqueName] = markupChild;
2502 }
Cary Clark8032b982017-07-28 11:04:54 -04002503 if (child->fParentIndex >= 2) {
2504 auto comment = child->fParent->fTokens.begin();
2505 std::advance(comment, child->fParentIndex - 2);
2506 if (Definition::Type::kBracket == comment->fType
2507 && (Bracket::kSlashStar == comment->fBracket
2508 || Bracket::kSlashSlash == comment->fBracket)) {
2509 TextParser parser(&*comment);
2510 do {
2511 parser.skipToAlpha();
2512 if (parser.eof()) {
2513 break;
2514 }
2515 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002516 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002517 if (Bracket::kSlashStar == comment->fBracket) {
2518 const char* commentEnd = parser.strnstr("*/", end);
2519 if (commentEnd) {
2520 end = commentEnd;
2521 }
2522 }
2523 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002524 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002525 Definition* commentChild = &markupDef->fTokens.back();
2526 markupChild->fChildren.emplace_back(commentChild);
2527 parser.skipTo(end);
2528 } while (!parser.eof());
2529 }
2530 }
2531 return true;
2532}
2533
2534bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2535 auto tokenIter = child->fParent->fTokens.begin();
2536 std::advance(tokenIter, child->fParentIndex);
2537 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002538 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002539 bool addConst = false;
2540 auto operatorCheck = tokenIter;
2541 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2542 operatorCheck = std::prev(tokenIter);
2543 }
2544 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002545 auto closeParen = std::next(tokenIter);
2546 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2547 '(' == closeParen->fContentStart[0]);
2548 nameEnd = closeParen->fContentEnd + 1;
2549 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002550 if (Definition::Type::kKeyWord == closeParen->fType &&
2551 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002552 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002553 }
Cary Clarka560c472017-11-27 10:44:06 -05002554 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002555 }
2556 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002557 if (addConst) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05002558 nameStr += " const";
Cary Clark154beea2017-10-26 07:58:48 -04002559 }
Cary Clark8032b982017-07-28 11:04:54 -04002560 while (tokenIter != child->fParent->fTokens.begin()) {
2561 auto testIter = std::prev(tokenIter);
2562 switch (testIter->fType) {
2563 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002564 if (testIter == child->fParent->fTokens.begin() &&
2565 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2566 KeyWord::kIfndef == child->fParent->fKeyWord ||
2567 KeyWord::kIf == child->fParent->fKeyWord)) {
2568 std::next(tokenIter);
2569 break;
2570 }
Cary Clark8032b982017-07-28 11:04:54 -04002571 goto keepGoing;
2572 case Definition::Type::kKeyWord: {
2573 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2574 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2575 goto keepGoing;
2576 }
2577 } break;
2578 case Definition::Type::kBracket:
2579 if (Bracket::kAngle == testIter->fBracket) {
2580 goto keepGoing;
2581 }
2582 break;
2583 case Definition::Type::kPunctuation:
2584 if (Punctuation::kSemicolon == testIter->fPunctuation
2585 || Punctuation::kLeftBrace == testIter->fPunctuation
2586 || Punctuation::kColon == testIter->fPunctuation) {
2587 break;
2588 }
2589 keepGoing:
2590 tokenIter = testIter;
2591 continue;
2592 default:
2593 break;
2594 }
2595 break;
2596 }
Cary Clark224c7002018-06-27 11:00:21 -04002597 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002598 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002599 tokenIter->fPrivate = string::npos != nameStr.find("::")
2600 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002601 this->checkName(&*tokenIter);
Cary Clark8032b982017-07-28 11:04:54 -04002602 auto testIter = child->fParent->fTokens.begin();
2603 SkASSERT(child->fParentIndex > 0);
2604 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002605 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2606 0 == tokenIter->fParentIndex) {
2607 tokenIter = std::next(tokenIter);
2608 }
Cary Clark8032b982017-07-28 11:04:54 -04002609 const char* start = tokenIter->fContentStart;
2610 const char* end = tokenIter->fContentEnd;
2611 const char kDebugCodeStr[] = "SkDEBUGCODE";
2612 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2613 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2614 std::advance(testIter, 1);
2615 start = testIter->fContentStart + 1;
2616 end = testIter->fContentEnd - 1;
2617 } else {
2618 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002619 do {
2620 std::advance(testIter, 1);
2621 if (testIter == child->fParent->fTokens.end()) {
2622 break;
2623 }
Cary Clark8032b982017-07-28 11:04:54 -04002624 switch (testIter->fType) {
2625 case Definition::Type::kPunctuation:
2626 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2627 || Punctuation::kLeftBrace == testIter->fPunctuation
2628 || Punctuation::kColon == testIter->fPunctuation);
2629 end = testIter->fStart;
2630 break;
2631 case Definition::Type::kKeyWord: {
2632 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2633 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2634 continue;
2635 }
2636 } break;
2637 default:
2638 continue;
2639 }
2640 break;
Cary Clark61313f32018-10-08 14:57:48 -04002641 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002642 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002643 while (end > start && ' ' >= end[-1]) {
2644 --end;
2645 }
Cary Clark9174bda2017-09-19 17:39:32 -04002646 if (!markupDef) {
2647 auto parentIter = child->fParent->fTokens.begin();
2648 SkASSERT(child->fParentIndex > 0);
2649 std::advance(parentIter, child->fParentIndex - 1);
2650 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002651 TextParser nameParser(methodName);
2652 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002653 return true; // expect this is inline class definition outside of class
2654 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002655 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2656 fParent, '\0');
2657 Definition* globalMarkupChild = &fGlobals.back();
2658 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2659 globalMarkupChild->fName = globalUniqueName;
2660 if (!this->findComments(*child, globalMarkupChild)) {
2661 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002662 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002663 if (globalMarkupChild->fUndocumented) {
2664 child->fUndocumented = true;
2665 } else {
2666 fIFunctionMap[globalUniqueName] = globalMarkupChild;
2667 }
Cary Clarka560c472017-11-27 10:44:06 -05002668 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002669 }
Cary Clark8032b982017-07-28 11:04:54 -04002670 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002671 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002672 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002673 {
2674 auto mapIter = fIClassMap.find(markupDef->fName);
2675 SkASSERT(fIClassMap.end() != mapIter);
2676 IClassDefinition& classDef = mapIter->second;
2677 SkASSERT(classDef.fStart);
2678 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2679 markupChild->fName = uniqueName;
2680 if (!this->findComments(*child, markupChild)) {
2681 return false;
2682 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002683 if (markupChild->fUndocumented) {
2684 tokenIter->fUndocumented = true;
2685 } else {
2686 classDef.fMethods[uniqueName] = markupChild;
2687 }
Cary Clark8032b982017-07-28 11:04:54 -04002688 }
Cary Clark8032b982017-07-28 11:04:54 -04002689 return true;
2690}
2691
Cary Clark8032b982017-07-28 11:04:54 -04002692bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002693 fPriorObject = nullptr;
2694 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002695 if (!this->parseObject(child, markupDef)) {
2696 return false;
2697 }
Cary Clark0d225392018-06-07 09:59:07 -04002698 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002699 }
2700 return true;
2701}
2702
2703bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2704 // set up for error reporting
2705 fLine = fChar = child->fStart;
2706 fEnd = child->fContentEnd;
2707 // todo: put original line number in child as well
2708 switch (child->fType) {
2709 case Definition::Type::kKeyWord:
2710 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002711 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002712 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002713 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002714 }
2715 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002716 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002717 case KeyWord::kConst:
2718 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002719 if (!this->parseConst(child, markupDef)) {
2720 return child->reportError<bool>("failed to parse const or constexpr");
2721 }
2722 break;
Cary Clark8032b982017-07-28 11:04:54 -04002723 case KeyWord::kEnum:
2724 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002725 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002726 }
2727 break;
2728 case KeyWord::kStruct:
2729 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002730 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002731 }
2732 break;
2733 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002734 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002735 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002736 }
2737 break;
2738 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002739 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002740 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002741 }
2742 break;
2743 case KeyWord::kUnion:
2744 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002745 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002746 }
2747 break;
Cary Clark61313f32018-10-08 14:57:48 -04002748 case KeyWord::kUsing:
2749 if (!this->parseUsing()) {
2750 return child->reportError<bool>("failed to parse using");
2751 }
2752 break;
Cary Clark8032b982017-07-28 11:04:54 -04002753 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002754 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002755 }
2756 break;
2757 case Definition::Type::kBracket:
2758 switch (child->fBracket) {
2759 case Bracket::kParen:
Cary Clark9174bda2017-09-19 17:39:32 -04002760 {
2761 auto tokenIter = child->fParent->fTokens.begin();
2762 std::advance(tokenIter, child->fParentIndex);
2763 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002764 TextParser previousToken(&*tokenIter);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002765 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002766 break;
2767 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002768 if (Bracket::kPound == child->fParent->fBracket &&
2769 KeyWord::kIf == child->fParent->fKeyWord) {
2770 // TODO: this will skip methods named defined() -- for the
2771 // moment there aren't any
2772 if (previousToken.startsWith("defined")) {
2773 break;
2774 }
2775 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002776 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2777 break;
2778 }
Cary Clark73fa9722017-08-29 17:36:51 -04002779 }
Cary Clark0d225392018-06-07 09:59:07 -04002780 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2781 break;
2782 }
Cary Clark8032b982017-07-28 11:04:54 -04002783 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002784 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002785 }
Cary Clark73fa9722017-08-29 17:36:51 -04002786 break;
Cary Clark8032b982017-07-28 11:04:54 -04002787 case Bracket::kSlashSlash:
2788 case Bracket::kSlashStar:
2789 // comments are picked up by parsing objects first
2790 break;
2791 case Bracket::kPound:
2792 // special-case the #xxx xxx_DEFINED entries
2793 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002794 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002795 case KeyWord::kIfndef:
2796 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002797 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002798 if (!this->parseObjects(child, markupDef)) {
2799 return false;
2800 }
2801 break;
2802 }
2803 goto preproError;
2804 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002805 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002806 break;
2807 }
2808 goto preproError;
2809 case KeyWord::kEndif:
2810 if (child->boilerplateEndIf()) {
2811 break;
2812 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002813 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002814 case KeyWord::kInclude:
2815 // ignored for now
2816 break;
2817 case KeyWord::kElse:
Cary Clarkabaffd82018-11-15 08:25:12 -05002818 if (!this->parseObjects(child, markupDef)) {
2819 return false;
2820 }
2821 break;
Cary Clark8032b982017-07-28 11:04:54 -04002822 case KeyWord::kElif:
2823 // todo: handle these
2824 break;
2825 default:
2826 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002827 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002828 }
2829 break;
2830 case Bracket::kAngle:
2831 // pick up templated function pieces when method is found
2832 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002833 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002834 if (!this->parseObjects(child, markupDef)) {
2835 return false;
2836 }
Cary Clark73fa9722017-08-29 17:36:51 -04002837 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002838 case Bracket::kSquare: {
2839 // check to see if parent is operator, the only case we handle so far
2840 auto prev = child->fParent->fTokens.begin();
2841 std::advance(prev, child->fParentIndex - 1);
2842 if (KeyWord::kOperator != prev->fKeyWord) {
2843 return child->reportError<bool>("expected operator overload");
2844 }
2845 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002846 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002847 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002848 }
2849 break;
2850 case Definition::Type::kWord:
2851 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002852 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002853 }
2854 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002855 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002856 }
2857 break;
2858 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002859 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002860 break;
2861 }
2862 return true;
2863}
2864
Cary Clarkbbfda252018-03-09 15:32:01 -05002865bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2866 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002867}
2868
Cary Clark2f466242017-12-11 16:03:17 -05002869bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2870 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002871 typedefParser.skipExact("typedef");
2872 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002873 string nameStr = typedefParser.typedefName();
2874 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002875 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2876 child->fLineCount, fParent, '\0');
2877 Definition* globalMarkupChild = &fGlobals.back();
2878 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2879 globalMarkupChild->fName = globalUniqueName;
2880 if (!this->findComments(*child, globalMarkupChild)) {
2881 return false;
2882 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002883 if (globalMarkupChild->fUndocumented) {
2884 child->fUndocumented = true;
2885 } else {
2886 fITypedefMap[globalUniqueName] = globalMarkupChild;
2887 }
Cary Clark0d225392018-06-07 09:59:07 -04002888 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002889 return true;
2890 }
2891 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002892 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002893 Definition* markupChild = &markupDef->fTokens.back();
2894 markupChild->fName = nameStr;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002895 this->checkName(markupChild);
Cary Clark2f466242017-12-11 16:03:17 -05002896 markupChild->fTerminator = markupChild->fContentEnd;
2897 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2898 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002899 child->fName = markupDef->fName + "::" + nameStr;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002900 this->checkName(child);
Cary Clarka90ea222018-10-16 10:30:28 -04002901 fITypedefMap[child->fName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002902 return true;
2903}
2904
2905bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04002906 // incomplete
2907 return true;
2908}
Cary Clark8032b982017-07-28 11:04:54 -04002909
Cary Clark61313f32018-10-08 14:57:48 -04002910bool IncludeParser::parseUsing() {
2911 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04002912 return true;
2913}
2914
2915bool IncludeParser::parseChar() {
2916 char test = *fChar;
2917 if ('\\' == fPrev) {
2918 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002919// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002920 fLine = fChar + 1;
2921 }
2922 goto done;
2923 }
2924 switch (test) {
2925 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002926// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002927 fLine = fChar + 1;
2928 if (fInChar) {
2929 return reportError<bool>("malformed char");
2930 }
2931 if (fInString) {
2932 return reportError<bool>("malformed string");
2933 }
2934 if (!this->checkForWord()) {
2935 return false;
2936 }
2937 if (Bracket::kPound == this->topBracket()) {
2938 KeyWord keyWord = fParent->fKeyWord;
2939 if (KeyWord::kNone == keyWord) {
2940 return this->reportError<bool>("unhandled preprocessor directive");
2941 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002942 if (fInDefine) {
2943 SkASSERT(KeyWord::kDefine == keyWord);
2944 fInDefine = false;
2945 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002946 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002947 this->popBracket();
2948 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002949 if (fInBrace) {
2950 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2951 fInBrace = nullptr;
2952 }
Cary Clark8032b982017-07-28 11:04:54 -04002953 } else if (Bracket::kSlashSlash == this->topBracket()) {
2954 this->popBracket();
2955 }
2956 break;
2957 case '*':
2958 if (!fInCharCommentString && '/' == fPrev) {
2959 this->pushBracket(Bracket::kSlashStar);
2960 }
2961 if (!this->checkForWord()) {
2962 return false;
2963 }
2964 if (!fInCharCommentString) {
2965 this->addPunctuation(Punctuation::kAsterisk);
2966 }
2967 break;
2968 case '/':
2969 if ('*' == fPrev) {
2970 if (!fInCharCommentString) {
2971 return reportError<bool>("malformed closing comment");
2972 }
2973 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002974 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002975 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002976 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002977 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002978 }
2979 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002980 }
Cary Clark8032b982017-07-28 11:04:54 -04002981 if (!fInCharCommentString && '/' == fPrev) {
2982 this->pushBracket(Bracket::kSlashSlash);
2983 break;
2984 }
2985 if (!this->checkForWord()) {
2986 return false;
2987 }
2988 break;
2989 case '\'':
2990 if (Bracket::kChar == this->topBracket()) {
2991 this->popBracket();
2992 } else if (!fInComment && !fInString) {
2993 if (fIncludeWord) {
2994 return this->reportError<bool>("word then single-quote");
2995 }
2996 this->pushBracket(Bracket::kChar);
2997 }
2998 break;
2999 case '\"':
3000 if (Bracket::kString == this->topBracket()) {
3001 this->popBracket();
3002 } else if (!fInComment && !fInChar) {
3003 if (fIncludeWord) {
3004 return this->reportError<bool>("word then double-quote");
3005 }
3006 this->pushBracket(Bracket::kString);
3007 }
3008 break;
Cary Clark8032b982017-07-28 11:04:54 -04003009 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003010 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04003011 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
3012 this->pushBracket(Bracket::kDebugCode);
3013 break;
3014 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003015 case ':':
3016 case '[':
3017 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04003018 if (fInCharCommentString) {
3019 break;
3020 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003021 if (fInDefine && fInBrace) {
3022 break;
3023 }
Cary Clark8032b982017-07-28 11:04:54 -04003024 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
3025 break;
3026 }
Cary Clark0d225392018-06-07 09:59:07 -04003027 if (fConstExpr) {
3028 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3029 fConstExpr = nullptr;
3030 }
Cary Clark8032b982017-07-28 11:04:54 -04003031 if (!fInBrace) {
3032 if (!this->checkForWord()) {
3033 return false;
3034 }
3035 if (':' == test && !fInFunction) {
3036 break;
3037 }
3038 if ('{' == test) {
3039 this->addPunctuation(Punctuation::kLeftBrace);
3040 } else if (':' == test) {
3041 this->addPunctuation(Punctuation::kColon);
3042 }
3043 }
3044 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
3045 && Bracket::kColon == fInBrace->fBracket) {
3046 Definition* braceParent = fParent->fParent;
3047 braceParent->fChildren.pop_back();
3048 braceParent->fTokens.pop_back();
3049 fParent = braceParent;
3050 fInBrace = nullptr;
3051 }
3052 this->pushBracket(
3053 '(' == test ? Bracket::kParen :
3054 '[' == test ? Bracket::kSquare :
3055 '{' == test ? Bracket::kBrace :
3056 Bracket::kColon);
3057 if (!fInBrace
3058 && ('{' == test || (':' == test && ' ' >= fChar[1]))
3059 && fInFunction) {
3060 fInBrace = fParent;
3061 }
3062 } break;
3063 case '<':
3064 if (fInCharCommentString || fInBrace) {
3065 break;
3066 }
3067 if (!this->checkForWord()) {
3068 return false;
3069 }
3070 if (fInEnum) {
3071 break;
3072 }
3073 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04003074 // this angle bracket may be an operator or may be a bracket
3075 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04003076 break;
3077 case ')':
3078 case ']':
3079 case '}': {
3080 if (fInCharCommentString) {
3081 break;
3082 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003083 if (fInDefine && fInBrace) {
3084 break;
3085 }
Cary Clark8032b982017-07-28 11:04:54 -04003086 if (!fInBrace) {
3087 if (!this->checkForWord()) {
3088 return false;
3089 }
3090 }
3091 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04003092 Bracket match = ')' == test ? Bracket::kParen :
3093 ']' == test ? Bracket::kSquare : Bracket::kBrace;
3094 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04003095 this->popBracket();
3096 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003097 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04003098 } else {
3099 fInFunction = '}' != test;
3100 }
Cary Clark73fa9722017-08-29 17:36:51 -04003101 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
3102 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04003103 } else if (Bracket::kAngle == this->topBracket()
3104 && match == this->grandParentBracket()) {
3105 this->popBracket();
3106 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04003107 } else {
3108 return reportError<bool>("malformed close bracket");
3109 }
3110 if (popBraceParent) {
3111 Definition* braceParent = fInBrace->fParent;
3112 braceParent->fChildren.pop_back();
3113 braceParent->fTokens.pop_back();
3114 fInBrace = nullptr;
3115 }
3116 } break;
3117 case '>':
3118 if (fInCharCommentString || fInBrace) {
3119 break;
3120 }
3121 if (!this->checkForWord()) {
3122 return false;
3123 }
3124 if (fInEnum) {
3125 break;
3126 }
Cary Clarka560c472017-11-27 10:44:06 -05003127 if (Bracket::kPound == this->topBracket()) {
3128 break;
3129 }
Cary Clark8032b982017-07-28 11:04:54 -04003130 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04003131 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04003132 this->popBracket();
3133 } else {
3134 return reportError<bool>("malformed close angle bracket");
3135 }
3136 break;
3137 case '#': {
3138 if (fInCharCommentString || fInBrace) {
3139 break;
3140 }
3141 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3142 this->pushBracket(Bracket::kPound);
3143 break;
3144 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003145 case ' ':
3146 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3147 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3148 fInBrace = fParent;
3149 // delimiting brackets are space ... unescaped-linefeed
3150 }
Cary Clark8032b982017-07-28 11:04:54 -04003151 case '&':
3152 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003153 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003154 case '-':
3155 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003156 if (fInCharCommentString || fInBrace) {
3157 break;
3158 }
3159 if (!this->checkForWord()) {
3160 return false;
3161 }
3162 break;
Cary Clark0d225392018-06-07 09:59:07 -04003163 case '=':
3164 if (fInCharCommentString || fInBrace) {
3165 break;
3166 }
3167 if (!this->checkForWord()) {
3168 return false;
3169 }
Cary Clarkd7895502018-07-18 15:10:08 -04003170 if (!fParent->fTokens.size()) {
3171 break;
3172 }
Cary Clark0d225392018-06-07 09:59:07 -04003173 {
3174 const Definition& lastToken = fParent->fTokens.back();
3175 if (lastToken.fType != Definition::Type::kWord) {
3176 break;
3177 }
3178 string name(lastToken.fContentStart, lastToken.length());
3179 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3180 break;
3181 }
3182 // find token on start of line
3183 auto lineIter = fParent->fTokens.end();
3184 do {
Cary Clark80247e52018-07-11 16:18:41 -04003185 if (fParent->fTokens.begin() == lineIter) {
3186 break;
3187 }
Cary Clark0d225392018-06-07 09:59:07 -04003188 --lineIter;
3189 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003190 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003191 ++lineIter;
3192 }
3193 Definition* lineStart = &*lineIter;
3194 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3195 bool sawConst = false;
3196 bool sawStatic = false;
3197 bool sawTemplate = false;
3198 bool sawType = false;
3199 while (&lastToken != &*lineIter) {
3200 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3201 if (sawConst || sawStatic || sawTemplate) {
3202 sawConst = false;
3203 break;
3204 }
3205 if (&lastToken == &*++lineIter) {
3206 break;
3207 }
3208 if (KeyWord::kTypename != lineIter->fKeyWord) {
3209 break;
3210 }
3211 if (&lastToken == &*++lineIter) {
3212 break;
3213 }
3214 if (Definition::Type::kWord != lineIter->fType) {
3215 break;
3216 }
3217 sawTemplate = true;
3218 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3219 if (sawConst || sawStatic) {
3220 sawConst = false;
3221 break;
3222 }
3223 sawStatic = true;
3224 } else if (KeyWord::kConst == lineIter->fKeyWord
3225 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3226 if (sawConst) {
3227 sawConst = false;
3228 break;
3229 }
3230 sawConst = true;
3231 } else {
3232 if (sawType) {
3233 sawType = false;
3234 break;
3235 }
3236 if (Definition::Type::kKeyWord == lineIter->fType
3237 && KeyProperty::kNumber
3238 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3239 sawType = true;
3240 } else if (Definition::Type::kWord == lineIter->fType) {
3241 string typeName(lineIter->fContentStart, lineIter->length());
3242 if ("Sk" != name.substr(0, 2)) {
3243 sawType = true;
3244 }
3245 }
3246 }
3247 ++lineIter;
3248 }
3249 if (sawType && sawConst) {
3250 // if found, name first
3251 lineStart->fName = name;
3252 lineStart->fMarkType = MarkType::kConst;
Cary Clarkcb6bef02018-11-29 12:05:25 -05003253 this->checkName(lineStart);
Cary Clark0d225392018-06-07 09:59:07 -04003254 fParent->fChildren.emplace_back(lineStart);
3255 fConstExpr = lineStart;
3256 }
3257 }
3258 break;
Cary Clark8032b982017-07-28 11:04:54 -04003259 case ';':
3260 if (fInCharCommentString || fInBrace) {
3261 break;
3262 }
3263 if (!this->checkForWord()) {
3264 return false;
3265 }
Cary Clark0d225392018-06-07 09:59:07 -04003266 if (fConstExpr) {
3267 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3268 fConstExpr = nullptr;
3269 }
Cary Clark8032b982017-07-28 11:04:54 -04003270 if (Definition::Type::kKeyWord == fParent->fType
3271 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003272 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3273 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003274 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3275 this->popObject();
3276 }
Cary Clark8032b982017-07-28 11:04:54 -04003277 if (KeyWord::kEnum == fParent->fKeyWord) {
3278 fInEnum = false;
3279 }
Cary Clark61313f32018-10-08 14:57:48 -04003280 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003281 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003282 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3283 this->popObject();
3284 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003285 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003286 } else if (Definition::Type::kBracket == fParent->fType
3287 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3288 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3289 list<Definition>::iterator baseIter = fParent->fTokens.end();
3290 list<Definition>::iterator namedIter = fParent->fTokens.end();
3291 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003292 fParent->fTokens.begin() != tokenIter; ) {
3293 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003294 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003295 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003296 if (namedIter != fParent->fTokens.end()) {
3297 return reportError<bool>("found two named member tokens");
3298 }
3299 namedIter = tokenIter;
3300 }
3301 baseIter = tokenIter;
3302 } else {
3303 break;
3304 }
3305 }
3306 // FIXME: if a member definition spans multiple lines, this won't work
3307 if (namedIter != fParent->fTokens.end()) {
3308 if (baseIter == namedIter) {
3309 return this->reportError<bool>("expected type before named token");
3310 }
3311 Definition* member = &*namedIter;
3312 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003313 if (!member->fTerminator) {
3314 member->fTerminator = member->fContentEnd;
3315 }
Cary Clark8032b982017-07-28 11:04:54 -04003316 fParent->fChildren.push_back(member);
3317 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3318 member->fChildren.push_back(&*nameType);
3319 }
Cary Clark8032b982017-07-28 11:04:54 -04003320 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003321 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003322 } else if (fParent->fChildren.size() > 0) {
3323 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003324 Definition* priorEnum = fPriorEnum;
3325 fPriorEnum = nullptr;
3326 if (!priorEnum) {
3327 while (fParent->fChildren.begin() != lastIter) {
3328 std::advance(lastIter, -1);
3329 priorEnum = *lastIter;
3330 if (Definition::Type::kBracket != priorEnum->fType ||
3331 (Bracket::kSlashSlash != priorEnum->fBracket
3332 && Bracket::kSlashStar != priorEnum->fBracket)) {
3333 break;
3334 }
Cary Clark8032b982017-07-28 11:04:54 -04003335 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003336 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003337 }
3338 if (Definition::Type::kKeyWord == priorEnum->fType
3339 && KeyWord::kEnum == priorEnum->fKeyWord) {
3340 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003341 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003342 while (tokenWalker != fParent->fTokens.end()) {
3343 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003344 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003345 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3346 break;
3347 }
3348 }
3349 while (tokenWalker != fParent->fTokens.end()) {
3350 std::advance(tokenWalker, 1);
3351 const Definition* test = &*tokenWalker;
3352 if (Definition::Type::kBracket != test->fType ||
3353 (Bracket::kSlashSlash != test->fBracket
3354 && Bracket::kSlashStar != test->fBracket)) {
3355 break;
3356 }
3357 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003358 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003359 Definition* start = &*tokenWalker;
3360 bool foundExpected = true;
3361 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3362 const Definition* test = &*tokenWalker;
3363 if (expected != test->fKeyWord) {
3364 foundExpected = false;
3365 break;
3366 }
3367 if (tokenWalker == fParent->fTokens.end()) {
3368 break;
3369 }
3370 std::advance(tokenWalker, 1);
3371 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003372 if (!foundExpected) {
3373 foundExpected = true;
3374 tokenWalker = saveTokenWalker;
3375 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3376 const Definition* test = &*tokenWalker;
3377 if (expected != test->fKeyWord) {
3378 foundExpected = false;
3379 break;
3380 }
3381 if (tokenWalker == fParent->fTokens.end()) {
3382 break;
3383 }
3384 if (KeyWord::kNone != expected) {
3385 std::advance(tokenWalker, 1);
3386 }
3387 }
3388 if (foundExpected) {
3389 auto nameToken = priorEnum->fTokens.begin();
3390 string enumName = string(nameToken->fContentStart,
3391 nameToken->fContentEnd - nameToken->fContentStart);
3392 const Definition* test = &*tokenWalker;
3393 string constType = string(test->fContentStart,
3394 test->fContentEnd - test->fContentStart);
3395 if (enumName != constType) {
3396 foundExpected = false;
3397 } else {
3398 std::advance(tokenWalker, 1);
3399 }
3400 }
3401 }
Cary Clark8032b982017-07-28 11:04:54 -04003402 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3403 const char* nameStart = tokenWalker->fStart;
3404 std::advance(tokenWalker, 1);
3405 if (tokenWalker != fParent->fTokens.end()) {
3406 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003407 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003408 start->fName = string(nameStart, tp.fChar - nameStart);
Cary Clarkcb6bef02018-11-29 12:05:25 -05003409 this->checkName(start);
Cary Clark8032b982017-07-28 11:04:54 -04003410 start->fContentEnd = fChar;
3411 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003412 fPriorEnum = priorEnum;
3413 }
Cary Clark8032b982017-07-28 11:04:54 -04003414 }
3415 }
3416 }
3417 this->addPunctuation(Punctuation::kSemicolon);
3418 fInFunction = false;
3419 break;
3420 case '~':
3421 if (fInEnum) {
3422 break;
3423 }
3424 case '0': case '1': case '2': case '3': case '4':
3425 case '5': case '6': case '7': case '8': case '9':
3426 // TODO: don't want to parse numbers, but do need to track for enum defs
3427 // break;
3428 case 'A': case 'B': case 'C': case 'D': case 'E':
3429 case 'F': case 'G': case 'H': case 'I': case 'J':
3430 case 'K': case 'L': case 'M': case 'N': case 'O':
3431 case 'P': case 'Q': case 'R': case 'S': case 'T':
3432 case 'U': case 'V': case 'W': case 'X': case 'Y':
3433 case 'Z': case '_':
3434 case 'a': case 'b': case 'c': case 'd': case 'e':
3435 case 'f': case 'g': case 'h': case 'i': case 'j':
3436 case 'k': case 'l': case 'm': case 'n': case 'o':
3437 case 'p': case 'q': case 'r': case 's': case 't':
3438 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003439 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003440 if (fInCharCommentString || fInBrace) {
3441 break;
3442 }
3443 if (!fIncludeWord) {
3444 fIncludeWord = fChar;
3445 }
3446 break;
3447 }
3448done:
3449 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003450 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003451 return true;
3452}
3453
3454void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003455 IncludeParser::ValidateKeyWords();
3456}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003457
Cary Clark186d08f2018-04-03 08:43:27 -04003458bool IncludeParser::references(const SkString& file) const {
3459 // if includes weren't passed one at a time, assume all references are valid
3460 if (fIncludeMap.empty()) {
3461 return true;
3462 }
3463 SkASSERT(file.endsWith(".bmh") );
3464 string root(file.c_str(), file.size() - 4);
3465 string kReference("_Reference");
3466 if (string::npos != root.find(kReference)) {
3467 root = root.substr(0, root.length() - kReference.length());
3468 }
3469 if (fIClassMap.end() != fIClassMap.find(root)) {
3470 return true;
3471 }
3472 if (fIStructMap.end() != fIStructMap.find(root)) {
3473 return true;
3474 }
Cary Clark224c7002018-06-27 11:00:21 -04003475 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3476 return true;
3477 }
Cary Clarka90ea222018-10-16 10:30:28 -04003478 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3479 return true;
3480 }
Cary Clark224c7002018-06-27 11:00:21 -04003481 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3482 return true;
3483 }
Cary Clark186d08f2018-04-03 08:43:27 -04003484 return false;
3485}
3486
Cary Clark2dc84ad2018-01-26 12:56:22 -05003487void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3488 if (!sk_isdir(includes)) {
3489 IncludeParser::RemoveOneFile(docs, includes);
3490 } else {
3491 SkOSFile::Iter it(includes, ".h");
3492 for (SkString file; it.next(&file); ) {
3493 SkString p = SkOSPath::Join(includes, file.c_str());
3494 const char* hunk = p.c_str();
3495 if (!SkStrEndsWith(hunk, ".h")) {
3496 continue;
3497 }
3498 IncludeParser::RemoveOneFile(docs, hunk);
3499 }
3500 }
3501}
3502
3503void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3504 const char* lastForward = strrchr(includesFile, '/');
3505 const char* lastBackward = strrchr(includesFile, '\\');
3506 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3507 if (!last) {
3508 last = includesFile;
3509 } else {
3510 last += 1;
3511 }
3512 SkString baseName(last);
3513 SkASSERT(baseName.endsWith(".h"));
3514 baseName.remove(baseName.size() - 2, 2);
3515 baseName.append("_Reference.bmh");
Cary Clarkcb6bef02018-11-29 12:05:25 -05003516 SkString fullName = docs ? SkOSPath::Join(docs, baseName.c_str()) : baseName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05003517 remove(fullName.c_str());
3518}
Cary Clark224c7002018-06-27 11:00:21 -04003519
Cary Clarkfd32e722018-11-16 14:36:02 -05003520static const char kMethodMissingStr[] =
3521 "If the method requires documentation, add to "
3522 "%s at minimum:\n" // path to bmh file
3523 "\n"
3524 "#Method %s\n" // method declaration less implementation details
3525 "#In SomeSubtopicName\n"
3526 "#Line # add a one line description here ##\n"
3527 "#Populate\n"
3528 "#NoExample\n"
3529 "// or better yet, use #Example and put C++ code here\n"
3530 "##\n"
3531 "#SeeAlso optional related symbols\n"
3532 "#Method ##\n"
3533 "\n"
3534 "Add to %s, at minimum:\n" // path to include
3535 "\n"
3536 "/** (description) Starts with present tense action verb\n"
3537 " and end with a period.\n"
3538 "%s" // @param, @return if needed go here
3539 "*/\n"
3540 "%s ...\n" // method declaration
3541 "\n"
3542 "If the method does not require documentation,\n"
3543 "add \"private\" or \"experimental\", as in:\n"
3544 "\n"
3545 "/** Experimental, do not use. And so on...\n"
3546 "*/\n"
3547 "%s ...\n" // method declaration
3548 "\n"
3549 ;
3550
3551// bDef does not have #Populate
3552static const char kMethodDiffersNoPopStr[] =
3553 "In %s:\n" // path to bmh file
3554 "#Method %s\n" // method declaration less implementation details
3555 "does not match doxygen comment of:\n"
3556 "%s.\n" // method declaration
3557 "\n"
3558 ;
3559
3560static const char kMethodDiffersStr[] =
3561 "In %s:\n" // path to include
3562 "%s\n" // method declaration
3563 "does not match doxygen comment.\n"
3564 "\n"
3565 ;
3566
3567void IncludeParser::suggestFix(Suggest suggest, const Definition& iDef,
3568 const RootDefinition* root, const Definition* bDef) {
3569 string methodNameStr(iDef.fContentStart, iDef.length());
3570 const char* methodName = methodNameStr.c_str();
3571 TextParser lessImplParser(&iDef);
3572 if (lessImplParser.skipExact("static")) {
3573 lessImplParser.skipWhiteSpace();
3574 }
3575 // TODO : handle debug wrapper
3576 /* bool inDebugWrapper = */ Definition::SkipImplementationWords(lessImplParser);
3577 string lessImplStr(lessImplParser.fChar, lessImplParser.fEnd - lessImplParser.fChar);
3578 const char* methodNameLessImpl = lessImplStr.c_str();
3579 // return result, if any is substr from 0 to location of iDef.fName
3580 size_t namePos = methodNameStr.find(iDef.fName);
3581 SkASSERT(string::npos != namePos);
3582 size_t funcEnd = namePos;
3583 while (funcEnd > 0 && ' ' >= methodNameStr[funcEnd - 1]) {
3584 funcEnd -= 1;
3585 }
3586 string funcRet = methodNameStr.substr(0, funcEnd);
3587// parameters, if any, are delimited by () and separate by ,
3588 TextParser parser(&iDef);
3589 parser.fChar += namePos + iDef.fName.length();
3590 const char* start = parser.fChar;
3591 vector<string> paramStrs;
3592 if ('(' == start[0]) {
3593 parser.skipToBalancedEndBracket('(', ')');
3594 TextParser params(&iDef);
3595 params.fChar = start + 1;
3596 params.fEnd = parser.fChar;
3597 while (!params.eof()) {
3598 const char* paramEnd = params.anyOf("=,)");
3599 const char* paramStart = paramEnd;
3600 while (paramStart > params.fChar && ' ' >= paramStart[-1]) {
3601 paramStart -= 1;
3602 }
3603 while (paramStart > params.fChar && (isalnum(paramStart[-1])
3604 || '_' == paramStart[-1])) {
3605 paramStart -= 1;
3606 }
3607 string param(paramStart, paramEnd - paramStart);
3608 paramStrs.push_back(param);
3609 params.fChar = params.anyOf(",)") + 1;
3610 }
3611 }
3612 string bmhFile = root ? root->fFileName : bDef ? bDef->fFileName : "a *.bmh file";
3613 bool hasFuncReturn = "" != funcRet && "void" != funcRet;
3614 switch(suggest) {
3615 case Suggest::kMethodMissing: {
3616 // if include @param, @return are missing, request them as well
3617 string paramDox;
3618 bool firstParam = true;
3619 for (auto paramStr : paramStrs) {
3620 if (firstParam) {
3621 paramDox += "\n";
3622 firstParam = false;
3623 }
3624 paramDox += " @param " + paramStr + " descriptive phrase\n";
3625 }
3626 if (hasFuncReturn) {
3627 paramDox += "\n";
3628 paramDox += " @return descriptive phrase\n";
3629 }
3630 SkDebugf(kMethodMissingStr, bmhFile.c_str(), methodNameLessImpl, iDef.fFileName.c_str(),
3631 paramDox.c_str(), methodName, methodName);
3632 } break;
3633 case Suggest::kMethodDiffers: {
3634 bool hasPop = std::any_of(bDef->fChildren.begin(), bDef->fChildren.end(),
3635 [](Definition* def) { return MarkType::kPopulate == def->fMarkType; });
3636 if (!hasPop) {
3637 SkDebugf(kMethodDiffersNoPopStr, bmhFile.c_str(), methodNameLessImpl, methodName);
3638 }
3639 SkDebugf(kMethodDiffersStr, iDef.fFileName.c_str(), methodName);
3640 } break;
3641 default:
3642 SkASSERT(0);
3643 }
3644}
3645
Cary Clark224c7002018-06-27 11:00:21 -04003646Bracket IncludeParser::topBracket() const {
3647 Definition* parent = this->parentBracket(fParent);
3648 return parent ? parent->fBracket : Bracket::kNone;
3649}