• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
8 #include "SkOSFile.h"
9 #include "SkOSPath.h"
10 
11 #include "bmhParser.h"
12 #include "includeParser.h"
13 
14 const IncludeKey kKeyWords[] = {
15     { "",           KeyWord::kNone,         KeyProperty::kNone           },
16     { "SK_API",     KeyWord::kSK_API,       KeyProperty::kModifier       },
17     { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
18     { "alignas",    KeyWord::kAlignAs,      KeyProperty::kModifier       },
19     { "bool",       KeyWord::kBool,         KeyProperty::kNumber         },
20     { "char",       KeyWord::kChar,         KeyProperty::kNumber         },
21     { "class",      KeyWord::kClass,        KeyProperty::kObject         },
22     { "const",      KeyWord::kConst,        KeyProperty::kModifier       },
23     { "constexpr",  KeyWord::kConstExpr,    KeyProperty::kModifier       },
24     { "define",     KeyWord::kDefine,       KeyProperty::kPreprocessor   },
25     { "double",     KeyWord::kDouble,       KeyProperty::kNumber         },
26     { "elif",       KeyWord::kElif,         KeyProperty::kPreprocessor   },
27     { "else",       KeyWord::kElse,         KeyProperty::kPreprocessor   },
28     { "endif",      KeyWord::kEndif,        KeyProperty::kPreprocessor   },
29     { "enum",       KeyWord::kEnum,         KeyProperty::kObject         },
30     { "error",      KeyWord::kError,        KeyProperty::kPreprocessor   },
31     { "float",      KeyWord::kFloat,        KeyProperty::kNumber         },
32     { "friend",     KeyWord::kFriend,       KeyProperty::kModifier       },
33     { "if",         KeyWord::kIf,           KeyProperty::kPreprocessor   },
34     { "ifdef",      KeyWord::kIfdef,        KeyProperty::kPreprocessor   },
35     { "ifndef",     KeyWord::kIfndef,       KeyProperty::kPreprocessor   },
36     { "include",    KeyWord::kInclude,      KeyProperty::kPreprocessor   },
37     { "inline",     KeyWord::kInline,       KeyProperty::kModifier       },
38     { "int",        KeyWord::kInt,          KeyProperty::kNumber         },
39     { "operator",   KeyWord::kOperator,     KeyProperty::kFunction       },
40     { "private",    KeyWord::kPrivate,      KeyProperty::kClassSection   },
41     { "protected",  KeyWord::kProtected,    KeyProperty::kClassSection   },
42     { "public",     KeyWord::kPublic,       KeyProperty::kClassSection   },
43     { "signed",     KeyWord::kSigned,       KeyProperty::kNumber         },
44     { "size_t",     KeyWord::kSize_t,       KeyProperty::kNumber         },
45     { "static",     KeyWord::kStatic,       KeyProperty::kModifier       },
46     { "struct",     KeyWord::kStruct,       KeyProperty::kObject         },
47     { "template",   KeyWord::kTemplate,     KeyProperty::kObject         },
48     { "typedef",    KeyWord::kTypedef,      KeyProperty::kObject         },
49     { "typename",   KeyWord::kTypename,     KeyProperty::kObject         },
50     { "uint16_t",   KeyWord::kUint16_t,     KeyProperty::kNumber         },
51     { "uint32_t",   KeyWord::kUint32_t,     KeyProperty::kNumber         },
52     { "uint64_t",   KeyWord::kUint64_t,     KeyProperty::kNumber         },
53     { "uint8_t",    KeyWord::kUint8_t,      KeyProperty::kNumber         },
54     { "uintptr_t",  KeyWord::kUintPtr_t,    KeyProperty::kNumber         },
55     { "union",      KeyWord::kUnion,        KeyProperty::kObject         },
56     { "unsigned",   KeyWord::kUnsigned,     KeyProperty::kNumber         },
57     { "using",      KeyWord::kUsing,        KeyProperty::kObject         },
58     { "void",       KeyWord::kVoid,         KeyProperty::kNumber         },
59 };
60 
61 const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
62 
FindKey(const char * start,const char * end)63 KeyWord IncludeParser::FindKey(const char* start, const char* end) {
64     int ch = 0;
65     for (size_t index = 0; index < kKeyWordCount; ) {
66         if (start[ch] > kKeyWords[index].fName[ch]) {
67             ++index;
68             if (ch > 0 && (index == kKeyWordCount ||
69                     kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1])) {
70                 return KeyWord::kNone;
71             }
72             continue;
73         }
74         if (start[ch] < kKeyWords[index].fName[ch]) {
75             return KeyWord::kNone;
76         }
77         ++ch;
78         if (start + ch >= end) {
79             if (end - start < (int) strlen(kKeyWords[index].fName)) {
80                 return KeyWord::kNone;
81             }
82             return kKeyWords[index].fKeyWord;
83         }
84     }
85     return KeyWord::kNone;
86 }
87 
ValidateKeyWords()88 void IncludeParser::ValidateKeyWords() {
89     for (size_t index = 1; index < kKeyWordCount; ++index) {
90         SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
91                 == (int) kKeyWords[index].fKeyWord);
92         SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
93     }
94 }
95 
addKeyword(KeyWord keyWord)96 void IncludeParser::addKeyword(KeyWord keyWord) {
97     fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
98     fIncludeWord = nullptr;
99     if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
100         Definition* def = &fParent->fTokens.back();
101         this->addDefinition(def);
102         if (KeyWord::kEnum == fParent->fKeyWord) {
103             fInEnum = true;
104         }
105     }
106 }
107 
looks_like_method(const TextParser & tp)108 static bool looks_like_method(const TextParser& tp) {
109     TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
110     t.skipSpace();
111     if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
112             && !t.skipExact("enum")) {
113         return true;
114     }
115     t.skipSpace();
116     if (t.skipExact("SK_API")) {
117         t.skipSpace();
118     }
119     if (!isupper(t.peek())) {
120         return true;
121     }
122     return nullptr != t.strnchr('(', t.fEnd);
123 }
124 
looks_like_forward_declaration(const TextParser & tp)125 static bool looks_like_forward_declaration(const TextParser& tp) {
126     TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
127     t.skipSpace();
128     if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
129             && !t.skipExact("enum")) {
130         return false;
131     }
132     t.skipSpace();
133     if (t.skipExact("SK_API")) {
134         t.skipSpace();
135     }
136     if (!isupper(t.peek())) {
137         return false;
138     }
139     t.skipToNonAlphaNum();
140     if (t.eof() || ';' != t.next()) {
141         return false;
142     }
143     if (t.eof() || '\n' != t.next()) {
144         return false;
145     }
146     return t.eof();
147 }
148 
looks_like_constructor(const TextParser & tp)149 static bool looks_like_constructor(const TextParser& tp) {
150     TextParser t(tp.fFileName, tp.fLine, tp.lineEnd(), tp.fLineCount);
151     t.skipSpace();
152     if (!isupper(t.peek())) {
153         if (':' == t.next() && ' ' >= t.peek()) {
154             return true;
155         }
156         return false;
157     }
158     t.skipToNonAlphaNum();
159     if ('(' != t.peek()) {
160         return false;
161     }
162     if (!t.skipToEndBracket(')')) {
163         return false;
164     }
165     SkAssertResult(')' == t.next());
166     t.skipSpace();
167     return tp.fChar == t.fChar;
168 }
169 
looks_like_class_decl(const TextParser & tp)170 static bool looks_like_class_decl(const TextParser& tp) {
171     TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
172     t.skipSpace();
173     if (!t.skipExact("class")) {
174         return false;
175     }
176     t.skipSpace();
177     if (t.skipExact("SK_API")) {
178         t.skipSpace();
179     }
180     if (!isupper(t.peek())) {
181         return false;
182     }
183     t.skipToNonAlphaNum();
184     return !t.skipToEndBracket('(');
185 }
186 
looks_like_const(const TextParser & tp)187 static bool looks_like_const(const TextParser& tp) {
188     TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
189     if (!t.startsWith("static constexpr ")) {
190         return false;
191     }
192     if (t.skipToEndBracket(" k")) {
193         SkAssertResult(t.skipExact(" k"));
194     } else if (t.skipToEndBracket(" SK_")) {
195         SkAssertResult(t.skipExact(" SK_"));
196     } else {
197         return false;
198     }
199     if (!isupper(t.peek())) {
200         return false;
201     }
202     return t.skipToEndBracket(" = ");
203 }
204 
looks_like_member(const TextParser & tp)205 static bool looks_like_member(const TextParser& tp) {
206     TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
207     const char* end = t.anyOf("(;");
208     if (!end || '(' == *end) {
209         return false;
210     }
211     bool foundMember = false;
212     do {
213         const char* next = t.anyOf(" ;");
214         if (';' == *next) {
215             break;
216         }
217         t.skipTo(next);
218         t.skipSpace();
219         foundMember = 'f' == t.fChar[0] && isupper(t.fChar[1]);
220     } while (true);
221     return foundMember;
222 }
223 
skip_constructor_initializers(TextParser & t)224 static void skip_constructor_initializers(TextParser& t) {
225     SkAssertResult(':' == t.next());
226     do {
227         t.skipWhiteSpace();
228         t.skipToNonAlphaNum();
229         t.skipWhiteSpace();
230         if ('{' == t.peek()) {
231             t.skipToBalancedEndBracket('{', '}');
232         }
233         do {
234             const char* limiter = t.anyOf("(,{");
235             t.skipTo(limiter);
236             if ('(' != t.peek()) {
237                 break;
238             }
239             t.skipToBalancedEndBracket('(', ')');
240         } while (true);
241         if ('{' == t.peek()) {
242             return;
243         }
244         SkAssertResult(',' == t.next());
245     } while (true);
246 }
247 
248 static const char kInline[] = "inline ";
249 static const char kSK_API[] = "SK_API ";
250 static const char kSK_WARN_UNUSED_RESULT[] = "SK_WARN_UNUSED_RESULT ";
251 
advanceInclude(TextParser & i)252 bool IncludeParser::advanceInclude(TextParser& i) {
253     if (!i.skipWhiteSpace(&fCheck.fIndent, &fCheck.fWriteReturn)) {
254         return false;
255     }
256     if (fCheck.fPrivateBrace) {
257         if (i.startsWith("};")) {
258             if (fCheck.fPrivateBrace == fCheck.fBraceCount) {
259                 fCheck.fPrivateBrace = 0;
260                 fCheck.fDoubleReturn = true;
261             } else {
262                 i.skipExact("};");
263                 fCheck.fBraceCount -= 1;
264             }
265             return false;
266         }
267         if (i.startsWith("public:")) {
268             if (fCheck.fBraceCount <= fCheck.fPrivateBrace) {
269                 fCheck.fPrivateBrace = 0;
270                 if (fCheck.fPrivateProtected) {
271                     i.skipExact("public:");
272                 }
273             } else {
274                 i.skipExact("public:");
275             }
276         } else {
277             fCheck.fBraceCount += i.skipToLineBalance('{', '}');
278         }
279         return false;
280     } else if (i.startsWith("};")) {
281         fCheck.fDoubleReturn = 2;
282     }
283     if (i.skipExact(kInline)) {
284         fCheck.fSkipInline = true;
285         return false;
286     }
287     if (i.skipExact(kSK_API)) {
288         fCheck.fSkipAPI = true;
289         return false;
290     }
291     if (i.skipExact(kSK_WARN_UNUSED_RESULT)) {
292         fCheck.fSkipWarnUnused = true;
293         return false;
294     }
295     if (i.skipExact("SK_ATTR_DEPRECATED")) {
296         i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
297         return false;
298     }
299     if (i.skipExact("SkDEBUGCODE")) {
300         i.skipWhiteSpace();
301         if ('(' != i.peek()) {
302             i.reportError("expected open paren");
303         }
304         TextParserSave save(&i);
305         SkAssertResult(i.skipToBalancedEndBracket('(', ')'));
306         fCheck.fInDebugCode = i.fChar - 1;
307         save.restore();
308         SkAssertResult('(' == i.next());
309     }
310     if ('{' == i.peek()) {
311         if (looks_like_method(i)) {
312             fCheck.fState = CheckCode::State::kMethod;
313             bool inBalance = false;
314             TextParser paren(i.fFileName, i.fStart, i.fEnd, i.fLineCount);
315             paren.skipToEndBracket('(');
316             paren.skipToBalancedEndBracket('(', ')');
317             inBalance = i.fChar < paren.fChar;
318             if (!inBalance) {
319                 if (!i.skipToBalancedEndBracket('{', '}')) {
320                     i.reportError("unbalanced open brace");
321                 }
322                 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
323                 return false;
324             }
325         } else if (looks_like_class_decl(i)) {
326             fCheck.fState = CheckCode::State::kClassDeclaration;
327             fCheck.fPrivateBrace = fCheck.fBraceCount + 1;
328             fCheck.fPrivateProtected = false;
329         }
330     }
331     if (':' == i.peek() && looks_like_constructor(i)) {
332         fCheck.fState = CheckCode::State::kConstructor;
333         skip_constructor_initializers(i);
334         return false;
335     }
336     if ('#' == i.peek()) {
337         i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
338         return false;
339     }
340     if (i.startsWith("//")) {
341         i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
342         return false;
343     }
344     if (i.startsWith("/*")) {
345         i.skipToEndBracket("*/");
346         i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
347         return false;
348     }
349     if (looks_like_forward_declaration(i)) {
350         fCheck.fState = CheckCode::State::kForwardDeclaration;
351         i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
352         return false;
353     }
354     if (i.skipExact("private:") || i.skipExact("protected:")) {
355         if (!fCheck.fBraceCount) {
356             i.reportError("expect private in brace");
357         }
358         fCheck.fPrivateBrace = fCheck.fBraceCount;
359         fCheck.fPrivateProtected = true;
360         return false;
361     }
362     const char* funcEnd = i.anyOf("(\n");
363     if (funcEnd && '(' == funcEnd[0] && '_' == *i.anyOf("_(")
364             && (i.contains("internal_", funcEnd, nullptr)
365             || i.contains("private_", funcEnd, nullptr)
366             || i.contains("legacy_", funcEnd, nullptr)
367             || i.contains("temporary_", funcEnd, nullptr))) {
368         i.skipTo(funcEnd);
369         if (!i.skipToBalancedEndBracket('(', ')')) {
370             i.reportError("unbalanced open parent");
371         }
372         i.skipSpace();
373         i.skipExact("const ");
374         i.skipSpace();
375         if (';' == i.peek()) {
376             i.next();
377         }
378         fCheck.fState = CheckCode::State::kNone;
379         return false;
380     }
381     return true;
382 }
383 
codeBlockAppend(string & result,string s) const384 void IncludeParser::codeBlockAppend(string& result, string s) const {
385     for (char c : s) {
386         this->codeBlockAppend(result, c);
387     }
388 }
389 
codeBlockAppend(string & result,char ch) const390 void IncludeParser::codeBlockAppend(string& result, char ch) const {
391     if (Elided::kYes == fElided && fCheck.fBraceCount) {
392         return;
393     }
394     this->stringAppend(result, ch);
395 }
396 
codeBlockSpaces(string & result,int indent) const397 void IncludeParser::codeBlockSpaces(string& result, int indent) const {
398     if (!indent) {
399         return;
400     }
401     if (Elided::kYes == fElided && fCheck.fBraceCount) {
402         return;
403     }
404     SkASSERT(indent > 0);
405     if (fDebugWriteCodeBlock) {
406         SkDebugf("%*c", indent, ' ');
407     }
408     result.append(indent, ' ');
409 }
410 
writeCodeBlock(const Definition & iDef)411 string IncludeParser::writeCodeBlock(const Definition& iDef) {
412     if (MarkType::kComment == iDef.fMarkType) {
413         return "";
414     }
415     if (iDef.fUndocumented) {
416         return "";
417     }
418     TextParser i(&iDef);
419     (void) i.skipExact("SkDEBUGCODE(");
420     if (MarkType::kConst == iDef.fMarkType && !i.fEnd) {
421         // TODO: end should have been set earlier
422         auto iter = iDef.fParent->fTokens.begin();
423         std::advance(iter, iDef.fParentIndex + 1);
424         SkASSERT(iter != iDef.fParent->fTokens.end());
425         i.fEnd = iter->fContentStart;
426     }
427     const char* loc;
428     if (MarkType::kMember == iDef.fMarkType) {
429         const char* parentEnd = iDef.fParent->fContentEnd;
430         TextParser newEnd(&iDef);
431         newEnd.fEnd = parentEnd;
432         const char* memberEnd = newEnd.anyOf(",};");
433         if (memberEnd && (';' == memberEnd[0] || ',' == memberEnd[0])) {
434             i.fEnd = memberEnd + 1;
435         }
436     }
437     if (i.contains("//", i.fEnd, &loc)) {
438         i.fEnd = loc;
439     }
440     if (i.contains("/*", i.fEnd, &loc)) {
441         i.fEnd = loc;
442     }
443     if (i.contains("{", i.fEnd, &loc)) {
444         bool inBalance = false;
445         if (MarkType::kMethod == iDef.fMarkType) {
446             TextParser paren(&iDef);
447             paren.skipToEndBracket('(');
448             paren.skipToBalancedEndBracket('(', ')');
449             inBalance = loc < paren.fChar;
450         }
451         if (!inBalance) {
452             i.fEnd = loc + 1;
453             while (i.fEnd < iDef.fContentEnd && ' ' >= i.fEnd[0]) {
454                 ++i.fEnd;
455             }
456         }
457     }
458     while (i.fEnd > i.fStart && ' ' == i.fEnd[-1]) {
459         --i.fEnd;
460     }
461     const char* before = iDef.fContentStart;
462     while (' ' == *--before)
463         ;
464     int startIndent = iDef.fContentStart - before - 1;
465     bool saveDebugWriteBlock = fDebugWriteCodeBlock;
466     fDebugWriteCodeBlock = false;
467     string result = writeCodeBlock(i, iDef.fMarkType, startIndent);
468     fDebugWriteCodeBlock = saveDebugWriteBlock;
469     if (!result.empty()) {
470         if (MarkType::kNone != fPreviousMarkType && iDef.fMarkType != fPreviousMarkType
471                 && ((MarkType::kEnum != fPreviousMarkType
472                 && MarkType::kEnumClass != fPreviousMarkType)
473                 || MarkType::kMember != iDef.fMarkType)
474                 && (MarkType::kMember != fPreviousMarkType
475                 || iDef.fParent == fPreviousDef->fParent)) {
476             result = "\n" + result;
477         }
478         if (fDebugWriteCodeBlock) {
479             SkDebugf("%s", result.c_str());
480         }
481         fPreviousDef = &iDef;
482         fPreviousMarkType = iDef.fMarkType;
483     }
484     for (auto& token : iDef.fTokens) {
485         result += this->writeCodeBlock(token);
486     }
487     if (MarkType::kEnum == iDef.fMarkType || MarkType::kEnumClass == iDef.fMarkType
488             || MarkType::kStruct == iDef.fMarkType || MarkType::kClass == iDef.fMarkType) {
489         this->codeBlockSpaces(result, startIndent);
490         this->codeBlockAppend(result, "};\n\n");
491     }
492     return result;
493 }
494 
writeCodeBlock(TextParser & i,MarkType markType,int startIndent)495 string IncludeParser::writeCodeBlock(TextParser& i, MarkType markType, int startIndent) {
496     string result;
497     char last;
498     int lastIndent = 0;
499     bool lastDoubleMeUp = false;
500     fCheck.reset();
501     if (MarkType::kDefine == markType) {
502         result = "#define ";
503     } else {
504         this->codeBlockSpaces(result, startIndent);
505     }
506     do {
507         if (!this->advanceInclude(i)) {
508             continue;
509         }
510         do {
511             last = i.peek();
512             SkASSERT(' ' < last);
513             if (fCheck.fInDebugCode == i.fChar) {
514                 fCheck.fInDebugCode = nullptr;
515                 i.next();   // skip close paren
516                 break;
517             }
518             if (CheckCode::State::kMethod == fCheck.fState) {
519                 this->codeBlockAppend(result, ';');
520                 fCheck.fState = CheckCode::State::kNone;
521             }
522             if (fCheck.fWriteReturn) {
523                 this->codeBlockAppend(result, '\n');
524                 bool doubleMeUp = i.startsWith("typedef ") || looks_like_const(i)
525                         || (!strncmp("struct ", i.fStart, 7) && looks_like_member(i));
526                 if ((!--fCheck.fDoubleReturn && !i.startsWith("};")) || i.startsWith("enum ")
527                         || i.startsWith("typedef ") || doubleMeUp || fCheck.fTypedefReturn
528                         || (fCheck.fIndent && (i.startsWith("class ") || i.startsWith("struct ")))) {
529                     if (lastIndent > 0 && (!doubleMeUp || !lastDoubleMeUp)) {
530                         this->codeBlockAppend(result, '\n');
531                     }
532                     fCheck.fTypedefReturn = false;
533                     lastDoubleMeUp = doubleMeUp;
534                 }
535                 if (doubleMeUp) {
536                     fCheck.fTypedefReturn = true;
537                 }
538                 lastIndent = fCheck.fIndent;
539             }
540             if (fCheck.fIndent) {
541                 size_t indent = fCheck.fIndent;
542                 if (fCheck.fSkipInline && indent > sizeof(kInline)) {
543                     indent -= sizeof(kInline) - 1;
544                 }
545                 if (fCheck.fSkipAPI && indent > sizeof(kSK_API)) {
546                     indent -= sizeof(kSK_API) - 1;
547                 }
548                 if (fCheck.fSkipWarnUnused && indent > sizeof(kSK_WARN_UNUSED_RESULT)) {
549                     indent -= sizeof(kSK_WARN_UNUSED_RESULT) - 1;
550                 }
551 
552                 this->codeBlockSpaces(result, indent);
553             }
554             this->codeBlockAppend(result, last);
555             fCheck.fWriteReturn = false;
556             fCheck.fIndent = 0;
557             fCheck.fBraceCount += '{' == last;
558             fCheck.fBraceCount -= '}' == last;
559             if (';' == last) {
560                 fCheck.fSkipInline = false;
561                 fCheck.fSkipAPI = false;
562                 fCheck.fSkipWarnUnused = false;
563             }
564             if (fCheck.fBraceCount < 0) {
565                 i.reportError("unbalanced close brace");
566                 return result;
567             }
568             i.next();
569         } while (!i.eof() && ' ' < i.peek() && !i.startsWith("//"));
570     } while (!i.eof());
571     if (CheckCode::State::kMethod == fCheck.fState) {
572         this->codeBlockAppend(result, ';');
573     }
574     bool elided = Elided::kYes == fElided;
575     bool elidedTemplate = elided && !strncmp(i.fStart, "template ", 9);
576     bool elidedTClass = elidedTemplate && MarkType::kClass == markType;
577     bool statementEnd = !result.empty() && (MarkType::kMethod == markType
578             || MarkType::kTypedef == markType || '}' == result.back());
579     bool semiEnd = !result.empty() && (',' == result.back() || ';' == result.back());
580     if (fCheck.fWriteReturn || elidedTClass) {
581         this->codeBlockAppend(result, '\n');
582     }
583     if (elided && ((MarkType::kFunction != markType && lastIndent > startIndent) || elidedTClass)) {
584         this->codeBlockAppend(result, '}');
585         statementEnd = true;
586     }
587     if (elided || statementEnd) {
588         this->codeBlockAppend(result, ";\n");
589     } else if (elidedTemplate || semiEnd) {
590         this->codeBlockAppend(result, '\n');
591     }
592     return result;
593 }
594 
checkForMissingParams(const vector<string> & methodParams,const vector<string> & foundParams)595 void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
596         const vector<string>& foundParams) {
597     for (auto& methodParam : methodParams) {
598         bool found = false;
599         for (auto& foundParam : foundParams) {
600             if (methodParam == foundParam) {
601                 found = true;
602                 break;
603             }
604         }
605         if (!found) {
606             this->writeIncompleteTag("Param", methodParam, 2);
607         }
608     }
609     for (auto& foundParam : foundParams) {
610         bool found = false;
611         for (auto& methodParam : methodParams) {
612             if (methodParam == foundParam) {
613                 found = true;
614                 break;
615             }
616         }
617         if (!found) {
618             this->reportError("doxygen param does not match method declaration");
619         }
620     }
621 }
622 
checkForWord()623 bool IncludeParser::checkForWord() {
624     if (!fIncludeWord) {
625         return true;
626     }
627     KeyWord keyWord = FindKey(fIncludeWord, fChar);
628     if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
629         Bracket bracket = this->topBracket();
630         if (Bracket::kParen == bracket) {
631             return true;
632         }
633     }
634     if (KeyWord::kNone != keyWord) {
635         if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
636             this->addKeyword(keyWord);
637             return true;
638         }
639     } else {
640         this->addWord();
641         return true;
642     }
643     Definition* poundDef = fParent;
644     if (!fParent) {
645         return reportError<bool>("expected parent");
646     }
647     if (Definition::Type::kBracket != poundDef->fType) {
648         return reportError<bool>("expected bracket");
649     }
650     if (Bracket::kPound != poundDef->fBracket) {
651         return reportError<bool>("expected preprocessor");
652     }
653     if (KeyWord::kNone != poundDef->fKeyWord) {
654         return reportError<bool>("already found keyword");
655     }
656     poundDef->fKeyWord = keyWord;
657     fIncludeWord = nullptr;
658     switch (keyWord) {
659         // these do not link to other # directives
660         case KeyWord::kDefine:
661             if (!fInBrace) {
662                 SkASSERT(!fInDefine);
663                 fInDefine = true;
664             }
665         case KeyWord::kInclude:
666         case KeyWord::kError:
667         break;
668         // these start a # directive link
669         case KeyWord::kIf:
670         case KeyWord::kIfdef:
671         case KeyWord::kIfndef:
672         break;
673         // these continue a # directive link
674         case KeyWord::kElif:
675         case KeyWord::kElse:
676             this->popObject();  // pop elif
677             if (Bracket::kPound != fParent->fBracket) {
678                 return this->reportError<bool>("expected preprocessor directive");
679             }
680             this->popBracket();  // pop if
681             poundDef->fParent = fParent;
682             fParent = poundDef;  // push elif back
683         break;
684         // this ends a # directive link
685         case KeyWord::kEndif:
686         // FIXME : should this be calling popBracket() instead?
687             this->popObject();  // pop endif
688             if (Bracket::kPound != fParent->fBracket) {
689                 return this->reportError<bool>("expected preprocessor directive");
690             }
691             this->popBracket();  // pop if/else
692         break;
693         default:
694             SkASSERT(0);
695     }
696     return true;
697 }
698 
className() const699 string IncludeParser::className() const {
700     string name(fParent->fName);
701     size_t slash = name.find_last_of("/");
702     if (string::npos == slash) {
703         slash = name.find_last_of("\\");
704     }
705     SkASSERT(string::npos != slash);
706     string result = name.substr(slash);
707     result = result.substr(1, result.size() - 3);
708     return result;
709 }
710 
writeCodeBlock()711 void IncludeParser::writeCodeBlock() {
712     fElided = Elided::kNo;
713     for (auto& classMapper : fIClassMap) {
714         fPreviousMarkType = MarkType::kNone;
715         fPreviousDef = nullptr;
716         classMapper.second.fCode = this->writeCodeBlock(classMapper.second);
717     }
718     for (auto& enumMapper : fIEnumMap) {
719         fPreviousMarkType = MarkType::kNone;
720         fPreviousDef = nullptr;
721         enumMapper.second->fCode = this->writeCodeBlock(*enumMapper.second);
722     }
723     for (auto& typedefMapper : fITypedefMap) {
724         fPreviousMarkType = MarkType::kNone;
725         fPreviousDef = nullptr;
726         typedefMapper.second->fCode = this->writeCodeBlock(*typedefMapper.second);
727     }
728     for (auto& defineMapper : fIDefineMap) {
729         fPreviousMarkType = MarkType::kNone;
730         fPreviousDef = nullptr;
731         defineMapper.second->fCode = this->writeCodeBlock(*defineMapper.second);
732     }
733 }
734 
checkName(Definition * def)735 void IncludeParser::checkName(Definition* def) {
736     SkASSERT(!def->fName.empty());
737     TextParser parser(def->fFileName, &def->fName.front(), &def->fName.back() + 1, def->fLineCount);
738     const vector<string> skipWords = { "deprecated", "experimental", "internal",  "private",
739             "legacy", "temporary" };
740     if (!parser.anyWord(skipWords, 0).empty()) {
741         def->fUndocumented = true;
742     }
743 }
744 
745 #include <sstream>
746 #include <iostream>
747 
checkTokens(list<Definition> & tokens,string key,string className,RootDefinition * root,BmhParser & bmhParser)748 void IncludeParser::checkTokens(list<Definition>& tokens, string key, string className,
749         RootDefinition* root, BmhParser& bmhParser) {
750     for (const auto& token : tokens) {
751         if (token.fPrivate) {
752             continue;
753         }
754         string fullName = key + "::" + token.fName;
755         const Definition* def = nullptr;
756         if (root) {
757             def = root->find(fullName, RootDefinition::AllowParens::kYes);
758         }
759         switch (token.fMarkType) {
760             case MarkType::kMethod: {
761                 if (this->isInternalName(token)) {
762                     continue;
763                 }
764                 if (!root) {
765                     if (token.fUndocumented) {
766                         break;
767                     }
768                     auto methIter = bmhParser.fMethodMap.find(token.fName);
769                     if (bmhParser.fMethodMap.end() != methIter) {
770                         def = &methIter->second;
771                         if (def->crossCheck2(token)) {
772                             def->fVisited = true;
773                         } else {
774                             this->suggestFix(Suggest::kMethodDiffers, token, root, def);
775                             fFailed = true;
776                         }
777                     } else {
778                         this->suggestFix(Suggest::kMethodMissing, token, root, nullptr);
779                         fFailed = true;
780                     }
781                     break;
782                 }
783                 if (!def) {
784                     string paramName = className + "::";
785                     paramName += string(token.fContentStart,
786                             token.fContentEnd - token.fContentStart);
787                     if (string::npos != paramName.find('\n')) {
788                         paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
789                                 paramName.end());
790                     }
791                     def = root->find(paramName, RootDefinition::AllowParens::kYes);
792                     if (!def && 0 == token.fName.find("operator")) {
793                         string operatorName = className + "::";
794                         TextParser oper("", token.fStart, token.fContentEnd, 0);
795                         const char* start = oper.strnstr("operator", token.fContentEnd);
796                         SkASSERT(start);
797                         oper.skipTo(start);
798                         oper.skipToEndBracket('(');
799                         int parens = 0;
800                         do {
801                             if ('(' == oper.peek()) {
802                                 ++parens;
803                             } else if (')' == oper.peek()) {
804                                 --parens;
805                             }
806                         } while (!oper.eof() && oper.next() && parens > 0);
807                         operatorName += string(start, oper.fChar - start);
808                         def = root->find(operatorName, RootDefinition::AllowParens::kYes);
809                     }
810                 }
811                 if (!def) {
812                     int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
813                     skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
814                     const char* tokenEnd = token.methodEnd();
815                     string constructorName = className + "::";
816                     constructorName += string(token.fContentStart + skip,
817                             tokenEnd - token.fContentStart - skip);
818                     def = root->find(constructorName, RootDefinition::AllowParens::kYes);
819                 }
820                 if (!def && 0 == token.fName.find("SK_")) {
821                     string incName = token.fName + "()";
822                     string macroName = className + "::" + incName;
823                     def = root->find(macroName, RootDefinition::AllowParens::kYes);
824                     if (def) {
825                         if (def->fName == incName) {
826                             def->fVisited = true;
827                             if ("SK_TO_STRING_NONVIRT" == token.fName) {
828                                 def = root->find(className + "::toString",
829                                         RootDefinition::AllowParens::kYes);
830                                 if (def) {
831                                     def->fVisited = true;
832                                 } else {
833                                     SkDebugf("missing toString bmh: %s\n", fullName.c_str());
834                                     fFailed = true;
835                                 }
836                             }
837                             break;
838                         } else {
839                             SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
840                             fFailed = true;
841                         }
842                     }
843                 }
844                 if (!def) {
845                     bool allLower = true;
846                     for (size_t index = 0; index < token.fName.length(); ++index) {
847                         if (!islower(token.fName[index])) {
848                             allLower = false;
849                             break;
850                         }
851                     }
852                     if (allLower) {
853                         string lowerName = className + "::" + token.fName + "()";
854                         def = root->find(lowerName, RootDefinition::AllowParens::kYes);
855                     }
856                 }
857                 if (!def) {
858                     if (0 == token.fName.find("SkDEBUGCODE")) {
859                         break;
860                     }
861                 }
862                 if (!def) {
863         // simple method names inside nested classes have a bug and are missing trailing parens
864                     string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
865                     def = root->find(withParens, RootDefinition::AllowParens::kNo);
866                 }
867                 if (!def) {
868                     if (!token.fUndocumented) {
869                         this->suggestFix(Suggest::kMethodMissing, token, root, nullptr);
870                         fFailed = true;
871                     }
872                     break;
873                 }
874                 if (token.fUndocumented) {
875                     // we can't report an error yet; if bmh documents this unnecessarily,
876                     // we'll detect that later. It may be that def points to similar
877                     // documented function.
878                     break;
879                 }
880                 if (def->crossCheck2(token)) {
881                     def->fVisited = true;
882                 } else {
883                     SkDebugf("method differs from bmh: %s\n", fullName.c_str());
884                     fFailed = true;
885                 }
886             } break;
887             case MarkType::kComment:
888                 break;
889             case MarkType::kEnumClass:
890             case MarkType::kEnum: {
891                 if (!def) {
892                     // work backwards from first word to deduce #Enum name
893                     TextParser firstMember("", token.fStart, token.fContentEnd, 0);
894                     SkAssertResult(firstMember.skipName("enum"));
895                     SkAssertResult(firstMember.skipToEndBracket('{'));
896                     firstMember.next();
897                     firstMember.skipWhiteSpace();
898                     SkASSERT('k' == firstMember.peek());
899                     const char* savePos = firstMember.fChar;
900                     firstMember.skipToNonName();
901                     const char* wordEnd = firstMember.fChar;
902                     firstMember.fChar = savePos;
903                     const char* lastUnderscore = nullptr;
904                     do {
905                         if (!firstMember.skipToEndBracket('_')) {
906                             break;
907                         }
908                         if (firstMember.fChar > wordEnd) {
909                             break;
910                         }
911                         lastUnderscore = firstMember.fChar;
912                     } while (firstMember.next());
913                     if (lastUnderscore) {
914                         ++lastUnderscore;
915                         string enumName(lastUnderscore, wordEnd - lastUnderscore);
916                         if (root) {
917                             string anonName = className + "::" + enumName + 's';
918                             def = root->find(anonName, RootDefinition::AllowParens::kYes);
919                         } else {
920                             auto enumIter = bmhParser.fEnumMap.find(enumName);
921                             if (bmhParser.fEnumMap.end() != enumIter) {
922                                 RootDefinition* rootDef = &enumIter->second;
923                                 def = rootDef;
924                             }
925                         }
926                     }
927                     if (!def && !root) {
928                         auto enumIter = bmhParser.fEnumMap.find(token.fName);
929                         if (bmhParser.fEnumMap.end() != enumIter) {
930                             def = &enumIter->second;
931                         }
932                         if (!def) {
933                             auto enumClassIter = bmhParser.fClassMap.find(token.fName);
934                             if (bmhParser.fClassMap.end() != enumClassIter) {
935                                 def = &enumClassIter->second;
936                             }
937                         }
938                     }
939                     if (!def) {
940                         if (!token.fUndocumented) {
941                             SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
942                             fFailed = true;
943                         }
944                         break;
945                     }
946                 }
947                 def->fVisited = true;
948                 bool hasCode = false;
949                 bool hasPopulate = true;
950                 for (auto& child : def->fChildren) {
951                     if (MarkType::kCode == child->fMarkType) {
952                         hasPopulate = std::any_of(child->fChildren.begin(),
953                                 child->fChildren.end(), [](auto grandChild){
954                                 return MarkType::kPopulate == grandChild->fMarkType; });
955                         if (!hasPopulate) {
956                             def = child;
957                         }
958                         hasCode = true;
959                         break;
960                     }
961                 }
962                 if (!hasCode && !root) {
963                     const Definition* topic = def->topicParent();
964                     hasCode = std::any_of(topic->fChildren.begin(), topic->fChildren.end(),
965                             [](Definition* def){ return MarkType::kCode == def->fMarkType
966                             && def->fChildren.size() > 0 && MarkType::kPopulate ==
967                             def->fChildren.front()->fMarkType; });
968                 }
969                 if (!hasCode) {
970                     SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
971                     fFailed = true;
972                     break;
973                 }
974                 if (!hasPopulate) {
975                     if (def->crossCheck(token)) {
976                         def->fVisited = true;
977                     } else {
978                         SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
979                         fFailed = true;
980                     }
981                 }
982                 for (auto& member : token.fTokens) {
983                     if (MarkType::kMember != member.fMarkType) {
984                         continue;
985                     }
986                     string constName = MarkType::kEnumClass == token.fMarkType ?
987                             fullName : className;
988                     if (root) {
989                         constName += "::" + member.fName;
990                         def = root->find(constName, RootDefinition::AllowParens::kYes);
991                     } else {
992                         auto enumMapper = bmhParser.fEnumMap.find(token.fName);
993                         if (bmhParser.fEnumMap.end() != enumMapper) {
994                             auto& enumDoc = enumMapper->second;
995                             auto memberIter = enumDoc.fLeaves.find(member.fName);
996                             if (enumDoc.fLeaves.end() != memberIter) {
997                                 def = &memberIter->second;
998                             }
999                         }
1000                     }
1001                     if (!def) {
1002                         string innerName = key + "::" + member.fName;
1003                         def = root->find(innerName, RootDefinition::AllowParens::kYes);
1004                     }
1005                     if (!def) {
1006                         if (!member.fUndocumented) {
1007                             SkDebugf("const missing from bmh: %s\n", constName.c_str());
1008                             fFailed = true;
1009                         }
1010                     } else {
1011                         def->fVisited = true;
1012                     }
1013                 }
1014                 } break;
1015             case MarkType::kMember:
1016                 if (def) {
1017                     def->fVisited = true;
1018                 } else {
1019                     SkDebugf("member missing from bmh: %s\n", fullName.c_str());
1020                     fFailed = true;
1021                 }
1022                 break;
1023             case MarkType::kTypedef:
1024                 if (!def && !root) {
1025                     auto typedefIter = bmhParser.fTypedefMap.find(token.fName);
1026                     if (bmhParser.fTypedefMap.end() != typedefIter) {
1027                         def = &typedefIter->second;
1028                     }
1029                 }
1030                 if (def) {
1031                     def->fVisited = true;
1032                 } else {
1033                     SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
1034                     fFailed = true;
1035                 }
1036                 break;
1037             case MarkType::kConst:
1038                 if (!def && !root) {
1039                     auto constIter = bmhParser.fConstMap.find(token.fName);
1040                     if (bmhParser.fConstMap.end() != constIter) {
1041                         def = &constIter->second;
1042                     }
1043                 }
1044                 if (def) {
1045                     def->fVisited = true;
1046                 } else {
1047                     if (!token.fUndocumented) {
1048                         SkDebugf("const missing from bmh: %s\n", fullName.c_str());
1049                         fFailed = true;
1050                     }
1051                 }
1052                 break;
1053             case MarkType::kDefine:
1054                 // TODO: incomplete
1055                 break;
1056             default:
1057                 SkASSERT(0);  // unhandled
1058                 break;
1059         }
1060     }
1061 }
1062 
crossCheck(BmhParser & bmhParser)1063 bool IncludeParser::crossCheck(BmhParser& bmhParser) {
1064     for (auto& classMapper : fIClassMap) {
1065         string className = classMapper.first;
1066         auto finder = bmhParser.fClassMap.find(className);
1067         if (bmhParser.fClassMap.end() == finder) {
1068             SkASSERT(string::npos != className.find("::"));
1069             continue;
1070         }
1071     }
1072     for (auto& classMapper : fIClassMap) {
1073         if (classMapper.second.fUndocumented) {
1074            continue;
1075         }
1076         string className = classMapper.first;
1077         std::istringstream iss(className);
1078         string classStr;
1079         string classBase;
1080         RootDefinition* root = nullptr;
1081         while (std::getline(iss, classStr, ':')) {
1082             if (root) {
1083                 if (!classStr.length()) {
1084                     continue;
1085                 }
1086                 classBase += "::" + classStr;
1087                 auto finder = root->fBranches.find(classBase);
1088                 if (root->fBranches.end() != finder) {
1089                     root = finder->second;
1090                 } else {
1091                     SkASSERT(0);
1092                 }
1093             } else {
1094                 classBase = classStr;
1095                 auto finder = bmhParser.fClassMap.find(classBase);
1096                 if (bmhParser.fClassMap.end() != finder) {
1097                     root = &finder->second;
1098                 } else {
1099                     SkASSERT(0);
1100                 }
1101             }
1102         }
1103         this->checkTokens(classMapper.second.fTokens, classMapper.first, className, root,
1104                 bmhParser);
1105     }
1106     this->checkTokens(fGlobals, "", "", nullptr, bmhParser);
1107     int crossChecks = 0;
1108     string firstCheck;
1109     for (auto& classMapper : fIClassMap) {
1110         string className = classMapper.first;
1111         auto finder = bmhParser.fClassMap.find(className);
1112         if (bmhParser.fClassMap.end() == finder) {
1113             continue;
1114         }
1115         RootDefinition* root = &finder->second;
1116         if (!root->dumpUnVisited()) {
1117             fFailed = true;
1118         }
1119         if (crossChecks) {
1120             SkDebugf(".");
1121         } else {
1122             SkDebugf("cross-check");
1123             firstCheck = className;
1124         }
1125         ++crossChecks;
1126     }
1127     if (crossChecks) {
1128         if (1 == crossChecks) {
1129             SkDebugf(" %s", firstCheck.c_str());
1130         }
1131         SkDebugf("\n");
1132     }
1133     bmhParser.fWroteOut = true;
1134     return !fFailed;
1135 }
1136 
defineClass(const Definition & includeDef,string name)1137 IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
1138         string name) {
1139     string className;
1140     const Definition* test = fParent;
1141     while (Definition::Type::kFileType != test->fType) {
1142         if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
1143             className = test->fName + "::";
1144             break;
1145         }
1146         test = test->fParent;
1147     }
1148     className += name;
1149     unordered_map<string, IClassDefinition>& map = fIClassMap;
1150     IClassDefinition& markupDef = map[className];
1151     if (markupDef.fStart) {
1152         typedef IClassDefinition* IClassDefPtr;
1153         return INHERITED::reportError<IClassDefPtr>("class already defined");
1154     }
1155     markupDef.fFileName = fFileName;
1156     markupDef.fStart = includeDef.fStart;
1157     markupDef.fContentStart = includeDef.fStart;
1158     markupDef.fName = className;
1159     this->checkName(&markupDef);
1160     markupDef.fContentEnd = includeDef.fContentEnd;
1161     markupDef.fTerminator = includeDef.fTerminator;
1162     markupDef.fParent = fParent;
1163     markupDef.fLineCount = fLineCount;
1164     markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
1165             MarkType::kStruct : MarkType::kClass;
1166     markupDef.fKeyWord = includeDef.fKeyWord;
1167     markupDef.fType = Definition::Type::kMark;
1168     auto tokenIter = includeDef.fParent->fTokens.begin();
1169     SkASSERT(includeDef.fParentIndex > 0);
1170     std::advance(tokenIter, includeDef.fParentIndex - 1);
1171     const Definition* priorComment = &*tokenIter;
1172     markupDef.fUndocumented = priorComment->fUndocumented;
1173     fParent = &markupDef;
1174     return &markupDef;
1175 }
1176 
dumpClassTokens(IClassDefinition & classDef)1177 void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
1178     auto& tokens = classDef.fTokens;
1179     bool wroteTail = true;
1180     for (auto& token : tokens) {
1181         if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
1182             continue;
1183         }
1184         if (wroteTail && MarkType::kMember != token.fMarkType) {
1185             this->writeBlockSeparator();
1186         }
1187         switch (token.fMarkType) {
1188             case MarkType::kConst:
1189                 this->dumpConst(token, classDef.fName);
1190             break;
1191             case MarkType::kEnum:
1192             case MarkType::kEnumClass:
1193                 this->dumpEnum(token, token.fName);
1194             break;
1195             case MarkType::kMethod:
1196                 if (!this->dumpMethod(token, classDef.fName)) {
1197                     wroteTail = false;
1198                     continue;
1199                 }
1200             break;
1201             case MarkType::kMember:
1202                 this->dumpMember(token);
1203                 continue;
1204             break;
1205             case MarkType::kTypedef:
1206                 this->dumpTypedef(token, classDef.fName);
1207             break;
1208             default:
1209                 SkASSERT(0);
1210         }
1211         this->dumpCommonTail(token);
1212         wroteTail = true;
1213     }
1214 }
dumpComment(const Definition & token)1215 void IncludeParser::dumpComment(const Definition& token) {
1216     fLineCount = token.fLineCount;
1217     fChar = fLine = token.fContentStart;
1218     fEnd = token.fContentEnd;
1219     if (MarkType::kMethod == token.fMarkType) {
1220         this->lf(2);
1221         this->writeTag("Populate");
1222         this->lf(2);
1223         return;
1224     }
1225     for (const auto& child : token.fTokens) {
1226         if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
1227             break;
1228         }
1229         if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
1230             if (child.fPrivate) {
1231                 break;
1232             }
1233             if (child.length() > 1) {
1234                 const char* start = child.fContentStart;
1235                 ptrdiff_t length = child.fContentEnd - start;
1236                 SkASSERT(length >= 0);
1237                 while (length && '/' == start[0]) {
1238                     start += 1;
1239                     --length;
1240                 }
1241                 while (length && '/' == start[length - 1]) {
1242                     length -= 1;
1243                     if (length && '*' == start[length - 1]) {
1244                         length -= 1;
1245                     }
1246                 }
1247                 if (length) {
1248                     this->lf(2);
1249                     if ("!< " == string(start, length).substr(0, 3)) {
1250                         return;
1251                     }
1252                     this->writeBlock(length, start);
1253                     this->lf(2);
1254                 }
1255             }
1256         }
1257     }
1258 }
1259 
dumpCommonTail(const Definition & token)1260 void IncludeParser::dumpCommonTail(const Definition& token) {
1261     this->lf(2);
1262     this->writeTag("Example");
1263     this->lf(1);
1264     this->writeString("// incomplete");
1265     this->lf(1);
1266     this->writeEndTag();
1267     this->lf(2);
1268     this->writeTag("SeeAlso");
1269     this->writeSpace();
1270     this->writeString("incomplete");
1271     this->lf(2);
1272     this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
1273     this->lf(2);
1274 }
1275 
dumpConst(const Definition & token,string className)1276 void IncludeParser::dumpConst(const Definition& token, string className) {
1277     this->writeTag("Const");
1278     this->writeSpace();
1279     this->writeString(token.fName);
1280     this->writeTagTable("Line", "incomplete");
1281     this->lf(2);
1282     this->dumpComment(token);
1283 }
1284 
dumpDefine(const Definition & token)1285 void IncludeParser::dumpDefine(const Definition& token) {
1286     this->writeTag("Define", token.fName);
1287     this->lf(2);
1288     this->writeTag("Code");
1289     this->lfAlways(1);
1290     this->writeString("###$");
1291     this->lfAlways(1);
1292     this->indentToColumn(4);
1293     this->writeBlock(token.fTerminator - token.fStart, token.fStart);
1294     this->lf(1);
1295     this->indentToColumn(0);
1296     this->writeString("$$$#");
1297 
1298     this->writeEndTag();
1299     this->lf(2);
1300     this->dumpComment(token);
1301     for (auto& child : token.fTokens) {
1302         if (MarkType::kComment == child.fMarkType) {
1303             continue;
1304         }
1305         this->writeTag("Param", child.fName);
1306         this->writeSpace();
1307         this->writeString("incomplete");
1308         this->writeSpace();
1309         this->writeString("##");
1310         this->lf(1);
1311     }
1312 }
1313 
dumpEnum(const Definition & token,string name)1314 void IncludeParser::dumpEnum(const Definition& token, string name) {
1315     string tagType(MarkType::kEnum == token.fMarkType ? "Enum" : "EnumClass");
1316     this->writeTag(tagType.c_str(), token.fName);
1317     this->lf(2);
1318     this->writeTag("Code");
1319     this->writeTag("Populate");
1320     this->writeEndTag();
1321     this->lf(2);
1322     this->dumpComment(token);
1323     string prior;
1324     for (auto& child : token.fTokens) {
1325         if (MarkType::kComment == child.fMarkType) {
1326             prior = string(child.fContentStart, child.length());
1327         }
1328         if (MarkType::kMember != child.fMarkType) {
1329             continue;
1330         }
1331         this->writeTag("Const");
1332         this->writeSpace();
1333         this->writeString(child.fName);
1334         this->writeSpace(2);
1335         this->writeString("0 # incomplete; replace '0' with member value");
1336         this->lf(1);
1337         this->writeTagNoLF("Line", "#");
1338         this->writeSpace();
1339         if ("/!< " == prior.substr(0, 4)) {
1340             this->writeString(prior.substr(4));
1341         } else {
1342             this->writeString("incomplete");
1343         }
1344         this->writeSpace();
1345         this->writeString("##");
1346         this->lf(1);
1347         this->writeString("# incomplete; add description or delete");
1348         this->writeEndTag();
1349     }
1350     this->lf(2);
1351     this->writeString("# incomplete; add description or delete");
1352     this->lf(2);
1353 }
1354 
dumpGlobals(string * globalFileName,long int * globalTell)1355 bool IncludeParser::dumpGlobals(string* globalFileName, long int* globalTell) {
1356     bool hasGlobals = !fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1357             || !fITemplateMap.empty()|| !fITypedefMap.empty() || !fIUnionMap.empty();
1358     if (!hasGlobals) {
1359         return true;
1360     }
1361     size_t lastBSlash = fFileName.rfind('\\');
1362     size_t lastSlash = fFileName.rfind('/');
1363     size_t lastDotH = fFileName.rfind(".h");
1364     SkASSERT(string::npos != lastDotH);
1365     if (string::npos != lastBSlash && (string::npos == lastSlash
1366             || lastBSlash < lastSlash)) {
1367         lastSlash = lastBSlash;
1368     } else if (string::npos == lastSlash) {
1369         lastSlash = -1;
1370     }
1371     lastSlash += 1;
1372     string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
1373     string fileName = globalsName + "_Reference.bmh";
1374     *globalFileName = fileName;
1375     fOut = fopen(fileName.c_str(), "wb");
1376     if (!fOut) {
1377         SkDebugf("could not open output file %s\n", globalsName.c_str());
1378         return false;
1379     }
1380     string prefixName = globalsName.substr(0, 2);
1381     string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
1382         ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
1383     this->writeTagNoLF("Topic", topicName);
1384     this->writeEndTag("Alias", topicName + "_Reference");
1385     this->lf(2);
1386     if (!fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1387             || !fITemplateMap.empty() || !fITypedefMap.empty() || !fIUnionMap.empty()) {
1388         this->writeTag("Code");
1389         this->writeTag("Populate");
1390         this->writeEndTag();
1391         this->lf(2);
1392     }
1393     std::map<int, Definition*> sortedDefs;
1394     for (const auto& entry : fIDefineMap) {
1395         sortedDefs[entry.second->fLineCount] = entry.second;
1396     }
1397     for (const auto& entry : fIFunctionMap) {
1398         sortedDefs[entry.second->fLineCount] = entry.second;
1399     }
1400     for (const auto& entry : fIEnumMap) {
1401         if (string::npos == entry.first.find("::")) {
1402             sortedDefs[entry.second->fLineCount] = entry.second;
1403         }
1404     }
1405     for (const auto& entry : fITemplateMap) {
1406         sortedDefs[entry.second->fLineCount] = entry.second;
1407     }
1408     for (const auto& entry : fITypedefMap) {
1409         sortedDefs[entry.second->fLineCount] = entry.second;
1410     }
1411     for (const auto& entry : fIUnionMap) {
1412         sortedDefs[entry.second->fLineCount] = entry.second;
1413     }
1414     for (const auto& entry : sortedDefs) {
1415         const Definition* def = entry.second;
1416         this->writeBlockSeparator();
1417         switch (def->fMarkType) {
1418             case MarkType::kDefine:
1419                 this->dumpDefine(*def);
1420                 break;
1421             case MarkType::kMethod:
1422                 if (!this->dumpMethod(*def, globalsName)) {
1423                     continue;
1424                 }
1425                 break;
1426             case MarkType::kEnum:
1427             case MarkType::kEnumClass:
1428                 this->dumpEnum(*def, globalsName);
1429                 break;
1430             case MarkType::kTemplate:
1431                 SkASSERT(0);  // incomplete
1432                 break;
1433             case MarkType::kTypedef: {
1434                 this->writeTag("Typedef");
1435                 this->writeSpace();
1436                 TextParser parser(def);
1437                 if (!parser.skipExact("typedef")) {
1438                     return false;
1439                 }
1440                 if (!parser.skipSpace()) {
1441                     return false;
1442                 }
1443                 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1444                 this->lf(2);
1445                 this->dumpComment(*def);
1446                 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1447                 this->lf(2);
1448                 } continue;
1449             case MarkType::kUnion:
1450                 SkASSERT(0);  // incomplete
1451                 break;
1452             default:
1453                 SkASSERT(0);
1454         }
1455         this->dumpCommonTail(*def);
1456     }
1457     *globalTell = ftell(fOut);
1458     this->writeEndTag("Topic", topicName);
1459     this->lfAlways(1);
1460 //    fclose(fOut);     // defer closing in case class needs to be also written here
1461     return true;
1462 }
1463 
isClone(const Definition & token)1464 bool IncludeParser::isClone(const Definition& token) {
1465     string name = token.fName;
1466     return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1467 }
1468 
isConstructor(const Definition & token,string className)1469 bool IncludeParser::isConstructor(const Definition& token, string className) {
1470     string name = token.fName;
1471     return 0 == name.find(className) || '~' == name[0];
1472 }
1473 
isInternalName(const Definition & token)1474 bool IncludeParser::isInternalName(const Definition& token) {
1475     string name = token.fName;
1476     // exception for this SkCanvas function .. for now
1477     if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1478         return false;
1479     }
1480     return name.substr(0, 7) == "android"
1481             || 0 == token.fName.find("internal_")
1482             || 0 == token.fName.find("Internal_")
1483             || 0 == token.fName.find("legacy_")
1484             || 0 == token.fName.find("temporary_")
1485             || 0 == token.fName.find("private_");
1486 }
1487 
isMember(const Definition & token) const1488 bool IncludeParser::isMember(const Definition& token) const {
1489     if ('f' == token.fStart[0] && isupper(token.fStart[1])) {
1490         return true;
1491     }
1492     if (!islower(token.fStart[0])) {
1493         return false;
1494     }
1495     // make an exception for SkTextBlob::RunBuffer, sole struct with members not in fXxxx format
1496     if (string::npos != token.fFileName.find("SkTextBlob.h")) {
1497         const Definition* structToken = token.fParent;
1498         if (!structToken) {
1499             return false;
1500         }
1501         if (KeyWord::kStruct != structToken->fKeyWord) {
1502             structToken = token.fParent->fParent;
1503             if (!structToken) {
1504                 return false;
1505             }
1506             if (KeyWord::kStruct != structToken->fKeyWord) {
1507                 return false;
1508             }
1509         }
1510         SkASSERT(structToken->fTokens.size() > 0);
1511         const Definition& child = structToken->fTokens.front();
1512         string structName(child.fContentStart, child.length());
1513         if ("RunBuffer" != structName) {
1514             return false;
1515         }
1516         string tokenName(token.fContentStart, token.length());
1517         string allowed[] = { "glyphs", "pos", "utf8text", "clusters" };
1518         for (auto allow : allowed) {
1519             if (allow == tokenName) {
1520                 return true;
1521             }
1522         }
1523     }
1524     return false;
1525 }
1526 
isOperator(const Definition & token)1527 bool IncludeParser::isOperator(const Definition& token) {
1528     return "operator" == token.fName.substr(0, 8);
1529 }
1530 
dumpMethod(const Definition & token,string className)1531 bool IncludeParser::dumpMethod(const Definition& token, string className) {
1532     if (std::any_of(token.fTokens.begin(), token.fTokens.end(),
1533             [=](const Definition& def) { return MarkType::kComment == def.fMarkType
1534             && this->isUndocumentable(def.fFileName, def.fContentStart, def.fContentEnd,
1535             def.fLineCount); } )) {
1536         return false;
1537     }
1538     this->writeTag("Method");
1539     this->writeSpace();
1540 
1541     string name = string(token.fStart ? token.fStart : token.fContentStart,
1542             token.length());
1543     this->writeBlock((int) name.size(), name.c_str());
1544     string inType;
1545     if (this->isConstructor(token, className)) {
1546         inType = "Constructor";
1547     } else if (this->isOperator(token)) {
1548         inType = "Operator";
1549     } else {
1550         inType = "incomplete";
1551     }
1552     this->writeTag("In", inType);
1553     this->writeTagTable("Line", "incomplete");
1554     this->lf(2);
1555     this->dumpComment(token);
1556     return true;
1557 }
1558 
dumpMember(const Definition & token)1559 void IncludeParser::dumpMember(const Definition& token) {
1560     this->writeTag("Member");
1561     this->writeSpace();
1562     this->writeDefinition(token, token.fName, 2);
1563     lf(1);
1564     for (auto child : token.fChildren) {
1565         this->writeDefinition(*child);
1566     }
1567     this->writeEndTag();
1568     lf(2);
1569 }
1570 
dumpTokens()1571 bool IncludeParser::dumpTokens() {
1572     string globalFileName;
1573     long int globalTell = 0;
1574     if (!this->dumpGlobals(&globalFileName, &globalTell)) {
1575         return false;
1576     }
1577     for (const auto& member : fIClassMap) {
1578         if (string::npos != member.first.find("::")) {
1579             continue;
1580         }
1581         if (!this->dumpTokens(member.first, globalFileName, &globalTell)) {
1582             return false;
1583         }
1584     }
1585     if (globalTell) {
1586         fclose(fOut);
1587         SkDebugf("wrote %s\n", globalFileName.c_str());
1588     }
1589     return true;
1590 }
1591 
1592     // dump equivalent markup
dumpTokens(string skClassName,string globalFileName,long int * globalTell)1593 bool IncludeParser::dumpTokens(string skClassName, string globalFileName, long int* globalTell) {
1594     string fileName = skClassName + "_Reference.bmh";
1595     if (globalFileName != fileName) {
1596         fOut = fopen(fileName.c_str(), "wb");
1597         if (!fOut) {
1598             SkDebugf("could not open output file %s\n", fileName.c_str());
1599             return false;
1600         }
1601     } else {
1602         fseek(fOut, *globalTell, SEEK_SET);
1603         this->lf(2);
1604         this->writeBlockSeparator();
1605         *globalTell = 0;
1606     }
1607     string prefixName = skClassName.substr(0, 2);
1608     string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1609         ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
1610     if (globalFileName != fileName) {
1611         this->writeTagNoLF("Topic", topicName);
1612         this->writeEndTag("Alias", topicName + "_Reference");
1613         this->lf(2);
1614     }
1615     auto& classMap = fIClassMap[skClassName];
1616     SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1617     const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
1618     this->writeTag(containerType, skClassName);
1619     this->lf(2);
1620     auto& tokens = classMap.fTokens;
1621     for (auto& token : tokens) {
1622         if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1623             continue;
1624         }
1625         this->writeDefinition(token);
1626         this->lf(1);
1627     }
1628     this->lf(2);
1629     this->writeTag("Code");
1630     this->writeTag("Populate");
1631     this->writeEndTag();
1632     this->lf(2);
1633     for (auto& oneClass : fIClassMap) {
1634         if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1635             continue;
1636         }
1637         string innerName = oneClass.first.substr(skClassName.length() + 2);
1638         this->writeBlockSeparator();
1639         KeyWord keyword = oneClass.second.fKeyWord;
1640         SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1641         const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
1642         this->writeTag(containerType, innerName);
1643         this->writeTagTable("Line", "incomplete");
1644         this->lf(2);
1645         this->writeTag("Code");
1646         this->writeEndTag("ToDo", "fill this in manually");
1647         this->writeEndTag();
1648         this->lf(2);
1649         for (auto& token : oneClass.second.fTokens) {
1650             if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1651                 continue;
1652             }
1653             this->writeDefinition(token);
1654         }
1655         this->lf(2);
1656         this->dumpClassTokens(oneClass.second);
1657         this->lf(2);
1658         this->writeEndTag(containerType, innerName);
1659         this->lf(2);
1660     }
1661     this->dumpClassTokens(classMap);
1662     this->writeEndTag(containerType, skClassName);
1663     this->lf(2);
1664     this->writeEndTag("Topic", topicName);
1665     this->lfAlways(1);
1666     fclose(fOut);
1667     SkDebugf("wrote %s\n", fileName.c_str());
1668     return true;
1669 }
1670 
dumpTypedef(const Definition & token,string className)1671 void IncludeParser::dumpTypedef(const Definition& token, string className) {
1672     this->writeTag("Typedef");
1673     this->writeSpace();
1674     this->writeString(token.fName);
1675     this->writeTagTable("Line", "incomplete");
1676     this->lf(2);
1677     this->dumpComment(token);
1678 }
1679 
elidedCodeBlock(const Definition & iDef)1680 string IncludeParser::elidedCodeBlock(const Definition& iDef) {
1681     SkASSERT(KeyWord::kStruct == iDef.fKeyWord || KeyWord::kClass == iDef.fKeyWord
1682             || KeyWord::kTemplate == iDef.fKeyWord);
1683     TextParser i(&iDef);
1684     fElided = Elided::kYes;
1685     MarkType markType = MarkType::kClass;
1686     if (KeyWord::kTemplate == iDef.fKeyWord) {  // may be function
1687         for (auto child : iDef.fChildren) {
1688             if (MarkType::kMethod == child->fMarkType) {
1689                 markType = MarkType::kFunction;
1690                 break;
1691             }
1692         }
1693     }
1694     return this->writeCodeBlock(i, markType, 0);
1695 }
1696 
filteredBlock(string inContents,string filterContents)1697  string IncludeParser::filteredBlock(string inContents, string filterContents) {
1698     string result;
1699     const unordered_map<string, Definition*>* mapPtr = nullptr;
1700     if ("Constant" == inContents) {
1701         mapPtr = &fIConstMap;
1702     } else {
1703         SkASSERT(0); // only Constant supported for now
1704     }
1705     vector<Definition*> consts;
1706     for (auto entry : *mapPtr) {
1707         if (string::npos == entry.first.find(filterContents)) {
1708             continue;
1709         }
1710         consts.push_back(entry.second);
1711     }
1712     std::sort(consts.begin(), consts.end(), [](Definition* def1, Definition* def2) {
1713         return def1->fLineCount < def2->fLineCount;
1714     } );
1715     for (auto oneConst : consts) {
1716         result += this->writeCodeBlock(*oneConst);
1717     }
1718     return result;
1719 }
1720 
findCommentAfter(const Definition & includeDef,Definition * markupDef)1721 bool IncludeParser::findCommentAfter(const Definition& includeDef, Definition* markupDef) {
1722     this->checkName(markupDef);
1723     const Definition* parent = includeDef.fParent;
1724     int index = includeDef.fParentIndex;
1725     auto wordIter = parent->fTokens.begin();
1726     std::advance(wordIter, index);
1727     SkASSERT(&*wordIter == &includeDef);
1728     size_t commentLine = 0;
1729     do {
1730         wordIter = std::next(wordIter);
1731         if (parent->fTokens.end() == wordIter) {
1732             break;
1733         }
1734         commentLine = wordIter->fLineCount;
1735     } while (Punctuation::kSemicolon != wordIter->fPunctuation);
1736     wordIter = std::next(wordIter);
1737     if (parent->fTokens.end() != wordIter && Bracket::kSlashSlash == wordIter->fBracket
1738             && wordIter->fLineCount == commentLine) {
1739         return this->parseComment(wordIter->fFileName, wordIter->fContentStart,
1740                 wordIter->fContentEnd, wordIter->fLineCount, markupDef, &markupDef->fUndocumented);
1741     }
1742     return true;
1743 }
1744 
findComments(const Definition & includeDef,Definition * markupDef)1745 bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1746     this->checkName(markupDef);
1747     // add comment preceding class, if any
1748     Definition* parent = includeDef.fParent;
1749     int index = includeDef.fParentIndex;
1750     auto wordIter = parent->fTokens.begin();
1751     std::advance(wordIter, index);
1752     SkASSERT(&*wordIter == &includeDef);
1753     while (parent->fTokens.begin() != wordIter) {
1754         auto testIter = std::prev(wordIter);
1755         if (Definition::Type::kWord != testIter->fType
1756             && Definition::Type::kKeyWord != testIter->fType
1757             && (Definition::Type::kBracket != testIter->fType
1758             || Bracket::kAngle != testIter->fBracket)
1759             && (Definition::Type::kPunctuation != testIter->fType
1760             || Punctuation::kAsterisk != testIter->fPunctuation)) {
1761             break;
1762         }
1763         wordIter = testIter;
1764     }
1765     auto commentIter = wordIter;
1766     while (parent->fTokens.begin() != commentIter) {
1767         auto testIter = std::prev(commentIter);
1768         bool isComment = Definition::Type::kBracket == testIter->fType
1769                 && (Bracket::kSlashSlash == testIter->fBracket
1770                 || Bracket::kSlashStar == testIter->fBracket);
1771         if (!isComment) {
1772             break;
1773         }
1774         commentIter = testIter;
1775     }
1776     while (commentIter != wordIter) {
1777         if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1778                 commentIter->fContentEnd, commentIter->fLineCount, markupDef,
1779                 &markupDef->fUndocumented)) {
1780             return false;
1781         }
1782         commentIter->fUndocumented = markupDef->fUndocumented;
1783         commentIter = std::next(commentIter);
1784     }
1785     return true;
1786 }
1787 
findIncludeObject(const Definition & includeDef,MarkType markType,string typeName)1788 Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1789         string typeName) {
1790     typedef Definition* DefinitionPtr;
1791     auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1792             [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1793     if (mapIter == fMaps.end()) {
1794         return nullptr;
1795     }
1796     if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1797         return reportError<DefinitionPtr>("invalid mark type");
1798     }
1799     string name = this->uniqueName(*mapIter->fInclude, typeName);
1800     Definition& markupDef = *(*mapIter->fInclude)[name];
1801     if (markupDef.fStart) {
1802         return reportError<DefinitionPtr>("definition already defined");
1803     }
1804     markupDef.fFileName = fFileName;
1805     markupDef.fStart = includeDef.fStart;
1806     markupDef.fContentStart = includeDef.fStart;
1807     this->checkName(&markupDef);
1808     markupDef.fName = name;
1809     markupDef.fContentEnd = includeDef.fContentEnd;
1810     markupDef.fTerminator = includeDef.fTerminator;
1811     markupDef.fParent = fParent;
1812     markupDef.fLineCount = includeDef.fLineCount;
1813     markupDef.fMarkType = markType;
1814     markupDef.fKeyWord = includeDef.fKeyWord;
1815     markupDef.fType = Definition::Type::kMark;
1816     return &markupDef;
1817 }
1818 
findMethod(const Definition & bmhDef)1819 Definition* IncludeParser::findMethod(const Definition& bmhDef) {
1820     auto doubleColon = bmhDef.fName.rfind("::");
1821     if (string::npos == doubleColon) {
1822         const auto& iGlobalMethod = fIFunctionMap.find(bmhDef.fName);
1823         SkASSERT(fIFunctionMap.end() != iGlobalMethod);
1824         return iGlobalMethod->second;
1825     }
1826     string className = bmhDef.fName.substr(0, doubleColon);
1827     const auto& iClass = fIClassMap.find(className);
1828     if (fIClassMap.end() == iClass) {
1829         return nullptr;
1830     }
1831     string methodName = bmhDef.fName.substr(doubleColon + 2);
1832     auto& iTokens = iClass->second.fTokens;
1833     const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
1834             [methodName](Definition& token) {
1835             return MarkType::kMethod == token.fMarkType
1836                     && !token.fUndocumented
1837                     && (methodName == token.fName
1838                     || methodName == token.fName + "()"); } );
1839     if (iTokens.end() != iMethod) {
1840         return &*iMethod;
1841     }
1842     size_t subClassPos = className.rfind("::");
1843     if (string::npos != subClassPos) {
1844         className = className.substr(subClassPos + 2);
1845     }
1846     // match may be constructor; compare strings to see if this is so
1847     if (string::npos == methodName.find('(')) {
1848         return nullptr;
1849     }
1850     auto stripper = [](string s) -> string {
1851         bool last = false;
1852         string result;
1853         for (char c : s) {
1854             if (' ' >= c) {
1855                 if (!last) {
1856                     last = true;
1857                     result += ' ';
1858                 }
1859                 continue;
1860             }
1861             result += c;
1862             last = false;
1863         }
1864         return result;
1865     };
1866     string strippedMethodName = stripper(methodName);
1867     if (strippedMethodName == methodName) {
1868         strippedMethodName = "";
1869     }
1870     const auto& cMethod = std::find_if(iTokens.begin(), iTokens.end(),
1871             [className, methodName, stripper, strippedMethodName](Definition& token) {
1872         if (MarkType::kMethod != token.fMarkType) {
1873             return false;
1874         }
1875         if (token.fUndocumented) {
1876             return false;
1877         }
1878         TextParser parser(&token);
1879         const char* match = parser.strnstr(className.c_str(), parser.fEnd);
1880         if (!match) {
1881             return false;
1882         }
1883         parser.skipTo(match);
1884         parser.skipExact(className.c_str());
1885         if ('(' != parser.peek()) {
1886             return false;
1887         }
1888         parser.skipToBalancedEndBracket('(', ')');
1889         string iMethodName(match, parser.fChar - match);
1890         if (methodName == iMethodName) {
1891             return true;
1892         }
1893         if (strippedMethodName.empty()) {
1894             return false;
1895         }
1896         string strippedIName = stripper(iMethodName);
1897         return strippedIName == strippedMethodName;
1898     } );
1899     SkAssertResult(iTokens.end() != cMethod);
1900     return &*cMethod;
1901 }
1902 
parentBracket(Definition * parent) const1903 Definition* IncludeParser::parentBracket(Definition* parent) const {
1904     while (parent && Definition::Type::kBracket != parent->fType) {
1905         parent = parent->fParent;
1906     }
1907     return parent;
1908 }
1909 
grandParentBracket() const1910 Bracket IncludeParser::grandParentBracket() const {
1911     Definition* parent = parentBracket(fParent);
1912     parent = parentBracket(parent ? parent->fParent : nullptr);
1913     return parent ? parent->fBracket : Bracket::kNone;
1914 }
1915 
inAlignAs() const1916 bool IncludeParser::inAlignAs() const {
1917     if (fParent->fTokens.size() < 2) {
1918         return false;
1919     }
1920     auto reverseIter = fParent->fTokens.end();
1921     bool checkForBracket = true;
1922     while (fParent->fTokens.begin() != reverseIter) {
1923         std::advance(reverseIter, -1);
1924         if (checkForBracket) {
1925             if (Definition::Type::kBracket != reverseIter->fType) {
1926                 return false;
1927             }
1928             if (Bracket::kParen != reverseIter->fBracket) {
1929                 return false;
1930             }
1931             checkForBracket = false;
1932             continue;
1933         }
1934         if (Definition::Type::kKeyWord != reverseIter->fType) {
1935             return false;
1936         }
1937         return KeyWord::kAlignAs == reverseIter->fKeyWord;
1938     }
1939     return false;
1940 }
1941 
include(string match) const1942 const Definition* IncludeParser::include(string match) const {
1943     for (auto& entry : fIncludeMap) {
1944         if (string::npos == entry.first.find(match)) {
1945             continue;
1946         }
1947         return &entry.second;
1948     }
1949     SkASSERT(0);
1950     return nullptr;
1951 }
1952 
1953 // caller just returns, so report error here
parseClass(Definition * includeDef,IsStruct isStruct)1954 bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1955     SkASSERT(includeDef->fTokens.size() > 0);
1956     // parse class header
1957     auto iter = includeDef->fTokens.begin();
1958     if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1959         // todo : documentation is ignoring this for now
1960         iter = std::next(iter);
1961     }
1962     bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1963     if (hasAlignAs) {
1964         iter = std::next(iter);
1965         if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1966             return includeDef->reportError<bool>("expected alignas argument");
1967         }
1968         iter = std::next(iter);
1969     }
1970     string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1971     includeDef->fName = nameStr;
1972     this->checkName(includeDef);
1973     iter = std::next(iter);
1974     if (iter == includeDef->fTokens.end()) {
1975         return true;  // forward declaration only
1976     }
1977     do {
1978         if (iter == includeDef->fTokens.end()) {
1979             return includeDef->reportError<bool>("unexpected end");
1980         }
1981         if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1982             break;
1983         }
1984     } while (static_cast<void>(iter = std::next(iter)), true);
1985     if (Punctuation::kLeftBrace != iter->fPunctuation) {
1986         return iter->reportError<bool>("expected left brace");
1987     }
1988     IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1989     if (!markupDef) {
1990         return iter->reportError<bool>("expected markup definition");
1991     }
1992     markupDef->fStart = iter->fStart;
1993     if (!this->findComments(*includeDef, markupDef)) {
1994         return iter->reportError<bool>("find comments failed");
1995     }
1996     if (markupDef->fUndocumented) {
1997         includeDef->fUndocumented = true;
1998     }
1999 //    if (1 != includeDef->fChildren.size()) {
2000 //        return false;  // fix me: SkCanvasClipVisitor isn't correctly parsed
2001 //    }
2002     auto includeDefIter = includeDef->fChildren.begin();
2003     if (hasAlignAs) {
2004         SkASSERT(includeDef->fChildren.end() != includeDefIter);
2005         SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
2006         std::advance(includeDefIter, 1);
2007     }
2008     if (includeDef->fChildren.end() != includeDefIter
2009             && Bracket::kAngle == (*includeDefIter)->fBracket) {
2010         std::advance(includeDefIter, 1);
2011     }
2012     includeDef = *includeDefIter;
2013     SkASSERT(Bracket::kBrace == includeDef->fBracket);
2014     iter = includeDef->fTokens.begin();
2015     // skip until public
2016     int publicIndex = 0;
2017     if (IsStruct::kNo == isStruct) {
2018         const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2019         size_t publicLen = strlen(publicName);
2020         while (iter != includeDef->fTokens.end()
2021                 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
2022                 || strncmp(iter->fStart, publicName, publicLen))) {
2023             iter->fPrivate = true;
2024             iter = std::next(iter);
2025             ++publicIndex;
2026         }
2027     }
2028     int keyIndex = publicIndex;
2029     KeyWord currentKey = KeyWord::kPublic;
2030     const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2031     size_t publicLen = strlen(publicName);
2032     const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
2033     size_t protectedLen = strlen(protectedName);
2034     const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
2035     size_t privateLen = strlen(privateName);
2036     auto childIter = includeDef->fChildren.begin();
2037     while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
2038         std::advance(childIter, 1);
2039     }
2040     while (childIter != includeDef->fChildren.end()) {
2041         Definition* child = *childIter;
2042         while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
2043             iter->fPrivate = KeyWord::kPublic != currentKey;
2044             const char* testStart = iter->fStart;
2045             size_t testLen = (size_t) (iter->fContentEnd - testStart);
2046             iter = std::next(iter);
2047             ++keyIndex;
2048             if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2049                 currentKey = KeyWord::kPublic;
2050                 break;
2051             }
2052             if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2053                 currentKey = KeyWord::kProtected;
2054                 break;
2055             }
2056             if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2057                 currentKey = KeyWord::kPrivate;
2058                 break;
2059             }
2060         }
2061         fLastObject = nullptr;
2062         if (KeyWord::kPublic == currentKey) {
2063             if (!this->parseObject(child, markupDef)) {
2064                 return false;
2065             }
2066         }
2067         fLastObject = child;
2068         childIter = std::next(childIter);
2069     }
2070     while (iter != includeDef->fTokens.end()) {
2071         iter->fPrivate = KeyWord::kPublic != currentKey;
2072         iter = std::next(iter);
2073     }
2074     SkASSERT(fParent->fParent);
2075     fParent = fParent->fParent;
2076     return true;
2077 }
2078 
isUndocumentable(string filename,const char * start,const char * end,int lineCount)2079 bool IncludeParser::isUndocumentable(string filename, const char* start, const char* end,
2080         int lineCount) {
2081     TextParser parser(filename, start, end, lineCount);
2082     const vector<string> skipWords = { "deprecated", "experimental", "private" };
2083     const vector<string> butNot = { "to be deprecated", "may be deprecated" };
2084     const vector<string> alsoNot = { "todo" };
2085     string match = parser.anyWord(skipWords, 0);
2086     if ("" != match) {
2087         if (parser.anyWord(alsoNot, 0).empty()
2088                 && ("deprecated" != match || parser.anyWord(butNot, 2).empty())) {
2089             return true;
2090         }
2091     }
2092     return false;
2093 }
2094 
parseComment(string filename,const char * start,const char * end,int lineCount,Definition * markupDef,bool * undocumentedPtr)2095 bool IncludeParser::parseComment(string filename, const char* start, const char* end,
2096         int lineCount, Definition* markupDef, bool* undocumentedPtr) {
2097     if (this->isUndocumentable(filename, start, end, lineCount)) {
2098         *undocumentedPtr = true;
2099     }
2100     // parse doxygen if present
2101     TextParser parser(filename, start, end, lineCount);
2102     if (parser.startsWith("**")) {
2103         parser.next();
2104         parser.next();
2105         parser.skipWhiteSpace();
2106         if ('\\' == parser.peek()) {
2107             parser.next();
2108             // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2109             if (parser.skipExact("file")) {
2110                 if (Definition::Type::kFileType != fParent->fType) {
2111                     return reportError<bool>("expected parent is file");
2112                 }
2113                 string filename = markupDef->fileName();
2114                 if (!parser.skipWord(filename.c_str())) {
2115                     return reportError<bool>("missing object type");
2116                 }
2117             } else if (parser.skipExact("fn")) {
2118                 SkASSERT(0);  // incomplete
2119             } else {
2120                 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2121                     return reportError<bool>("missing object type");
2122                 }
2123                 if (!parser.skipWord(markupDef->fName.c_str()) &&
2124                         KeyWord::kEnum != markupDef->fKeyWord) {
2125                     return reportError<bool>("missing object name");
2126                 }
2127             }
2128         }
2129     }
2130     // remove leading '*' if present
2131     Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2132     while (!parser.eof() && parser.skipWhiteSpace()) {
2133         while ('*' == parser.peek()) {
2134             parser.next();
2135             if (parser.eof()) {
2136                 break;
2137             }
2138             parser.skipWhiteSpace();
2139         }
2140         if (parser.eof()) {
2141             break;
2142         }
2143         const char* lineEnd = parser.trimmedLineEnd();
2144         markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
2145                 parser.fLineCount, parent, '\0');
2146         parser.skipToEndBracket('\n');
2147     }
2148     return true;
2149 }
2150 
2151 /*
2152     find comment either in front of or after the const def and then extract if the
2153     const is undocumented
2154  */
parseConst(Definition * child,Definition * markupDef)2155 bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
2156     if (!markupDef) {
2157         fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2158                 child->fLineCount, fParent, '\0');
2159         Definition* globalMarkupChild = &fGlobals.back();
2160         string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
2161         globalMarkupChild->fName = globalUniqueName;
2162         if (!this->findComments(*child, globalMarkupChild)) {
2163             return false;
2164         }
2165         if (!this->findCommentAfter(*child, globalMarkupChild)) {
2166             return false;
2167         }
2168         if (globalMarkupChild->fUndocumented) {
2169             child->fUndocumented = true;
2170         } else {
2171             fIConstMap[globalUniqueName] = globalMarkupChild;
2172         }
2173         return true;
2174     }
2175     markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2176         child->fLineCount, markupDef, '\0');
2177     Definition* markupChild = &markupDef->fTokens.back();
2178     markupChild->fName = child->fName;
2179     markupChild->fTerminator = markupChild->fContentEnd;
2180     IClassDefinition& classDef = fIClassMap[markupDef->fName];
2181     classDef.fConsts[child->fName] = markupChild;
2182     if (!this->findComments(*child, markupChild)) {
2183         return false;
2184     }
2185     if (!this->findCommentAfter(*child, markupChild)) {
2186         return false;
2187     }
2188     if (markupChild->fUndocumented) {
2189         child->fUndocumented = true;
2190     } else {
2191         fIConstMap[child->fName] = markupChild;
2192     }
2193     return true;
2194 }
2195 
parseDefine(Definition * child,Definition * markupDef)2196 bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2197     TextParser parser(child);
2198     if (!parser.skipExact("#define")) {
2199         return false;
2200     }
2201     if (!parser.skipSpace()) {
2202         return false;
2203     }
2204     const char* nameStart = parser.fChar;
2205     parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2206     if (parser.eof()) {
2207         return true;    // do nothing if #define doesn't define anything
2208     }
2209     string nameStr(nameStart, parser.fChar - nameStart);
2210     struct Param {
2211         const char* fStart;
2212         const char* fEnd;
2213     };
2214     vector<Param> params;
2215     if ('(' == parser.peek()) {
2216         parser.next();
2217         if (!parser.skipSpace()) {
2218             return false;
2219         }
2220         do {
2221             const char* paramStart = parser.fChar;
2222             if (!parser.skipExact("...")) {
2223                 parser.skipToNonAlphaNum();
2224             }
2225             if (parser.eof()) {
2226                 return false;
2227             }
2228             params.push_back({paramStart, parser.fChar});
2229             if (!parser.skipSpace()) {
2230                 return false;
2231             }
2232             if (')' == parser.peek()) {
2233                 parser.next();
2234                 break;
2235             }
2236             if (',' != parser.next()) {
2237                 return false;
2238             }
2239             if (!parser.skipSpace()) {
2240                 return false;
2241             }
2242         } while (true);
2243     }
2244     if (!parser.skipSpace()) {
2245         return false;
2246     }
2247     if (!markupDef) {
2248         fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2249                 child->fLineCount, fParent, '\0');
2250         Definition* globalMarkupChild = &fGlobals.back();
2251         string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2252         globalMarkupChild->fName = globalUniqueName;
2253         globalMarkupChild->fTerminator = child->fContentEnd;
2254         if (!this->findComments(*child, globalMarkupChild)) {
2255             return false;
2256         }
2257         if (!globalMarkupChild->fUndocumented) {
2258             fIDefineMap[globalUniqueName] = globalMarkupChild;
2259         }
2260         for (Param param : params) {
2261             globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2262                     child->fLineCount, globalMarkupChild, '\0');
2263             Definition* paramChild = &globalMarkupChild->fTokens.back();
2264             paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
2265             this->checkName(paramChild);
2266             paramChild->fTerminator = param.fEnd;
2267         }
2268         return true;
2269     }
2270     markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2271             child->fLineCount, markupDef, '\0');
2272     Definition* markupChild = &markupDef->fTokens.back();
2273     markupChild->fName = nameStr;
2274     markupChild->fTerminator = markupChild->fContentEnd;
2275     IClassDefinition& classDef = fIClassMap[markupDef->fName];
2276     if (!this->findComments(*child, markupChild)) {
2277         return false;
2278     }
2279     if (markupChild->fUndocumented) {
2280         child->fUndocumented = true;
2281     } else {
2282         classDef.fDefines[nameStr] = markupChild;
2283         fIDefineMap[nameStr] = markupChild;
2284     }
2285     return true;
2286 }
2287 
parseEnum(Definition * child,Definition * markupDef)2288 bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
2289 	if (!child->fTokens.size()) {
2290 		return true;	// if enum is a forward declaration, do nothing
2291 	}
2292     bool isEnumClass = false;
2293     Definition* parent = child;
2294     auto token = parent->fTokens.begin();
2295     if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2296         isEnumClass = true;
2297         parent = &*token;
2298         token = parent->fTokens.begin();
2299     }
2300     SkASSERT(Definition::Type::kWord == token->fType);
2301     string nameStr = string(token->fStart, token->fContentEnd - token->fStart);
2302     Definition* markupChild;
2303     if (!markupDef) {
2304         fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2305                 child->fLineCount, fParent, '\0');
2306         markupChild = &fGlobals.back();
2307         string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2308         markupChild->fName = globalUniqueName;
2309         markupChild->fTerminator = child->fContentEnd;
2310         if (!markupChild->fUndocumented) {
2311             fIEnumMap[globalUniqueName] = markupChild;
2312         }
2313     } else {
2314         markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2315             child->fLineCount, markupDef, '\0');
2316         markupChild = &markupDef->fTokens.back();
2317     }
2318     SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2319     markupChild->fKeyWord = KeyWord::kEnum;
2320     if (isEnumClass) {
2321         markupChild->fMarkType = MarkType::kEnumClass;
2322     }
2323     if (markupDef) {
2324         markupChild->fName = markupDef->fName + "::" + nameStr;
2325     }
2326     if (!this->findComments(*child, markupChild)) {
2327         return false;
2328     }
2329     if (markupChild->fUndocumented) {
2330         child->fUndocumented = true;
2331     }
2332     if (!this->parseEnumConst(token, parent->fTokens.end(), markupChild)) {
2333         return false;
2334     }
2335     for (auto outsideMember : child->fChildren) {
2336         if (Definition::Type::kBracket == outsideMember->fType) {
2337             continue;
2338         }
2339         SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2340         if (KeyWord::kClass == outsideMember->fKeyWord) {
2341             continue;
2342         }
2343         SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2344         markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
2345                 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
2346         Definition* member = &markupChild->fTokens.back();
2347         member->fName = outsideMember->fName;
2348         this->checkName(member);
2349         // FIXME: ? add comment as well ?
2350         markupChild->fChildren.push_back(member);
2351     }
2352     if (markupDef) {
2353         IClassDefinition& classDef = fIClassMap[markupDef->fName];
2354         SkASSERT(classDef.fStart);
2355         string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
2356         string fullName = markupChild->fName;
2357         markupChild->fName = uniqueName;
2358         classDef.fEnums[uniqueName] = markupChild;
2359         if (!markupChild->fUndocumented) {
2360             fIEnumMap[fullName] = markupChild;
2361         }
2362     }
2363     return true;
2364 }
2365 
parseOneEnumConst(list<Definition> & constList,Definition * markupChild,bool skipWord)2366 bool IncludeParser::parseOneEnumConst(list<Definition>& constList,
2367         Definition* markupChild, bool skipWord) {
2368     auto memberIter = constList.begin();
2369     const auto memberIterEnd = constList.end();
2370     if (skipWord) {
2371         SkASSERT(Definition::Type::kWord == memberIter->fType);
2372         memberIter = std::next(memberIter);
2373         SkASSERT(memberIterEnd != memberIter);
2374     }
2375     // token array has parse atoms; child array has comments
2376     bool undocumented = false;
2377     while (memberIterEnd != memberIter) {
2378         while (Bracket::kSlashStar == memberIter->fBracket) {
2379             if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2380                     memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2381                 return false;
2382             }
2383             memberIter = std::next(memberIter);
2384             if (memberIterEnd == memberIter) {
2385                 return false;
2386             }
2387         }
2388         if (Bracket::kPound == memberIter->fBracket) {
2389             KeyWord keyWord = memberIter->fKeyWord;
2390             bool sawIf = KeyWord::kIfdef == keyWord || KeyWord::kIf == keyWord
2391                     || KeyWord::kElif == keyWord;
2392             if (sawIf || KeyWord::kElse == keyWord) {
2393                 if (!parseOneEnumConst(memberIter->fTokens, markupChild, sawIf)) {
2394                     return false;
2395                 }
2396             } else {
2397                 SkASSERT(KeyWord::kEndif == keyWord || KeyWord::kError == keyWord);
2398             }
2399             memberIter = std::next(memberIter);
2400             if (memberIterEnd == memberIter) {
2401                 break;
2402             }
2403             continue;
2404         }
2405         while (Definition::Type::kWord != memberIter->fType) {
2406             memberIter = std::next(memberIter);
2407             if (memberIterEnd == memberIter) {
2408                 return false;
2409             }
2410         }
2411         auto memberStart = memberIter;
2412         Definition* memberEnd = nullptr;
2413         const char* last;
2414         do {
2415             last = memberIter->fContentEnd;
2416             memberIter = std::next(memberIter);
2417             if (memberIterEnd == memberIter) {
2418                 break;
2419             }
2420             memberEnd = &*memberIter;
2421         } while (string::npos == string(last, memberIter->fContentStart).find(','));
2422         if (!memberEnd) {
2423             return false;
2424         }
2425         if (memberIterEnd != memberIter && Bracket::kSlashSlash == memberIter->fBracket) {
2426             if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2427                     memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2428                 return false;
2429             }
2430             memberIter = std::next(memberIter);
2431         }
2432         markupChild->fTokens.emplace_back(MarkType::kMember, memberStart->fContentStart,
2433                 memberEnd->fContentEnd, memberStart->fLineCount, markupChild, '\0');
2434         Definition* markupMember = &markupChild->fTokens.back();
2435         string name = string(memberStart->fContentStart, memberStart->length());
2436         memberStart->fName = name;
2437         markupMember->fName = name;
2438         this->checkName(markupMember);
2439         memberStart->fUndocumented = markupMember->fUndocumented;
2440         memberStart->fMarkType = MarkType::kMember;
2441         undocumented = false;
2442     }
2443     return true;
2444 }
2445 
parseEnumConst(list<Definition>::iterator & tokenIter,const list<Definition>::iterator & tokenEnd,Definition * markupChild)2446 bool IncludeParser::parseEnumConst(list<Definition>::iterator& tokenIter,
2447         const list<Definition>::iterator& tokenEnd, Definition* markupChild) {
2448     SkASSERT(Definition::Type::kWord == tokenIter->fType);  // should be enum name
2449     tokenIter = std::next(tokenIter);
2450     SkASSERT(tokenEnd != tokenIter);
2451     if (Definition::Type::kKeyWord == tokenIter->fType) {
2452         SkASSERT((unsigned) tokenIter->fKeyWord < SK_ARRAY_COUNT(kKeyWords));
2453         SkASSERT(KeyProperty::kNumber == kKeyWords[(int) tokenIter->fKeyWord].fProperty);
2454         tokenIter = std::next(tokenIter);
2455         SkASSERT(tokenEnd != tokenIter);
2456     }
2457     SkASSERT(Punctuation::kLeftBrace == tokenIter->fPunctuation);
2458     tokenIter = std::next(tokenIter);
2459     SkASSERT(tokenEnd != tokenIter);
2460     SkASSERT(Bracket::kBrace == tokenIter->fBracket);
2461     return parseOneEnumConst(tokenIter->fTokens, markupChild, false);
2462 }
2463 
parseInclude(string name)2464 bool IncludeParser::parseInclude(string name) {
2465     fParent = &fIncludeMap[name];
2466     fParent->fName = name;
2467     this->checkName(fParent);
2468     fParent->fFileName = fFileName;
2469     fParent->fType = Definition::Type::kFileType;
2470     fParent->fContentStart = fChar;
2471     fParent->fContentEnd = fEnd;
2472     // parse include file into tree
2473     while (fChar < fEnd) {
2474         if (!this->parseChar()) {
2475             return false;
2476         }
2477     }
2478     // parse tree and add named objects to maps
2479     fParent = &fIncludeMap[name];
2480     if (!this->parseObjects(fParent, nullptr)) {
2481         return false;
2482     }
2483     return true;
2484 }
2485 
parseMember(Definition * child,Definition * markupDef)2486 bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2487     const char* typeStart = child->fChildren[0]->fContentStart;
2488     markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
2489         child->fLineCount, markupDef, '\0');
2490     Definition* markupChild = &markupDef->fTokens.back();
2491     TextParser nameParser(child);
2492     nameParser.skipToNonName();
2493     string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2494     IClassDefinition& classDef = fIClassMap[markupDef->fName];
2495     string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2496     markupChild->fName = uniqueName;
2497     this->checkName(markupChild);
2498     markupChild->fTerminator = markupChild->fContentEnd;
2499     if (!markupChild->fUndocumented) {
2500         classDef.fMembers[uniqueName] = markupChild;
2501     }
2502     if (child->fParentIndex >= 2) {
2503         auto comment = child->fParent->fTokens.begin();
2504         std::advance(comment, child->fParentIndex - 2);
2505         if (Definition::Type::kBracket == comment->fType
2506                 && (Bracket::kSlashStar == comment->fBracket
2507                 || Bracket::kSlashSlash == comment->fBracket)) {
2508             TextParser parser(&*comment);
2509             do {
2510                 parser.skipToAlpha();
2511                 if (parser.eof()) {
2512                     break;
2513                 }
2514                 const char* start = parser.fChar;
2515                 const char* end = parser.trimmedBracketEnd('\n');
2516                 if (Bracket::kSlashStar == comment->fBracket) {
2517                     const char* commentEnd = parser.strnstr("*/", end);
2518                     if (commentEnd) {
2519                         end = commentEnd;
2520                     }
2521                 }
2522                 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
2523                         markupDef, '\0');
2524                 Definition* commentChild = &markupDef->fTokens.back();
2525                 markupChild->fChildren.emplace_back(commentChild);
2526                 parser.skipTo(end);
2527             } while (!parser.eof());
2528         }
2529     }
2530     return true;
2531 }
2532 
parseMethod(Definition * child,Definition * markupDef)2533 bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2534     auto tokenIter = child->fParent->fTokens.begin();
2535     std::advance(tokenIter, child->fParentIndex);
2536     tokenIter = std::prev(tokenIter);
2537     const char* nameEnd = tokenIter->fContentEnd;
2538     bool addConst = false;
2539     auto operatorCheck = tokenIter;
2540     if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2541         operatorCheck = std::prev(tokenIter);
2542     }
2543     if (KeyWord::kOperator == operatorCheck->fKeyWord) {
2544         auto closeParen = std::next(tokenIter);
2545         SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2546                 '(' == closeParen->fContentStart[0]);
2547         nameEnd = closeParen->fContentEnd + 1;
2548         closeParen = std::next(closeParen);
2549         if (Definition::Type::kKeyWord == closeParen->fType &&
2550                 KeyWord::kConst == closeParen->fKeyWord) {
2551             addConst = true;
2552         }
2553         tokenIter = operatorCheck;
2554     }
2555     string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
2556     if (addConst) {
2557         nameStr += " const";
2558     }
2559     while (tokenIter != child->fParent->fTokens.begin()) {
2560         auto testIter = std::prev(tokenIter);
2561         switch (testIter->fType) {
2562             case Definition::Type::kWord:
2563                 if (testIter == child->fParent->fTokens.begin() &&
2564                         (KeyWord::kIfdef == child->fParent->fKeyWord ||
2565                         KeyWord::kIfndef == child->fParent->fKeyWord ||
2566                         KeyWord::kIf == child->fParent->fKeyWord)) {
2567                     std::next(tokenIter);
2568                     break;
2569                 }
2570                 goto keepGoing;
2571             case Definition::Type::kKeyWord: {
2572                 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2573                 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2574                     goto keepGoing;
2575                 }
2576             } break;
2577             case Definition::Type::kBracket:
2578                 if (Bracket::kAngle == testIter->fBracket) {
2579                     goto keepGoing;
2580                 }
2581                 break;
2582             case Definition::Type::kPunctuation:
2583                 if (Punctuation::kSemicolon == testIter->fPunctuation
2584                         || Punctuation::kLeftBrace == testIter->fPunctuation
2585                         || Punctuation::kColon == testIter->fPunctuation) {
2586                     break;
2587                 }
2588             keepGoing:
2589                 tokenIter = testIter;
2590                 continue;
2591             default:
2592                 break;
2593         }
2594         break;
2595     }
2596     tokenIter->fName = nameStr;     // simple token stream, OK if name is duplicate
2597     tokenIter->fMarkType = MarkType::kMethod;
2598     tokenIter->fPrivate = string::npos != nameStr.find("::")
2599             && KeyWord::kTemplate != child->fParent->fKeyWord;
2600     this->checkName(&*tokenIter);
2601     auto testIter = child->fParent->fTokens.begin();
2602     SkASSERT(child->fParentIndex > 0);
2603     std::advance(testIter, child->fParentIndex - 1);
2604     if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2605             0 == tokenIter->fParentIndex) {
2606         tokenIter = std::next(tokenIter);
2607     }
2608     const char* start = tokenIter->fContentStart;
2609     const char* end = tokenIter->fContentEnd;
2610     const char kDebugCodeStr[] = "SkDEBUGCODE";
2611     const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2612     if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2613         std::advance(testIter, 1);
2614         start = testIter->fContentStart + 1;
2615         end = testIter->fContentEnd - 1;
2616     } else {
2617         end = testIter->fContentEnd;
2618         do {
2619             std::advance(testIter, 1);
2620             if (testIter == child->fParent->fTokens.end()) {
2621                 break;
2622             }
2623             switch (testIter->fType) {
2624                 case Definition::Type::kPunctuation:
2625                     SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2626                             || Punctuation::kLeftBrace == testIter->fPunctuation
2627                             || Punctuation::kColon == testIter->fPunctuation);
2628                     end = testIter->fStart;
2629                     break;
2630                 case Definition::Type::kKeyWord: {
2631                     KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2632                     if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2633                         continue;
2634                     }
2635                     } break;
2636                 default:
2637                     continue;
2638             }
2639             break;
2640         } while (true);
2641     }
2642     while (end > start && ' ' >= end[-1]) {
2643         --end;
2644     }
2645     if (!markupDef) {
2646         auto parentIter = child->fParent->fTokens.begin();
2647         SkASSERT(child->fParentIndex > 0);
2648         std::advance(parentIter, child->fParentIndex - 1);
2649         Definition* methodName = &*parentIter;
2650         TextParser nameParser(methodName);
2651         if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
2652             return true;  // expect this is inline class definition outside of class
2653         }
2654         fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2655                 fParent, '\0');
2656         Definition* globalMarkupChild = &fGlobals.back();
2657         string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2658         globalMarkupChild->fName = globalUniqueName;
2659         if (!this->findComments(*child, globalMarkupChild)) {
2660             return false;
2661         }
2662         if (globalMarkupChild->fUndocumented) {
2663             child->fUndocumented = true;
2664         } else {
2665             fIFunctionMap[globalUniqueName] = globalMarkupChild;
2666         }
2667         return true;
2668     }
2669     markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2670             markupDef, '\0');
2671     Definition* markupChild = &markupDef->fTokens.back();
2672     {
2673         auto mapIter = fIClassMap.find(markupDef->fName);
2674         SkASSERT(fIClassMap.end() != mapIter);
2675         IClassDefinition& classDef = mapIter->second;
2676         SkASSERT(classDef.fStart);
2677         string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2678         markupChild->fName = uniqueName;
2679         if (!this->findComments(*child, markupChild)) {
2680             return false;
2681         }
2682         if (markupChild->fUndocumented) {
2683             tokenIter->fUndocumented = true;
2684         } else {
2685             classDef.fMethods[uniqueName] = markupChild;
2686         }
2687     }
2688     return true;
2689 }
2690 
parseObjects(Definition * parent,Definition * markupDef)2691 bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
2692     fPriorObject = nullptr;
2693     for (auto child : parent->fChildren) {
2694         if (!this->parseObject(child, markupDef)) {
2695             return false;
2696         }
2697         fPriorObject = child;
2698     }
2699     return true;
2700 }
2701 
parseObject(Definition * child,Definition * markupDef)2702 bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2703     // set up for error reporting
2704     fLine = fChar = child->fStart;
2705     fEnd = child->fContentEnd;
2706     // todo: put original line number in child as well
2707     switch (child->fType) {
2708         case Definition::Type::kKeyWord:
2709             switch (child->fKeyWord) {
2710                 case KeyWord::kClass:
2711                     if (!this->parseClass(child, IsStruct::kNo)) {
2712                         return false;
2713                     }
2714                     break;
2715                 case KeyWord::kStatic:
2716                 case KeyWord::kConst:
2717                 case KeyWord::kConstExpr:
2718                     if (!this->parseConst(child, markupDef)) {
2719                         return child->reportError<bool>("failed to parse const or constexpr");
2720                     }
2721                     break;
2722                 case KeyWord::kEnum:
2723                     if (!this->parseEnum(child, markupDef)) {
2724                         return child->reportError<bool>("failed to parse enum");
2725                     }
2726                     break;
2727                 case KeyWord::kStruct:
2728                     if (!this->parseClass(child, IsStruct::kYes)) {
2729                         return child->reportError<bool>("failed to parse struct");
2730                     }
2731                     break;
2732                 case KeyWord::kTemplate:
2733                     if (!this->parseTemplate(child, markupDef)) {
2734                         return child->reportError<bool>("failed to parse template");
2735                     }
2736                     break;
2737                 case KeyWord::kTypedef:
2738                     if (!this->parseTypedef(child, markupDef)) {
2739                         return child->reportError<bool>("failed to parse typedef");
2740                     }
2741                     break;
2742                 case KeyWord::kUnion:
2743                     if (!this->parseUnion()) {
2744                         return child->reportError<bool>("failed to parse union");
2745                     }
2746                     break;
2747                 case KeyWord::kUsing:
2748                     if (!this->parseUsing()) {
2749                         return child->reportError<bool>("failed to parse using");
2750                     }
2751                     break;
2752                 default:
2753                     return child->reportError<bool>("unhandled keyword");
2754             }
2755             break;
2756         case Definition::Type::kBracket:
2757             switch (child->fBracket) {
2758                 case Bracket::kParen:
2759                     {
2760                         auto tokenIter = child->fParent->fTokens.begin();
2761                         std::advance(tokenIter, child->fParentIndex);
2762                         tokenIter = std::prev(tokenIter);
2763                         TextParser previousToken(&*tokenIter);
2764                         if (this->isMember(*tokenIter)) {
2765                             break;
2766                         }
2767                         if (Bracket::kPound == child->fParent->fBracket &&
2768                                 KeyWord::kIf == child->fParent->fKeyWord) {
2769                             // TODO: this will skip methods named defined() -- for the
2770                             // moment there aren't any
2771                             if (previousToken.startsWith("defined")) {
2772                                 break;
2773                             }
2774                         }
2775                         if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2776                             break;
2777                         }
2778                     }
2779                     if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2780                         break;
2781                     }
2782                     if (!this->parseMethod(child, markupDef)) {
2783                         return child->reportError<bool>("failed to parse method");
2784                     }
2785                 break;
2786                 case Bracket::kSlashSlash:
2787                 case Bracket::kSlashStar:
2788                     // comments are picked up by parsing objects first
2789                     break;
2790                 case Bracket::kPound:
2791                     // special-case the #xxx xxx_DEFINED entries
2792                     switch (child->fKeyWord) {
2793                         case KeyWord::kIf:
2794                         case KeyWord::kIfndef:
2795                         case KeyWord::kIfdef:
2796                             if (child->boilerplateIfDef()) {
2797                                 if (!this->parseObjects(child, markupDef)) {
2798                                     return false;
2799                                 }
2800                                 break;
2801                             }
2802                             goto preproError;
2803                         case KeyWord::kDefine:
2804                             if (this->parseDefine(child, markupDef)) {
2805                                 break;
2806                             }
2807                             goto preproError;
2808                         case KeyWord::kEndif:
2809                             if (child->boilerplateEndIf()) {
2810                                 break;
2811                             }
2812                         case KeyWord::kError:
2813                         case KeyWord::kInclude:
2814                             // ignored for now
2815                             break;
2816                         case KeyWord::kElse:
2817                             if (!this->parseObjects(child, markupDef)) {
2818                                 return false;
2819                             }
2820                             break;
2821                         case KeyWord::kElif:
2822                             // todo: handle these
2823                             break;
2824                         default:
2825                         preproError:
2826                             return child->reportError<bool>("unhandled preprocessor");
2827                     }
2828                     break;
2829                 case Bracket::kAngle:
2830                     // pick up templated function pieces when method is found
2831                     break;
2832                 case Bracket::kDebugCode:
2833                     if (!this->parseObjects(child, markupDef)) {
2834                         return false;
2835                     }
2836                     break;
2837                 case Bracket::kSquare: {
2838                     // check to see if parent is operator, the only case we handle so far
2839                     auto prev = child->fParent->fTokens.begin();
2840                     std::advance(prev, child->fParentIndex - 1);
2841                     if (KeyWord::kOperator != prev->fKeyWord) {
2842                         return child->reportError<bool>("expected operator overload");
2843                     }
2844                     } break;
2845                 default:
2846                     return child->reportError<bool>("unhandled bracket");
2847             }
2848             break;
2849         case Definition::Type::kWord:
2850             if (MarkType::kMember != child->fMarkType) {
2851                 return child->reportError<bool>("unhandled word type");
2852             }
2853             if (!this->parseMember(child, markupDef)) {
2854                 return child->reportError<bool>("unparsable member");
2855             }
2856             break;
2857         default:
2858             return child->reportError<bool>("unhandled type");
2859             break;
2860     }
2861     return true;
2862 }
2863 
parseTemplate(Definition * child,Definition * markupDef)2864 bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2865     return this->parseObjects(child, markupDef);
2866 }
2867 
parseTypedef(Definition * child,Definition * markupDef)2868 bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2869     TextParser typedefParser(child);
2870     typedefParser.skipExact("typedef");
2871     typedefParser.skipWhiteSpace();
2872     string nameStr = typedefParser.typedefName();
2873     if (!markupDef) {
2874         fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2875                 child->fLineCount, fParent, '\0');
2876         Definition* globalMarkupChild = &fGlobals.back();
2877         string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2878         globalMarkupChild->fName = globalUniqueName;
2879         if (!this->findComments(*child, globalMarkupChild)) {
2880             return false;
2881         }
2882         if (globalMarkupChild->fUndocumented) {
2883             child->fUndocumented = true;
2884         } else {
2885             fITypedefMap[globalUniqueName] = globalMarkupChild;
2886         }
2887         child->fName = nameStr;
2888         return true;
2889     }
2890     markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2891         child->fLineCount, markupDef, '\0');
2892     Definition* markupChild = &markupDef->fTokens.back();
2893     markupChild->fName = nameStr;
2894     this->checkName(markupChild);
2895     markupChild->fTerminator = markupChild->fContentEnd;
2896     IClassDefinition& classDef = fIClassMap[markupDef->fName];
2897     classDef.fTypedefs[nameStr] = markupChild;
2898     child->fName = markupDef->fName + "::" + nameStr;
2899     this->checkName(child);
2900     fITypedefMap[child->fName] = markupChild;
2901     return true;
2902 }
2903 
parseUnion()2904 bool IncludeParser::parseUnion() {
2905     // incomplete
2906     return true;
2907 }
2908 
parseUsing()2909 bool IncludeParser::parseUsing() {
2910     // incomplete
2911     return true;
2912 }
2913 
parseChar()2914 bool IncludeParser::parseChar() {
2915     char test = *fChar;
2916     if ('\\' == fPrev) {
2917         if ('\n' == test) {
2918 //            ++fLineCount;
2919             fLine = fChar + 1;
2920         }
2921         goto done;
2922     }
2923     switch (test) {
2924         case '\n':
2925 //            ++fLineCount;
2926             fLine = fChar + 1;
2927             if (fInChar) {
2928                 return reportError<bool>("malformed char");
2929             }
2930             if (fInString) {
2931                 return reportError<bool>("malformed string");
2932             }
2933             if (!this->checkForWord()) {
2934                 return false;
2935             }
2936             if (Bracket::kPound == this->topBracket()) {
2937                 KeyWord keyWord = fParent->fKeyWord;
2938                 if (KeyWord::kNone == keyWord) {
2939                     return this->reportError<bool>("unhandled preprocessor directive");
2940                 }
2941                 if (fInDefine) {
2942                     SkASSERT(KeyWord::kDefine == keyWord);
2943                     fInDefine = false;
2944                 }
2945                 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
2946                     this->popBracket();
2947                 }
2948                 if (fInBrace) {
2949                     SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2950                     fInBrace = nullptr;
2951                 }
2952             } else if (Bracket::kSlashSlash == this->topBracket()) {
2953                 this->popBracket();
2954             }
2955             break;
2956         case '*':
2957             if (!fInCharCommentString && '/' == fPrev) {
2958                 this->pushBracket(Bracket::kSlashStar);
2959             }
2960             if (!this->checkForWord()) {
2961                 return false;
2962             }
2963             if (!fInCharCommentString) {
2964                 this->addPunctuation(Punctuation::kAsterisk);
2965             }
2966             break;
2967         case '/':
2968             if ('*' == fPrev) {
2969                 if (!fInCharCommentString) {
2970                     return reportError<bool>("malformed closing comment");
2971                 }
2972                 if (Bracket::kSlashStar == this->topBracket()) {
2973                     TextParserSave save(this);
2974                     this->next();  // include close in bracket
2975                     this->popBracket();
2976                     save.restore(); // put things back so nothing is skipped
2977                 }
2978                 break;
2979             }
2980             if (!fInCharCommentString && '/' == fPrev) {
2981                 this->pushBracket(Bracket::kSlashSlash);
2982                 break;
2983             }
2984             if (!this->checkForWord()) {
2985                 return false;
2986             }
2987             break;
2988         case '\'':
2989             if (Bracket::kChar == this->topBracket()) {
2990                 this->popBracket();
2991             } else if (!fInComment && !fInString) {
2992                 if (fIncludeWord) {
2993                     return this->reportError<bool>("word then single-quote");
2994                 }
2995                 this->pushBracket(Bracket::kChar);
2996             }
2997             break;
2998         case '\"':
2999             if (Bracket::kString == this->topBracket()) {
3000                 this->popBracket();
3001             } else if (!fInComment && !fInChar) {
3002                 if (fIncludeWord) {
3003                     return this->reportError<bool>("word then double-quote");
3004                 }
3005                 this->pushBracket(Bracket::kString);
3006             }
3007             break;
3008         case '(':
3009             if (fIncludeWord && fChar - fIncludeWord >= 10 &&
3010                     !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
3011                 this->pushBracket(Bracket::kDebugCode);
3012                 break;
3013             }
3014         case ':':
3015         case '[':
3016         case '{': {
3017             if (fInCharCommentString) {
3018                 break;
3019             }
3020             if (fInDefine && fInBrace) {
3021                 break;
3022             }
3023             if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
3024                 break;
3025             }
3026             if (fConstExpr) {
3027                 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3028                 fConstExpr = nullptr;
3029             }
3030             if (!fInBrace) {
3031                 if (!this->checkForWord()) {
3032                     return false;
3033                 }
3034                 if (':' == test && !fInFunction) {
3035                     break;
3036                 }
3037                 if ('{' == test) {
3038                     this->addPunctuation(Punctuation::kLeftBrace);
3039                 } else if (':' == test) {
3040                     this->addPunctuation(Punctuation::kColon);
3041                 }
3042             }
3043             if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
3044                     && Bracket::kColon == fInBrace->fBracket) {
3045                 Definition* braceParent = fParent->fParent;
3046                 braceParent->fChildren.pop_back();
3047                 braceParent->fTokens.pop_back();
3048                 fParent = braceParent;
3049                 fInBrace = nullptr;
3050             }
3051             this->pushBracket(
3052                     '(' == test ? Bracket::kParen :
3053                     '[' == test ? Bracket::kSquare :
3054                     '{' == test ? Bracket::kBrace :
3055                                   Bracket::kColon);
3056             if (!fInBrace
3057                     && ('{' == test || (':' == test && ' ' >= fChar[1]))
3058                     && fInFunction) {
3059                 fInBrace = fParent;
3060             }
3061             } break;
3062         case '<':
3063             if (fInCharCommentString || fInBrace) {
3064                 break;
3065             }
3066             if (!this->checkForWord()) {
3067                 return false;
3068             }
3069             if (fInEnum) {
3070                 break;
3071             }
3072             this->pushBracket(Bracket::kAngle);
3073             // this angle bracket may be an operator or may be a bracket
3074             // wait for balancing close angle, if any, to decide
3075             break;
3076         case ')':
3077         case ']':
3078         case '}': {
3079             if (fInCharCommentString) {
3080                 break;
3081             }
3082             if (fInDefine && fInBrace) {
3083                 break;
3084             }
3085             if (!fInBrace) {
3086                 if (!this->checkForWord()) {
3087                     return false;
3088                 }
3089             }
3090             bool popBraceParent = fInBrace == fParent;
3091             Bracket match = ')' == test ? Bracket::kParen :
3092                     ']' == test ? Bracket::kSquare : Bracket::kBrace;
3093             if (match == this->topBracket()) {
3094                 this->popBracket();
3095                 if (!fInFunction) {
3096                     fInFunction = ')' == test && !this->inAlignAs();
3097                 } else {
3098                     fInFunction = '}' != test;
3099                 }
3100             } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
3101                 this->popBracket();
3102             } else if (Bracket::kAngle == this->topBracket()
3103                     && match == this->grandParentBracket()) {
3104                 this->popBracket();
3105                 this->popBracket();
3106             } else {
3107                 return reportError<bool>("malformed close bracket");
3108             }
3109             if (popBraceParent) {
3110                 Definition* braceParent = fInBrace->fParent;
3111                 braceParent->fChildren.pop_back();
3112                 braceParent->fTokens.pop_back();
3113                 fInBrace = nullptr;
3114             }
3115             } break;
3116         case '>':
3117             if (fInCharCommentString || fInBrace) {
3118                 break;
3119             }
3120             if (!this->checkForWord()) {
3121                 return false;
3122             }
3123             if (fInEnum) {
3124                 break;
3125             }
3126             if (Bracket::kPound == this->topBracket()) {
3127                 break;
3128             }
3129             if (Bracket::kAngle == this->topBracket()) {
3130                 // looks like angle pair are braces, not operators
3131                 this->popBracket();
3132             } else {
3133                 return reportError<bool>("malformed close angle bracket");
3134             }
3135             break;
3136         case '#': {
3137             if (fInCharCommentString || fInBrace) {
3138                 break;
3139             }
3140             SkASSERT(!fIncludeWord);  // don't expect this, curious if it is triggered
3141             this->pushBracket(Bracket::kPound);
3142             break;
3143         }
3144         case ' ':
3145             if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3146                 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3147                 fInBrace = fParent;
3148                 // delimiting brackets are space ... unescaped-linefeed
3149             }
3150         case '&':
3151         case ',':
3152         case '+':
3153         case '-':
3154         case '!':
3155             if (fInCharCommentString || fInBrace) {
3156                 break;
3157             }
3158             if (!this->checkForWord()) {
3159                 return false;
3160             }
3161             break;
3162         case '=':
3163             if (fInCharCommentString || fInBrace) {
3164                 break;
3165             }
3166             if (!this->checkForWord()) {
3167                 return false;
3168             }
3169             if (!fParent->fTokens.size()) {
3170                 break;
3171             }
3172             {
3173                 const Definition& lastToken = fParent->fTokens.back();
3174                 if (lastToken.fType != Definition::Type::kWord) {
3175                     break;
3176                 }
3177                 string name(lastToken.fContentStart, lastToken.length());
3178                 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3179                     break;
3180                 }
3181                 // find token on start of line
3182                 auto lineIter = fParent->fTokens.end();
3183                 do {
3184                     if (fParent->fTokens.begin() == lineIter) {
3185                         break;
3186                     }
3187                     --lineIter;
3188                 } while (lineIter->fContentStart > fLine);
3189                 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
3190                     ++lineIter;
3191                 }
3192                 Definition* lineStart = &*lineIter;
3193                 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3194                 bool sawConst = false;
3195                 bool sawStatic = false;
3196                 bool sawTemplate = false;
3197                 bool sawType = false;
3198                 while (&lastToken != &*lineIter) {
3199                     if (KeyWord::kTemplate == lineIter->fKeyWord) {
3200                         if (sawConst || sawStatic || sawTemplate) {
3201                             sawConst = false;
3202                             break;
3203                         }
3204                         if (&lastToken == &*++lineIter) {
3205                             break;
3206                         }
3207                         if (KeyWord::kTypename != lineIter->fKeyWord) {
3208                             break;
3209                         }
3210                         if (&lastToken == &*++lineIter) {
3211                             break;
3212                         }
3213                         if (Definition::Type::kWord != lineIter->fType) {
3214                             break;
3215                         }
3216                         sawTemplate = true;
3217                     } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3218                         if (sawConst || sawStatic) {
3219                             sawConst = false;
3220                             break;
3221                         }
3222                         sawStatic = true;
3223                     } else if (KeyWord::kConst == lineIter->fKeyWord
3224                             || KeyWord::kConstExpr == lineIter->fKeyWord) {
3225                         if (sawConst) {
3226                             sawConst = false;
3227                             break;
3228                         }
3229                         sawConst = true;
3230                     } else {
3231                         if (sawType) {
3232                             sawType = false;
3233                             break;
3234                         }
3235                         if (Definition::Type::kKeyWord == lineIter->fType
3236                                 && KeyProperty::kNumber
3237                                 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3238                             sawType = true;
3239                         } else if (Definition::Type::kWord == lineIter->fType) {
3240                             string typeName(lineIter->fContentStart, lineIter->length());
3241                             if ("Sk" != name.substr(0, 2)) {
3242                                 sawType = true;
3243                             }
3244                         }
3245                     }
3246                     ++lineIter;
3247                 }
3248                 if (sawType && sawConst) {
3249                     // if found, name first
3250                     lineStart->fName = name;
3251                     lineStart->fMarkType = MarkType::kConst;
3252                     this->checkName(lineStart);
3253                     fParent->fChildren.emplace_back(lineStart);
3254                     fConstExpr = lineStart;
3255                 }
3256             }
3257             break;
3258         case ';':
3259             if (fInCharCommentString || fInBrace) {
3260                 break;
3261             }
3262             if (!this->checkForWord()) {
3263                 return false;
3264             }
3265             if (fConstExpr) {
3266                 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3267                 fConstExpr = nullptr;
3268             }
3269             if (Definition::Type::kKeyWord == fParent->fType
3270                     && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
3271                 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3272                 if (parentIsClass && fParent->fParent &&
3273                         KeyWord::kEnum == fParent->fParent->fKeyWord) {
3274                     this->popObject();
3275                 }
3276                 if (KeyWord::kEnum == fParent->fKeyWord) {
3277                     fInEnum = false;
3278                 }
3279                 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
3280                 this->popObject();
3281                 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3282                     this->popObject();
3283                 }
3284                 fPriorEnum = nullptr;
3285             } else if (Definition::Type::kBracket == fParent->fType
3286                     && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3287                     && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3288                 list<Definition>::iterator baseIter = fParent->fTokens.end();
3289                 list<Definition>::iterator namedIter  = fParent->fTokens.end();
3290                 for (auto tokenIter = fParent->fTokens.end();
3291                         fParent->fTokens.begin() != tokenIter; ) {
3292                     --tokenIter;
3293                     if (tokenIter->fLineCount == fLineCount) {
3294                         if (this->isMember(*tokenIter)) {
3295                             if (namedIter != fParent->fTokens.end()) {
3296                                 return reportError<bool>("found two named member tokens");
3297                             }
3298                             namedIter = tokenIter;
3299                         }
3300                         baseIter = tokenIter;
3301                     } else {
3302                         break;
3303                     }
3304                 }
3305                 // FIXME: if a member definition spans multiple lines, this won't work
3306                 if (namedIter != fParent->fTokens.end()) {
3307                     if (baseIter == namedIter) {
3308                         return this->reportError<bool>("expected type before named token");
3309                     }
3310                     Definition* member = &*namedIter;
3311                     member->fMarkType = MarkType::kMember;
3312                     if (!member->fTerminator) {
3313                         member->fTerminator = member->fContentEnd;
3314                     }
3315                     fParent->fChildren.push_back(member);
3316                     for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3317                         member->fChildren.push_back(&*nameType);
3318                     }
3319                 }
3320                 fPriorEnum = nullptr;
3321             } else if (fParent->fChildren.size() > 0) {
3322                 auto lastIter = fParent->fChildren.end();
3323                 Definition* priorEnum = fPriorEnum;
3324                 fPriorEnum = nullptr;
3325                 if (!priorEnum) {
3326                     while (fParent->fChildren.begin() != lastIter) {
3327                         std::advance(lastIter, -1);
3328                         priorEnum = *lastIter;
3329                         if (Definition::Type::kBracket != priorEnum->fType ||
3330                                 (Bracket::kSlashSlash != priorEnum->fBracket
3331                                 && Bracket::kSlashStar != priorEnum->fBracket)) {
3332                             break;
3333                         }
3334                     }
3335                     fPriorIndex = priorEnum->fParentIndex;
3336                 }
3337                 if (Definition::Type::kKeyWord == priorEnum->fType
3338                         && KeyWord::kEnum == priorEnum->fKeyWord) {
3339                     auto tokenWalker = fParent->fTokens.begin();
3340                     std::advance(tokenWalker, fPriorIndex);
3341                     while (tokenWalker != fParent->fTokens.end()) {
3342                         std::advance(tokenWalker, 1);
3343                         ++fPriorIndex;
3344                         if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3345                             break;
3346                         }
3347                     }
3348                     while (tokenWalker != fParent->fTokens.end()) {
3349                         std::advance(tokenWalker, 1);
3350                         const Definition* test = &*tokenWalker;
3351                         if (Definition::Type::kBracket != test->fType ||
3352                                 (Bracket::kSlashSlash != test->fBracket
3353                                 && Bracket::kSlashStar != test->fBracket)) {
3354                             break;
3355                         }
3356                     }
3357                     auto saveTokenWalker = tokenWalker;
3358                     Definition* start = &*tokenWalker;
3359                     bool foundExpected = true;
3360                     for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3361                         const Definition* test = &*tokenWalker;
3362                         if (expected != test->fKeyWord) {
3363                             foundExpected = false;
3364                             break;
3365                         }
3366                         if (tokenWalker == fParent->fTokens.end()) {
3367                             break;
3368                         }
3369                         std::advance(tokenWalker, 1);
3370                     }
3371                     if (!foundExpected) {
3372                         foundExpected = true;
3373                         tokenWalker = saveTokenWalker;
3374                         for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3375                             const Definition* test = &*tokenWalker;
3376                             if (expected != test->fKeyWord) {
3377                                 foundExpected = false;
3378                                 break;
3379                             }
3380                             if (tokenWalker == fParent->fTokens.end()) {
3381                                 break;
3382                             }
3383                             if (KeyWord::kNone != expected) {
3384                                 std::advance(tokenWalker, 1);
3385                             }
3386                         }
3387                         if (foundExpected) {
3388                             auto nameToken = priorEnum->fTokens.begin();
3389                             string enumName = string(nameToken->fContentStart,
3390                                     nameToken->fContentEnd - nameToken->fContentStart);
3391                             const Definition* test = &*tokenWalker;
3392                             string constType = string(test->fContentStart,
3393                                     test->fContentEnd - test->fContentStart);
3394                             if (enumName != constType) {
3395                                 foundExpected = false;
3396                             } else {
3397                                 std::advance(tokenWalker, 1);
3398                             }
3399                         }
3400                     }
3401                     if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3402                         const char* nameStart = tokenWalker->fStart;
3403                         std::advance(tokenWalker, 1);
3404                         if (tokenWalker != fParent->fTokens.end()) {
3405                             TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
3406                             tp.skipToNonName();
3407                             start->fName = string(nameStart, tp.fChar - nameStart);
3408                             this->checkName(start);
3409                             start->fContentEnd = fChar;
3410                             priorEnum->fChildren.emplace_back(start);
3411                             fPriorEnum = priorEnum;
3412                         }
3413                     }
3414                 }
3415             }
3416             this->addPunctuation(Punctuation::kSemicolon);
3417             fInFunction = false;
3418             break;
3419         case '~':
3420             if (fInEnum) {
3421                 break;
3422             }
3423         case '0': case '1': case '2': case '3': case '4':
3424         case '5': case '6': case '7': case '8': case '9':
3425             // TODO: don't want to parse numbers, but do need to track for enum defs
3426         //    break;
3427         case 'A': case 'B': case 'C': case 'D': case 'E':
3428         case 'F': case 'G': case 'H': case 'I': case 'J':
3429         case 'K': case 'L': case 'M': case 'N': case 'O':
3430         case 'P': case 'Q': case 'R': case 'S': case 'T':
3431         case 'U': case 'V': case 'W': case 'X': case 'Y':
3432         case 'Z': case '_':
3433         case 'a': case 'b': case 'c': case 'd': case 'e':
3434         case 'f': case 'g': case 'h': case 'i': case 'j':
3435         case 'k': case 'l': case 'm': case 'n': case 'o':
3436         case 'p': case 'q': case 'r': case 's': case 't':
3437         case 'u': case 'v': case 'w': case 'x': case 'y':
3438         case 'z':
3439             if (fInCharCommentString || fInBrace) {
3440                 break;
3441             }
3442             if (!fIncludeWord) {
3443                 fIncludeWord = fChar;
3444             }
3445             break;
3446     }
3447 done:
3448     fPrev = test;
3449     this->next();
3450     return true;
3451 }
3452 
validate() const3453 void IncludeParser::validate() const {
3454     IncludeParser::ValidateKeyWords();
3455 }
3456 
references(const SkString & file) const3457 bool IncludeParser::references(const SkString& file) const {
3458     // if includes weren't passed one at a time, assume all references are valid
3459     if (fIncludeMap.empty()) {
3460         return true;
3461     }
3462     SkASSERT(file.endsWith(".bmh") );
3463     string root(file.c_str(), file.size() - 4);
3464     string kReference("_Reference");
3465     if (string::npos != root.find(kReference)) {
3466         root = root.substr(0, root.length() - kReference.length());
3467     }
3468     if (fIClassMap.end() != fIClassMap.find(root)) {
3469         return true;
3470     }
3471     if (fIStructMap.end() != fIStructMap.find(root)) {
3472         return true;
3473     }
3474     if (fIEnumMap.end() != fIEnumMap.find(root)) {
3475         return true;
3476     }
3477     if (fITypedefMap.end() != fITypedefMap.find(root)) {
3478         return true;
3479     }
3480     if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3481         return true;
3482     }
3483     return false;
3484 }
3485 
RemoveFile(const char * docs,const char * includes)3486 void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3487     if (!sk_isdir(includes)) {
3488         IncludeParser::RemoveOneFile(docs, includes);
3489     } else {
3490         SkOSFile::Iter it(includes, ".h");
3491         for (SkString file; it.next(&file); ) {
3492             SkString p = SkOSPath::Join(includes, file.c_str());
3493             const char* hunk = p.c_str();
3494             if (!SkStrEndsWith(hunk, ".h")) {
3495                 continue;
3496             }
3497             IncludeParser::RemoveOneFile(docs, hunk);
3498         }
3499     }
3500 }
3501 
RemoveOneFile(const char * docs,const char * includesFile)3502 void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3503     const char* lastForward = strrchr(includesFile, '/');
3504     const char* lastBackward = strrchr(includesFile, '\\');
3505     const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3506     if (!last) {
3507         last = includesFile;
3508     } else {
3509         last += 1;
3510     }
3511     SkString baseName(last);
3512     SkASSERT(baseName.endsWith(".h"));
3513     baseName.remove(baseName.size() - 2, 2);
3514     baseName.append("_Reference.bmh");
3515     SkString fullName = docs ? SkOSPath::Join(docs, baseName.c_str()) : baseName;
3516     remove(fullName.c_str());
3517 }
3518 
3519 static const char kMethodMissingStr[] =
3520     "If the method requires documentation, add to "
3521     "%s at minimum:\n"  // path to bmh file
3522     "\n"
3523     "#Method %s\n" // method declaration less implementation details
3524     "#In  SomeSubtopicName\n"
3525     "#Line # add a one line description here ##\n"
3526     "#Populate\n"
3527     "#NoExample\n"
3528     "// or better yet, use #Example and put C++ code here\n"
3529     "##\n"
3530     "#SeeAlso optional related symbols\n"
3531     "#Method ##\n"
3532     "\n"
3533     "Add to %s, at minimum:\n"  // path to include
3534     "\n"
3535     "/** (description) Starts with present tense action verb\n"
3536     "    and end with a period.\n"
3537     "%s"   // @param, @return if needed go here
3538     "*/\n"
3539     "%s ...\n" // method declaration
3540     "\n"
3541     "If the method does not require documentation,\n"
3542     "add \"private\" or \"experimental\", as in:\n"
3543     "\n"
3544     "/** Experimental, do not use. And so on...\n"
3545     "*/\n"
3546     "%s ...\n" // method declaration
3547     "\n"
3548     ;
3549 
3550 // bDef does not have #Populate
3551 static const char kMethodDiffersNoPopStr[] =
3552     "In %s:\n"              // path to bmh file
3553     "#Method %s\n"          // method declaration less implementation details
3554     "does not match doxygen comment of:\n"
3555     "%s.\n"                 // method declaration
3556     "\n"
3557     ;
3558 
3559 static const char kMethodDiffersStr[] =
3560     "In %s:\n"                        // path to include
3561     "%s\n"                            // method declaration
3562     "does not match doxygen comment.\n"
3563     "\n"
3564     ;
3565 
suggestFix(Suggest suggest,const Definition & iDef,const RootDefinition * root,const Definition * bDef)3566 void IncludeParser::suggestFix(Suggest suggest, const Definition& iDef,
3567         const RootDefinition* root, const Definition* bDef) {
3568     string methodNameStr(iDef.fContentStart, iDef.length());
3569     const char* methodName = methodNameStr.c_str();
3570     TextParser lessImplParser(&iDef);
3571     if (lessImplParser.skipExact("static")) {
3572         lessImplParser.skipWhiteSpace();
3573     }
3574     // TODO : handle debug wrapper
3575     /* bool inDebugWrapper = */ Definition::SkipImplementationWords(lessImplParser);
3576     string lessImplStr(lessImplParser.fChar, lessImplParser.fEnd - lessImplParser.fChar);
3577     const char* methodNameLessImpl = lessImplStr.c_str();
3578     // return result, if any is substr from 0 to location of iDef.fName
3579     size_t namePos = methodNameStr.find(iDef.fName);
3580     SkASSERT(string::npos != namePos);
3581     size_t funcEnd = namePos;
3582     while (funcEnd > 0 && ' ' >= methodNameStr[funcEnd - 1]) {
3583         funcEnd -= 1;
3584     }
3585     string funcRet = methodNameStr.substr(0, funcEnd);
3586 // parameters, if any, are delimited by () and separate by ,
3587     TextParser parser(&iDef);
3588     parser.fChar += namePos + iDef.fName.length();
3589     const char* start = parser.fChar;
3590     vector<string> paramStrs;
3591     if ('(' == start[0]) {
3592         parser.skipToBalancedEndBracket('(', ')');
3593         TextParser params(&iDef);
3594         params.fChar = start + 1;
3595         params.fEnd = parser.fChar;
3596         while (!params.eof()) {
3597             const char* paramEnd = params.anyOf("=,)");
3598             const char* paramStart = paramEnd;
3599             while (paramStart > params.fChar && ' ' >= paramStart[-1]) {
3600                 paramStart -= 1;
3601             }
3602             while (paramStart > params.fChar && (isalnum(paramStart[-1])
3603                     || '_' == paramStart[-1])) {
3604                 paramStart -= 1;
3605             }
3606             string param(paramStart, paramEnd - paramStart);
3607             paramStrs.push_back(param);
3608             params.fChar = params.anyOf(",)") + 1;
3609         }
3610     }
3611     string bmhFile = root ? root->fFileName : bDef ? bDef->fFileName : "a *.bmh file";
3612     bool hasFuncReturn = "" != funcRet && "void" != funcRet;
3613     switch(suggest) {
3614         case Suggest::kMethodMissing: {
3615             // if include @param, @return are missing, request them as well
3616             string paramDox;
3617             bool firstParam = true;
3618             for (auto paramStr : paramStrs) {
3619                 if (firstParam) {
3620                     paramDox += "\n";
3621                     firstParam = false;
3622                 }
3623                 paramDox += "    @param " + paramStr + "  descriptive phrase\n";
3624             }
3625             if (hasFuncReturn) {
3626                 paramDox += "\n";
3627                 paramDox += "    @return descriptive phrase\n";
3628             }
3629             SkDebugf(kMethodMissingStr, bmhFile.c_str(), methodNameLessImpl, iDef.fFileName.c_str(),
3630                     paramDox.c_str(), methodName, methodName);
3631             } break;
3632         case Suggest::kMethodDiffers: {
3633             bool hasPop = std::any_of(bDef->fChildren.begin(), bDef->fChildren.end(),
3634                     [](Definition* def) { return MarkType::kPopulate == def->fMarkType; });
3635             if (!hasPop) {
3636                 SkDebugf(kMethodDiffersNoPopStr, bmhFile.c_str(), methodNameLessImpl, methodName);
3637             }
3638             SkDebugf(kMethodDiffersStr, iDef.fFileName.c_str(), methodName);
3639             } break;
3640         default:
3641             SkASSERT(0);
3642     }
3643 }
3644 
topBracket() const3645 Bracket IncludeParser::topBracket() const {
3646     Definition* parent = this->parentBracket(fParent);
3647     return parent ? parent->fBracket : Bracket::kNone;
3648 }
3649