blob: c86a18719fdd192709fa03977d700d943b9d0465 [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Cary Clark2dc84ad2018-01-26 12:56:22 -05008#include "SkOSFile.h"
9#include "SkOSPath.h"
Cary Clark8032b982017-07-28 11:04:54 -040010
Cary Clark2da9fb82018-11-01 09:29:36 -040011#include "bmhParser.h"
12#include "includeParser.h"
13
Cary Clark8032b982017-07-28 11:04:54 -040014const IncludeKey kKeyWords[] = {
15 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040016 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040017 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clarkd2ca79c2018-08-10 13:09:13 -040018 { "alignas", KeyWord::kAlignAs, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040019 { "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 },
Cary Clark2dc84ad2018-01-26 12:56:22 -050030 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
Cary Clark8032b982017-07-28 11:04:54 -040031 { "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 },
Cary Clark0d225392018-06-07 09:59:07 -040049 { "typename", KeyWord::kTypename, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040050 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040051 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040052 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
53 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark4dc5a452018-05-21 11:56:57 -040054 { "uintptr_t", KeyWord::kUintPtr_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040055 { "union", KeyWord::kUnion, KeyProperty::kObject },
56 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
Cary Clark61313f32018-10-08 14:57:48 -040057 { "using", KeyWord::kUsing, KeyProperty::kObject },
Cary Clark8032b982017-07-28 11:04:54 -040058 { "void", KeyWord::kVoid, KeyProperty::kNumber },
59};
60
61const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
62
63KeyWord 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;
Cary Clark61313f32018-10-08 14:57:48 -040068 if (ch > 0 && (index == kKeyWordCount ||
69 kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1])) {
Cary Clark8032b982017-07-28 11:04:54 -040070 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) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040079 if (end - start < (int) strlen(kKeyWords[index].fName)) {
80 return KeyWord::kNone;
81 }
Cary Clark8032b982017-07-28 11:04:54 -040082 return kKeyWords[index].fKeyWord;
83 }
84 }
85 return KeyWord::kNone;
86}
87
88void 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
96void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050097 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040098 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
Cary Clark61313f32018-10-08 14:57:48 -0400108static 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
125static 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
149static 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
170static 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
187static 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
205static 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
224static 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
248static const char kInline[] = "inline ";
249static const char kSK_API[] = "SK_API ";
250static const char kSK_WARN_UNUSED_RESULT[] = "SK_WARN_UNUSED_RESULT ";
251
252bool IncludeParser::advanceInclude(TextParser& i) {
253 i.skipWhiteSpace(&fCheck.fIndent, &fCheck.fWriteReturn);
254 if (fCheck.fPrivateBrace) {
255 if (i.startsWith("};")) {
256 if (fCheck.fPrivateBrace == fCheck.fBraceCount) {
257 fCheck.fPrivateBrace = 0;
258 fCheck.fDoubleReturn = true;
259 } else {
260 i.skipExact("};");
261 fCheck.fBraceCount -= 1;
262 }
263 return false;
264 }
265 if (i.startsWith("public:")) {
266 if (fCheck.fBraceCount <= fCheck.fPrivateBrace) {
267 fCheck.fPrivateBrace = 0;
268 if (fCheck.fPrivateProtected) {
269 i.skipExact("public:");
270 }
271 } else {
272 i.skipExact("public:");
273 }
274 } else {
275 fCheck.fBraceCount += i.skipToLineBalance('{', '}');
276 }
277 return false;
278 } else if (i.startsWith("};")) {
279 fCheck.fDoubleReturn = 2;
280 }
281 if (i.skipExact(kInline)) {
282 fCheck.fSkipInline = true;
283 return false;
284 }
285 if (i.skipExact(kSK_API)) {
286 fCheck.fSkipAPI = true;
287 return false;
288 }
289 if (i.skipExact(kSK_WARN_UNUSED_RESULT)) {
290 fCheck.fSkipWarnUnused = true;
291 return false;
292 }
293 if (i.skipExact("SK_ATTR_DEPRECATED")) {
294 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
295 return false;
296 }
297 if (i.skipExact("SkDEBUGCODE")) {
298 i.skipWhiteSpace();
299 if ('(' != i.peek()) {
300 i.reportError("expected open paren");
301 }
302 TextParserSave save(&i);
303 SkAssertResult(i.skipToBalancedEndBracket('(', ')'));
304 fCheck.fInDebugCode = i.fChar - 1;
305 save.restore();
306 SkAssertResult('(' == i.next());
307 }
308 if ('{' == i.peek()) {
309 if (looks_like_method(i)) {
310 fCheck.fState = CheckCode::State::kMethod;
311 if (!i.skipToBalancedEndBracket('{', '}')) {
312 i.reportError("unbalanced open brace");
313 }
314 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
315 return false;
316 } else if (looks_like_class_decl(i)) {
317 fCheck.fState = CheckCode::State::kClassDeclaration;
318 fCheck.fPrivateBrace = fCheck.fBraceCount + 1;
319 fCheck.fPrivateProtected = false;
320 }
321 }
322 if (':' == i.peek() && looks_like_constructor(i)) {
323 fCheck.fState = CheckCode::State::kConstructor;
324 skip_constructor_initializers(i);
325 return false;
326 }
327 if ('#' == i.peek()) {
328 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
329 return false;
330 }
331 if (i.startsWith("//")) {
332 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
333 return false;
334 }
335 if (i.startsWith("/*")) {
336 i.skipToEndBracket("*/");
337 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
338 return false;
339 }
340 if (looks_like_forward_declaration(i)) {
341 fCheck.fState = CheckCode::State::kForwardDeclaration;
342 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
343 return false;
344 }
345 if (i.skipExact("private:") || i.skipExact("protected:")) {
346 if (!fCheck.fBraceCount) {
347 i.reportError("expect private in brace");
348 }
349 fCheck.fPrivateBrace = fCheck.fBraceCount;
350 fCheck.fPrivateProtected = true;
351 return false;
352 }
353 const char* funcEnd = i.anyOf("(\n");
354 if (funcEnd && '(' == funcEnd[0] && '_' == *i.anyOf("_(")
355 && (i.contains("internal_", funcEnd, nullptr)
356 || i.contains("private_", funcEnd, nullptr)
357 || i.contains("legacy_", funcEnd, nullptr)
358 || i.contains("temporary_", funcEnd, nullptr))) {
359 i.skipTo(funcEnd);
360 if (!i.skipToBalancedEndBracket('(', ')')) {
361 i.reportError("unbalanced open parent");
362 }
363 i.skipSpace();
364 i.skipExact("const ");
365 i.skipSpace();
366 if (';' == i.peek()) {
367 i.next();
368 }
369 fCheck.fState = CheckCode::State::kNone;
370 return false;
371 }
372 return true;
373}
374
375void IncludeParser::codeBlockAppend(string& result, char ch) const {
376 if (Elided::kYes == fElided && fCheck.fBraceCount) {
377 return;
378 }
379 this->stringAppend(result, ch);
380}
381
382void IncludeParser::codeBlockSpaces(string& result, int indent) const {
383 if (!indent) {
384 return;
385 }
386 if (Elided::kYes == fElided && fCheck.fBraceCount) {
387 return;
388 }
389 SkASSERT(indent > 0);
390 if (fDebugWriteCodeBlock) {
391 SkDebugf("%*c", indent, ' ');
392 }
393 result.append(indent, ' ');
394}
395
396string IncludeParser::writeCodeBlock(const Definition& iDef, MarkType markType) {
397 TextParser i(&iDef);
398 fElided = Elided::kNo;
399 const char* before = iDef.fContentStart;
400 while (' ' == *--before)
401 ;
402 int startIndent = iDef.fContentStart - before - 1;
403 return writeCodeBlock(i, markType, startIndent);
404}
405
406string IncludeParser::writeCodeBlock(TextParser& i, MarkType markType, int startIndent) {
407 string result;
408 char last;
409 int lastIndent = 0;
410 bool lastDoubleMeUp = false;
411 fCheck.reset();
Cary Clarka90ea222018-10-16 10:30:28 -0400412 if (MarkType::kDefine == markType) {
413 result = "#define ";
414 } else {
415 this->codeBlockSpaces(result, startIndent);
416 }
Cary Clark61313f32018-10-08 14:57:48 -0400417 do {
418 if (!this->advanceInclude(i)) {
419 continue;
420 }
421 do {
422 last = i.peek();
423 SkASSERT(' ' < last);
424 if (fCheck.fInDebugCode == i.fChar) {
425 fCheck.fInDebugCode = nullptr;
426 i.next(); // skip close paren
427 break;
428 }
429 if (CheckCode::State::kMethod == fCheck.fState) {
430 this->codeBlockAppend(result, ';');
431 fCheck.fState = CheckCode::State::kNone;
432 }
433 if (fCheck.fWriteReturn) {
434 this->codeBlockAppend(result, '\n');
435 bool doubleMeUp = i.startsWith("typedef ") || looks_like_const(i)
436 || (!strncmp("struct ", i.fStart, 7) && looks_like_member(i));
437 if ((!--fCheck.fDoubleReturn && !i.startsWith("};")) || i.startsWith("enum ")
438 || i.startsWith("typedef ") || doubleMeUp || fCheck.fTypedefReturn
439 || (fCheck.fIndent && (i.startsWith("class ") || i.startsWith("struct ")))) {
440 if (lastIndent > 0 && (!doubleMeUp || !lastDoubleMeUp)) {
441 this->codeBlockAppend(result, '\n');
442 }
443 fCheck.fTypedefReturn = false;
444 lastDoubleMeUp = doubleMeUp;
445 }
446 if (doubleMeUp) {
447 fCheck.fTypedefReturn = true;
448 }
449 lastIndent = fCheck.fIndent;
450 }
451 if (fCheck.fIndent) {
452 size_t indent = fCheck.fIndent;
453 if (fCheck.fSkipInline && indent > sizeof(kInline)) {
454 indent -= sizeof(kInline) - 1;
455 }
456 if (fCheck.fSkipAPI && indent > sizeof(kSK_API)) {
457 indent -= sizeof(kSK_API) - 1;
458 }
459 if (fCheck.fSkipWarnUnused && indent > sizeof(kSK_WARN_UNUSED_RESULT)) {
460 indent -= sizeof(kSK_WARN_UNUSED_RESULT) - 1;
461 }
462
463 this->codeBlockSpaces(result, indent);
464 }
465 this->codeBlockAppend(result, last);
466 fCheck.fWriteReturn = false;
467 fCheck.fIndent = 0;
468 fCheck.fBraceCount += '{' == last;
469 fCheck.fBraceCount -= '}' == last;
470 if (';' == last) {
471 fCheck.fSkipInline = false;
472 fCheck.fSkipAPI = false;
473 fCheck.fSkipWarnUnused = false;
474 }
475 if (fCheck.fBraceCount < 0) {
476 i.reportError("unbalanced close brace");
477 return result;
478 }
479 i.next();
480 } while (!i.eof() && ' ' < i.peek() && !i.startsWith("//"));
481 } while (!i.eof());
482 if (CheckCode::State::kMethod == fCheck.fState) {
483 this->codeBlockAppend(result, ';');
484 }
485 bool elidedTemplate = Elided::kYes == fElided && !strncmp(i.fStart, "template ", 9);
486 bool elidedTClass = elidedTemplate && MarkType::kClass == markType;
487 if (fCheck.fWriteReturn || elidedTClass) {
488 this->codeBlockAppend(result, '\n');
489 }
490 if ((MarkType::kFunction != markType && lastIndent > startIndent) || elidedTClass) {
491 this->codeBlockAppend(result, '}');
492 }
493 this->codeBlockAppend(result, ';');
494 if (MarkType::kFunction != markType || elidedTemplate) {
495 this->codeBlockAppend(result, '\n');
496 }
497 return result;
498}
499
Ben Wagner63fd7602017-10-09 15:45:33 -0400500void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400501 const vector<string>& foundParams) {
502 for (auto& methodParam : methodParams) {
503 bool found = false;
504 for (auto& foundParam : foundParams) {
505 if (methodParam == foundParam) {
506 found = true;
507 break;
508 }
509 }
510 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400511 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400512 }
513 }
514 for (auto& foundParam : foundParams) {
515 bool found = false;
516 for (auto& methodParam : methodParams) {
517 if (methodParam == foundParam) {
518 found = true;
519 break;
520 }
521 }
522 if (!found) {
523 this->reportError("doxygen param does not match method declaration");
524 }
525 }
526}
527
528bool IncludeParser::checkForWord() {
529 if (!fIncludeWord) {
530 return true;
531 }
532 KeyWord keyWord = FindKey(fIncludeWord, fChar);
Cary Clark224c7002018-06-27 11:00:21 -0400533 if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
534 Bracket bracket = this->topBracket();
535 if (Bracket::kParen == bracket) {
536 return true;
537 }
538 }
Cary Clark8032b982017-07-28 11:04:54 -0400539 if (KeyWord::kNone != keyWord) {
540 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
541 this->addKeyword(keyWord);
542 return true;
543 }
544 } else {
545 this->addWord();
546 return true;
547 }
548 Definition* poundDef = fParent;
549 if (!fParent) {
550 return reportError<bool>("expected parent");
551 }
552 if (Definition::Type::kBracket != poundDef->fType) {
553 return reportError<bool>("expected bracket");
554 }
555 if (Bracket::kPound != poundDef->fBracket) {
556 return reportError<bool>("expected preprocessor");
557 }
558 if (KeyWord::kNone != poundDef->fKeyWord) {
559 return reportError<bool>("already found keyword");
560 }
561 poundDef->fKeyWord = keyWord;
562 fIncludeWord = nullptr;
563 switch (keyWord) {
564 // these do not link to other # directives
565 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400566 if (!fInBrace) {
567 SkASSERT(!fInDefine);
568 fInDefine = true;
569 }
Cary Clark8032b982017-07-28 11:04:54 -0400570 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500571 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400572 break;
573 // these start a # directive link
574 case KeyWord::kIf:
575 case KeyWord::kIfdef:
576 case KeyWord::kIfndef:
577 break;
578 // these continue a # directive link
579 case KeyWord::kElif:
Cary Clarkabaffd82018-11-15 08:25:12 -0500580 case KeyWord::kElse:
Cary Clark8032b982017-07-28 11:04:54 -0400581 this->popObject(); // pop elif
582 if (Bracket::kPound != fParent->fBracket) {
583 return this->reportError<bool>("expected preprocessor directive");
584 }
585 this->popBracket(); // pop if
586 poundDef->fParent = fParent;
Cary Clarkabaffd82018-11-15 08:25:12 -0500587 fParent = poundDef; // push elif back
588 break;
Cary Clark8032b982017-07-28 11:04:54 -0400589 // this ends a # directive link
590 case KeyWord::kEndif:
591 // FIXME : should this be calling popBracket() instead?
592 this->popObject(); // pop endif
593 if (Bracket::kPound != fParent->fBracket) {
594 return this->reportError<bool>("expected preprocessor directive");
595 }
596 this->popBracket(); // pop if/else
597 break;
598 default:
599 SkASSERT(0);
600 }
601 return true;
602}
603
604string IncludeParser::className() const {
605 string name(fParent->fName);
606 size_t slash = name.find_last_of("/");
607 if (string::npos == slash) {
608 slash = name.find_last_of("\\");
609 }
610 SkASSERT(string::npos != slash);
611 string result = name.substr(slash);
612 result = result.substr(1, result.size() - 3);
613 return result;
614}
615
Cary Clarka90ea222018-10-16 10:30:28 -0400616void IncludeParser::writeCodeBlock() {
Cary Clark61313f32018-10-08 14:57:48 -0400617 for (auto& classMapper : fIClassMap) {
Cary Clarka90ea222018-10-16 10:30:28 -0400618 classMapper.second.fCode = this->writeCodeBlock(classMapper.second, MarkType::kClass);
Cary Clark61313f32018-10-08 14:57:48 -0400619 }
620 for (auto& enumMapper : fIEnumMap) {
Cary Clarka90ea222018-10-16 10:30:28 -0400621 enumMapper.second->fCode = this->writeCodeBlock(*enumMapper.second,
622 enumMapper.second->fMarkType);
623 }
624 for (auto& typedefMapper : fITypedefMap) {
625 typedefMapper.second->fCode = this->writeCodeBlock(*typedefMapper.second,
626 typedefMapper.second->fMarkType);
627 }
628 for (auto& defineMapper : fIDefineMap) {
629 defineMapper.second->fCode = this->writeCodeBlock(*defineMapper.second,
630 defineMapper.second->fMarkType);
Cary Clark61313f32018-10-08 14:57:48 -0400631 }
632}
633
Cary Clark884dd7d2017-10-11 10:37:52 -0400634#include <sstream>
635#include <iostream>
636
Cary Clarkfd32e722018-11-16 14:36:02 -0500637void IncludeParser::checkTokens(list<Definition>& tokens, string key, string className,
638 RootDefinition* root, BmhParser& bmhParser) {
639 for (const auto& token : tokens) {
640 if (token.fPrivate) {
641 continue;
642 }
643 string fullName = key + "::" + token.fName;
644 const Definition* def = nullptr;
645 if (root) {
646 def = root->find(fullName, RootDefinition::AllowParens::kYes);
647 }
648 switch (token.fMarkType) {
649 case MarkType::kMethod: {
650 if (this->isInternalName(token)) {
651 continue;
652 }
653 if (!root) {
654 if (token.fUndocumented) {
655 break;
656 }
657 auto methIter = bmhParser.fMethodMap.find(token.fName);
658 if (bmhParser.fMethodMap.end() != methIter) {
659 def = &methIter->second;
660 if (def->crossCheck2(token)) {
661 def->fVisited = true;
662 } else {
663 this->suggestFix(Suggest::kMethodDiffers, token, root, def);
664 fFailed = true;
665 }
666 } else {
667 this->suggestFix(Suggest::kMethodMissing, token, root, nullptr);
668 fFailed = true;
669 }
670 break;
671 }
672 if (!def) {
673 string paramName = className + "::";
674 paramName += string(token.fContentStart,
675 token.fContentEnd - token.fContentStart);
676 if (string::npos != paramName.find('\n')) {
677 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
678 paramName.end());
679 }
680 def = root->find(paramName, RootDefinition::AllowParens::kYes);
681 if (!def && 0 == token.fName.find("operator")) {
682 string operatorName = className + "::";
683 TextParser oper("", token.fStart, token.fContentEnd, 0);
684 const char* start = oper.strnstr("operator", token.fContentEnd);
685 SkASSERT(start);
686 oper.skipTo(start);
687 oper.skipToEndBracket('(');
688 int parens = 0;
689 do {
690 if ('(' == oper.peek()) {
691 ++parens;
692 } else if (')' == oper.peek()) {
693 --parens;
694 }
695 } while (!oper.eof() && oper.next() && parens > 0);
696 operatorName += string(start, oper.fChar - start);
697 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
698 }
699 }
700 if (!def) {
701 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
702 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
703 const char* tokenEnd = token.methodEnd();
704 string constructorName = className + "::";
705 constructorName += string(token.fContentStart + skip,
706 tokenEnd - token.fContentStart - skip);
707 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
708 }
709 if (!def && 0 == token.fName.find("SK_")) {
710 string incName = token.fName + "()";
711 string macroName = className + "::" + incName;
712 def = root->find(macroName, RootDefinition::AllowParens::kYes);
713 if (def) {
714 if (def->fName == incName) {
715 def->fVisited = true;
716 if ("SK_TO_STRING_NONVIRT" == token.fName) {
717 def = root->find(className + "::toString",
718 RootDefinition::AllowParens::kYes);
719 if (def) {
720 def->fVisited = true;
721 } else {
722 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
723 fFailed = true;
724 }
725 }
726 break;
727 } else {
728 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
729 fFailed = true;
730 }
731 }
732 }
733 if (!def) {
734 bool allLower = true;
735 for (size_t index = 0; index < token.fName.length(); ++index) {
736 if (!islower(token.fName[index])) {
737 allLower = false;
738 break;
739 }
740 }
741 if (allLower) {
742 string lowerName = className + "::" + token.fName + "()";
743 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
744 }
745 }
746 if (!def) {
747 if (0 == token.fName.find("SkDEBUGCODE")) {
748 break;
749 }
750 }
751 if (!def) {
752 // simple method names inside nested classes have a bug and are missing trailing parens
753 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
754 def = root->find(withParens, RootDefinition::AllowParens::kNo);
755 }
756 if (!def) {
757 if (!token.fUndocumented) {
758 this->suggestFix(Suggest::kMethodMissing, token, root, nullptr);
759 fFailed = true;
760 }
761 break;
762 }
763 if (token.fUndocumented) {
764 // we can't report an error yet; if bmh documents this unnecessarily,
765 // we'll detect that later. It may be that def points to similar
766 // documented function.
767 break;
768 }
769 if (def->crossCheck2(token)) {
770 def->fVisited = true;
771 } else {
772 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
773 fFailed = true;
774 }
775 } break;
776 case MarkType::kComment:
777 break;
778 case MarkType::kEnumClass:
779 case MarkType::kEnum: {
780 if (!def) {
781 // work backwards from first word to deduce #Enum name
782 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
783 SkAssertResult(firstMember.skipName("enum"));
784 SkAssertResult(firstMember.skipToEndBracket('{'));
785 firstMember.next();
786 firstMember.skipWhiteSpace();
787 SkASSERT('k' == firstMember.peek());
788 const char* savePos = firstMember.fChar;
789 firstMember.skipToNonName();
790 const char* wordEnd = firstMember.fChar;
791 firstMember.fChar = savePos;
792 const char* lastUnderscore = nullptr;
793 do {
794 if (!firstMember.skipToEndBracket('_')) {
795 break;
796 }
797 if (firstMember.fChar > wordEnd) {
798 break;
799 }
800 lastUnderscore = firstMember.fChar;
801 } while (firstMember.next());
802 if (lastUnderscore) {
803 ++lastUnderscore;
804 string enumName(lastUnderscore, wordEnd - lastUnderscore);
805 if (root) {
806 string anonName = className + "::" + enumName + 's';
807 def = root->find(anonName, RootDefinition::AllowParens::kYes);
808 } else {
809 auto enumIter = bmhParser.fEnumMap.find(enumName);
810 if (bmhParser.fEnumMap.end() != enumIter) {
811 RootDefinition* rootDef = &enumIter->second;
812 def = rootDef;
813 }
814 }
815 }
816 if (!def && !root) {
817 auto enumIter = bmhParser.fEnumMap.find(token.fName);
818 if (bmhParser.fEnumMap.end() != enumIter) {
819 def = &enumIter->second;
820 }
821 if (!def) {
822 auto enumClassIter = bmhParser.fClassMap.find(token.fName);
823 if (bmhParser.fClassMap.end() != enumClassIter) {
824 def = &enumClassIter->second;
825 }
826 }
827 }
828 if (!def) {
829 if (!token.fUndocumented) {
830 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
831 fFailed = true;
832 }
833 break;
834 }
835 }
836 def->fVisited = true;
837 bool hasCode = false;
838 bool hasPopulate = true;
839 for (auto& child : def->fChildren) {
840 if (MarkType::kCode == child->fMarkType) {
841 hasPopulate = std::any_of(child->fChildren.begin(),
842 child->fChildren.end(), [](auto grandChild){
843 return MarkType::kPopulate == grandChild->fMarkType; });
844 if (!hasPopulate) {
845 def = child;
846 }
847 hasCode = true;
848 break;
849 }
850 }
851 if (!hasCode && !root) {
852 const Definition* topic = def->topicParent();
853 hasCode = std::any_of(topic->fChildren.begin(), topic->fChildren.end(),
854 [](Definition* def){ return MarkType::kCode == def->fMarkType
855 && def->fChildren.size() > 0 && MarkType::kPopulate ==
856 def->fChildren.front()->fMarkType; });
857 }
858 if (!hasCode) {
859 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
860 fFailed = true;
861 break;
862 }
863 if (!hasPopulate) {
864 if (def->crossCheck(token)) {
865 def->fVisited = true;
866 } else {
867 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
868 fFailed = true;
869 }
870 }
871 for (auto& member : token.fTokens) {
872 if (MarkType::kMember != member.fMarkType) {
873 continue;
874 }
875 string constName = MarkType::kEnumClass == token.fMarkType ?
876 fullName : className;
877 if (root) {
878 constName += "::" + member.fName;
879 def = root->find(constName, RootDefinition::AllowParens::kYes);
880 } else {
881 auto enumMapper = bmhParser.fEnumMap.find(token.fName);
882 if (bmhParser.fEnumMap.end() != enumMapper) {
883 auto& enumDoc = enumMapper->second;
884 auto memberIter = enumDoc.fLeaves.find(member.fName);
885 if (enumDoc.fLeaves.end() != memberIter) {
886 def = &memberIter->second;
887 }
888 }
889 }
890 if (!def) {
891 string innerName = key + "::" + member.fName;
892 def = root->find(innerName, RootDefinition::AllowParens::kYes);
893 }
894 if (!def) {
895 if (!member.fUndocumented) {
896 SkDebugf("const missing from bmh: %s\n", constName.c_str());
897 fFailed = true;
898 }
899 } else {
900 def->fVisited = true;
901 }
902 }
903 } break;
904 case MarkType::kMember:
905 if (def) {
906 def->fVisited = true;
907 } else {
908 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
909 fFailed = true;
910 }
911 break;
912 case MarkType::kTypedef:
913 if (!def && !root) {
914 auto typedefIter = bmhParser.fTypedefMap.find(token.fName);
915 if (bmhParser.fTypedefMap.end() != typedefIter) {
916 def = &typedefIter->second;
917 }
918 }
919 if (def) {
920 def->fVisited = true;
921 } else {
922 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
923 fFailed = true;
924 }
925 break;
926 case MarkType::kConst:
927 if (!def && !root) {
928 auto constIter = bmhParser.fConstMap.find(token.fName);
929 if (bmhParser.fConstMap.end() != constIter) {
930 def = &constIter->second;
931 }
932 }
933 if (def) {
934 def->fVisited = true;
935 } else {
936 if (!token.fUndocumented) {
937 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
938 fFailed = true;
939 }
940 }
941 break;
942 case MarkType::kDefine:
943 // TODO: incomplete
944 break;
945 default:
946 SkASSERT(0); // unhandled
947 break;
948 }
949 }
950}
951
Cary Clark8032b982017-07-28 11:04:54 -0400952bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400953 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400954 string className = classMapper.first;
955 auto finder = bmhParser.fClassMap.find(className);
956 if (bmhParser.fClassMap.end() == finder) {
957 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400958 continue;
959 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400960 }
961 for (auto& classMapper : fIClassMap) {
Cary Clarkabaffd82018-11-15 08:25:12 -0500962 if (classMapper.second.fUndocumented) {
963 continue;
964 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400965 string className = classMapper.first;
966 std::istringstream iss(className);
967 string classStr;
968 string classBase;
969 RootDefinition* root = nullptr;
970 while (std::getline(iss, classStr, ':')) {
971 if (root) {
972 if (!classStr.length()) {
973 continue;
974 }
975 classBase += "::" + classStr;
976 auto finder = root->fBranches.find(classBase);
977 if (root->fBranches.end() != finder) {
978 root = finder->second;
979 } else {
980 SkASSERT(0);
981 }
982 } else {
983 classBase = classStr;
984 auto finder = bmhParser.fClassMap.find(classBase);
985 if (bmhParser.fClassMap.end() != finder) {
986 root = &finder->second;
987 } else {
988 SkASSERT(0);
989 }
990 }
991 }
Cary Clarkfd32e722018-11-16 14:36:02 -0500992 this->checkTokens(classMapper.second.fTokens, classMapper.first, className, root,
993 bmhParser);
Cary Clark8032b982017-07-28 11:04:54 -0400994 }
Cary Clarkfd32e722018-11-16 14:36:02 -0500995 this->checkTokens(fGlobals, "", "", nullptr, bmhParser);
Cary Clark7cfcbca2018-01-04 16:11:51 -0500996 int crossChecks = 0;
997 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400998 for (auto& classMapper : fIClassMap) {
999 string className = classMapper.first;
1000 auto finder = bmhParser.fClassMap.find(className);
1001 if (bmhParser.fClassMap.end() == finder) {
1002 continue;
1003 }
1004 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -05001005 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -05001006 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -04001007 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001008 if (crossChecks) {
1009 SkDebugf(".");
1010 } else {
1011 SkDebugf("cross-check");
1012 firstCheck = className;
1013 }
1014 ++crossChecks;
1015 }
1016 if (crossChecks) {
1017 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -05001018 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -05001019 }
1020 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -04001021 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001022 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -05001023 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -04001024}
1025
1026IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001027 string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001028 string className;
1029 const Definition* test = fParent;
1030 while (Definition::Type::kFileType != test->fType) {
1031 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
1032 className = test->fName + "::";
1033 break;
1034 }
1035 test = test->fParent;
1036 }
Ben Wagner63fd7602017-10-09 15:45:33 -04001037 className += name;
Cary Clark8032b982017-07-28 11:04:54 -04001038 unordered_map<string, IClassDefinition>& map = fIClassMap;
1039 IClassDefinition& markupDef = map[className];
1040 if (markupDef.fStart) {
1041 typedef IClassDefinition* IClassDefPtr;
1042 return INHERITED::reportError<IClassDefPtr>("class already defined");
1043 }
1044 markupDef.fFileName = fFileName;
1045 markupDef.fStart = includeDef.fStart;
1046 markupDef.fContentStart = includeDef.fStart;
1047 markupDef.fName = className;
1048 markupDef.fContentEnd = includeDef.fContentEnd;
1049 markupDef.fTerminator = includeDef.fTerminator;
1050 markupDef.fParent = fParent;
1051 markupDef.fLineCount = fLineCount;
1052 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
1053 MarkType::kStruct : MarkType::kClass;
1054 markupDef.fKeyWord = includeDef.fKeyWord;
1055 markupDef.fType = Definition::Type::kMark;
Cary Clarkabaffd82018-11-15 08:25:12 -05001056 auto tokenIter = includeDef.fParent->fTokens.begin();
1057 SkASSERT(includeDef.fParentIndex > 0);
1058 std::advance(tokenIter, includeDef.fParentIndex - 1);
1059 const Definition* priorComment = &*tokenIter;
1060 markupDef.fUndocumented = priorComment->fUndocumented;
Cary Clark8032b982017-07-28 11:04:54 -04001061 fParent = &markupDef;
1062 return &markupDef;
1063}
1064
1065void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
1066 auto& tokens = classDef.fTokens;
1067 for (auto& token : tokens) {
1068 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
1069 continue;
1070 }
1071 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001072 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -04001073 }
1074 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -04001075 case MarkType::kConst:
1076 this->dumpConst(token, classDef.fName);
1077 break;
Cary Clark8032b982017-07-28 11:04:54 -04001078 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -05001079 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -05001080 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -04001081 break;
1082 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001083 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -04001084 break;
1085 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -04001086 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -04001087 continue;
1088 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001089 case MarkType::kTypedef:
1090 this->dumpTypedef(token, classDef.fName);
1091 break;
Cary Clark8032b982017-07-28 11:04:54 -04001092 default:
1093 SkASSERT(0);
1094 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001095 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -04001096 }
1097}
Cary Clark9174bda2017-09-19 17:39:32 -04001098void IncludeParser::dumpComment(const Definition& token) {
1099 fLineCount = token.fLineCount;
1100 fChar = fLine = token.fContentStart;
1101 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001102 bool sawParam = false;
1103 bool multiline = false;
1104 bool sawReturn = false;
1105 bool sawComment = false;
1106 bool methodHasReturn = false;
1107 vector<string> methodParams;
1108 vector<string> foundParams;
1109 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -04001110 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
1111 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001112 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -04001113 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001114 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -04001115 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -04001116 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -05001117 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -04001118 && !methodParser.strnchr('~', methodParser.fEnd);
1119 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
1120 const char* nextEnd = paren;
1121 do {
1122 string paramName;
1123 methodParser.fChar = nextEnd + 1;
1124 methodParser.skipSpace();
1125 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
1126 continue;
1127 }
1128 methodParams.push_back(paramName);
1129 } while (')' != nextEnd[0]);
1130 }
Cary Clark9174bda2017-09-19 17:39:32 -04001131 for (const auto& child : token.fTokens) {
1132 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
1133 break;
1134 }
Cary Clark8032b982017-07-28 11:04:54 -04001135 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001136 if (child.fPrivate) {
1137 break;
1138 }
Cary Clark8032b982017-07-28 11:04:54 -04001139 if ('@' == child.fContentStart[0]) {
1140 TextParser parser(&child);
1141 do {
1142 parser.next();
1143 if (parser.startsWith("param ")) {
1144 parser.skipWord("param");
1145 const char* parmStart = parser.fChar;
1146 parser.skipToSpace();
1147 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
1148 parser.skipWhiteSpace();
1149 do {
1150 size_t nextComma = parmName.find(',');
1151 string piece;
1152 if (string::npos == nextComma) {
1153 piece = parmName;
1154 parmName = "";
1155 } else {
1156 piece = parmName.substr(0, nextComma);
1157 parmName = parmName.substr(nextComma + 1);
1158 }
1159 if (sawParam) {
1160 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001161 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001162 }
Cary Clark9174bda2017-09-19 17:39:32 -04001163 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001164 } else {
1165 if (sawComment) {
1166 this->nl();
1167 }
1168 this->lf(2);
1169 }
1170 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -04001171 this->writeTag("Param", piece);
1172 this->writeSpace(2);
1173 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1174 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001175 sawParam = true;
1176 sawComment = false;
1177 } while (parmName.length());
1178 parser.skipTo(parser.fEnd);
1179 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
1180 parser.skipWord("return");
1181 if ('s' == parser.peek()) {
1182 parser.next();
1183 }
1184 if (sawParam) {
1185 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001186 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001187 }
Cary Clark9174bda2017-09-19 17:39:32 -04001188 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001189 }
1190 this->checkForMissingParams(methodParams, foundParams);
1191 sawParam = false;
1192 sawComment = false;
1193 multiline = false;
1194 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001195 this->writeTag("Return");
1196 this->writeSpace(2);
1197 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1198 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001199 sawReturn = true;
1200 parser.skipTo(parser.fEnd);
1201 } else {
1202 this->reportError("unexpected doxygen directive");
1203 }
1204 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -04001205 } else if (child.length() > 1) {
1206 const char* start = child.fContentStart;
1207 ptrdiff_t length = child.fContentEnd - start;
1208 SkASSERT(length >= 0);
1209 while (length && '/' == start[0]) {
1210 start += 1;
1211 --length;
Cary Clark8032b982017-07-28 11:04:54 -04001212 }
Cary Clark9174bda2017-09-19 17:39:32 -04001213 while (length && '/' == start[length - 1]) {
1214 length -= 1;
1215 if (length && '*' == start[length - 1]) {
1216 length -= 1;
1217 }
1218 }
1219 if (length) {
1220 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
1221 if (sawParam || sawReturn) {
1222 this->indentToColumn(8);
1223 }
1224 this->writeBlock(length, start);
1225 this->writeSpace();
1226 sawComment = true;
1227 if (sawParam || sawReturn) {
1228 multiline = true;
1229 }
Cary Clark8032b982017-07-28 11:04:54 -04001230 }
1231 }
1232 }
1233 }
1234 if (sawParam || sawReturn) {
1235 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001236 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001237 }
Cary Clark9174bda2017-09-19 17:39:32 -04001238 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001239 }
1240 if (!sawReturn) {
1241 if (!sawParam) {
1242 if (sawComment) {
1243 this->nl();
1244 }
1245 this->lf(2);
1246 }
1247 this->checkForMissingParams(methodParams, foundParams);
1248 }
1249 if (methodHasReturn != sawReturn) {
1250 if (!methodHasReturn) {
1251 this->reportError("unexpected doxygen return");
1252 } else {
1253 if (sawComment) {
1254 this->nl();
1255 }
1256 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -04001257 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -04001258 }
1259 }
1260}
1261
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001262void IncludeParser::dumpCommonTail(const Definition& token) {
1263 this->lf(2);
1264 this->writeTag("Example");
1265 this->lf(1);
1266 this->writeString("// incomplete");
1267 this->lf(1);
1268 this->writeEndTag();
1269 this->lf(2);
1270 this->writeTag("SeeAlso");
1271 this->writeSpace();
1272 this->writeString("incomplete");
1273 this->lf(2);
1274 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
1275 this->lf(2);
1276}
1277
Cary Clark224c7002018-06-27 11:00:21 -04001278void IncludeParser::dumpConst(const Definition& token, string className) {
1279 this->writeTag("Const");
1280 this->writeSpace();
1281 this->writeString(token.fName);
1282 this->writeTagTable("Line", "incomplete");
1283 this->lf(2);
1284 this->dumpComment(token);
1285}
1286
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001287void IncludeParser::dumpDefine(const Definition& token) {
1288 this->writeTag("Define", token.fName);
1289 this->lf(2);
1290 this->writeTag("Code");
1291 this->lfAlways(1);
1292 this->writeString("###$");
1293 this->lfAlways(1);
1294 this->indentToColumn(4);
1295 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
1296 this->lf(1);
1297 this->indentToColumn(0);
1298 this->writeString("$$$#");
1299
1300 this->writeEndTag();
1301 this->lf(2);
1302 this->dumpComment(token);
1303 for (auto& child : token.fTokens) {
1304 if (MarkType::kComment == child.fMarkType) {
1305 continue;
1306 }
1307 this->writeTag("Param", child.fName);
1308 this->writeSpace();
1309 this->writeString("incomplete");
1310 this->writeSpace();
1311 this->writeString("##");
1312 this->lf(1);
1313 }
1314}
1315
1316void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001317 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -04001318 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001319 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -04001320 this->lfAlways(1);
1321 this->indentToColumn(4);
1322 this->writeString("enum");
1323 this->writeSpace();
1324 if ("_anonymous" != token.fName.substr(0, 10)) {
1325 this->writeString(token.fName);
1326 this->writeSpace();
1327 }
1328 this->writeString("{");
1329 this->lfAlways(1);
1330 for (auto& child : token.fChildren) {
1331 this->indentToColumn(8);
1332 this->writeString(child->fName);
1333 if (child->length()) {
1334 this->writeSpace();
1335 this->writeBlock(child->length(), child->fContentStart);
1336 }
1337 if (',' != fLastChar) {
1338 this->writeString(",");
1339 }
1340 this->lfAlways(1);
1341 }
1342 this->indentToColumn(4);
1343 this->writeString("};");
1344 this->lf(1);
1345 this->writeString("##");
1346 this->lf(2);
1347 this->dumpComment(token);
1348 for (auto& child : token.fChildren) {
Cary Clark61313f32018-10-08 14:57:48 -04001349 // TODO: get comments before or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001350 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -04001351 this->writeSpace();
1352 this->writeString(child->fName);
1353 TextParser val(child);
1354 if (!val.eof()) {
1355 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
1356 val.next();
1357 val.skipSpace();
1358 const char* valEnd = val.anyOf(",\n");
1359 if (!valEnd) {
1360 valEnd = val.fEnd;
1361 }
1362 this->writeSpace();
1363 this->writeBlock(valEnd - val.fStart, val.fStart);
1364 } else {
1365 this->writeSpace();
1366 this->writeDefinition(*child);
1367 }
1368 }
1369 this->lf(1);
1370 for (auto comment : child->fChildren) {
1371 if (MarkType::kComment == comment->fMarkType) {
1372 TextParser parser(comment);
1373 parser.skipExact("*");
1374 parser.skipExact("*");
1375 while (!parser.eof() && parser.skipWhiteSpace()) {
1376 parser.skipExact("*");
1377 parser.skipWhiteSpace();
1378 const char* start = parser.fChar;
1379 parser.skipToEndBracket('\n');
1380 this->lf(1);
1381 this->writeBlock(parser.fChar - start, start);
1382 }
1383 }
1384 }
1385 this->writeEndTag();
1386 }
1387 this->lf(2);
1388}
1389
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001390bool IncludeParser::dumpGlobals(string* globalFileName, long int* globalTell) {
1391 bool hasGlobals = !fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1392 || !fITemplateMap.empty()|| !fITypedefMap.empty() || !fIUnionMap.empty();
1393 if (!hasGlobals) {
Cary Clark224c7002018-06-27 11:00:21 -04001394 return true;
1395 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001396 size_t lastBSlash = fFileName.rfind('\\');
1397 size_t lastSlash = fFileName.rfind('/');
1398 size_t lastDotH = fFileName.rfind(".h");
1399 SkASSERT(string::npos != lastDotH);
1400 if (string::npos != lastBSlash && (string::npos == lastSlash
1401 || lastBSlash < lastSlash)) {
1402 lastSlash = lastBSlash;
1403 } else if (string::npos == lastSlash) {
1404 lastSlash = -1;
1405 }
1406 lastSlash += 1;
1407 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
1408 string fileName = globalsName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001409 *globalFileName = fileName;
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001410 fOut = fopen(fileName.c_str(), "wb");
1411 if (!fOut) {
1412 SkDebugf("could not open output file %s\n", globalsName.c_str());
1413 return false;
1414 }
1415 string prefixName = globalsName.substr(0, 2);
1416 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
1417 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
1418 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -04001419 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001420 this->lf(2);
1421 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001422 this->writeTag("Populate");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001423 this->writeEndTag();
1424 this->lf(2);
1425 if (!fIDefineMap.empty()) {
1426 this->writeTag("Subtopic", "Define");
1427 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -04001428 this->writeEndTag();
1429 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001430 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001431 if (!fIFunctionMap.empty()) {
1432 this->writeTag("Subtopic", "Function");
1433 this->writeTag("Populate");
1434 this->writeEndTag();
1435 this->lf(2);
1436 }
1437 if (!fIEnumMap.empty()) {
1438 this->writeTag("Subtopic", "Enum");
1439 this->writeTag("Populate");
1440 this->writeEndTag();
1441 this->lf(2);
1442 }
1443 if (!fITemplateMap.empty()) {
1444 this->writeTag("Subtopic", "Template");
1445 this->writeTag("Populate");
1446 this->writeEndTag();
1447 this->lf(2);
1448 }
1449 if (!fITypedefMap.empty()) {
1450 this->writeTag("Subtopic", "Typedef");
1451 this->writeTag("Populate");
1452 this->writeEndTag();
1453 this->lf(2);
1454 }
1455 if (!fIUnionMap.empty()) {
1456 this->writeTag("Subtopic", "Union");
1457 this->writeTag("Populate");
1458 this->writeEndTag();
1459 this->lf(2);
1460 }
1461 std::map<int, Definition*> sortedDefs;
1462 for (const auto& entry : fIDefineMap) {
1463 sortedDefs[entry.second->fLineCount] = entry.second;
1464 }
1465 for (const auto& entry : fIFunctionMap) {
1466 sortedDefs[entry.second->fLineCount] = entry.second;
1467 }
1468 for (const auto& entry : fIEnumMap) {
1469 sortedDefs[entry.second->fLineCount] = entry.second;
1470 }
1471 for (const auto& entry : fITemplateMap) {
1472 sortedDefs[entry.second->fLineCount] = entry.second;
1473 }
1474 for (const auto& entry : fITypedefMap) {
1475 sortedDefs[entry.second->fLineCount] = entry.second;
1476 }
1477 for (const auto& entry : fIUnionMap) {
1478 sortedDefs[entry.second->fLineCount] = entry.second;
1479 }
1480 for (const auto& entry : sortedDefs) {
1481 const Definition* def = entry.second;
1482 this->writeBlockSeparator();
1483 switch (def->fMarkType) {
1484 case MarkType::kDefine:
1485 this->dumpDefine(*def);
1486 break;
1487 case MarkType::kMethod:
1488 this->dumpMethod(*def, globalsName);
1489 break;
1490 case MarkType::kEnum:
1491 case MarkType::kEnumClass:
1492 this->dumpEnum(*def, globalsName);
1493 break;
1494 case MarkType::kTemplate:
1495 SkASSERT(0); // incomplete
1496 break;
1497 case MarkType::kTypedef: {
1498 this->writeTag("Typedef");
1499 this->writeSpace();
1500 TextParser parser(def);
1501 if (!parser.skipExact("typedef")) {
1502 return false;
1503 }
1504 if (!parser.skipSpace()) {
1505 return false;
1506 }
1507 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1508 this->lf(2);
1509 this->dumpComment(*def);
1510 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1511 this->lf(2);
1512 } continue;
1513 case MarkType::kUnion:
1514 SkASSERT(0); // incomplete
1515 break;
1516 default:
1517 SkASSERT(0);
1518 }
1519 this->dumpCommonTail(*def);
1520 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001521 *globalTell = ftell(fOut);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001522 this->writeEndTag("Topic", topicName);
1523 this->lfAlways(1);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001524// fclose(fOut); // defer closing in case class needs to be also written here
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001525 SkDebugf("wrote %s\n", fileName.c_str());
1526 return true;
1527}
1528
1529bool IncludeParser::isClone(const Definition& token) {
1530 string name = token.fName;
1531 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1532}
1533
1534bool IncludeParser::isConstructor(const Definition& token, string className) {
1535 string name = token.fName;
1536 return 0 == name.find(className) || '~' == name[0];
1537}
1538
1539bool IncludeParser::isInternalName(const Definition& token) {
1540 string name = token.fName;
1541 // exception for this SkCanvas function .. for now
1542 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1543 return false;
1544 }
1545 return name.substr(0, 7) == "android"
1546 || 0 == token.fName.find("internal_")
1547 || 0 == token.fName.find("Internal_")
1548 || 0 == token.fName.find("legacy_")
1549 || 0 == token.fName.find("temporary_")
1550 || 0 == token.fName.find("private_");
1551}
1552
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001553bool IncludeParser::isMember(const Definition& token) const {
1554 if ('f' == token.fStart[0] && isupper(token.fStart[1])) {
1555 return true;
1556 }
1557 if (!islower(token.fStart[0])) {
1558 return false;
1559 }
1560 // make an exception for SkTextBlob::RunBuffer, sole struct with members not in fXxxx format
1561 if (string::npos != token.fFileName.find("SkTextBlob.h")) {
1562 const Definition* structToken = token.fParent;
1563 if (!structToken) {
1564 return false;
1565 }
1566 if (KeyWord::kStruct != structToken->fKeyWord) {
1567 structToken = token.fParent->fParent;
1568 if (!structToken) {
1569 return false;
1570 }
1571 if (KeyWord::kStruct != structToken->fKeyWord) {
1572 return false;
1573 }
1574 }
1575 SkASSERT(structToken->fTokens.size() > 0);
1576 const Definition& child = structToken->fTokens.front();
1577 string structName(child.fContentStart, child.length());
1578 if ("RunBuffer" != structName) {
1579 return false;
1580 }
1581 string tokenName(token.fContentStart, token.length());
1582 string allowed[] = { "glyphs", "pos", "utf8text", "clusters" };
1583 for (auto allow : allowed) {
1584 if (allow == tokenName) {
1585 return true;
1586 }
1587 }
1588 }
1589 return false;
1590}
1591
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001592bool IncludeParser::isOperator(const Definition& token) {
1593 return "operator" == token.fName.substr(0, 8);
1594}
1595
1596void IncludeParser::dumpMethod(const Definition& token, string className) {
1597 this->writeTag("Method");
1598 this->writeSpace();
1599
1600 string name = string(token.fStart ? token.fStart : token.fContentStart,
1601 token.length());
1602 if (this->isOperator(token)) {
1603 string spaceConst(" const");
1604 size_t constPos = name.rfind(spaceConst);
1605 if (name.length() - spaceConst.length() == constPos) {
1606 name = name.substr(0, constPos) + "_const";
1607 }
1608 }
Cary Clark224c7002018-06-27 11:00:21 -04001609 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001610 string inType;
1611 if (this->isConstructor(token, className)) {
1612 inType = "Constructor";
1613 } else if (this->isOperator(token)) {
1614 inType = "Operator";
1615 } else {
1616 inType = "incomplete";
1617 }
1618 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001619 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001620 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001621 this->dumpComment(token);
1622}
1623
1624void IncludeParser::dumpMember(const Definition& token) {
1625 this->writeTag("Member");
1626 this->writeSpace();
1627 this->writeDefinition(token, token.fName, 2);
1628 lf(1);
1629 for (auto child : token.fChildren) {
1630 this->writeDefinition(*child);
1631 }
1632 this->writeEndTag();
1633 lf(2);
1634}
1635
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001636bool IncludeParser::dumpTokens() {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001637 string globalFileName;
1638 long int globalTell = 0;
1639 if (!this->dumpGlobals(&globalFileName, &globalTell)) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001640 return false;
1641 }
Cary Clark9174bda2017-09-19 17:39:32 -04001642 for (const auto& member : fIClassMap) {
1643 if (string::npos != member.first.find("::")) {
1644 continue;
1645 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001646 if (!this->dumpTokens(member.first, globalFileName, &globalTell)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001647 return false;
1648 }
1649 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001650 if (globalTell) {
1651 fclose(fOut);
1652 }
Cary Clark9174bda2017-09-19 17:39:32 -04001653 return true;
1654}
1655
Ben Wagner63fd7602017-10-09 15:45:33 -04001656 // dump equivalent markup
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001657bool IncludeParser::dumpTokens(string skClassName, string globalFileName, long int* globalTell) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001658 string fileName = skClassName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001659 if (globalFileName != fileName) {
1660 fOut = fopen(fileName.c_str(), "wb");
1661 if (!fOut) {
1662 SkDebugf("could not open output file %s\n", fileName.c_str());
1663 return false;
1664 }
1665 } else {
1666 fseek(fOut, *globalTell, SEEK_SET);
1667 this->lf(2);
1668 this->writeBlockSeparator();
1669 *globalTell = 0;
Cary Clark8032b982017-07-28 11:04:54 -04001670 }
1671 string prefixName = skClassName.substr(0, 2);
1672 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1673 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001674 if (globalFileName != fileName) {
1675 this->writeTagNoLF("Topic", topicName);
1676 this->writeEndTag("Alias", topicName + "_Reference");
1677 this->lf(2);
1678 }
Cary Clark8032b982017-07-28 11:04:54 -04001679 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001680 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1681 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001682 this->writeTag(containerType, skClassName);
1683 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001684 auto& tokens = classMap.fTokens;
1685 for (auto& token : tokens) {
1686 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1687 continue;
1688 }
Cary Clark9174bda2017-09-19 17:39:32 -04001689 this->writeDefinition(token);
1690 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001691 }
1692 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001693 bool hasClass = false;
1694 bool hasConst = !fIEnumMap.empty();
1695 bool hasConstructor = false;
1696 bool hasMember = false;
1697 bool hasOperator = false;
Cary Clark224c7002018-06-27 11:00:21 -04001698 bool hasStruct = false;
Cary Clark8032b982017-07-28 11:04:54 -04001699 for (const auto& oneClass : fIClassMap) {
1700 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1701 continue;
1702 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001703 hasClass = true;
1704 break;
Cary Clark8032b982017-07-28 11:04:54 -04001705 }
Cary Clark224c7002018-06-27 11:00:21 -04001706 for (const auto& oneStruct : fIStructMap) {
1707 if (skClassName + "::" != oneStruct.first.substr(0, skClassName.length() + 2)) {
1708 continue;
1709 }
1710 hasStruct = true;
1711 break;
1712 }
Cary Clark8032b982017-07-28 11:04:54 -04001713 for (const auto& token : classMap.fTokens) {
1714 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1715 continue;
1716 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001717 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001718 continue;
1719 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001720 if (token.fUndocumented) {
1721 continue;
1722 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001723 if (this->isConstructor(token, skClassName)) {
1724 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001725 continue;
1726 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001727 if (this->isOperator(token)) {
1728 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001729 continue;
1730 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001731 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001732 continue;
1733 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001734 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001735 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001736 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001737 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001738 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001739 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001740
1741 if (hasClass) {
Cary Clark224c7002018-06-27 11:00:21 -04001742 this->writeTag("Subtopic", "Class");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001743 this->writeTag("Populate");
1744 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001745 this->lf(2);
1746 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001747 if (hasConst) {
1748 this->writeTag("Subtopic", "Constant");
1749 this->writeTag("Populate");
1750 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001751 this->lf(2);
1752 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001753 if (hasConstructor) {
1754 this->writeTag("Subtopic", "Constructor");
1755 this->writeTag("Populate");
1756 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001757 this->lf(2);
1758 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001759 if (hasOperator) {
1760 this->writeTag("Subtopic", "Operator");
1761 this->writeTag("Populate");
1762 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001763 this->lf(2);
1764 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001765 if (hasMember) {
1766 this->writeTag("Subtopic", "Member_Function");
1767 this->writeTag("Populate");
1768 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001769 this->lf(2);
1770 }
Cary Clark224c7002018-06-27 11:00:21 -04001771 if (hasStruct) {
1772 this->writeTag("Subtopic", "Struct");
1773 this->writeTag("Populate");
1774 this->writeEndTag();
1775 this->lf(2);
1776 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001777 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001778 this->writeBlockSeparator();
1779 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001780 this->lf(2);
1781 this->writeTag("Example");
1782 this->lfcr();
1783 this->writeString("// incomplete");
1784 this->writeEndTag();
1785 this->lf(2);
1786 this->writeTag("SeeAlso", "incomplete");
1787 this->lf(2);
1788 this->writeEndTag("Enum", oneEnum.first);
1789 this->lf(2);
1790 }
Cary Clark8032b982017-07-28 11:04:54 -04001791 for (auto& oneClass : fIClassMap) {
1792 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1793 continue;
1794 }
1795 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001796 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001797 KeyWord keyword = oneClass.second.fKeyWord;
1798 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1799 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001800 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001801 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001802 this->lf(2);
1803 this->writeTag("Code");
1804 this->writeEndTag("ToDo", "fill this in manually");
1805 this->writeEndTag();
1806 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001807 for (auto& token : oneClass.second.fTokens) {
1808 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1809 continue;
1810 }
Cary Clark9174bda2017-09-19 17:39:32 -04001811 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001812 }
1813 this->lf(2);
1814 this->dumpClassTokens(oneClass.second);
1815 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001816 this->writeEndTag(containerType, innerName);
1817 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001818 }
1819 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001820 this->writeEndTag(containerType, skClassName);
1821 this->lf(2);
1822 this->writeEndTag("Topic", topicName);
1823 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001824 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001825 SkDebugf("wrote %s\n", fileName.c_str());
1826 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001827}
1828
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001829void IncludeParser::dumpTypedef(const Definition& token, string className) {
1830 this->writeTag("Typedef");
1831 this->writeSpace();
1832 this->writeString(token.fName);
1833 this->writeTagTable("Line", "incomplete");
1834 this->lf(2);
1835 this->dumpComment(token);
1836}
1837
Cary Clark61313f32018-10-08 14:57:48 -04001838string IncludeParser::elidedCodeBlock(const Definition& iDef) {
1839 SkASSERT(KeyWord::kStruct == iDef.fKeyWord || KeyWord::kClass == iDef.fKeyWord
1840 || KeyWord::kTemplate == iDef.fKeyWord);
1841 TextParser i(&iDef);
1842 fElided = Elided::kYes;
1843 MarkType markType = MarkType::kClass;
1844 if (KeyWord::kTemplate == iDef.fKeyWord) { // may be function
1845 for (auto child : iDef.fChildren) {
1846 if (MarkType::kMethod == child->fMarkType) {
1847 markType = MarkType::kFunction;
1848 break;
1849 }
1850 }
1851 }
1852 return this->writeCodeBlock(i, markType, 0);
1853}
1854
Cary Clarka90ea222018-10-16 10:30:28 -04001855 string IncludeParser::filteredBlock(string inContents, string filterContents) {
1856 string result;
1857 const unordered_map<string, Definition*>* mapPtr = nullptr;
1858 MarkType markType = MarkType::kNone;
1859 if ("Constant" == inContents) {
1860 mapPtr = &fIConstMap;
1861 markType = MarkType::kConst;
1862 } else {
1863 SkASSERT(0); // only Constant supported for now
1864 }
Cary Clark4b076532018-10-29 14:17:22 -04001865 vector<Definition*> consts;
Cary Clarka90ea222018-10-16 10:30:28 -04001866 for (auto entry : *mapPtr) {
1867 if (string::npos == entry.first.find(filterContents)) {
1868 continue;
1869 }
Cary Clark4b076532018-10-29 14:17:22 -04001870 consts.push_back(entry.second);
1871 }
1872 std::sort(consts.begin(), consts.end(), [](Definition* def1, Definition* def2) {
1873 return def1->fLineCount < def2->fLineCount;
1874 } );
1875 for (auto oneConst : consts) {
1876 result += this->writeCodeBlock(*oneConst, markType);
Cary Clarka90ea222018-10-16 10:30:28 -04001877 }
1878 return result;
1879}
1880
Cary Clarkabaffd82018-11-15 08:25:12 -05001881bool IncludeParser::findCommentAfter(const Definition& includeDef, Definition* markupDef) {
1882 const Definition* parent = includeDef.fParent;
1883 int index = includeDef.fParentIndex;
1884 auto wordIter = parent->fTokens.begin();
1885 std::advance(wordIter, index);
1886 SkASSERT(&*wordIter == &includeDef);
1887 size_t commentLine = 0;
1888 do {
1889 wordIter = std::next(wordIter);
1890 if (parent->fTokens.end() == wordIter) {
1891 break;
1892 }
1893 commentLine = wordIter->fLineCount;
1894 } while (Punctuation::kSemicolon != wordIter->fPunctuation);
1895 wordIter = std::next(wordIter);
1896 if (parent->fTokens.end() != wordIter && Bracket::kSlashSlash == wordIter->fBracket
1897 && wordIter->fLineCount == commentLine) {
1898 return this->parseComment(wordIter->fFileName, wordIter->fContentStart,
1899 wordIter->fContentEnd, wordIter->fLineCount, markupDef, &markupDef->fUndocumented);
1900 }
1901 return true;
1902}
1903
Cary Clark8032b982017-07-28 11:04:54 -04001904bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1905 // add comment preceding class, if any
Cary Clarkabaffd82018-11-15 08:25:12 -05001906 Definition* parent = includeDef.fParent;
Cary Clark8032b982017-07-28 11:04:54 -04001907 int index = includeDef.fParentIndex;
1908 auto wordIter = parent->fTokens.begin();
1909 std::advance(wordIter, index);
1910 SkASSERT(&*wordIter == &includeDef);
1911 while (parent->fTokens.begin() != wordIter) {
1912 auto testIter = std::prev(wordIter);
1913 if (Definition::Type::kWord != testIter->fType
1914 && Definition::Type::kKeyWord != testIter->fType
1915 && (Definition::Type::kBracket != testIter->fType
1916 || Bracket::kAngle != testIter->fBracket)
1917 && (Definition::Type::kPunctuation != testIter->fType
1918 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1919 break;
1920 }
1921 wordIter = testIter;
1922 }
1923 auto commentIter = wordIter;
1924 while (parent->fTokens.begin() != commentIter) {
1925 auto testIter = std::prev(commentIter);
1926 bool isComment = Definition::Type::kBracket == testIter->fType
1927 && (Bracket::kSlashSlash == testIter->fBracket
1928 || Bracket::kSlashStar == testIter->fBracket);
1929 if (!isComment) {
1930 break;
1931 }
1932 commentIter = testIter;
1933 }
1934 while (commentIter != wordIter) {
1935 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
Cary Clarkabaffd82018-11-15 08:25:12 -05001936 commentIter->fContentEnd, commentIter->fLineCount, markupDef,
1937 &markupDef->fUndocumented)) {
Cary Clark8032b982017-07-28 11:04:54 -04001938 return false;
1939 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001940 commentIter->fUndocumented = markupDef->fUndocumented;
Cary Clark8032b982017-07-28 11:04:54 -04001941 commentIter = std::next(commentIter);
1942 }
1943 return true;
1944}
1945
Cary Clark0d225392018-06-07 09:59:07 -04001946Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1947 string typeName) {
1948 typedef Definition* DefinitionPtr;
1949 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1950 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1951 if (mapIter == fMaps.end()) {
1952 return nullptr;
1953 }
1954 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1955 return reportError<DefinitionPtr>("invalid mark type");
1956 }
1957 string name = this->uniqueName(*mapIter->fInclude, typeName);
1958 Definition& markupDef = *(*mapIter->fInclude)[name];
1959 if (markupDef.fStart) {
1960 return reportError<DefinitionPtr>("definition already defined");
1961 }
1962 markupDef.fFileName = fFileName;
1963 markupDef.fStart = includeDef.fStart;
1964 markupDef.fContentStart = includeDef.fStart;
1965 markupDef.fName = name;
1966 markupDef.fContentEnd = includeDef.fContentEnd;
1967 markupDef.fTerminator = includeDef.fTerminator;
1968 markupDef.fParent = fParent;
1969 markupDef.fLineCount = includeDef.fLineCount;
1970 markupDef.fMarkType = markType;
1971 markupDef.fKeyWord = includeDef.fKeyWord;
1972 markupDef.fType = Definition::Type::kMark;
1973 return &markupDef;
1974}
1975
Cary Clarka64e4ee2018-10-18 08:30:34 -04001976Definition* IncludeParser::findMethod(const Definition& bmhDef) {
Cary Clark09d80c02018-10-31 12:14:03 -04001977 auto doubleColon = bmhDef.fName.rfind("::");
1978 if (string::npos == doubleColon) {
1979 const auto& iGlobalMethod = fIFunctionMap.find(bmhDef.fName);
1980 SkASSERT(fIFunctionMap.end() != iGlobalMethod);
1981 return iGlobalMethod->second;
1982 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001983 string className = bmhDef.fName.substr(0, doubleColon);
1984 const auto& iClass = fIClassMap.find(className);
Cary Clark77b3f3a2018-11-07 14:59:03 -05001985 if (fIClassMap.end() == iClass) {
1986 return nullptr;
1987 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001988 string methodName = bmhDef.fName.substr(doubleColon + 2);
1989 auto& iTokens = iClass->second.fTokens;
1990 const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
1991 [methodName](Definition& token) {
Cary Clark09d80c02018-10-31 12:14:03 -04001992 return MarkType::kMethod == token.fMarkType
Cary Clarkabaffd82018-11-15 08:25:12 -05001993 && !token.fUndocumented
Cary Clark09d80c02018-10-31 12:14:03 -04001994 && (methodName == token.fName
1995 || methodName == token.fName + "()"); } );
1996 if (iTokens.end() != iMethod) {
1997 return &*iMethod;
1998 }
1999 size_t subClassPos = className.rfind("::");
2000 if (string::npos != subClassPos) {
2001 className = className.substr(subClassPos + 2);
2002 }
2003 // match may be constructor; compare strings to see if this is so
2004 SkASSERT(string::npos != methodName.find('('));
2005 auto stripper = [](string s) -> string {
2006 bool last = false;
2007 string result;
2008 for (char c : s) {
2009 if (' ' >= c) {
2010 if (!last) {
2011 last = true;
2012 result += ' ';
2013 }
2014 continue;
2015 }
2016 result += c;
2017 last = false;
2018 }
2019 return result;
2020 };
2021 string strippedMethodName = stripper(methodName);
2022 if (strippedMethodName == methodName) {
2023 strippedMethodName = "";
2024 }
2025 const auto& cMethod = std::find_if(iTokens.begin(), iTokens.end(),
2026 [className, methodName, stripper, strippedMethodName](Definition& token) {
2027 if (MarkType::kMethod != token.fMarkType) {
2028 return false;
2029 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002030 if (token.fUndocumented) {
2031 return false;
2032 }
Cary Clark09d80c02018-10-31 12:14:03 -04002033 TextParser parser(&token);
2034 const char* match = parser.strnstr(className.c_str(), parser.fEnd);
2035 if (!match) {
2036 return false;
2037 }
2038 parser.skipTo(match);
2039 parser.skipExact(className.c_str());
2040 if ('(' != parser.peek()) {
2041 return false;
2042 }
2043 parser.skipToBalancedEndBracket('(', ')');
2044 string iMethodName(match, parser.fChar - match);
2045 if (methodName == iMethodName) {
2046 return true;
2047 }
2048 if ("" == strippedMethodName) {
2049 return false;
2050 }
2051 string strippedIName = stripper(iMethodName);
2052 return strippedIName == strippedMethodName;
2053 } );
2054 SkAssertResult(iTokens.end() != cMethod);
2055 return &*cMethod;
Cary Clarka64e4ee2018-10-18 08:30:34 -04002056}
2057
Cary Clark224c7002018-06-27 11:00:21 -04002058Definition* IncludeParser::parentBracket(Definition* parent) const {
2059 while (parent && Definition::Type::kBracket != parent->fType) {
2060 parent = parent->fParent;
2061 }
2062 return parent;
2063}
2064
2065Bracket IncludeParser::grandParentBracket() const {
2066 Definition* parent = parentBracket(fParent);
2067 parent = parentBracket(parent ? parent->fParent : nullptr);
2068 return parent ? parent->fBracket : Bracket::kNone;
2069}
2070
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002071bool IncludeParser::inAlignAs() const {
2072 if (fParent->fTokens.size() < 2) {
2073 return false;
2074 }
2075 auto reverseIter = fParent->fTokens.end();
2076 bool checkForBracket = true;
2077 while (fParent->fTokens.begin() != reverseIter) {
2078 std::advance(reverseIter, -1);
2079 if (checkForBracket) {
2080 if (Definition::Type::kBracket != reverseIter->fType) {
2081 return false;
2082 }
2083 if (Bracket::kParen != reverseIter->fBracket) {
2084 return false;
2085 }
2086 checkForBracket = false;
2087 continue;
2088 }
2089 if (Definition::Type::kKeyWord != reverseIter->fType) {
2090 return false;
2091 }
2092 return KeyWord::kAlignAs == reverseIter->fKeyWord;
2093 }
2094 return false;
2095}
2096
Cary Clark61313f32018-10-08 14:57:48 -04002097const Definition* IncludeParser::include(string match) const {
2098 for (auto& entry : fIncludeMap) {
2099 if (string::npos == entry.first.find(match)) {
2100 continue;
2101 }
2102 return &entry.second;
2103 }
2104 SkASSERT(0);
2105 return nullptr;
2106}
2107
Cary Clark137b8742018-05-30 09:21:49 -04002108// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04002109bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
2110 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04002111 // parse class header
2112 auto iter = includeDef->fTokens.begin();
2113 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
2114 // todo : documentation is ignoring this for now
2115 iter = std::next(iter);
2116 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002117 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
2118 if (hasAlignAs) {
2119 iter = std::next(iter);
2120 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
2121 return includeDef->reportError<bool>("expected alignas argument");
2122 }
2123 iter = std::next(iter);
2124 }
Cary Clark8032b982017-07-28 11:04:54 -04002125 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
2126 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04002127 iter = std::next(iter);
2128 if (iter == includeDef->fTokens.end()) {
2129 return true; // forward declaration only
2130 }
Cary Clark8032b982017-07-28 11:04:54 -04002131 do {
2132 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002133 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04002134 }
2135 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
2136 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002137 }
Cary Clark8032b982017-07-28 11:04:54 -04002138 } while (static_cast<void>(iter = std::next(iter)), true);
2139 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04002140 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04002141 }
2142 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
2143 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04002144 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04002145 }
2146 markupDef->fStart = iter->fStart;
2147 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002148 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04002149 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002150 if (markupDef->fUndocumented) {
2151 includeDef->fUndocumented = true;
2152 }
Cary Clark8032b982017-07-28 11:04:54 -04002153// if (1 != includeDef->fChildren.size()) {
2154// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
2155// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002156 auto includeDefIter = includeDef->fChildren.begin();
2157 if (hasAlignAs) {
2158 SkASSERT(includeDef->fChildren.end() != includeDefIter);
2159 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
2160 std::advance(includeDefIter, 1);
2161 }
2162 if (includeDef->fChildren.end() != includeDefIter
2163 && Bracket::kAngle == (*includeDefIter)->fBracket) {
2164 std::advance(includeDefIter, 1);
2165 }
2166 includeDef = *includeDefIter;
2167 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04002168 iter = includeDef->fTokens.begin();
2169 // skip until public
2170 int publicIndex = 0;
2171 if (IsStruct::kNo == isStruct) {
2172 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2173 size_t publicLen = strlen(publicName);
2174 while (iter != includeDef->fTokens.end()
2175 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
2176 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04002177 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002178 iter = std::next(iter);
2179 ++publicIndex;
2180 }
2181 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002182 int keyIndex = publicIndex;
2183 KeyWord currentKey = KeyWord::kPublic;
2184 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2185 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04002186 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
2187 size_t protectedLen = strlen(protectedName);
2188 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
2189 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04002190 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002191 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04002192 std::advance(childIter, 1);
2193 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002194 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04002195 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04002196 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04002197 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04002198 const char* testStart = iter->fStart;
2199 size_t testLen = (size_t) (iter->fContentEnd - testStart);
2200 iter = std::next(iter);
2201 ++keyIndex;
2202 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2203 currentKey = KeyWord::kPublic;
2204 break;
2205 }
2206 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2207 currentKey = KeyWord::kProtected;
2208 break;
2209 }
2210 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2211 currentKey = KeyWord::kPrivate;
2212 break;
2213 }
2214 }
2215 fLastObject = nullptr;
2216 if (KeyWord::kPublic == currentKey) {
2217 if (!this->parseObject(child, markupDef)) {
2218 return false;
2219 }
Cary Clark8032b982017-07-28 11:04:54 -04002220 }
Cary Clark73fa9722017-08-29 17:36:51 -04002221 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002222 childIter = std::next(childIter);
2223 }
Cary Clark137b8742018-05-30 09:21:49 -04002224 while (iter != includeDef->fTokens.end()) {
2225 iter->fPrivate = KeyWord::kPublic != currentKey;
2226 iter = std::next(iter);
2227 }
Cary Clark8032b982017-07-28 11:04:54 -04002228 SkASSERT(fParent->fParent);
2229 fParent = fParent->fParent;
2230 return true;
2231}
2232
Cary Clarkabaffd82018-11-15 08:25:12 -05002233bool IncludeParser::isUndocumentable(string filename, const char* start, const char* end,
2234 int lineCount) {
Cary Clark8032b982017-07-28 11:04:54 -04002235 TextParser parser(filename, start, end, lineCount);
Cary Clarkabaffd82018-11-15 08:25:12 -05002236 const vector<string> skipWords = { "experimental", "deprecated", "private" };
2237 const vector<string> butNot = { "to be deprecated", "may be deprecated" };
2238 const vector<string> alsoNot = { "todo" };
2239 string match = parser.anyWord(skipWords, 0);
2240 if ("" != match) {
2241 if ("" == parser.anyWord(alsoNot, 0)
2242 && ("deprecated" != match || "" == parser.anyWord(butNot, 2))) {
2243 return true;
2244 }
2245 }
2246 return false;
2247}
2248
2249bool IncludeParser::parseComment(string filename, const char* start, const char* end,
2250 int lineCount, Definition* markupDef, bool* undocumentedPtr) {
2251 if (this->isUndocumentable(filename, start, end, lineCount)) {
2252 *undocumentedPtr = true;
2253 }
Cary Clark8032b982017-07-28 11:04:54 -04002254 // parse doxygen if present
Cary Clarkabaffd82018-11-15 08:25:12 -05002255 TextParser parser(filename, start, end, lineCount);
Cary Clark8032b982017-07-28 11:04:54 -04002256 if (parser.startsWith("**")) {
2257 parser.next();
2258 parser.next();
2259 parser.skipWhiteSpace();
2260 if ('\\' == parser.peek()) {
2261 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002262 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2263 if (parser.skipExact("file")) {
2264 if (Definition::Type::kFileType != fParent->fType) {
2265 return reportError<bool>("expected parent is file");
2266 }
2267 string filename = markupDef->fileName();
2268 if (!parser.skipWord(filename.c_str())) {
2269 return reportError<bool>("missing object type");
2270 }
2271 } else if (parser.skipExact("fn")) {
2272 SkASSERT(0); // incomplete
2273 } else {
2274 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2275 return reportError<bool>("missing object type");
2276 }
2277 if (!parser.skipWord(markupDef->fName.c_str()) &&
2278 KeyWord::kEnum != markupDef->fKeyWord) {
2279 return reportError<bool>("missing object name");
2280 }
Cary Clark8032b982017-07-28 11:04:54 -04002281 }
Cary Clark8032b982017-07-28 11:04:54 -04002282 }
2283 }
2284 // remove leading '*' if present
2285 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2286 while (!parser.eof() && parser.skipWhiteSpace()) {
2287 while ('*' == parser.peek()) {
2288 parser.next();
2289 if (parser.eof()) {
2290 break;
2291 }
2292 parser.skipWhiteSpace();
2293 }
2294 if (parser.eof()) {
2295 break;
2296 }
2297 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002298 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002299 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002300 parser.skipToEndBracket('\n');
2301 }
2302 return true;
2303}
2304
Cary Clarkabaffd82018-11-15 08:25:12 -05002305/*
2306 find comment either in front of or after the const def and then extract if the
2307 const is undocumented
2308 */
Cary Clarkd98f78c2018-04-26 08:32:37 -04002309bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002310 if (!markupDef) {
2311 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2312 child->fLineCount, fParent, '\0');
2313 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002314 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002315 globalMarkupChild->fName = globalUniqueName;
2316 if (!this->findComments(*child, globalMarkupChild)) {
2317 return false;
2318 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002319 if (!this->findCommentAfter(*child, globalMarkupChild)) {
2320 return false;
2321 }
2322 if (globalMarkupChild->fUndocumented) {
2323 child->fUndocumented = true;
2324 } else {
2325 fIConstMap[globalUniqueName] = globalMarkupChild;
2326 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002327 return true;
2328 }
2329 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2330 child->fLineCount, markupDef, '\0');
2331 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002332 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002333 markupChild->fTerminator = markupChild->fContentEnd;
2334 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002335 classDef.fConsts[child->fName] = markupChild;
Cary Clarkabaffd82018-11-15 08:25:12 -05002336 if (!this->findComments(*child, markupChild)) {
2337 return false;
2338 }
2339 if (!this->findCommentAfter(*child, markupChild)) {
2340 return false;
2341 }
2342 if (markupChild->fUndocumented) {
2343 child->fUndocumented = true;
2344 } else {
2345 fIConstMap[child->fName] = markupChild;
2346 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002347 return true;
2348}
2349
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002350bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2351 TextParser parser(child);
2352 if (!parser.skipExact("#define")) {
2353 return false;
2354 }
2355 if (!parser.skipSpace()) {
2356 return false;
2357 }
2358 const char* nameStart = parser.fChar;
2359 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2360 if (parser.eof()) {
2361 return true; // do nothing if #define doesn't define anything
2362 }
2363 string nameStr(nameStart, parser.fChar - nameStart);
2364 struct Param {
2365 const char* fStart;
2366 const char* fEnd;
2367 };
2368 vector<Param> params;
2369 if ('(' == parser.peek()) {
2370 parser.next();
2371 if (!parser.skipSpace()) {
2372 return false;
2373 }
2374 do {
2375 const char* paramStart = parser.fChar;
2376 if (!parser.skipExact("...")) {
2377 parser.skipToNonAlphaNum();
2378 }
2379 if (parser.eof()) {
2380 return false;
2381 }
2382 params.push_back({paramStart, parser.fChar});
2383 if (!parser.skipSpace()) {
2384 return false;
2385 }
2386 if (')' == parser.peek()) {
2387 parser.next();
2388 break;
2389 }
2390 if (',' != parser.next()) {
2391 return false;
2392 }
2393 if (!parser.skipSpace()) {
2394 return false;
2395 }
2396 } while (true);
2397 }
2398 if (!parser.skipSpace()) {
2399 return false;
2400 }
2401 if (!markupDef) {
2402 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2403 child->fLineCount, fParent, '\0');
2404 Definition* globalMarkupChild = &fGlobals.back();
2405 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2406 globalMarkupChild->fName = globalUniqueName;
2407 globalMarkupChild->fTerminator = child->fContentEnd;
2408 if (!this->findComments(*child, globalMarkupChild)) {
2409 return false;
2410 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002411 if (!globalMarkupChild->fUndocumented) {
2412 fIDefineMap[globalUniqueName] = globalMarkupChild;
2413 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002414 for (Param param : params) {
2415 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2416 child->fLineCount, globalMarkupChild, '\0');
2417 Definition* paramChild = &globalMarkupChild->fTokens.back();
2418 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
2419 paramChild->fTerminator = param.fEnd;
2420 }
2421 return true;
2422 }
2423 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2424 child->fLineCount, markupDef, '\0');
2425 Definition* markupChild = &markupDef->fTokens.back();
2426 markupChild->fName = nameStr;
2427 markupChild->fTerminator = markupChild->fContentEnd;
2428 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2429 if (!this->findComments(*child, markupChild)) {
2430 return false;
2431 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002432 if (markupChild->fUndocumented) {
2433 child->fUndocumented = true;
2434 } else {
2435 classDef.fDefines[nameStr] = markupChild;
2436 fIDefineMap[nameStr] = markupChild;
2437 }
Cary Clark8032b982017-07-28 11:04:54 -04002438 return true;
2439}
2440
2441bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002442 if (!child->fTokens.size()) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002443 return true; // if enum is a forward declaration, do nothing
2444 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002445 bool isEnumClass = false;
2446 Definition* parent = child;
2447 auto token = parent->fTokens.begin();
2448 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2449 isEnumClass = true;
2450 parent = &*token;
2451 token = parent->fTokens.begin();
Cary Clark8032b982017-07-28 11:04:54 -04002452 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002453 SkASSERT(Definition::Type::kWord == token->fType);
2454 string nameStr = string(token->fStart, token->fContentEnd - token->fStart);
Cary Clark2dc84ad2018-01-26 12:56:22 -05002455 Definition* markupChild;
2456 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002457 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2458 child->fLineCount, fParent, '\0');
2459 markupChild = &fGlobals.back();
2460 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2461 markupChild->fName = globalUniqueName;
2462 markupChild->fTerminator = child->fContentEnd;
Cary Clarkabaffd82018-11-15 08:25:12 -05002463 if (!markupChild->fUndocumented) {
2464 fIEnumMap[globalUniqueName] = markupChild;
2465 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002466 } else {
2467 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002468 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002469 markupChild = &markupDef->fTokens.back();
2470 }
Cary Clark8032b982017-07-28 11:04:54 -04002471 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2472 markupChild->fKeyWord = KeyWord::kEnum;
Cary Clarkabaffd82018-11-15 08:25:12 -05002473 if (isEnumClass) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04002474 markupChild->fMarkType = MarkType::kEnumClass;
2475 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002476 if (markupDef) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002477 markupChild->fName = markupDef->fName + "::" + nameStr;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002478 }
Cary Clark8032b982017-07-28 11:04:54 -04002479 if (!this->findComments(*child, markupChild)) {
2480 return false;
2481 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002482 if (markupChild->fUndocumented) {
2483 child->fUndocumented = true;
2484 }
2485 if (!this->parseEnumConst(token, parent->fTokens.end(), markupChild)) {
2486 return false;
2487 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002488 for (auto outsideMember : child->fChildren) {
2489 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002490 continue;
2491 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002492 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2493 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002494 continue;
2495 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002496 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2497 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002498 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002499 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002500 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04002501 // FIXME: ? add comment as well ?
2502 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002503 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002504 if (markupDef) {
2505 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2506 SkASSERT(classDef.fStart);
2507 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002508 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002509 markupChild->fName = uniqueName;
2510 classDef.fEnums[uniqueName] = markupChild;
Cary Clarkabaffd82018-11-15 08:25:12 -05002511 if (!markupChild->fUndocumented) {
2512 fIEnumMap[fullName] = markupChild;
2513 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002514 }
Cary Clark8032b982017-07-28 11:04:54 -04002515 return true;
2516}
2517
Cary Clarkabaffd82018-11-15 08:25:12 -05002518bool IncludeParser::parseOneEnumConst(list<Definition>& constList,
2519 Definition* markupChild, bool skipWord) {
2520 auto memberIter = constList.begin();
2521 const auto memberIterEnd = constList.end();
2522 if (skipWord) {
2523 SkASSERT(Definition::Type::kWord == memberIter->fType);
2524 memberIter = std::next(memberIter);
2525 SkASSERT(memberIterEnd != memberIter);
2526 }
2527 // token array has parse atoms; child array has comments
2528 bool undocumented = false;
2529 while (memberIterEnd != memberIter) {
2530 while (Bracket::kSlashStar == memberIter->fBracket) {
2531 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2532 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2533 return false;
2534 }
2535 memberIter = std::next(memberIter);
2536 if (memberIterEnd == memberIter) {
2537 return false;
2538 }
2539 }
2540 if (Bracket::kPound == memberIter->fBracket) {
2541 KeyWord keyWord = memberIter->fKeyWord;
2542 bool sawIf = KeyWord::kIfdef == keyWord || KeyWord::kIf == keyWord
2543 || KeyWord::kElif == keyWord;
2544 if (sawIf || KeyWord::kElse == keyWord) {
2545 if (!parseOneEnumConst(memberIter->fTokens, markupChild, sawIf)) {
2546 return false;
2547 }
2548 } else {
2549 SkASSERT(KeyWord::kEndif == keyWord || KeyWord::kError == keyWord);
2550 }
2551 memberIter = std::next(memberIter);
2552 if (memberIterEnd == memberIter) {
2553 break;
2554 }
2555 continue;
2556 }
2557 while (Definition::Type::kWord != memberIter->fType) {
2558 memberIter = std::next(memberIter);
2559 if (memberIterEnd == memberIter) {
2560 return false;
2561 }
2562 }
2563 auto memberStart = memberIter;
2564 Definition* memberEnd = nullptr;
2565 const char* last;
2566 do {
2567 last = memberIter->fContentEnd;
2568 memberIter = std::next(memberIter);
2569 if (memberIterEnd == memberIter) {
2570 break;
2571 }
2572 memberEnd = &*memberIter;
2573 } while (string::npos == string(last, memberIter->fContentStart).find(','));
2574 if (!memberEnd) {
2575 return false;
2576 }
2577 if (memberIterEnd != memberIter && Bracket::kSlashSlash == memberIter->fBracket) {
2578 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2579 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2580 return false;
2581 }
2582 memberIter = std::next(memberIter);
2583 }
2584 markupChild->fTokens.emplace_back(MarkType::kMember, memberStart->fContentStart,
2585 memberEnd->fContentEnd, memberStart->fLineCount, markupChild, '\0');
2586 Definition* markupMember = &markupChild->fTokens.back();
2587 string name = string(memberStart->fContentStart, memberStart->length());
2588 memberStart->fName = name;
2589 markupMember->fName = name;
2590 memberStart->fUndocumented = undocumented;
2591 markupMember->fUndocumented = undocumented;
2592 memberStart->fMarkType = MarkType::kMember;
2593 undocumented = false;
2594 }
2595 return true;
2596}
2597
2598bool IncludeParser::parseEnumConst(list<Definition>::iterator& tokenIter,
2599 const list<Definition>::iterator& tokenEnd, Definition* markupChild) {
2600 SkASSERT(Definition::Type::kWord == tokenIter->fType); // should be enum name
2601 tokenIter = std::next(tokenIter);
2602 SkASSERT(tokenEnd != tokenIter);
2603 if (Definition::Type::kKeyWord == tokenIter->fType) {
2604 SkASSERT((unsigned) tokenIter->fKeyWord < SK_ARRAY_COUNT(kKeyWords));
2605 SkASSERT(KeyProperty::kNumber == kKeyWords[(int) tokenIter->fKeyWord].fProperty);
2606 tokenIter = std::next(tokenIter);
2607 SkASSERT(tokenEnd != tokenIter);
2608 }
2609 SkASSERT(Punctuation::kLeftBrace == tokenIter->fPunctuation);
2610 tokenIter = std::next(tokenIter);
2611 SkASSERT(tokenEnd != tokenIter);
2612 SkASSERT(Bracket::kBrace == tokenIter->fBracket);
2613 return parseOneEnumConst(tokenIter->fTokens, markupChild, false);
2614}
2615
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002616bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002617 fParent = &fIncludeMap[name];
2618 fParent->fName = name;
2619 fParent->fFileName = fFileName;
2620 fParent->fType = Definition::Type::kFileType;
2621 fParent->fContentStart = fChar;
2622 fParent->fContentEnd = fEnd;
2623 // parse include file into tree
2624 while (fChar < fEnd) {
2625 if (!this->parseChar()) {
2626 return false;
2627 }
2628 }
2629 // parse tree and add named objects to maps
2630 fParent = &fIncludeMap[name];
2631 if (!this->parseObjects(fParent, nullptr)) {
2632 return false;
2633 }
2634 return true;
2635}
2636
2637bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2638 const char* typeStart = child->fChildren[0]->fContentStart;
2639 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002640 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002641 Definition* markupChild = &markupDef->fTokens.back();
2642 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002643 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002644 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2645 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2646 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2647 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04002648 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clarkabaffd82018-11-15 08:25:12 -05002649 if (!markupChild->fUndocumented) {
2650 classDef.fMembers[uniqueName] = markupChild;
2651 }
Cary Clark8032b982017-07-28 11:04:54 -04002652 if (child->fParentIndex >= 2) {
2653 auto comment = child->fParent->fTokens.begin();
2654 std::advance(comment, child->fParentIndex - 2);
2655 if (Definition::Type::kBracket == comment->fType
2656 && (Bracket::kSlashStar == comment->fBracket
2657 || Bracket::kSlashSlash == comment->fBracket)) {
2658 TextParser parser(&*comment);
2659 do {
2660 parser.skipToAlpha();
2661 if (parser.eof()) {
2662 break;
2663 }
2664 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002665 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002666 if (Bracket::kSlashStar == comment->fBracket) {
2667 const char* commentEnd = parser.strnstr("*/", end);
2668 if (commentEnd) {
2669 end = commentEnd;
2670 }
2671 }
2672 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002673 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002674 Definition* commentChild = &markupDef->fTokens.back();
2675 markupChild->fChildren.emplace_back(commentChild);
2676 parser.skipTo(end);
2677 } while (!parser.eof());
2678 }
2679 }
2680 return true;
2681}
2682
2683bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2684 auto tokenIter = child->fParent->fTokens.begin();
2685 std::advance(tokenIter, child->fParentIndex);
2686 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002687 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002688 bool addConst = false;
2689 auto operatorCheck = tokenIter;
2690 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2691 operatorCheck = std::prev(tokenIter);
2692 }
2693 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002694 auto closeParen = std::next(tokenIter);
2695 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2696 '(' == closeParen->fContentStart[0]);
2697 nameEnd = closeParen->fContentEnd + 1;
2698 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002699 if (Definition::Type::kKeyWord == closeParen->fType &&
2700 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002701 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002702 }
Cary Clarka560c472017-11-27 10:44:06 -05002703 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002704 }
2705 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002706 if (addConst) {
2707 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04002708 }
Cary Clark8032b982017-07-28 11:04:54 -04002709 while (tokenIter != child->fParent->fTokens.begin()) {
2710 auto testIter = std::prev(tokenIter);
2711 switch (testIter->fType) {
2712 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002713 if (testIter == child->fParent->fTokens.begin() &&
2714 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2715 KeyWord::kIfndef == child->fParent->fKeyWord ||
2716 KeyWord::kIf == child->fParent->fKeyWord)) {
2717 std::next(tokenIter);
2718 break;
2719 }
Cary Clark8032b982017-07-28 11:04:54 -04002720 goto keepGoing;
2721 case Definition::Type::kKeyWord: {
2722 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2723 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2724 goto keepGoing;
2725 }
2726 } break;
2727 case Definition::Type::kBracket:
2728 if (Bracket::kAngle == testIter->fBracket) {
2729 goto keepGoing;
2730 }
2731 break;
2732 case Definition::Type::kPunctuation:
2733 if (Punctuation::kSemicolon == testIter->fPunctuation
2734 || Punctuation::kLeftBrace == testIter->fPunctuation
2735 || Punctuation::kColon == testIter->fPunctuation) {
2736 break;
2737 }
2738 keepGoing:
2739 tokenIter = testIter;
2740 continue;
2741 default:
2742 break;
2743 }
2744 break;
2745 }
Cary Clark224c7002018-06-27 11:00:21 -04002746 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002747 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002748 tokenIter->fPrivate = string::npos != nameStr.find("::")
2749 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04002750 auto testIter = child->fParent->fTokens.begin();
2751 SkASSERT(child->fParentIndex > 0);
2752 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002753 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2754 0 == tokenIter->fParentIndex) {
2755 tokenIter = std::next(tokenIter);
2756 }
Cary Clark8032b982017-07-28 11:04:54 -04002757 const char* start = tokenIter->fContentStart;
2758 const char* end = tokenIter->fContentEnd;
2759 const char kDebugCodeStr[] = "SkDEBUGCODE";
2760 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2761 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2762 std::advance(testIter, 1);
2763 start = testIter->fContentStart + 1;
2764 end = testIter->fContentEnd - 1;
2765 } else {
2766 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002767 do {
2768 std::advance(testIter, 1);
2769 if (testIter == child->fParent->fTokens.end()) {
2770 break;
2771 }
Cary Clark8032b982017-07-28 11:04:54 -04002772 switch (testIter->fType) {
2773 case Definition::Type::kPunctuation:
2774 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2775 || Punctuation::kLeftBrace == testIter->fPunctuation
2776 || Punctuation::kColon == testIter->fPunctuation);
2777 end = testIter->fStart;
2778 break;
2779 case Definition::Type::kKeyWord: {
2780 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2781 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2782 continue;
2783 }
2784 } break;
2785 default:
2786 continue;
2787 }
2788 break;
Cary Clark61313f32018-10-08 14:57:48 -04002789 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002790 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002791 while (end > start && ' ' >= end[-1]) {
2792 --end;
2793 }
Cary Clark9174bda2017-09-19 17:39:32 -04002794 if (!markupDef) {
2795 auto parentIter = child->fParent->fTokens.begin();
2796 SkASSERT(child->fParentIndex > 0);
2797 std::advance(parentIter, child->fParentIndex - 1);
2798 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002799 TextParser nameParser(methodName);
2800 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002801 return true; // expect this is inline class definition outside of class
2802 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002803 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2804 fParent, '\0');
2805 Definition* globalMarkupChild = &fGlobals.back();
2806 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2807 globalMarkupChild->fName = globalUniqueName;
2808 if (!this->findComments(*child, globalMarkupChild)) {
2809 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002810 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002811 if (globalMarkupChild->fUndocumented) {
2812 child->fUndocumented = true;
2813 } else {
2814 fIFunctionMap[globalUniqueName] = globalMarkupChild;
2815 }
Cary Clarka560c472017-11-27 10:44:06 -05002816 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002817 }
Cary Clark8032b982017-07-28 11:04:54 -04002818 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002819 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002820 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002821 {
2822 auto mapIter = fIClassMap.find(markupDef->fName);
2823 SkASSERT(fIClassMap.end() != mapIter);
2824 IClassDefinition& classDef = mapIter->second;
2825 SkASSERT(classDef.fStart);
2826 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2827 markupChild->fName = uniqueName;
2828 if (!this->findComments(*child, markupChild)) {
2829 return false;
2830 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002831 if (markupChild->fUndocumented) {
2832 tokenIter->fUndocumented = true;
2833 } else {
2834 classDef.fMethods[uniqueName] = markupChild;
2835 }
Cary Clark8032b982017-07-28 11:04:54 -04002836 }
Cary Clark8032b982017-07-28 11:04:54 -04002837 return true;
2838}
2839
Cary Clark8032b982017-07-28 11:04:54 -04002840bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002841 fPriorObject = nullptr;
2842 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002843 if (!this->parseObject(child, markupDef)) {
2844 return false;
2845 }
Cary Clark0d225392018-06-07 09:59:07 -04002846 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002847 }
2848 return true;
2849}
2850
2851bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2852 // set up for error reporting
2853 fLine = fChar = child->fStart;
2854 fEnd = child->fContentEnd;
2855 // todo: put original line number in child as well
2856 switch (child->fType) {
2857 case Definition::Type::kKeyWord:
2858 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002859 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002860 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002861 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002862 }
2863 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002864 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002865 case KeyWord::kConst:
2866 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002867 if (!this->parseConst(child, markupDef)) {
2868 return child->reportError<bool>("failed to parse const or constexpr");
2869 }
2870 break;
Cary Clark8032b982017-07-28 11:04:54 -04002871 case KeyWord::kEnum:
2872 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002873 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002874 }
2875 break;
2876 case KeyWord::kStruct:
2877 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002878 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002879 }
2880 break;
2881 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002882 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002883 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002884 }
2885 break;
2886 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002887 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002888 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002889 }
2890 break;
2891 case KeyWord::kUnion:
2892 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002893 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002894 }
2895 break;
Cary Clark61313f32018-10-08 14:57:48 -04002896 case KeyWord::kUsing:
2897 if (!this->parseUsing()) {
2898 return child->reportError<bool>("failed to parse using");
2899 }
2900 break;
Cary Clark8032b982017-07-28 11:04:54 -04002901 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002902 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002903 }
2904 break;
2905 case Definition::Type::kBracket:
2906 switch (child->fBracket) {
2907 case Bracket::kParen:
Cary Clark9174bda2017-09-19 17:39:32 -04002908 {
2909 auto tokenIter = child->fParent->fTokens.begin();
2910 std::advance(tokenIter, child->fParentIndex);
2911 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002912 TextParser previousToken(&*tokenIter);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002913 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002914 break;
2915 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002916 if (Bracket::kPound == child->fParent->fBracket &&
2917 KeyWord::kIf == child->fParent->fKeyWord) {
2918 // TODO: this will skip methods named defined() -- for the
2919 // moment there aren't any
2920 if (previousToken.startsWith("defined")) {
2921 break;
2922 }
2923 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002924 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2925 break;
2926 }
Cary Clark73fa9722017-08-29 17:36:51 -04002927 }
Cary Clark0d225392018-06-07 09:59:07 -04002928 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2929 break;
2930 }
Cary Clark8032b982017-07-28 11:04:54 -04002931 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002932 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002933 }
Cary Clark73fa9722017-08-29 17:36:51 -04002934 break;
Cary Clark8032b982017-07-28 11:04:54 -04002935 case Bracket::kSlashSlash:
2936 case Bracket::kSlashStar:
2937 // comments are picked up by parsing objects first
2938 break;
2939 case Bracket::kPound:
2940 // special-case the #xxx xxx_DEFINED entries
2941 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002942 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002943 case KeyWord::kIfndef:
2944 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002945 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002946 if (!this->parseObjects(child, markupDef)) {
2947 return false;
2948 }
2949 break;
2950 }
2951 goto preproError;
2952 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002953 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002954 break;
2955 }
2956 goto preproError;
2957 case KeyWord::kEndif:
2958 if (child->boilerplateEndIf()) {
2959 break;
2960 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002961 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002962 case KeyWord::kInclude:
2963 // ignored for now
2964 break;
2965 case KeyWord::kElse:
Cary Clarkabaffd82018-11-15 08:25:12 -05002966 if (!this->parseObjects(child, markupDef)) {
2967 return false;
2968 }
2969 break;
Cary Clark8032b982017-07-28 11:04:54 -04002970 case KeyWord::kElif:
2971 // todo: handle these
2972 break;
2973 default:
2974 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002975 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002976 }
2977 break;
2978 case Bracket::kAngle:
2979 // pick up templated function pieces when method is found
2980 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002981 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002982 if (!this->parseObjects(child, markupDef)) {
2983 return false;
2984 }
Cary Clark73fa9722017-08-29 17:36:51 -04002985 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002986 case Bracket::kSquare: {
2987 // check to see if parent is operator, the only case we handle so far
2988 auto prev = child->fParent->fTokens.begin();
2989 std::advance(prev, child->fParentIndex - 1);
2990 if (KeyWord::kOperator != prev->fKeyWord) {
2991 return child->reportError<bool>("expected operator overload");
2992 }
2993 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002994 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002995 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002996 }
2997 break;
2998 case Definition::Type::kWord:
2999 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04003000 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04003001 }
3002 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04003003 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04003004 }
3005 break;
3006 default:
Cary Clark9174bda2017-09-19 17:39:32 -04003007 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04003008 break;
3009 }
3010 return true;
3011}
3012
Cary Clarkbbfda252018-03-09 15:32:01 -05003013bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
3014 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04003015}
3016
Cary Clark2f466242017-12-11 16:03:17 -05003017bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
3018 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05003019 typedefParser.skipExact("typedef");
3020 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05003021 string nameStr = typedefParser.typedefName();
3022 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003023 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
3024 child->fLineCount, fParent, '\0');
3025 Definition* globalMarkupChild = &fGlobals.back();
3026 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
3027 globalMarkupChild->fName = globalUniqueName;
3028 if (!this->findComments(*child, globalMarkupChild)) {
3029 return false;
3030 }
Cary Clarkabaffd82018-11-15 08:25:12 -05003031 if (globalMarkupChild->fUndocumented) {
3032 child->fUndocumented = true;
3033 } else {
3034 fITypedefMap[globalUniqueName] = globalMarkupChild;
3035 }
Cary Clark0d225392018-06-07 09:59:07 -04003036 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05003037 return true;
3038 }
3039 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05003040 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05003041 Definition* markupChild = &markupDef->fTokens.back();
3042 markupChild->fName = nameStr;
3043 markupChild->fTerminator = markupChild->fContentEnd;
3044 IClassDefinition& classDef = fIClassMap[markupDef->fName];
3045 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04003046 child->fName = markupDef->fName + "::" + nameStr;
Cary Clarka90ea222018-10-16 10:30:28 -04003047 fITypedefMap[child->fName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04003048 return true;
3049}
3050
3051bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04003052 // incomplete
3053 return true;
3054}
Cary Clark8032b982017-07-28 11:04:54 -04003055
Cary Clark61313f32018-10-08 14:57:48 -04003056bool IncludeParser::parseUsing() {
3057 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04003058 return true;
3059}
3060
3061bool IncludeParser::parseChar() {
3062 char test = *fChar;
3063 if ('\\' == fPrev) {
3064 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04003065// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04003066 fLine = fChar + 1;
3067 }
3068 goto done;
3069 }
3070 switch (test) {
3071 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04003072// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04003073 fLine = fChar + 1;
3074 if (fInChar) {
3075 return reportError<bool>("malformed char");
3076 }
3077 if (fInString) {
3078 return reportError<bool>("malformed string");
3079 }
3080 if (!this->checkForWord()) {
3081 return false;
3082 }
3083 if (Bracket::kPound == this->topBracket()) {
3084 KeyWord keyWord = fParent->fKeyWord;
3085 if (KeyWord::kNone == keyWord) {
3086 return this->reportError<bool>("unhandled preprocessor directive");
3087 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003088 if (fInDefine) {
3089 SkASSERT(KeyWord::kDefine == keyWord);
3090 fInDefine = false;
3091 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05003092 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04003093 this->popBracket();
3094 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003095 if (fInBrace) {
3096 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
3097 fInBrace = nullptr;
3098 }
Cary Clark8032b982017-07-28 11:04:54 -04003099 } else if (Bracket::kSlashSlash == this->topBracket()) {
3100 this->popBracket();
3101 }
3102 break;
3103 case '*':
3104 if (!fInCharCommentString && '/' == fPrev) {
3105 this->pushBracket(Bracket::kSlashStar);
3106 }
3107 if (!this->checkForWord()) {
3108 return false;
3109 }
3110 if (!fInCharCommentString) {
3111 this->addPunctuation(Punctuation::kAsterisk);
3112 }
3113 break;
3114 case '/':
3115 if ('*' == fPrev) {
3116 if (!fInCharCommentString) {
3117 return reportError<bool>("malformed closing comment");
3118 }
3119 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04003120 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05003121 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04003122 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05003123 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04003124 }
3125 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04003126 }
Cary Clark8032b982017-07-28 11:04:54 -04003127 if (!fInCharCommentString && '/' == fPrev) {
3128 this->pushBracket(Bracket::kSlashSlash);
3129 break;
3130 }
3131 if (!this->checkForWord()) {
3132 return false;
3133 }
3134 break;
3135 case '\'':
3136 if (Bracket::kChar == this->topBracket()) {
3137 this->popBracket();
3138 } else if (!fInComment && !fInString) {
3139 if (fIncludeWord) {
3140 return this->reportError<bool>("word then single-quote");
3141 }
3142 this->pushBracket(Bracket::kChar);
3143 }
3144 break;
3145 case '\"':
3146 if (Bracket::kString == this->topBracket()) {
3147 this->popBracket();
3148 } else if (!fInComment && !fInChar) {
3149 if (fIncludeWord) {
3150 return this->reportError<bool>("word then double-quote");
3151 }
3152 this->pushBracket(Bracket::kString);
3153 }
3154 break;
Cary Clark8032b982017-07-28 11:04:54 -04003155 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003156 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04003157 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
3158 this->pushBracket(Bracket::kDebugCode);
3159 break;
3160 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003161 case ':':
3162 case '[':
3163 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04003164 if (fInCharCommentString) {
3165 break;
3166 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003167 if (fInDefine && fInBrace) {
3168 break;
3169 }
Cary Clark8032b982017-07-28 11:04:54 -04003170 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
3171 break;
3172 }
Cary Clark0d225392018-06-07 09:59:07 -04003173 if (fConstExpr) {
3174 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3175 fConstExpr = nullptr;
3176 }
Cary Clark8032b982017-07-28 11:04:54 -04003177 if (!fInBrace) {
3178 if (!this->checkForWord()) {
3179 return false;
3180 }
3181 if (':' == test && !fInFunction) {
3182 break;
3183 }
3184 if ('{' == test) {
3185 this->addPunctuation(Punctuation::kLeftBrace);
3186 } else if (':' == test) {
3187 this->addPunctuation(Punctuation::kColon);
3188 }
3189 }
3190 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
3191 && Bracket::kColon == fInBrace->fBracket) {
3192 Definition* braceParent = fParent->fParent;
3193 braceParent->fChildren.pop_back();
3194 braceParent->fTokens.pop_back();
3195 fParent = braceParent;
3196 fInBrace = nullptr;
3197 }
3198 this->pushBracket(
3199 '(' == test ? Bracket::kParen :
3200 '[' == test ? Bracket::kSquare :
3201 '{' == test ? Bracket::kBrace :
3202 Bracket::kColon);
3203 if (!fInBrace
3204 && ('{' == test || (':' == test && ' ' >= fChar[1]))
3205 && fInFunction) {
3206 fInBrace = fParent;
3207 }
3208 } break;
3209 case '<':
3210 if (fInCharCommentString || fInBrace) {
3211 break;
3212 }
3213 if (!this->checkForWord()) {
3214 return false;
3215 }
3216 if (fInEnum) {
3217 break;
3218 }
3219 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04003220 // this angle bracket may be an operator or may be a bracket
3221 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04003222 break;
3223 case ')':
3224 case ']':
3225 case '}': {
3226 if (fInCharCommentString) {
3227 break;
3228 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003229 if (fInDefine && fInBrace) {
3230 break;
3231 }
Cary Clark8032b982017-07-28 11:04:54 -04003232 if (!fInBrace) {
3233 if (!this->checkForWord()) {
3234 return false;
3235 }
3236 }
3237 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04003238 Bracket match = ')' == test ? Bracket::kParen :
3239 ']' == test ? Bracket::kSquare : Bracket::kBrace;
3240 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04003241 this->popBracket();
3242 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003243 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04003244 } else {
3245 fInFunction = '}' != test;
3246 }
Cary Clark73fa9722017-08-29 17:36:51 -04003247 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
3248 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04003249 } else if (Bracket::kAngle == this->topBracket()
3250 && match == this->grandParentBracket()) {
3251 this->popBracket();
3252 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04003253 } else {
3254 return reportError<bool>("malformed close bracket");
3255 }
3256 if (popBraceParent) {
3257 Definition* braceParent = fInBrace->fParent;
3258 braceParent->fChildren.pop_back();
3259 braceParent->fTokens.pop_back();
3260 fInBrace = nullptr;
3261 }
3262 } break;
3263 case '>':
3264 if (fInCharCommentString || fInBrace) {
3265 break;
3266 }
3267 if (!this->checkForWord()) {
3268 return false;
3269 }
3270 if (fInEnum) {
3271 break;
3272 }
Cary Clarka560c472017-11-27 10:44:06 -05003273 if (Bracket::kPound == this->topBracket()) {
3274 break;
3275 }
Cary Clark8032b982017-07-28 11:04:54 -04003276 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04003277 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04003278 this->popBracket();
3279 } else {
3280 return reportError<bool>("malformed close angle bracket");
3281 }
3282 break;
3283 case '#': {
3284 if (fInCharCommentString || fInBrace) {
3285 break;
3286 }
3287 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3288 this->pushBracket(Bracket::kPound);
3289 break;
3290 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003291 case ' ':
3292 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3293 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3294 fInBrace = fParent;
3295 // delimiting brackets are space ... unescaped-linefeed
3296 }
Cary Clark8032b982017-07-28 11:04:54 -04003297 case '&':
3298 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003299 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003300 case '-':
3301 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003302 if (fInCharCommentString || fInBrace) {
3303 break;
3304 }
3305 if (!this->checkForWord()) {
3306 return false;
3307 }
3308 break;
Cary Clark0d225392018-06-07 09:59:07 -04003309 case '=':
3310 if (fInCharCommentString || fInBrace) {
3311 break;
3312 }
3313 if (!this->checkForWord()) {
3314 return false;
3315 }
Cary Clarkd7895502018-07-18 15:10:08 -04003316 if (!fParent->fTokens.size()) {
3317 break;
3318 }
Cary Clark0d225392018-06-07 09:59:07 -04003319 {
3320 const Definition& lastToken = fParent->fTokens.back();
3321 if (lastToken.fType != Definition::Type::kWord) {
3322 break;
3323 }
3324 string name(lastToken.fContentStart, lastToken.length());
3325 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3326 break;
3327 }
3328 // find token on start of line
3329 auto lineIter = fParent->fTokens.end();
3330 do {
Cary Clark80247e52018-07-11 16:18:41 -04003331 if (fParent->fTokens.begin() == lineIter) {
3332 break;
3333 }
Cary Clark0d225392018-06-07 09:59:07 -04003334 --lineIter;
3335 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003336 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003337 ++lineIter;
3338 }
3339 Definition* lineStart = &*lineIter;
3340 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3341 bool sawConst = false;
3342 bool sawStatic = false;
3343 bool sawTemplate = false;
3344 bool sawType = false;
3345 while (&lastToken != &*lineIter) {
3346 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3347 if (sawConst || sawStatic || sawTemplate) {
3348 sawConst = false;
3349 break;
3350 }
3351 if (&lastToken == &*++lineIter) {
3352 break;
3353 }
3354 if (KeyWord::kTypename != lineIter->fKeyWord) {
3355 break;
3356 }
3357 if (&lastToken == &*++lineIter) {
3358 break;
3359 }
3360 if (Definition::Type::kWord != lineIter->fType) {
3361 break;
3362 }
3363 sawTemplate = true;
3364 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3365 if (sawConst || sawStatic) {
3366 sawConst = false;
3367 break;
3368 }
3369 sawStatic = true;
3370 } else if (KeyWord::kConst == lineIter->fKeyWord
3371 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3372 if (sawConst) {
3373 sawConst = false;
3374 break;
3375 }
3376 sawConst = true;
3377 } else {
3378 if (sawType) {
3379 sawType = false;
3380 break;
3381 }
3382 if (Definition::Type::kKeyWord == lineIter->fType
3383 && KeyProperty::kNumber
3384 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3385 sawType = true;
3386 } else if (Definition::Type::kWord == lineIter->fType) {
3387 string typeName(lineIter->fContentStart, lineIter->length());
3388 if ("Sk" != name.substr(0, 2)) {
3389 sawType = true;
3390 }
3391 }
3392 }
3393 ++lineIter;
3394 }
3395 if (sawType && sawConst) {
3396 // if found, name first
3397 lineStart->fName = name;
3398 lineStart->fMarkType = MarkType::kConst;
3399 fParent->fChildren.emplace_back(lineStart);
3400 fConstExpr = lineStart;
3401 }
3402 }
3403 break;
Cary Clark8032b982017-07-28 11:04:54 -04003404 case ';':
3405 if (fInCharCommentString || fInBrace) {
3406 break;
3407 }
3408 if (!this->checkForWord()) {
3409 return false;
3410 }
Cary Clark0d225392018-06-07 09:59:07 -04003411 if (fConstExpr) {
3412 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3413 fConstExpr = nullptr;
3414 }
Cary Clark8032b982017-07-28 11:04:54 -04003415 if (Definition::Type::kKeyWord == fParent->fType
3416 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003417 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3418 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003419 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3420 this->popObject();
3421 }
Cary Clark8032b982017-07-28 11:04:54 -04003422 if (KeyWord::kEnum == fParent->fKeyWord) {
3423 fInEnum = false;
3424 }
Cary Clark61313f32018-10-08 14:57:48 -04003425 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003426 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003427 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3428 this->popObject();
3429 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003430 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003431 } else if (Definition::Type::kBracket == fParent->fType
3432 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3433 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3434 list<Definition>::iterator baseIter = fParent->fTokens.end();
3435 list<Definition>::iterator namedIter = fParent->fTokens.end();
3436 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003437 fParent->fTokens.begin() != tokenIter; ) {
3438 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003439 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003440 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003441 if (namedIter != fParent->fTokens.end()) {
3442 return reportError<bool>("found two named member tokens");
3443 }
3444 namedIter = tokenIter;
3445 }
3446 baseIter = tokenIter;
3447 } else {
3448 break;
3449 }
3450 }
3451 // FIXME: if a member definition spans multiple lines, this won't work
3452 if (namedIter != fParent->fTokens.end()) {
3453 if (baseIter == namedIter) {
3454 return this->reportError<bool>("expected type before named token");
3455 }
3456 Definition* member = &*namedIter;
3457 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003458 if (!member->fTerminator) {
3459 member->fTerminator = member->fContentEnd;
3460 }
Cary Clark8032b982017-07-28 11:04:54 -04003461 fParent->fChildren.push_back(member);
3462 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3463 member->fChildren.push_back(&*nameType);
3464 }
Cary Clark8032b982017-07-28 11:04:54 -04003465 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003466 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003467 } else if (fParent->fChildren.size() > 0) {
3468 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003469 Definition* priorEnum = fPriorEnum;
3470 fPriorEnum = nullptr;
3471 if (!priorEnum) {
3472 while (fParent->fChildren.begin() != lastIter) {
3473 std::advance(lastIter, -1);
3474 priorEnum = *lastIter;
3475 if (Definition::Type::kBracket != priorEnum->fType ||
3476 (Bracket::kSlashSlash != priorEnum->fBracket
3477 && Bracket::kSlashStar != priorEnum->fBracket)) {
3478 break;
3479 }
Cary Clark8032b982017-07-28 11:04:54 -04003480 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003481 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003482 }
3483 if (Definition::Type::kKeyWord == priorEnum->fType
3484 && KeyWord::kEnum == priorEnum->fKeyWord) {
3485 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003486 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003487 while (tokenWalker != fParent->fTokens.end()) {
3488 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003489 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003490 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3491 break;
3492 }
3493 }
3494 while (tokenWalker != fParent->fTokens.end()) {
3495 std::advance(tokenWalker, 1);
3496 const Definition* test = &*tokenWalker;
3497 if (Definition::Type::kBracket != test->fType ||
3498 (Bracket::kSlashSlash != test->fBracket
3499 && Bracket::kSlashStar != test->fBracket)) {
3500 break;
3501 }
3502 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003503 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003504 Definition* start = &*tokenWalker;
3505 bool foundExpected = true;
3506 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3507 const Definition* test = &*tokenWalker;
3508 if (expected != test->fKeyWord) {
3509 foundExpected = false;
3510 break;
3511 }
3512 if (tokenWalker == fParent->fTokens.end()) {
3513 break;
3514 }
3515 std::advance(tokenWalker, 1);
3516 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003517 if (!foundExpected) {
3518 foundExpected = true;
3519 tokenWalker = saveTokenWalker;
3520 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3521 const Definition* test = &*tokenWalker;
3522 if (expected != test->fKeyWord) {
3523 foundExpected = false;
3524 break;
3525 }
3526 if (tokenWalker == fParent->fTokens.end()) {
3527 break;
3528 }
3529 if (KeyWord::kNone != expected) {
3530 std::advance(tokenWalker, 1);
3531 }
3532 }
3533 if (foundExpected) {
3534 auto nameToken = priorEnum->fTokens.begin();
3535 string enumName = string(nameToken->fContentStart,
3536 nameToken->fContentEnd - nameToken->fContentStart);
3537 const Definition* test = &*tokenWalker;
3538 string constType = string(test->fContentStart,
3539 test->fContentEnd - test->fContentStart);
3540 if (enumName != constType) {
3541 foundExpected = false;
3542 } else {
3543 std::advance(tokenWalker, 1);
3544 }
3545 }
3546 }
Cary Clark8032b982017-07-28 11:04:54 -04003547 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3548 const char* nameStart = tokenWalker->fStart;
3549 std::advance(tokenWalker, 1);
3550 if (tokenWalker != fParent->fTokens.end()) {
3551 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003552 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003553 start->fName = string(nameStart, tp.fChar - nameStart);
3554 start->fContentEnd = fChar;
3555 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003556 fPriorEnum = priorEnum;
3557 }
Cary Clark8032b982017-07-28 11:04:54 -04003558 }
3559 }
3560 }
3561 this->addPunctuation(Punctuation::kSemicolon);
3562 fInFunction = false;
3563 break;
3564 case '~':
3565 if (fInEnum) {
3566 break;
3567 }
3568 case '0': case '1': case '2': case '3': case '4':
3569 case '5': case '6': case '7': case '8': case '9':
3570 // TODO: don't want to parse numbers, but do need to track for enum defs
3571 // break;
3572 case 'A': case 'B': case 'C': case 'D': case 'E':
3573 case 'F': case 'G': case 'H': case 'I': case 'J':
3574 case 'K': case 'L': case 'M': case 'N': case 'O':
3575 case 'P': case 'Q': case 'R': case 'S': case 'T':
3576 case 'U': case 'V': case 'W': case 'X': case 'Y':
3577 case 'Z': case '_':
3578 case 'a': case 'b': case 'c': case 'd': case 'e':
3579 case 'f': case 'g': case 'h': case 'i': case 'j':
3580 case 'k': case 'l': case 'm': case 'n': case 'o':
3581 case 'p': case 'q': case 'r': case 's': case 't':
3582 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003583 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003584 if (fInCharCommentString || fInBrace) {
3585 break;
3586 }
3587 if (!fIncludeWord) {
3588 fIncludeWord = fChar;
3589 }
3590 break;
3591 }
3592done:
3593 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003594 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003595 return true;
3596}
3597
3598void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003599 IncludeParser::ValidateKeyWords();
3600}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003601
Cary Clark186d08f2018-04-03 08:43:27 -04003602bool IncludeParser::references(const SkString& file) const {
3603 // if includes weren't passed one at a time, assume all references are valid
3604 if (fIncludeMap.empty()) {
3605 return true;
3606 }
3607 SkASSERT(file.endsWith(".bmh") );
3608 string root(file.c_str(), file.size() - 4);
3609 string kReference("_Reference");
3610 if (string::npos != root.find(kReference)) {
3611 root = root.substr(0, root.length() - kReference.length());
3612 }
3613 if (fIClassMap.end() != fIClassMap.find(root)) {
3614 return true;
3615 }
3616 if (fIStructMap.end() != fIStructMap.find(root)) {
3617 return true;
3618 }
Cary Clark224c7002018-06-27 11:00:21 -04003619 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3620 return true;
3621 }
Cary Clarka90ea222018-10-16 10:30:28 -04003622 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3623 return true;
3624 }
Cary Clark224c7002018-06-27 11:00:21 -04003625 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3626 return true;
3627 }
Cary Clark186d08f2018-04-03 08:43:27 -04003628 return false;
3629}
3630
Cary Clark2dc84ad2018-01-26 12:56:22 -05003631void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3632 if (!sk_isdir(includes)) {
3633 IncludeParser::RemoveOneFile(docs, includes);
3634 } else {
3635 SkOSFile::Iter it(includes, ".h");
3636 for (SkString file; it.next(&file); ) {
3637 SkString p = SkOSPath::Join(includes, file.c_str());
3638 const char* hunk = p.c_str();
3639 if (!SkStrEndsWith(hunk, ".h")) {
3640 continue;
3641 }
3642 IncludeParser::RemoveOneFile(docs, hunk);
3643 }
3644 }
3645}
3646
3647void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3648 const char* lastForward = strrchr(includesFile, '/');
3649 const char* lastBackward = strrchr(includesFile, '\\');
3650 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3651 if (!last) {
3652 last = includesFile;
3653 } else {
3654 last += 1;
3655 }
3656 SkString baseName(last);
3657 SkASSERT(baseName.endsWith(".h"));
3658 baseName.remove(baseName.size() - 2, 2);
3659 baseName.append("_Reference.bmh");
3660 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
3661 remove(fullName.c_str());
3662}
Cary Clark224c7002018-06-27 11:00:21 -04003663
Cary Clarkfd32e722018-11-16 14:36:02 -05003664static const char kMethodMissingStr[] =
3665 "If the method requires documentation, add to "
3666 "%s at minimum:\n" // path to bmh file
3667 "\n"
3668 "#Method %s\n" // method declaration less implementation details
3669 "#In SomeSubtopicName\n"
3670 "#Line # add a one line description here ##\n"
3671 "#Populate\n"
3672 "#NoExample\n"
3673 "// or better yet, use #Example and put C++ code here\n"
3674 "##\n"
3675 "#SeeAlso optional related symbols\n"
3676 "#Method ##\n"
3677 "\n"
3678 "Add to %s, at minimum:\n" // path to include
3679 "\n"
3680 "/** (description) Starts with present tense action verb\n"
3681 " and end with a period.\n"
3682 "%s" // @param, @return if needed go here
3683 "*/\n"
3684 "%s ...\n" // method declaration
3685 "\n"
3686 "If the method does not require documentation,\n"
3687 "add \"private\" or \"experimental\", as in:\n"
3688 "\n"
3689 "/** Experimental, do not use. And so on...\n"
3690 "*/\n"
3691 "%s ...\n" // method declaration
3692 "\n"
3693 ;
3694
3695// bDef does not have #Populate
3696static const char kMethodDiffersNoPopStr[] =
3697 "In %s:\n" // path to bmh file
3698 "#Method %s\n" // method declaration less implementation details
3699 "does not match doxygen comment of:\n"
3700 "%s.\n" // method declaration
3701 "\n"
3702 ;
3703
3704static const char kMethodDiffersStr[] =
3705 "In %s:\n" // path to include
3706 "%s\n" // method declaration
3707 "does not match doxygen comment.\n"
3708 "\n"
3709 ;
3710
3711void IncludeParser::suggestFix(Suggest suggest, const Definition& iDef,
3712 const RootDefinition* root, const Definition* bDef) {
3713 string methodNameStr(iDef.fContentStart, iDef.length());
3714 const char* methodName = methodNameStr.c_str();
3715 TextParser lessImplParser(&iDef);
3716 if (lessImplParser.skipExact("static")) {
3717 lessImplParser.skipWhiteSpace();
3718 }
3719 // TODO : handle debug wrapper
3720 /* bool inDebugWrapper = */ Definition::SkipImplementationWords(lessImplParser);
3721 string lessImplStr(lessImplParser.fChar, lessImplParser.fEnd - lessImplParser.fChar);
3722 const char* methodNameLessImpl = lessImplStr.c_str();
3723 // return result, if any is substr from 0 to location of iDef.fName
3724 size_t namePos = methodNameStr.find(iDef.fName);
3725 SkASSERT(string::npos != namePos);
3726 size_t funcEnd = namePos;
3727 while (funcEnd > 0 && ' ' >= methodNameStr[funcEnd - 1]) {
3728 funcEnd -= 1;
3729 }
3730 string funcRet = methodNameStr.substr(0, funcEnd);
3731// parameters, if any, are delimited by () and separate by ,
3732 TextParser parser(&iDef);
3733 parser.fChar += namePos + iDef.fName.length();
3734 const char* start = parser.fChar;
3735 vector<string> paramStrs;
3736 if ('(' == start[0]) {
3737 parser.skipToBalancedEndBracket('(', ')');
3738 TextParser params(&iDef);
3739 params.fChar = start + 1;
3740 params.fEnd = parser.fChar;
3741 while (!params.eof()) {
3742 const char* paramEnd = params.anyOf("=,)");
3743 const char* paramStart = paramEnd;
3744 while (paramStart > params.fChar && ' ' >= paramStart[-1]) {
3745 paramStart -= 1;
3746 }
3747 while (paramStart > params.fChar && (isalnum(paramStart[-1])
3748 || '_' == paramStart[-1])) {
3749 paramStart -= 1;
3750 }
3751 string param(paramStart, paramEnd - paramStart);
3752 paramStrs.push_back(param);
3753 params.fChar = params.anyOf(",)") + 1;
3754 }
3755 }
3756 string bmhFile = root ? root->fFileName : bDef ? bDef->fFileName : "a *.bmh file";
3757 bool hasFuncReturn = "" != funcRet && "void" != funcRet;
3758 switch(suggest) {
3759 case Suggest::kMethodMissing: {
3760 // if include @param, @return are missing, request them as well
3761 string paramDox;
3762 bool firstParam = true;
3763 for (auto paramStr : paramStrs) {
3764 if (firstParam) {
3765 paramDox += "\n";
3766 firstParam = false;
3767 }
3768 paramDox += " @param " + paramStr + " descriptive phrase\n";
3769 }
3770 if (hasFuncReturn) {
3771 paramDox += "\n";
3772 paramDox += " @return descriptive phrase\n";
3773 }
3774 SkDebugf(kMethodMissingStr, bmhFile.c_str(), methodNameLessImpl, iDef.fFileName.c_str(),
3775 paramDox.c_str(), methodName, methodName);
3776 } break;
3777 case Suggest::kMethodDiffers: {
3778 bool hasPop = std::any_of(bDef->fChildren.begin(), bDef->fChildren.end(),
3779 [](Definition* def) { return MarkType::kPopulate == def->fMarkType; });
3780 if (!hasPop) {
3781 SkDebugf(kMethodDiffersNoPopStr, bmhFile.c_str(), methodNameLessImpl, methodName);
3782 }
3783 SkDebugf(kMethodDiffersStr, iDef.fFileName.c_str(), methodName);
3784 } break;
3785 default:
3786 SkASSERT(0);
3787 }
3788}
3789
Cary Clark224c7002018-06-27 11:00:21 -04003790Bracket IncludeParser::topBracket() const {
3791 Definition* parent = this->parentBracket(fParent);
3792 return parent ? parent->fBracket : Bracket::kNone;
3793}