blob: 7fb5938aee1f6f13c9ba0904d1a513b3f765594f [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) {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500253 if (!i.skipWhiteSpace(&fCheck.fIndent, &fCheck.fWriteReturn)) {
254 return false;
255 }
Cary Clark61313f32018-10-08 14:57:48 -0400256 if (fCheck.fPrivateBrace) {
257 if (i.startsWith("};")) {
258 if (fCheck.fPrivateBrace == fCheck.fBraceCount) {
259 fCheck.fPrivateBrace = 0;
260 fCheck.fDoubleReturn = true;
261 } else {
262 i.skipExact("};");
263 fCheck.fBraceCount -= 1;
264 }
265 return false;
266 }
267 if (i.startsWith("public:")) {
268 if (fCheck.fBraceCount <= fCheck.fPrivateBrace) {
269 fCheck.fPrivateBrace = 0;
270 if (fCheck.fPrivateProtected) {
271 i.skipExact("public:");
272 }
273 } else {
274 i.skipExact("public:");
275 }
276 } else {
277 fCheck.fBraceCount += i.skipToLineBalance('{', '}');
278 }
279 return false;
280 } else if (i.startsWith("};")) {
281 fCheck.fDoubleReturn = 2;
282 }
283 if (i.skipExact(kInline)) {
284 fCheck.fSkipInline = true;
285 return false;
286 }
287 if (i.skipExact(kSK_API)) {
288 fCheck.fSkipAPI = true;
289 return false;
290 }
291 if (i.skipExact(kSK_WARN_UNUSED_RESULT)) {
292 fCheck.fSkipWarnUnused = true;
293 return false;
294 }
295 if (i.skipExact("SK_ATTR_DEPRECATED")) {
296 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
297 return false;
298 }
299 if (i.skipExact("SkDEBUGCODE")) {
300 i.skipWhiteSpace();
301 if ('(' != i.peek()) {
302 i.reportError("expected open paren");
303 }
304 TextParserSave save(&i);
305 SkAssertResult(i.skipToBalancedEndBracket('(', ')'));
306 fCheck.fInDebugCode = i.fChar - 1;
307 save.restore();
308 SkAssertResult('(' == i.next());
309 }
310 if ('{' == i.peek()) {
311 if (looks_like_method(i)) {
312 fCheck.fState = CheckCode::State::kMethod;
313 if (!i.skipToBalancedEndBracket('{', '}')) {
314 i.reportError("unbalanced open brace");
315 }
316 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
317 return false;
318 } else if (looks_like_class_decl(i)) {
319 fCheck.fState = CheckCode::State::kClassDeclaration;
320 fCheck.fPrivateBrace = fCheck.fBraceCount + 1;
321 fCheck.fPrivateProtected = false;
322 }
323 }
324 if (':' == i.peek() && looks_like_constructor(i)) {
325 fCheck.fState = CheckCode::State::kConstructor;
326 skip_constructor_initializers(i);
327 return false;
328 }
329 if ('#' == i.peek()) {
330 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
331 return false;
332 }
333 if (i.startsWith("//")) {
334 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
335 return false;
336 }
337 if (i.startsWith("/*")) {
338 i.skipToEndBracket("*/");
339 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
340 return false;
341 }
342 if (looks_like_forward_declaration(i)) {
343 fCheck.fState = CheckCode::State::kForwardDeclaration;
344 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
345 return false;
346 }
347 if (i.skipExact("private:") || i.skipExact("protected:")) {
348 if (!fCheck.fBraceCount) {
349 i.reportError("expect private in brace");
350 }
351 fCheck.fPrivateBrace = fCheck.fBraceCount;
352 fCheck.fPrivateProtected = true;
353 return false;
354 }
355 const char* funcEnd = i.anyOf("(\n");
356 if (funcEnd && '(' == funcEnd[0] && '_' == *i.anyOf("_(")
357 && (i.contains("internal_", funcEnd, nullptr)
358 || i.contains("private_", funcEnd, nullptr)
359 || i.contains("legacy_", funcEnd, nullptr)
360 || i.contains("temporary_", funcEnd, nullptr))) {
361 i.skipTo(funcEnd);
362 if (!i.skipToBalancedEndBracket('(', ')')) {
363 i.reportError("unbalanced open parent");
364 }
365 i.skipSpace();
366 i.skipExact("const ");
367 i.skipSpace();
368 if (';' == i.peek()) {
369 i.next();
370 }
371 fCheck.fState = CheckCode::State::kNone;
372 return false;
373 }
374 return true;
375}
376
Cary Clarkcb6bef02018-11-29 12:05:25 -0500377void IncludeParser::codeBlockAppend(string& result, string s) const {
378 for (char c : s) {
379 this->codeBlockAppend(result, c);
380 }
381}
382
Cary Clark61313f32018-10-08 14:57:48 -0400383void IncludeParser::codeBlockAppend(string& result, char ch) const {
384 if (Elided::kYes == fElided && fCheck.fBraceCount) {
385 return;
386 }
387 this->stringAppend(result, ch);
388}
389
390void IncludeParser::codeBlockSpaces(string& result, int indent) const {
391 if (!indent) {
392 return;
393 }
394 if (Elided::kYes == fElided && fCheck.fBraceCount) {
395 return;
396 }
397 SkASSERT(indent > 0);
398 if (fDebugWriteCodeBlock) {
399 SkDebugf("%*c", indent, ' ');
400 }
401 result.append(indent, ' ');
402}
403
Cary Clarkcb6bef02018-11-29 12:05:25 -0500404string IncludeParser::writeCodeBlock(const Definition& iDef) {
405 if (MarkType::kComment == iDef.fMarkType) {
406 return "";
407 }
408 if (iDef.fUndocumented) {
409 return "";
410 }
Cary Clark61313f32018-10-08 14:57:48 -0400411 TextParser i(&iDef);
Cary Clarkcb6bef02018-11-29 12:05:25 -0500412 (void) i.skipExact("SkDEBUGCODE(");
413 if (MarkType::kConst == iDef.fMarkType && !i.fEnd) {
414 // TODO: end should have been set earlier
415 auto iter = iDef.fParent->fTokens.begin();
416 std::advance(iter, iDef.fParentIndex + 1);
417 SkASSERT(iter != iDef.fParent->fTokens.end());
418 i.fEnd = iter->fContentStart;
419 }
420 const char* loc;
421 if (MarkType::kMember == iDef.fMarkType) {
422 const char* parentEnd = iDef.fParent->fContentEnd;
423 TextParser newEnd(&iDef);
424 newEnd.fEnd = parentEnd;
425 const char* memberEnd = newEnd.anyOf(",};");
426 if (memberEnd && (';' == memberEnd[0] || ',' == memberEnd[0])) {
427 i.fEnd = memberEnd + 1;
428 }
429 }
430 if (i.contains("//", i.fEnd, &loc)) {
431 i.fEnd = loc;
432 }
433 if (i.contains("/*", i.fEnd, &loc)) {
434 i.fEnd = loc;
435 }
436 if (i.contains("{", i.fEnd, &loc)) {
437 i.fEnd = loc + 1;
438 while (i.fEnd < iDef.fContentEnd && ' ' >= i.fEnd[0]) {
439 ++i.fEnd;
440 }
441 }
442 while (i.fEnd > i.fStart && ' ' == i.fEnd[-1]) {
443 --i.fEnd;
444 }
Cary Clark61313f32018-10-08 14:57:48 -0400445 const char* before = iDef.fContentStart;
446 while (' ' == *--before)
447 ;
448 int startIndent = iDef.fContentStart - before - 1;
Cary Clarkcb6bef02018-11-29 12:05:25 -0500449 bool saveDebugWriteBlock = fDebugWriteCodeBlock;
450 fDebugWriteCodeBlock = false;
451 string result = writeCodeBlock(i, iDef.fMarkType, startIndent);
452 fDebugWriteCodeBlock = saveDebugWriteBlock;
453 if (!result.empty()) {
454 if (MarkType::kNone != fPreviousMarkType && iDef.fMarkType != fPreviousMarkType
455 && ((MarkType::kEnum != fPreviousMarkType
456 && MarkType::kEnumClass != fPreviousMarkType)
457 || MarkType::kMember != iDef.fMarkType)
458 && (MarkType::kMember != fPreviousMarkType
459 || iDef.fParent == fPreviousDef->fParent)) {
460 result = "\n" + result;
461 }
462 if (fDebugWriteCodeBlock) {
463 SkDebugf("%s", result.c_str());
464 }
465 fPreviousDef = &iDef;
466 fPreviousMarkType = iDef.fMarkType;
467 }
468 for (auto& token : iDef.fTokens) {
469 result += this->writeCodeBlock(token);
470 }
471 if (MarkType::kEnum == iDef.fMarkType || MarkType::kEnumClass == iDef.fMarkType
472 || MarkType::kStruct == iDef.fMarkType || MarkType::kClass == iDef.fMarkType) {
473 this->codeBlockSpaces(result, startIndent);
474 this->codeBlockAppend(result, "};\n\n");
475 }
476 return result;
Cary Clark61313f32018-10-08 14:57:48 -0400477}
478
479string IncludeParser::writeCodeBlock(TextParser& i, MarkType markType, int startIndent) {
480 string result;
481 char last;
482 int lastIndent = 0;
483 bool lastDoubleMeUp = false;
484 fCheck.reset();
Cary Clarka90ea222018-10-16 10:30:28 -0400485 if (MarkType::kDefine == markType) {
486 result = "#define ";
487 } else {
488 this->codeBlockSpaces(result, startIndent);
489 }
Cary Clark61313f32018-10-08 14:57:48 -0400490 do {
491 if (!this->advanceInclude(i)) {
492 continue;
493 }
494 do {
495 last = i.peek();
496 SkASSERT(' ' < last);
497 if (fCheck.fInDebugCode == i.fChar) {
498 fCheck.fInDebugCode = nullptr;
499 i.next(); // skip close paren
500 break;
501 }
502 if (CheckCode::State::kMethod == fCheck.fState) {
503 this->codeBlockAppend(result, ';');
504 fCheck.fState = CheckCode::State::kNone;
505 }
506 if (fCheck.fWriteReturn) {
507 this->codeBlockAppend(result, '\n');
508 bool doubleMeUp = i.startsWith("typedef ") || looks_like_const(i)
509 || (!strncmp("struct ", i.fStart, 7) && looks_like_member(i));
510 if ((!--fCheck.fDoubleReturn && !i.startsWith("};")) || i.startsWith("enum ")
511 || i.startsWith("typedef ") || doubleMeUp || fCheck.fTypedefReturn
512 || (fCheck.fIndent && (i.startsWith("class ") || i.startsWith("struct ")))) {
513 if (lastIndent > 0 && (!doubleMeUp || !lastDoubleMeUp)) {
514 this->codeBlockAppend(result, '\n');
515 }
516 fCheck.fTypedefReturn = false;
517 lastDoubleMeUp = doubleMeUp;
518 }
519 if (doubleMeUp) {
520 fCheck.fTypedefReturn = true;
521 }
522 lastIndent = fCheck.fIndent;
523 }
524 if (fCheck.fIndent) {
525 size_t indent = fCheck.fIndent;
526 if (fCheck.fSkipInline && indent > sizeof(kInline)) {
527 indent -= sizeof(kInline) - 1;
528 }
529 if (fCheck.fSkipAPI && indent > sizeof(kSK_API)) {
530 indent -= sizeof(kSK_API) - 1;
531 }
532 if (fCheck.fSkipWarnUnused && indent > sizeof(kSK_WARN_UNUSED_RESULT)) {
533 indent -= sizeof(kSK_WARN_UNUSED_RESULT) - 1;
534 }
535
536 this->codeBlockSpaces(result, indent);
537 }
538 this->codeBlockAppend(result, last);
539 fCheck.fWriteReturn = false;
540 fCheck.fIndent = 0;
541 fCheck.fBraceCount += '{' == last;
542 fCheck.fBraceCount -= '}' == last;
543 if (';' == last) {
544 fCheck.fSkipInline = false;
545 fCheck.fSkipAPI = false;
546 fCheck.fSkipWarnUnused = false;
547 }
548 if (fCheck.fBraceCount < 0) {
549 i.reportError("unbalanced close brace");
550 return result;
551 }
552 i.next();
553 } while (!i.eof() && ' ' < i.peek() && !i.startsWith("//"));
554 } while (!i.eof());
555 if (CheckCode::State::kMethod == fCheck.fState) {
556 this->codeBlockAppend(result, ';');
557 }
Cary Clarkcb6bef02018-11-29 12:05:25 -0500558 bool elided = Elided::kYes == fElided;
559 bool elidedTemplate = elided && !strncmp(i.fStart, "template ", 9);
Cary Clark61313f32018-10-08 14:57:48 -0400560 bool elidedTClass = elidedTemplate && MarkType::kClass == markType;
Cary Clarkcb6bef02018-11-29 12:05:25 -0500561 bool statementEnd = !result.empty() && (MarkType::kMethod == markType
562 || MarkType::kTypedef == markType || '}' == result.back());
563 bool semiEnd = !result.empty() && (',' == result.back() || ';' == result.back());
Cary Clark61313f32018-10-08 14:57:48 -0400564 if (fCheck.fWriteReturn || elidedTClass) {
565 this->codeBlockAppend(result, '\n');
566 }
Cary Clarkcb6bef02018-11-29 12:05:25 -0500567 if (elided && ((MarkType::kFunction != markType && lastIndent > startIndent) || elidedTClass)) {
Cary Clark61313f32018-10-08 14:57:48 -0400568 this->codeBlockAppend(result, '}');
Cary Clarkcb6bef02018-11-29 12:05:25 -0500569 statementEnd = true;
Cary Clark61313f32018-10-08 14:57:48 -0400570 }
Cary Clarkcb6bef02018-11-29 12:05:25 -0500571 if (elided || statementEnd) {
572 this->codeBlockAppend(result, ";\n");
573 } else if (elidedTemplate || semiEnd) {
Cary Clark61313f32018-10-08 14:57:48 -0400574 this->codeBlockAppend(result, '\n');
575 }
576 return result;
577}
578
Ben Wagner63fd7602017-10-09 15:45:33 -0400579void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400580 const vector<string>& foundParams) {
581 for (auto& methodParam : methodParams) {
582 bool found = false;
583 for (auto& foundParam : foundParams) {
584 if (methodParam == foundParam) {
585 found = true;
586 break;
587 }
588 }
589 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400590 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400591 }
592 }
593 for (auto& foundParam : foundParams) {
594 bool found = false;
595 for (auto& methodParam : methodParams) {
596 if (methodParam == foundParam) {
597 found = true;
598 break;
599 }
600 }
601 if (!found) {
602 this->reportError("doxygen param does not match method declaration");
603 }
604 }
605}
606
607bool IncludeParser::checkForWord() {
608 if (!fIncludeWord) {
609 return true;
610 }
611 KeyWord keyWord = FindKey(fIncludeWord, fChar);
Cary Clark224c7002018-06-27 11:00:21 -0400612 if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
613 Bracket bracket = this->topBracket();
614 if (Bracket::kParen == bracket) {
615 return true;
616 }
617 }
Cary Clark8032b982017-07-28 11:04:54 -0400618 if (KeyWord::kNone != keyWord) {
619 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
620 this->addKeyword(keyWord);
621 return true;
622 }
623 } else {
624 this->addWord();
625 return true;
626 }
627 Definition* poundDef = fParent;
628 if (!fParent) {
629 return reportError<bool>("expected parent");
630 }
631 if (Definition::Type::kBracket != poundDef->fType) {
632 return reportError<bool>("expected bracket");
633 }
634 if (Bracket::kPound != poundDef->fBracket) {
635 return reportError<bool>("expected preprocessor");
636 }
637 if (KeyWord::kNone != poundDef->fKeyWord) {
638 return reportError<bool>("already found keyword");
639 }
640 poundDef->fKeyWord = keyWord;
641 fIncludeWord = nullptr;
642 switch (keyWord) {
643 // these do not link to other # directives
644 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400645 if (!fInBrace) {
646 SkASSERT(!fInDefine);
647 fInDefine = true;
648 }
Cary Clark8032b982017-07-28 11:04:54 -0400649 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500650 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400651 break;
652 // these start a # directive link
653 case KeyWord::kIf:
654 case KeyWord::kIfdef:
655 case KeyWord::kIfndef:
656 break;
657 // these continue a # directive link
658 case KeyWord::kElif:
Cary Clarkabaffd82018-11-15 08:25:12 -0500659 case KeyWord::kElse:
Cary Clark8032b982017-07-28 11:04:54 -0400660 this->popObject(); // pop elif
661 if (Bracket::kPound != fParent->fBracket) {
662 return this->reportError<bool>("expected preprocessor directive");
663 }
664 this->popBracket(); // pop if
665 poundDef->fParent = fParent;
Cary Clarkabaffd82018-11-15 08:25:12 -0500666 fParent = poundDef; // push elif back
667 break;
Cary Clark8032b982017-07-28 11:04:54 -0400668 // this ends a # directive link
669 case KeyWord::kEndif:
670 // FIXME : should this be calling popBracket() instead?
671 this->popObject(); // pop endif
672 if (Bracket::kPound != fParent->fBracket) {
673 return this->reportError<bool>("expected preprocessor directive");
674 }
675 this->popBracket(); // pop if/else
676 break;
677 default:
678 SkASSERT(0);
679 }
680 return true;
681}
682
683string IncludeParser::className() const {
684 string name(fParent->fName);
685 size_t slash = name.find_last_of("/");
686 if (string::npos == slash) {
687 slash = name.find_last_of("\\");
688 }
689 SkASSERT(string::npos != slash);
690 string result = name.substr(slash);
691 result = result.substr(1, result.size() - 3);
692 return result;
693}
694
Cary Clarka90ea222018-10-16 10:30:28 -0400695void IncludeParser::writeCodeBlock() {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500696 fElided = Elided::kNo;
Cary Clark61313f32018-10-08 14:57:48 -0400697 for (auto& classMapper : fIClassMap) {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500698 fPreviousMarkType = MarkType::kNone;
699 fPreviousDef = nullptr;
700 classMapper.second.fCode = this->writeCodeBlock(classMapper.second);
Cary Clark61313f32018-10-08 14:57:48 -0400701 }
702 for (auto& enumMapper : fIEnumMap) {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500703 fPreviousMarkType = MarkType::kNone;
704 fPreviousDef = nullptr;
705 enumMapper.second->fCode = this->writeCodeBlock(*enumMapper.second);
Cary Clarka90ea222018-10-16 10:30:28 -0400706 }
707 for (auto& typedefMapper : fITypedefMap) {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500708 fPreviousMarkType = MarkType::kNone;
709 fPreviousDef = nullptr;
710 typedefMapper.second->fCode = this->writeCodeBlock(*typedefMapper.second);
Cary Clarka90ea222018-10-16 10:30:28 -0400711 }
712 for (auto& defineMapper : fIDefineMap) {
Cary Clarkcb6bef02018-11-29 12:05:25 -0500713 fPreviousMarkType = MarkType::kNone;
714 fPreviousDef = nullptr;
715 defineMapper.second->fCode = this->writeCodeBlock(*defineMapper.second);
716 }
717}
718
719void IncludeParser::checkName(Definition* def) {
720 SkASSERT(!def->fName.empty());
721 TextParser parser(def->fFileName, &def->fName.front(), &def->fName.back() + 1, def->fLineCount);
722 const vector<string> skipWords = { "deprecated", "experimental", "internal", "private",
723 "legacy", "temporary" };
724 if (!parser.anyWord(skipWords, 0).empty()) {
725 def->fUndocumented = true;
Cary Clark61313f32018-10-08 14:57:48 -0400726 }
727}
728
Cary Clark884dd7d2017-10-11 10:37:52 -0400729#include <sstream>
730#include <iostream>
731
Cary Clarkfd32e722018-11-16 14:36:02 -0500732void IncludeParser::checkTokens(list<Definition>& tokens, string key, string className,
733 RootDefinition* root, BmhParser& bmhParser) {
734 for (const auto& token : tokens) {
735 if (token.fPrivate) {
736 continue;
737 }
738 string fullName = key + "::" + token.fName;
739 const Definition* def = nullptr;
740 if (root) {
741 def = root->find(fullName, RootDefinition::AllowParens::kYes);
742 }
743 switch (token.fMarkType) {
744 case MarkType::kMethod: {
745 if (this->isInternalName(token)) {
746 continue;
747 }
748 if (!root) {
749 if (token.fUndocumented) {
750 break;
751 }
752 auto methIter = bmhParser.fMethodMap.find(token.fName);
753 if (bmhParser.fMethodMap.end() != methIter) {
754 def = &methIter->second;
755 if (def->crossCheck2(token)) {
756 def->fVisited = true;
757 } else {
758 this->suggestFix(Suggest::kMethodDiffers, token, root, def);
759 fFailed = true;
760 }
761 } else {
762 this->suggestFix(Suggest::kMethodMissing, token, root, nullptr);
763 fFailed = true;
764 }
765 break;
766 }
767 if (!def) {
768 string paramName = className + "::";
769 paramName += string(token.fContentStart,
770 token.fContentEnd - token.fContentStart);
771 if (string::npos != paramName.find('\n')) {
772 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
773 paramName.end());
774 }
775 def = root->find(paramName, RootDefinition::AllowParens::kYes);
776 if (!def && 0 == token.fName.find("operator")) {
777 string operatorName = className + "::";
778 TextParser oper("", token.fStart, token.fContentEnd, 0);
779 const char* start = oper.strnstr("operator", token.fContentEnd);
780 SkASSERT(start);
781 oper.skipTo(start);
782 oper.skipToEndBracket('(');
783 int parens = 0;
784 do {
785 if ('(' == oper.peek()) {
786 ++parens;
787 } else if (')' == oper.peek()) {
788 --parens;
789 }
790 } while (!oper.eof() && oper.next() && parens > 0);
791 operatorName += string(start, oper.fChar - start);
792 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
793 }
794 }
795 if (!def) {
796 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
797 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
798 const char* tokenEnd = token.methodEnd();
799 string constructorName = className + "::";
800 constructorName += string(token.fContentStart + skip,
801 tokenEnd - token.fContentStart - skip);
802 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
803 }
804 if (!def && 0 == token.fName.find("SK_")) {
805 string incName = token.fName + "()";
806 string macroName = className + "::" + incName;
807 def = root->find(macroName, RootDefinition::AllowParens::kYes);
808 if (def) {
809 if (def->fName == incName) {
810 def->fVisited = true;
811 if ("SK_TO_STRING_NONVIRT" == token.fName) {
812 def = root->find(className + "::toString",
813 RootDefinition::AllowParens::kYes);
814 if (def) {
815 def->fVisited = true;
816 } else {
817 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
818 fFailed = true;
819 }
820 }
821 break;
822 } else {
823 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
824 fFailed = true;
825 }
826 }
827 }
828 if (!def) {
829 bool allLower = true;
830 for (size_t index = 0; index < token.fName.length(); ++index) {
831 if (!islower(token.fName[index])) {
832 allLower = false;
833 break;
834 }
835 }
836 if (allLower) {
837 string lowerName = className + "::" + token.fName + "()";
838 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
839 }
840 }
841 if (!def) {
842 if (0 == token.fName.find("SkDEBUGCODE")) {
843 break;
844 }
845 }
846 if (!def) {
847 // simple method names inside nested classes have a bug and are missing trailing parens
848 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
849 def = root->find(withParens, RootDefinition::AllowParens::kNo);
850 }
851 if (!def) {
852 if (!token.fUndocumented) {
853 this->suggestFix(Suggest::kMethodMissing, token, root, nullptr);
854 fFailed = true;
855 }
856 break;
857 }
858 if (token.fUndocumented) {
859 // we can't report an error yet; if bmh documents this unnecessarily,
860 // we'll detect that later. It may be that def points to similar
861 // documented function.
862 break;
863 }
864 if (def->crossCheck2(token)) {
865 def->fVisited = true;
866 } else {
867 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
868 fFailed = true;
869 }
870 } break;
871 case MarkType::kComment:
872 break;
873 case MarkType::kEnumClass:
874 case MarkType::kEnum: {
875 if (!def) {
876 // work backwards from first word to deduce #Enum name
877 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
878 SkAssertResult(firstMember.skipName("enum"));
879 SkAssertResult(firstMember.skipToEndBracket('{'));
880 firstMember.next();
881 firstMember.skipWhiteSpace();
882 SkASSERT('k' == firstMember.peek());
883 const char* savePos = firstMember.fChar;
884 firstMember.skipToNonName();
885 const char* wordEnd = firstMember.fChar;
886 firstMember.fChar = savePos;
887 const char* lastUnderscore = nullptr;
888 do {
889 if (!firstMember.skipToEndBracket('_')) {
890 break;
891 }
892 if (firstMember.fChar > wordEnd) {
893 break;
894 }
895 lastUnderscore = firstMember.fChar;
896 } while (firstMember.next());
897 if (lastUnderscore) {
898 ++lastUnderscore;
899 string enumName(lastUnderscore, wordEnd - lastUnderscore);
900 if (root) {
901 string anonName = className + "::" + enumName + 's';
902 def = root->find(anonName, RootDefinition::AllowParens::kYes);
903 } else {
904 auto enumIter = bmhParser.fEnumMap.find(enumName);
905 if (bmhParser.fEnumMap.end() != enumIter) {
906 RootDefinition* rootDef = &enumIter->second;
907 def = rootDef;
908 }
909 }
910 }
911 if (!def && !root) {
912 auto enumIter = bmhParser.fEnumMap.find(token.fName);
913 if (bmhParser.fEnumMap.end() != enumIter) {
914 def = &enumIter->second;
915 }
916 if (!def) {
917 auto enumClassIter = bmhParser.fClassMap.find(token.fName);
918 if (bmhParser.fClassMap.end() != enumClassIter) {
919 def = &enumClassIter->second;
920 }
921 }
922 }
923 if (!def) {
924 if (!token.fUndocumented) {
925 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
926 fFailed = true;
927 }
928 break;
929 }
930 }
931 def->fVisited = true;
932 bool hasCode = false;
933 bool hasPopulate = true;
934 for (auto& child : def->fChildren) {
935 if (MarkType::kCode == child->fMarkType) {
936 hasPopulate = std::any_of(child->fChildren.begin(),
937 child->fChildren.end(), [](auto grandChild){
938 return MarkType::kPopulate == grandChild->fMarkType; });
939 if (!hasPopulate) {
940 def = child;
941 }
942 hasCode = true;
943 break;
944 }
945 }
946 if (!hasCode && !root) {
947 const Definition* topic = def->topicParent();
948 hasCode = std::any_of(topic->fChildren.begin(), topic->fChildren.end(),
949 [](Definition* def){ return MarkType::kCode == def->fMarkType
950 && def->fChildren.size() > 0 && MarkType::kPopulate ==
951 def->fChildren.front()->fMarkType; });
952 }
953 if (!hasCode) {
954 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
955 fFailed = true;
956 break;
957 }
958 if (!hasPopulate) {
959 if (def->crossCheck(token)) {
960 def->fVisited = true;
961 } else {
962 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
963 fFailed = true;
964 }
965 }
966 for (auto& member : token.fTokens) {
967 if (MarkType::kMember != member.fMarkType) {
968 continue;
969 }
970 string constName = MarkType::kEnumClass == token.fMarkType ?
971 fullName : className;
972 if (root) {
973 constName += "::" + member.fName;
974 def = root->find(constName, RootDefinition::AllowParens::kYes);
975 } else {
976 auto enumMapper = bmhParser.fEnumMap.find(token.fName);
977 if (bmhParser.fEnumMap.end() != enumMapper) {
978 auto& enumDoc = enumMapper->second;
979 auto memberIter = enumDoc.fLeaves.find(member.fName);
980 if (enumDoc.fLeaves.end() != memberIter) {
981 def = &memberIter->second;
982 }
983 }
984 }
985 if (!def) {
986 string innerName = key + "::" + member.fName;
987 def = root->find(innerName, RootDefinition::AllowParens::kYes);
988 }
989 if (!def) {
990 if (!member.fUndocumented) {
991 SkDebugf("const missing from bmh: %s\n", constName.c_str());
992 fFailed = true;
993 }
994 } else {
995 def->fVisited = true;
996 }
997 }
998 } break;
999 case MarkType::kMember:
1000 if (def) {
1001 def->fVisited = true;
1002 } else {
1003 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
1004 fFailed = true;
1005 }
1006 break;
1007 case MarkType::kTypedef:
1008 if (!def && !root) {
1009 auto typedefIter = bmhParser.fTypedefMap.find(token.fName);
1010 if (bmhParser.fTypedefMap.end() != typedefIter) {
1011 def = &typedefIter->second;
1012 }
1013 }
1014 if (def) {
1015 def->fVisited = true;
1016 } else {
1017 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
1018 fFailed = true;
1019 }
1020 break;
1021 case MarkType::kConst:
1022 if (!def && !root) {
1023 auto constIter = bmhParser.fConstMap.find(token.fName);
1024 if (bmhParser.fConstMap.end() != constIter) {
1025 def = &constIter->second;
1026 }
1027 }
1028 if (def) {
1029 def->fVisited = true;
1030 } else {
1031 if (!token.fUndocumented) {
1032 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
1033 fFailed = true;
1034 }
1035 }
1036 break;
1037 case MarkType::kDefine:
1038 // TODO: incomplete
1039 break;
1040 default:
1041 SkASSERT(0); // unhandled
1042 break;
1043 }
1044 }
1045}
1046
Cary Clark8032b982017-07-28 11:04:54 -04001047bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -04001048 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001049 string className = classMapper.first;
1050 auto finder = bmhParser.fClassMap.find(className);
1051 if (bmhParser.fClassMap.end() == finder) {
1052 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -04001053 continue;
1054 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001055 }
1056 for (auto& classMapper : fIClassMap) {
Cary Clarkabaffd82018-11-15 08:25:12 -05001057 if (classMapper.second.fUndocumented) {
1058 continue;
1059 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001060 string className = classMapper.first;
1061 std::istringstream iss(className);
1062 string classStr;
1063 string classBase;
1064 RootDefinition* root = nullptr;
1065 while (std::getline(iss, classStr, ':')) {
1066 if (root) {
1067 if (!classStr.length()) {
1068 continue;
1069 }
1070 classBase += "::" + classStr;
1071 auto finder = root->fBranches.find(classBase);
1072 if (root->fBranches.end() != finder) {
1073 root = finder->second;
1074 } else {
1075 SkASSERT(0);
1076 }
1077 } else {
1078 classBase = classStr;
1079 auto finder = bmhParser.fClassMap.find(classBase);
1080 if (bmhParser.fClassMap.end() != finder) {
1081 root = &finder->second;
1082 } else {
1083 SkASSERT(0);
1084 }
1085 }
1086 }
Cary Clarkfd32e722018-11-16 14:36:02 -05001087 this->checkTokens(classMapper.second.fTokens, classMapper.first, className, root,
1088 bmhParser);
Cary Clark8032b982017-07-28 11:04:54 -04001089 }
Cary Clarkfd32e722018-11-16 14:36:02 -05001090 this->checkTokens(fGlobals, "", "", nullptr, bmhParser);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001091 int crossChecks = 0;
1092 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -04001093 for (auto& classMapper : fIClassMap) {
1094 string className = classMapper.first;
1095 auto finder = bmhParser.fClassMap.find(className);
1096 if (bmhParser.fClassMap.end() == finder) {
1097 continue;
1098 }
1099 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -05001100 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -05001101 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -04001102 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001103 if (crossChecks) {
1104 SkDebugf(".");
1105 } else {
1106 SkDebugf("cross-check");
1107 firstCheck = className;
1108 }
1109 ++crossChecks;
1110 }
1111 if (crossChecks) {
1112 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -05001113 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -05001114 }
1115 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -04001116 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001117 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -05001118 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -04001119}
1120
1121IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001122 string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001123 string className;
1124 const Definition* test = fParent;
1125 while (Definition::Type::kFileType != test->fType) {
1126 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
1127 className = test->fName + "::";
1128 break;
1129 }
1130 test = test->fParent;
1131 }
Ben Wagner63fd7602017-10-09 15:45:33 -04001132 className += name;
Cary Clark8032b982017-07-28 11:04:54 -04001133 unordered_map<string, IClassDefinition>& map = fIClassMap;
1134 IClassDefinition& markupDef = map[className];
1135 if (markupDef.fStart) {
1136 typedef IClassDefinition* IClassDefPtr;
1137 return INHERITED::reportError<IClassDefPtr>("class already defined");
1138 }
1139 markupDef.fFileName = fFileName;
1140 markupDef.fStart = includeDef.fStart;
1141 markupDef.fContentStart = includeDef.fStart;
1142 markupDef.fName = className;
Cary Clarkcb6bef02018-11-29 12:05:25 -05001143 this->checkName(&markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04001144 markupDef.fContentEnd = includeDef.fContentEnd;
1145 markupDef.fTerminator = includeDef.fTerminator;
1146 markupDef.fParent = fParent;
1147 markupDef.fLineCount = fLineCount;
1148 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
1149 MarkType::kStruct : MarkType::kClass;
1150 markupDef.fKeyWord = includeDef.fKeyWord;
1151 markupDef.fType = Definition::Type::kMark;
Cary Clarkabaffd82018-11-15 08:25:12 -05001152 auto tokenIter = includeDef.fParent->fTokens.begin();
1153 SkASSERT(includeDef.fParentIndex > 0);
1154 std::advance(tokenIter, includeDef.fParentIndex - 1);
1155 const Definition* priorComment = &*tokenIter;
1156 markupDef.fUndocumented = priorComment->fUndocumented;
Cary Clark8032b982017-07-28 11:04:54 -04001157 fParent = &markupDef;
1158 return &markupDef;
1159}
1160
1161void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
1162 auto& tokens = classDef.fTokens;
Cary Clarkcb6bef02018-11-29 12:05:25 -05001163 bool wroteTail = true;
Cary Clark8032b982017-07-28 11:04:54 -04001164 for (auto& token : tokens) {
1165 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
1166 continue;
1167 }
Cary Clarkcb6bef02018-11-29 12:05:25 -05001168 if (wroteTail && MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001169 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -04001170 }
1171 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -04001172 case MarkType::kConst:
1173 this->dumpConst(token, classDef.fName);
1174 break;
Cary Clark8032b982017-07-28 11:04:54 -04001175 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -05001176 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -05001177 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -04001178 break;
1179 case MarkType::kMethod:
Cary Clarkcb6bef02018-11-29 12:05:25 -05001180 if (!this->dumpMethod(token, classDef.fName)) {
1181 wroteTail = false;
1182 continue;
1183 }
Cary Clark8032b982017-07-28 11:04:54 -04001184 break;
1185 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -04001186 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -04001187 continue;
1188 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001189 case MarkType::kTypedef:
1190 this->dumpTypedef(token, classDef.fName);
1191 break;
Cary Clark8032b982017-07-28 11:04:54 -04001192 default:
1193 SkASSERT(0);
1194 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001195 this->dumpCommonTail(token);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001196 wroteTail = true;
Cary Clark8032b982017-07-28 11:04:54 -04001197 }
1198}
Cary Clark9174bda2017-09-19 17:39:32 -04001199void IncludeParser::dumpComment(const Definition& token) {
1200 fLineCount = token.fLineCount;
1201 fChar = fLine = token.fContentStart;
1202 fEnd = token.fContentEnd;
Cary Clark9174bda2017-09-19 17:39:32 -04001203 if (MarkType::kMethod == token.fMarkType) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001204 this->lf(2);
1205 this->writeTag("Populate");
1206 this->lf(2);
1207 return;
Cary Clark8032b982017-07-28 11:04:54 -04001208 }
Cary Clark9174bda2017-09-19 17:39:32 -04001209 for (const auto& child : token.fTokens) {
1210 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
1211 break;
1212 }
Cary Clark8032b982017-07-28 11:04:54 -04001213 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001214 if (child.fPrivate) {
1215 break;
1216 }
Cary Clarkcb6bef02018-11-29 12:05:25 -05001217 if (child.length() > 1) {
Cary Clark9174bda2017-09-19 17:39:32 -04001218 const char* start = child.fContentStart;
1219 ptrdiff_t length = child.fContentEnd - start;
1220 SkASSERT(length >= 0);
1221 while (length && '/' == start[0]) {
1222 start += 1;
1223 --length;
Cary Clark8032b982017-07-28 11:04:54 -04001224 }
Cary Clark9174bda2017-09-19 17:39:32 -04001225 while (length && '/' == start[length - 1]) {
1226 length -= 1;
1227 if (length && '*' == start[length - 1]) {
1228 length -= 1;
1229 }
1230 }
1231 if (length) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001232 this->lf(2);
1233 if ("!< " == string(start, length).substr(0, 3)) {
1234 return;
Cary Clark9174bda2017-09-19 17:39:32 -04001235 }
1236 this->writeBlock(length, start);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001237 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001238 }
1239 }
1240 }
1241 }
Cary Clark8032b982017-07-28 11:04:54 -04001242}
1243
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001244void IncludeParser::dumpCommonTail(const Definition& token) {
1245 this->lf(2);
1246 this->writeTag("Example");
1247 this->lf(1);
1248 this->writeString("// incomplete");
1249 this->lf(1);
1250 this->writeEndTag();
1251 this->lf(2);
1252 this->writeTag("SeeAlso");
1253 this->writeSpace();
1254 this->writeString("incomplete");
1255 this->lf(2);
1256 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
1257 this->lf(2);
1258}
1259
Cary Clark224c7002018-06-27 11:00:21 -04001260void IncludeParser::dumpConst(const Definition& token, string className) {
1261 this->writeTag("Const");
1262 this->writeSpace();
1263 this->writeString(token.fName);
1264 this->writeTagTable("Line", "incomplete");
1265 this->lf(2);
1266 this->dumpComment(token);
1267}
1268
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001269void IncludeParser::dumpDefine(const Definition& token) {
1270 this->writeTag("Define", token.fName);
1271 this->lf(2);
1272 this->writeTag("Code");
1273 this->lfAlways(1);
1274 this->writeString("###$");
1275 this->lfAlways(1);
1276 this->indentToColumn(4);
1277 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
1278 this->lf(1);
1279 this->indentToColumn(0);
1280 this->writeString("$$$#");
1281
1282 this->writeEndTag();
1283 this->lf(2);
1284 this->dumpComment(token);
1285 for (auto& child : token.fTokens) {
1286 if (MarkType::kComment == child.fMarkType) {
1287 continue;
1288 }
1289 this->writeTag("Param", child.fName);
1290 this->writeSpace();
1291 this->writeString("incomplete");
1292 this->writeSpace();
1293 this->writeString("##");
1294 this->lf(1);
1295 }
1296}
1297
1298void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001299 string tagType(MarkType::kEnum == token.fMarkType ? "Enum" : "EnumClass");
1300 this->writeTag(tagType.c_str(), token.fName);
Cary Clark9174bda2017-09-19 17:39:32 -04001301 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001302 this->writeTag("Code");
Cary Clarkcb6bef02018-11-29 12:05:25 -05001303 this->writeTag("Populate");
1304 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001305 this->lf(2);
1306 this->dumpComment(token);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001307 string prior;
1308 for (auto& child : token.fTokens) {
1309 if (MarkType::kComment == child.fMarkType) {
1310 prior = string(child.fContentStart, child.length());
1311 }
1312 if (MarkType::kMember != child.fMarkType) {
1313 continue;
1314 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001315 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -04001316 this->writeSpace();
Cary Clarkcb6bef02018-11-29 12:05:25 -05001317 this->writeString(child.fName);
1318 this->writeSpace(2);
1319 this->writeString("0 # incomplete; replace '0' with member value");
Cary Clark9174bda2017-09-19 17:39:32 -04001320 this->lf(1);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001321 this->writeTagNoLF("Line", "#");
1322 this->writeSpace();
1323 if ("/!< " == prior.substr(0, 4)) {
1324 this->writeString(prior.substr(4));
1325 } else {
1326 this->writeString("incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001327 }
Cary Clarkcb6bef02018-11-29 12:05:25 -05001328 this->writeSpace();
1329 this->writeString("##");
1330 this->lf(1);
1331 this->writeString("# incomplete; add description or delete");
Cary Clark9174bda2017-09-19 17:39:32 -04001332 this->writeEndTag();
1333 }
1334 this->lf(2);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001335 this->writeString("# incomplete; add description or delete");
1336 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001337}
1338
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001339bool IncludeParser::dumpGlobals(string* globalFileName, long int* globalTell) {
1340 bool hasGlobals = !fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1341 || !fITemplateMap.empty()|| !fITypedefMap.empty() || !fIUnionMap.empty();
1342 if (!hasGlobals) {
Cary Clark224c7002018-06-27 11:00:21 -04001343 return true;
1344 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001345 size_t lastBSlash = fFileName.rfind('\\');
1346 size_t lastSlash = fFileName.rfind('/');
1347 size_t lastDotH = fFileName.rfind(".h");
1348 SkASSERT(string::npos != lastDotH);
1349 if (string::npos != lastBSlash && (string::npos == lastSlash
1350 || lastBSlash < lastSlash)) {
1351 lastSlash = lastBSlash;
1352 } else if (string::npos == lastSlash) {
1353 lastSlash = -1;
1354 }
1355 lastSlash += 1;
1356 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
1357 string fileName = globalsName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001358 *globalFileName = fileName;
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001359 fOut = fopen(fileName.c_str(), "wb");
1360 if (!fOut) {
1361 SkDebugf("could not open output file %s\n", globalsName.c_str());
1362 return false;
1363 }
1364 string prefixName = globalsName.substr(0, 2);
1365 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
1366 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
1367 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -04001368 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001369 this->lf(2);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001370 if (!fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1371 || !fITemplateMap.empty() || !fITypedefMap.empty() || !fIUnionMap.empty()) {
1372 this->writeTag("Code");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001373 this->writeTag("Populate");
1374 this->writeEndTag();
1375 this->lf(2);
1376 }
1377 std::map<int, Definition*> sortedDefs;
1378 for (const auto& entry : fIDefineMap) {
1379 sortedDefs[entry.second->fLineCount] = entry.second;
1380 }
1381 for (const auto& entry : fIFunctionMap) {
1382 sortedDefs[entry.second->fLineCount] = entry.second;
1383 }
1384 for (const auto& entry : fIEnumMap) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001385 if (string::npos == entry.first.find("::")) {
1386 sortedDefs[entry.second->fLineCount] = entry.second;
1387 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001388 }
1389 for (const auto& entry : fITemplateMap) {
1390 sortedDefs[entry.second->fLineCount] = entry.second;
1391 }
1392 for (const auto& entry : fITypedefMap) {
1393 sortedDefs[entry.second->fLineCount] = entry.second;
1394 }
1395 for (const auto& entry : fIUnionMap) {
1396 sortedDefs[entry.second->fLineCount] = entry.second;
1397 }
1398 for (const auto& entry : sortedDefs) {
1399 const Definition* def = entry.second;
1400 this->writeBlockSeparator();
1401 switch (def->fMarkType) {
1402 case MarkType::kDefine:
1403 this->dumpDefine(*def);
1404 break;
1405 case MarkType::kMethod:
Cary Clarkcb6bef02018-11-29 12:05:25 -05001406 if (!this->dumpMethod(*def, globalsName)) {
1407 continue;
1408 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001409 break;
1410 case MarkType::kEnum:
1411 case MarkType::kEnumClass:
1412 this->dumpEnum(*def, globalsName);
1413 break;
1414 case MarkType::kTemplate:
1415 SkASSERT(0); // incomplete
1416 break;
1417 case MarkType::kTypedef: {
1418 this->writeTag("Typedef");
1419 this->writeSpace();
1420 TextParser parser(def);
1421 if (!parser.skipExact("typedef")) {
1422 return false;
1423 }
1424 if (!parser.skipSpace()) {
1425 return false;
1426 }
1427 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1428 this->lf(2);
1429 this->dumpComment(*def);
1430 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1431 this->lf(2);
1432 } continue;
1433 case MarkType::kUnion:
1434 SkASSERT(0); // incomplete
1435 break;
1436 default:
1437 SkASSERT(0);
1438 }
1439 this->dumpCommonTail(*def);
1440 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001441 *globalTell = ftell(fOut);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001442 this->writeEndTag("Topic", topicName);
1443 this->lfAlways(1);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001444// fclose(fOut); // defer closing in case class needs to be also written here
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001445 return true;
1446}
1447
1448bool IncludeParser::isClone(const Definition& token) {
1449 string name = token.fName;
1450 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1451}
1452
1453bool IncludeParser::isConstructor(const Definition& token, string className) {
1454 string name = token.fName;
1455 return 0 == name.find(className) || '~' == name[0];
1456}
1457
1458bool IncludeParser::isInternalName(const Definition& token) {
1459 string name = token.fName;
1460 // exception for this SkCanvas function .. for now
1461 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1462 return false;
1463 }
1464 return name.substr(0, 7) == "android"
1465 || 0 == token.fName.find("internal_")
1466 || 0 == token.fName.find("Internal_")
1467 || 0 == token.fName.find("legacy_")
1468 || 0 == token.fName.find("temporary_")
1469 || 0 == token.fName.find("private_");
1470}
1471
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001472bool IncludeParser::isMember(const Definition& token) const {
1473 if ('f' == token.fStart[0] && isupper(token.fStart[1])) {
1474 return true;
1475 }
1476 if (!islower(token.fStart[0])) {
1477 return false;
1478 }
1479 // make an exception for SkTextBlob::RunBuffer, sole struct with members not in fXxxx format
1480 if (string::npos != token.fFileName.find("SkTextBlob.h")) {
1481 const Definition* structToken = token.fParent;
1482 if (!structToken) {
1483 return false;
1484 }
1485 if (KeyWord::kStruct != structToken->fKeyWord) {
1486 structToken = token.fParent->fParent;
1487 if (!structToken) {
1488 return false;
1489 }
1490 if (KeyWord::kStruct != structToken->fKeyWord) {
1491 return false;
1492 }
1493 }
1494 SkASSERT(structToken->fTokens.size() > 0);
1495 const Definition& child = structToken->fTokens.front();
1496 string structName(child.fContentStart, child.length());
1497 if ("RunBuffer" != structName) {
1498 return false;
1499 }
1500 string tokenName(token.fContentStart, token.length());
1501 string allowed[] = { "glyphs", "pos", "utf8text", "clusters" };
1502 for (auto allow : allowed) {
1503 if (allow == tokenName) {
1504 return true;
1505 }
1506 }
1507 }
1508 return false;
1509}
1510
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001511bool IncludeParser::isOperator(const Definition& token) {
1512 return "operator" == token.fName.substr(0, 8);
1513}
1514
Cary Clarkcb6bef02018-11-29 12:05:25 -05001515bool IncludeParser::dumpMethod(const Definition& token, string className) {
1516 if (std::any_of(token.fTokens.begin(), token.fTokens.end(),
1517 [=](const Definition& def) { return MarkType::kComment == def.fMarkType
1518 && this->isUndocumentable(def.fFileName, def.fContentStart, def.fContentEnd,
1519 def.fLineCount); } )) {
1520 return false;
1521 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001522 this->writeTag("Method");
1523 this->writeSpace();
1524
1525 string name = string(token.fStart ? token.fStart : token.fContentStart,
1526 token.length());
Cary Clark224c7002018-06-27 11:00:21 -04001527 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001528 string inType;
1529 if (this->isConstructor(token, className)) {
1530 inType = "Constructor";
1531 } else if (this->isOperator(token)) {
1532 inType = "Operator";
1533 } else {
1534 inType = "incomplete";
1535 }
1536 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001537 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001538 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001539 this->dumpComment(token);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001540 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001541}
1542
1543void IncludeParser::dumpMember(const Definition& token) {
1544 this->writeTag("Member");
1545 this->writeSpace();
1546 this->writeDefinition(token, token.fName, 2);
1547 lf(1);
1548 for (auto child : token.fChildren) {
1549 this->writeDefinition(*child);
1550 }
1551 this->writeEndTag();
1552 lf(2);
1553}
1554
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001555bool IncludeParser::dumpTokens() {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001556 string globalFileName;
1557 long int globalTell = 0;
1558 if (!this->dumpGlobals(&globalFileName, &globalTell)) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001559 return false;
1560 }
Cary Clark9174bda2017-09-19 17:39:32 -04001561 for (const auto& member : fIClassMap) {
1562 if (string::npos != member.first.find("::")) {
1563 continue;
1564 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001565 if (!this->dumpTokens(member.first, globalFileName, &globalTell)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001566 return false;
1567 }
1568 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001569 if (globalTell) {
1570 fclose(fOut);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001571 SkDebugf("wrote %s\n", globalFileName.c_str());
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001572 }
Cary Clark9174bda2017-09-19 17:39:32 -04001573 return true;
1574}
1575
Ben Wagner63fd7602017-10-09 15:45:33 -04001576 // dump equivalent markup
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001577bool IncludeParser::dumpTokens(string skClassName, string globalFileName, long int* globalTell) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001578 string fileName = skClassName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001579 if (globalFileName != fileName) {
1580 fOut = fopen(fileName.c_str(), "wb");
1581 if (!fOut) {
1582 SkDebugf("could not open output file %s\n", fileName.c_str());
1583 return false;
1584 }
1585 } else {
1586 fseek(fOut, *globalTell, SEEK_SET);
1587 this->lf(2);
1588 this->writeBlockSeparator();
1589 *globalTell = 0;
Cary Clark8032b982017-07-28 11:04:54 -04001590 }
1591 string prefixName = skClassName.substr(0, 2);
1592 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1593 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001594 if (globalFileName != fileName) {
1595 this->writeTagNoLF("Topic", topicName);
1596 this->writeEndTag("Alias", topicName + "_Reference");
1597 this->lf(2);
1598 }
Cary Clark8032b982017-07-28 11:04:54 -04001599 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001600 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1601 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001602 this->writeTag(containerType, skClassName);
1603 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001604 auto& tokens = classMap.fTokens;
1605 for (auto& token : tokens) {
1606 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1607 continue;
1608 }
Cary Clark9174bda2017-09-19 17:39:32 -04001609 this->writeDefinition(token);
1610 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001611 }
1612 this->lf(2);
Cary Clarkcb6bef02018-11-29 12:05:25 -05001613 this->writeTag("Code");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001614 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001615 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001616 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001617 for (auto& oneClass : fIClassMap) {
1618 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1619 continue;
1620 }
1621 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001622 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001623 KeyWord keyword = oneClass.second.fKeyWord;
1624 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1625 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001626 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001627 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001628 this->lf(2);
1629 this->writeTag("Code");
1630 this->writeEndTag("ToDo", "fill this in manually");
1631 this->writeEndTag();
1632 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001633 for (auto& token : oneClass.second.fTokens) {
1634 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1635 continue;
1636 }
Cary Clark9174bda2017-09-19 17:39:32 -04001637 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001638 }
1639 this->lf(2);
1640 this->dumpClassTokens(oneClass.second);
1641 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001642 this->writeEndTag(containerType, innerName);
1643 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001644 }
1645 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001646 this->writeEndTag(containerType, skClassName);
1647 this->lf(2);
1648 this->writeEndTag("Topic", topicName);
1649 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001650 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001651 SkDebugf("wrote %s\n", fileName.c_str());
1652 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001653}
1654
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001655void IncludeParser::dumpTypedef(const Definition& token, string className) {
1656 this->writeTag("Typedef");
1657 this->writeSpace();
1658 this->writeString(token.fName);
1659 this->writeTagTable("Line", "incomplete");
1660 this->lf(2);
1661 this->dumpComment(token);
1662}
1663
Cary Clark61313f32018-10-08 14:57:48 -04001664string IncludeParser::elidedCodeBlock(const Definition& iDef) {
1665 SkASSERT(KeyWord::kStruct == iDef.fKeyWord || KeyWord::kClass == iDef.fKeyWord
1666 || KeyWord::kTemplate == iDef.fKeyWord);
1667 TextParser i(&iDef);
1668 fElided = Elided::kYes;
1669 MarkType markType = MarkType::kClass;
1670 if (KeyWord::kTemplate == iDef.fKeyWord) { // may be function
1671 for (auto child : iDef.fChildren) {
1672 if (MarkType::kMethod == child->fMarkType) {
1673 markType = MarkType::kFunction;
1674 break;
1675 }
1676 }
1677 }
1678 return this->writeCodeBlock(i, markType, 0);
1679}
1680
Cary Clarka90ea222018-10-16 10:30:28 -04001681 string IncludeParser::filteredBlock(string inContents, string filterContents) {
1682 string result;
1683 const unordered_map<string, Definition*>* mapPtr = nullptr;
Cary Clarka90ea222018-10-16 10:30:28 -04001684 if ("Constant" == inContents) {
1685 mapPtr = &fIConstMap;
Cary Clarka90ea222018-10-16 10:30:28 -04001686 } else {
1687 SkASSERT(0); // only Constant supported for now
1688 }
Cary Clark4b076532018-10-29 14:17:22 -04001689 vector<Definition*> consts;
Cary Clarka90ea222018-10-16 10:30:28 -04001690 for (auto entry : *mapPtr) {
1691 if (string::npos == entry.first.find(filterContents)) {
1692 continue;
1693 }
Cary Clark4b076532018-10-29 14:17:22 -04001694 consts.push_back(entry.second);
1695 }
1696 std::sort(consts.begin(), consts.end(), [](Definition* def1, Definition* def2) {
1697 return def1->fLineCount < def2->fLineCount;
1698 } );
1699 for (auto oneConst : consts) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001700 result += this->writeCodeBlock(*oneConst);
Cary Clarka90ea222018-10-16 10:30:28 -04001701 }
1702 return result;
1703}
1704
Cary Clarkabaffd82018-11-15 08:25:12 -05001705bool IncludeParser::findCommentAfter(const Definition& includeDef, Definition* markupDef) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001706 this->checkName(markupDef);
Cary Clarkabaffd82018-11-15 08:25:12 -05001707 const Definition* parent = includeDef.fParent;
1708 int index = includeDef.fParentIndex;
1709 auto wordIter = parent->fTokens.begin();
1710 std::advance(wordIter, index);
1711 SkASSERT(&*wordIter == &includeDef);
1712 size_t commentLine = 0;
1713 do {
1714 wordIter = std::next(wordIter);
1715 if (parent->fTokens.end() == wordIter) {
1716 break;
1717 }
1718 commentLine = wordIter->fLineCount;
1719 } while (Punctuation::kSemicolon != wordIter->fPunctuation);
1720 wordIter = std::next(wordIter);
1721 if (parent->fTokens.end() != wordIter && Bracket::kSlashSlash == wordIter->fBracket
1722 && wordIter->fLineCount == commentLine) {
1723 return this->parseComment(wordIter->fFileName, wordIter->fContentStart,
1724 wordIter->fContentEnd, wordIter->fLineCount, markupDef, &markupDef->fUndocumented);
1725 }
1726 return true;
1727}
1728
Cary Clark8032b982017-07-28 11:04:54 -04001729bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05001730 this->checkName(markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04001731 // add comment preceding class, if any
Cary Clarkabaffd82018-11-15 08:25:12 -05001732 Definition* parent = includeDef.fParent;
Cary Clark8032b982017-07-28 11:04:54 -04001733 int index = includeDef.fParentIndex;
1734 auto wordIter = parent->fTokens.begin();
1735 std::advance(wordIter, index);
1736 SkASSERT(&*wordIter == &includeDef);
1737 while (parent->fTokens.begin() != wordIter) {
1738 auto testIter = std::prev(wordIter);
1739 if (Definition::Type::kWord != testIter->fType
1740 && Definition::Type::kKeyWord != testIter->fType
1741 && (Definition::Type::kBracket != testIter->fType
1742 || Bracket::kAngle != testIter->fBracket)
1743 && (Definition::Type::kPunctuation != testIter->fType
1744 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1745 break;
1746 }
1747 wordIter = testIter;
1748 }
1749 auto commentIter = wordIter;
1750 while (parent->fTokens.begin() != commentIter) {
1751 auto testIter = std::prev(commentIter);
1752 bool isComment = Definition::Type::kBracket == testIter->fType
1753 && (Bracket::kSlashSlash == testIter->fBracket
1754 || Bracket::kSlashStar == testIter->fBracket);
1755 if (!isComment) {
1756 break;
1757 }
1758 commentIter = testIter;
1759 }
1760 while (commentIter != wordIter) {
1761 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
Cary Clarkabaffd82018-11-15 08:25:12 -05001762 commentIter->fContentEnd, commentIter->fLineCount, markupDef,
1763 &markupDef->fUndocumented)) {
Cary Clark8032b982017-07-28 11:04:54 -04001764 return false;
1765 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001766 commentIter->fUndocumented = markupDef->fUndocumented;
Cary Clark8032b982017-07-28 11:04:54 -04001767 commentIter = std::next(commentIter);
1768 }
1769 return true;
1770}
1771
Cary Clark0d225392018-06-07 09:59:07 -04001772Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1773 string typeName) {
1774 typedef Definition* DefinitionPtr;
1775 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1776 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1777 if (mapIter == fMaps.end()) {
1778 return nullptr;
1779 }
1780 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1781 return reportError<DefinitionPtr>("invalid mark type");
1782 }
1783 string name = this->uniqueName(*mapIter->fInclude, typeName);
1784 Definition& markupDef = *(*mapIter->fInclude)[name];
1785 if (markupDef.fStart) {
1786 return reportError<DefinitionPtr>("definition already defined");
1787 }
1788 markupDef.fFileName = fFileName;
1789 markupDef.fStart = includeDef.fStart;
1790 markupDef.fContentStart = includeDef.fStart;
Cary Clarkcb6bef02018-11-29 12:05:25 -05001791 this->checkName(&markupDef);
Cary Clark0d225392018-06-07 09:59:07 -04001792 markupDef.fName = name;
1793 markupDef.fContentEnd = includeDef.fContentEnd;
1794 markupDef.fTerminator = includeDef.fTerminator;
1795 markupDef.fParent = fParent;
1796 markupDef.fLineCount = includeDef.fLineCount;
1797 markupDef.fMarkType = markType;
1798 markupDef.fKeyWord = includeDef.fKeyWord;
1799 markupDef.fType = Definition::Type::kMark;
1800 return &markupDef;
1801}
1802
Cary Clarka64e4ee2018-10-18 08:30:34 -04001803Definition* IncludeParser::findMethod(const Definition& bmhDef) {
Cary Clark09d80c02018-10-31 12:14:03 -04001804 auto doubleColon = bmhDef.fName.rfind("::");
1805 if (string::npos == doubleColon) {
1806 const auto& iGlobalMethod = fIFunctionMap.find(bmhDef.fName);
1807 SkASSERT(fIFunctionMap.end() != iGlobalMethod);
1808 return iGlobalMethod->second;
1809 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001810 string className = bmhDef.fName.substr(0, doubleColon);
1811 const auto& iClass = fIClassMap.find(className);
Cary Clark77b3f3a2018-11-07 14:59:03 -05001812 if (fIClassMap.end() == iClass) {
1813 return nullptr;
1814 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001815 string methodName = bmhDef.fName.substr(doubleColon + 2);
1816 auto& iTokens = iClass->second.fTokens;
1817 const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
1818 [methodName](Definition& token) {
Cary Clark09d80c02018-10-31 12:14:03 -04001819 return MarkType::kMethod == token.fMarkType
Cary Clarkabaffd82018-11-15 08:25:12 -05001820 && !token.fUndocumented
Cary Clark09d80c02018-10-31 12:14:03 -04001821 && (methodName == token.fName
1822 || methodName == token.fName + "()"); } );
1823 if (iTokens.end() != iMethod) {
1824 return &*iMethod;
1825 }
1826 size_t subClassPos = className.rfind("::");
1827 if (string::npos != subClassPos) {
1828 className = className.substr(subClassPos + 2);
1829 }
1830 // match may be constructor; compare strings to see if this is so
1831 SkASSERT(string::npos != methodName.find('('));
1832 auto stripper = [](string s) -> string {
1833 bool last = false;
1834 string result;
1835 for (char c : s) {
1836 if (' ' >= c) {
1837 if (!last) {
1838 last = true;
1839 result += ' ';
1840 }
1841 continue;
1842 }
1843 result += c;
1844 last = false;
1845 }
1846 return result;
1847 };
1848 string strippedMethodName = stripper(methodName);
1849 if (strippedMethodName == methodName) {
1850 strippedMethodName = "";
1851 }
1852 const auto& cMethod = std::find_if(iTokens.begin(), iTokens.end(),
1853 [className, methodName, stripper, strippedMethodName](Definition& token) {
1854 if (MarkType::kMethod != token.fMarkType) {
1855 return false;
1856 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001857 if (token.fUndocumented) {
1858 return false;
1859 }
Cary Clark09d80c02018-10-31 12:14:03 -04001860 TextParser parser(&token);
1861 const char* match = parser.strnstr(className.c_str(), parser.fEnd);
1862 if (!match) {
1863 return false;
1864 }
1865 parser.skipTo(match);
1866 parser.skipExact(className.c_str());
1867 if ('(' != parser.peek()) {
1868 return false;
1869 }
1870 parser.skipToBalancedEndBracket('(', ')');
1871 string iMethodName(match, parser.fChar - match);
1872 if (methodName == iMethodName) {
1873 return true;
1874 }
Cary Clarkcb6bef02018-11-29 12:05:25 -05001875 if (strippedMethodName.empty()) {
Cary Clark09d80c02018-10-31 12:14:03 -04001876 return false;
1877 }
1878 string strippedIName = stripper(iMethodName);
1879 return strippedIName == strippedMethodName;
1880 } );
1881 SkAssertResult(iTokens.end() != cMethod);
1882 return &*cMethod;
Cary Clarka64e4ee2018-10-18 08:30:34 -04001883}
1884
Cary Clark224c7002018-06-27 11:00:21 -04001885Definition* IncludeParser::parentBracket(Definition* parent) const {
1886 while (parent && Definition::Type::kBracket != parent->fType) {
1887 parent = parent->fParent;
1888 }
1889 return parent;
1890}
1891
1892Bracket IncludeParser::grandParentBracket() const {
1893 Definition* parent = parentBracket(fParent);
1894 parent = parentBracket(parent ? parent->fParent : nullptr);
1895 return parent ? parent->fBracket : Bracket::kNone;
1896}
1897
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001898bool IncludeParser::inAlignAs() const {
1899 if (fParent->fTokens.size() < 2) {
1900 return false;
1901 }
1902 auto reverseIter = fParent->fTokens.end();
1903 bool checkForBracket = true;
1904 while (fParent->fTokens.begin() != reverseIter) {
1905 std::advance(reverseIter, -1);
1906 if (checkForBracket) {
1907 if (Definition::Type::kBracket != reverseIter->fType) {
1908 return false;
1909 }
1910 if (Bracket::kParen != reverseIter->fBracket) {
1911 return false;
1912 }
1913 checkForBracket = false;
1914 continue;
1915 }
1916 if (Definition::Type::kKeyWord != reverseIter->fType) {
1917 return false;
1918 }
1919 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1920 }
1921 return false;
1922}
1923
Cary Clark61313f32018-10-08 14:57:48 -04001924const Definition* IncludeParser::include(string match) const {
1925 for (auto& entry : fIncludeMap) {
1926 if (string::npos == entry.first.find(match)) {
1927 continue;
1928 }
1929 return &entry.second;
1930 }
1931 SkASSERT(0);
1932 return nullptr;
1933}
1934
Cary Clark137b8742018-05-30 09:21:49 -04001935// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001936bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1937 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001938 // parse class header
1939 auto iter = includeDef->fTokens.begin();
1940 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1941 // todo : documentation is ignoring this for now
1942 iter = std::next(iter);
1943 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001944 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1945 if (hasAlignAs) {
1946 iter = std::next(iter);
1947 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1948 return includeDef->reportError<bool>("expected alignas argument");
1949 }
1950 iter = std::next(iter);
1951 }
Cary Clark8032b982017-07-28 11:04:54 -04001952 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1953 includeDef->fName = nameStr;
Cary Clarkcb6bef02018-11-29 12:05:25 -05001954 this->checkName(includeDef);
Cary Clark9174bda2017-09-19 17:39:32 -04001955 iter = std::next(iter);
1956 if (iter == includeDef->fTokens.end()) {
1957 return true; // forward declaration only
1958 }
Cary Clark8032b982017-07-28 11:04:54 -04001959 do {
1960 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001961 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001962 }
1963 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1964 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001965 }
Cary Clark8032b982017-07-28 11:04:54 -04001966 } while (static_cast<void>(iter = std::next(iter)), true);
1967 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001968 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001969 }
1970 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1971 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001972 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001973 }
1974 markupDef->fStart = iter->fStart;
1975 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001976 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001977 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001978 if (markupDef->fUndocumented) {
1979 includeDef->fUndocumented = true;
1980 }
Cary Clark8032b982017-07-28 11:04:54 -04001981// if (1 != includeDef->fChildren.size()) {
1982// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1983// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001984 auto includeDefIter = includeDef->fChildren.begin();
1985 if (hasAlignAs) {
1986 SkASSERT(includeDef->fChildren.end() != includeDefIter);
1987 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
1988 std::advance(includeDefIter, 1);
1989 }
1990 if (includeDef->fChildren.end() != includeDefIter
1991 && Bracket::kAngle == (*includeDefIter)->fBracket) {
1992 std::advance(includeDefIter, 1);
1993 }
1994 includeDef = *includeDefIter;
1995 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04001996 iter = includeDef->fTokens.begin();
1997 // skip until public
1998 int publicIndex = 0;
1999 if (IsStruct::kNo == isStruct) {
2000 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2001 size_t publicLen = strlen(publicName);
2002 while (iter != includeDef->fTokens.end()
2003 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
2004 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04002005 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002006 iter = std::next(iter);
2007 ++publicIndex;
2008 }
2009 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002010 int keyIndex = publicIndex;
2011 KeyWord currentKey = KeyWord::kPublic;
2012 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2013 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04002014 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
2015 size_t protectedLen = strlen(protectedName);
2016 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
2017 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04002018 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002019 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04002020 std::advance(childIter, 1);
2021 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002022 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04002023 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04002024 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04002025 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04002026 const char* testStart = iter->fStart;
2027 size_t testLen = (size_t) (iter->fContentEnd - testStart);
2028 iter = std::next(iter);
2029 ++keyIndex;
2030 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2031 currentKey = KeyWord::kPublic;
2032 break;
2033 }
2034 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2035 currentKey = KeyWord::kProtected;
2036 break;
2037 }
2038 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2039 currentKey = KeyWord::kPrivate;
2040 break;
2041 }
2042 }
2043 fLastObject = nullptr;
2044 if (KeyWord::kPublic == currentKey) {
2045 if (!this->parseObject(child, markupDef)) {
2046 return false;
2047 }
Cary Clark8032b982017-07-28 11:04:54 -04002048 }
Cary Clark73fa9722017-08-29 17:36:51 -04002049 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002050 childIter = std::next(childIter);
2051 }
Cary Clark137b8742018-05-30 09:21:49 -04002052 while (iter != includeDef->fTokens.end()) {
2053 iter->fPrivate = KeyWord::kPublic != currentKey;
2054 iter = std::next(iter);
2055 }
Cary Clark8032b982017-07-28 11:04:54 -04002056 SkASSERT(fParent->fParent);
2057 fParent = fParent->fParent;
2058 return true;
2059}
2060
Cary Clarkabaffd82018-11-15 08:25:12 -05002061bool IncludeParser::isUndocumentable(string filename, const char* start, const char* end,
2062 int lineCount) {
Cary Clark8032b982017-07-28 11:04:54 -04002063 TextParser parser(filename, start, end, lineCount);
Cary Clarkcb6bef02018-11-29 12:05:25 -05002064 const vector<string> skipWords = { "deprecated", "experimental", "private" };
Cary Clarkabaffd82018-11-15 08:25:12 -05002065 const vector<string> butNot = { "to be deprecated", "may be deprecated" };
2066 const vector<string> alsoNot = { "todo" };
2067 string match = parser.anyWord(skipWords, 0);
2068 if ("" != match) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05002069 if (parser.anyWord(alsoNot, 0).empty()
2070 && ("deprecated" != match || parser.anyWord(butNot, 2).empty())) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002071 return true;
2072 }
2073 }
2074 return false;
2075}
2076
2077bool IncludeParser::parseComment(string filename, const char* start, const char* end,
2078 int lineCount, Definition* markupDef, bool* undocumentedPtr) {
2079 if (this->isUndocumentable(filename, start, end, lineCount)) {
2080 *undocumentedPtr = true;
2081 }
Cary Clark8032b982017-07-28 11:04:54 -04002082 // parse doxygen if present
Cary Clarkabaffd82018-11-15 08:25:12 -05002083 TextParser parser(filename, start, end, lineCount);
Cary Clark8032b982017-07-28 11:04:54 -04002084 if (parser.startsWith("**")) {
2085 parser.next();
2086 parser.next();
2087 parser.skipWhiteSpace();
2088 if ('\\' == parser.peek()) {
2089 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002090 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2091 if (parser.skipExact("file")) {
2092 if (Definition::Type::kFileType != fParent->fType) {
2093 return reportError<bool>("expected parent is file");
2094 }
2095 string filename = markupDef->fileName();
2096 if (!parser.skipWord(filename.c_str())) {
2097 return reportError<bool>("missing object type");
2098 }
2099 } else if (parser.skipExact("fn")) {
2100 SkASSERT(0); // incomplete
2101 } else {
2102 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2103 return reportError<bool>("missing object type");
2104 }
2105 if (!parser.skipWord(markupDef->fName.c_str()) &&
2106 KeyWord::kEnum != markupDef->fKeyWord) {
2107 return reportError<bool>("missing object name");
2108 }
Cary Clark8032b982017-07-28 11:04:54 -04002109 }
Cary Clark8032b982017-07-28 11:04:54 -04002110 }
2111 }
2112 // remove leading '*' if present
2113 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2114 while (!parser.eof() && parser.skipWhiteSpace()) {
2115 while ('*' == parser.peek()) {
2116 parser.next();
2117 if (parser.eof()) {
2118 break;
2119 }
2120 parser.skipWhiteSpace();
2121 }
2122 if (parser.eof()) {
2123 break;
2124 }
2125 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002126 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002127 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002128 parser.skipToEndBracket('\n');
2129 }
2130 return true;
2131}
2132
Cary Clarkabaffd82018-11-15 08:25:12 -05002133/*
2134 find comment either in front of or after the const def and then extract if the
2135 const is undocumented
2136 */
Cary Clarkd98f78c2018-04-26 08:32:37 -04002137bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002138 if (!markupDef) {
2139 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2140 child->fLineCount, fParent, '\0');
2141 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002142 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002143 globalMarkupChild->fName = globalUniqueName;
2144 if (!this->findComments(*child, globalMarkupChild)) {
2145 return false;
2146 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002147 if (!this->findCommentAfter(*child, globalMarkupChild)) {
2148 return false;
2149 }
2150 if (globalMarkupChild->fUndocumented) {
2151 child->fUndocumented = true;
2152 } else {
2153 fIConstMap[globalUniqueName] = globalMarkupChild;
2154 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002155 return true;
2156 }
2157 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2158 child->fLineCount, markupDef, '\0');
2159 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002160 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002161 markupChild->fTerminator = markupChild->fContentEnd;
2162 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002163 classDef.fConsts[child->fName] = markupChild;
Cary Clarkabaffd82018-11-15 08:25:12 -05002164 if (!this->findComments(*child, markupChild)) {
2165 return false;
2166 }
2167 if (!this->findCommentAfter(*child, markupChild)) {
2168 return false;
2169 }
2170 if (markupChild->fUndocumented) {
2171 child->fUndocumented = true;
2172 } else {
2173 fIConstMap[child->fName] = markupChild;
2174 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002175 return true;
2176}
2177
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002178bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2179 TextParser parser(child);
2180 if (!parser.skipExact("#define")) {
2181 return false;
2182 }
2183 if (!parser.skipSpace()) {
2184 return false;
2185 }
2186 const char* nameStart = parser.fChar;
2187 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2188 if (parser.eof()) {
2189 return true; // do nothing if #define doesn't define anything
2190 }
2191 string nameStr(nameStart, parser.fChar - nameStart);
2192 struct Param {
2193 const char* fStart;
2194 const char* fEnd;
2195 };
2196 vector<Param> params;
2197 if ('(' == parser.peek()) {
2198 parser.next();
2199 if (!parser.skipSpace()) {
2200 return false;
2201 }
2202 do {
2203 const char* paramStart = parser.fChar;
2204 if (!parser.skipExact("...")) {
2205 parser.skipToNonAlphaNum();
2206 }
2207 if (parser.eof()) {
2208 return false;
2209 }
2210 params.push_back({paramStart, parser.fChar});
2211 if (!parser.skipSpace()) {
2212 return false;
2213 }
2214 if (')' == parser.peek()) {
2215 parser.next();
2216 break;
2217 }
2218 if (',' != parser.next()) {
2219 return false;
2220 }
2221 if (!parser.skipSpace()) {
2222 return false;
2223 }
2224 } while (true);
2225 }
2226 if (!parser.skipSpace()) {
2227 return false;
2228 }
2229 if (!markupDef) {
2230 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2231 child->fLineCount, fParent, '\0');
2232 Definition* globalMarkupChild = &fGlobals.back();
2233 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2234 globalMarkupChild->fName = globalUniqueName;
2235 globalMarkupChild->fTerminator = child->fContentEnd;
2236 if (!this->findComments(*child, globalMarkupChild)) {
2237 return false;
2238 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002239 if (!globalMarkupChild->fUndocumented) {
2240 fIDefineMap[globalUniqueName] = globalMarkupChild;
2241 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002242 for (Param param : params) {
2243 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2244 child->fLineCount, globalMarkupChild, '\0');
2245 Definition* paramChild = &globalMarkupChild->fTokens.back();
2246 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
Cary Clarkcb6bef02018-11-29 12:05:25 -05002247 this->checkName(paramChild);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002248 paramChild->fTerminator = param.fEnd;
2249 }
2250 return true;
2251 }
2252 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2253 child->fLineCount, markupDef, '\0');
2254 Definition* markupChild = &markupDef->fTokens.back();
2255 markupChild->fName = nameStr;
2256 markupChild->fTerminator = markupChild->fContentEnd;
2257 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2258 if (!this->findComments(*child, markupChild)) {
2259 return false;
2260 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002261 if (markupChild->fUndocumented) {
2262 child->fUndocumented = true;
2263 } else {
2264 classDef.fDefines[nameStr] = markupChild;
2265 fIDefineMap[nameStr] = markupChild;
2266 }
Cary Clark8032b982017-07-28 11:04:54 -04002267 return true;
2268}
2269
2270bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002271 if (!child->fTokens.size()) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002272 return true; // if enum is a forward declaration, do nothing
2273 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002274 bool isEnumClass = false;
2275 Definition* parent = child;
2276 auto token = parent->fTokens.begin();
2277 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2278 isEnumClass = true;
2279 parent = &*token;
2280 token = parent->fTokens.begin();
Cary Clark8032b982017-07-28 11:04:54 -04002281 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002282 SkASSERT(Definition::Type::kWord == token->fType);
2283 string nameStr = string(token->fStart, token->fContentEnd - token->fStart);
Cary Clark2dc84ad2018-01-26 12:56:22 -05002284 Definition* markupChild;
2285 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002286 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2287 child->fLineCount, fParent, '\0');
2288 markupChild = &fGlobals.back();
2289 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2290 markupChild->fName = globalUniqueName;
2291 markupChild->fTerminator = child->fContentEnd;
Cary Clarkabaffd82018-11-15 08:25:12 -05002292 if (!markupChild->fUndocumented) {
2293 fIEnumMap[globalUniqueName] = markupChild;
2294 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002295 } else {
2296 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002297 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002298 markupChild = &markupDef->fTokens.back();
2299 }
Cary Clark8032b982017-07-28 11:04:54 -04002300 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2301 markupChild->fKeyWord = KeyWord::kEnum;
Cary Clarkabaffd82018-11-15 08:25:12 -05002302 if (isEnumClass) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04002303 markupChild->fMarkType = MarkType::kEnumClass;
2304 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002305 if (markupDef) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002306 markupChild->fName = markupDef->fName + "::" + nameStr;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002307 }
Cary Clark8032b982017-07-28 11:04:54 -04002308 if (!this->findComments(*child, markupChild)) {
2309 return false;
2310 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002311 if (markupChild->fUndocumented) {
2312 child->fUndocumented = true;
2313 }
2314 if (!this->parseEnumConst(token, parent->fTokens.end(), markupChild)) {
2315 return false;
2316 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002317 for (auto outsideMember : child->fChildren) {
2318 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002319 continue;
2320 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002321 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2322 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002323 continue;
2324 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002325 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2326 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002327 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002328 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002329 member->fName = outsideMember->fName;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002330 this->checkName(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002331 // FIXME: ? add comment as well ?
2332 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002333 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002334 if (markupDef) {
2335 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2336 SkASSERT(classDef.fStart);
2337 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002338 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002339 markupChild->fName = uniqueName;
2340 classDef.fEnums[uniqueName] = markupChild;
Cary Clarkabaffd82018-11-15 08:25:12 -05002341 if (!markupChild->fUndocumented) {
2342 fIEnumMap[fullName] = markupChild;
2343 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002344 }
Cary Clark8032b982017-07-28 11:04:54 -04002345 return true;
2346}
2347
Cary Clarkabaffd82018-11-15 08:25:12 -05002348bool IncludeParser::parseOneEnumConst(list<Definition>& constList,
2349 Definition* markupChild, bool skipWord) {
2350 auto memberIter = constList.begin();
2351 const auto memberIterEnd = constList.end();
2352 if (skipWord) {
2353 SkASSERT(Definition::Type::kWord == memberIter->fType);
2354 memberIter = std::next(memberIter);
2355 SkASSERT(memberIterEnd != memberIter);
2356 }
2357 // token array has parse atoms; child array has comments
2358 bool undocumented = false;
2359 while (memberIterEnd != memberIter) {
2360 while (Bracket::kSlashStar == memberIter->fBracket) {
2361 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2362 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2363 return false;
2364 }
2365 memberIter = std::next(memberIter);
2366 if (memberIterEnd == memberIter) {
2367 return false;
2368 }
2369 }
2370 if (Bracket::kPound == memberIter->fBracket) {
2371 KeyWord keyWord = memberIter->fKeyWord;
2372 bool sawIf = KeyWord::kIfdef == keyWord || KeyWord::kIf == keyWord
2373 || KeyWord::kElif == keyWord;
2374 if (sawIf || KeyWord::kElse == keyWord) {
2375 if (!parseOneEnumConst(memberIter->fTokens, markupChild, sawIf)) {
2376 return false;
2377 }
2378 } else {
2379 SkASSERT(KeyWord::kEndif == keyWord || KeyWord::kError == keyWord);
2380 }
2381 memberIter = std::next(memberIter);
2382 if (memberIterEnd == memberIter) {
2383 break;
2384 }
2385 continue;
2386 }
2387 while (Definition::Type::kWord != memberIter->fType) {
2388 memberIter = std::next(memberIter);
2389 if (memberIterEnd == memberIter) {
2390 return false;
2391 }
2392 }
2393 auto memberStart = memberIter;
2394 Definition* memberEnd = nullptr;
2395 const char* last;
2396 do {
2397 last = memberIter->fContentEnd;
2398 memberIter = std::next(memberIter);
2399 if (memberIterEnd == memberIter) {
2400 break;
2401 }
2402 memberEnd = &*memberIter;
2403 } while (string::npos == string(last, memberIter->fContentStart).find(','));
2404 if (!memberEnd) {
2405 return false;
2406 }
2407 if (memberIterEnd != memberIter && Bracket::kSlashSlash == memberIter->fBracket) {
2408 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2409 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2410 return false;
2411 }
2412 memberIter = std::next(memberIter);
2413 }
2414 markupChild->fTokens.emplace_back(MarkType::kMember, memberStart->fContentStart,
2415 memberEnd->fContentEnd, memberStart->fLineCount, markupChild, '\0');
2416 Definition* markupMember = &markupChild->fTokens.back();
2417 string name = string(memberStart->fContentStart, memberStart->length());
2418 memberStart->fName = name;
2419 markupMember->fName = name;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002420 this->checkName(markupMember);
2421 memberStart->fUndocumented = markupMember->fUndocumented;
Cary Clarkabaffd82018-11-15 08:25:12 -05002422 memberStart->fMarkType = MarkType::kMember;
2423 undocumented = false;
2424 }
2425 return true;
2426}
2427
2428bool IncludeParser::parseEnumConst(list<Definition>::iterator& tokenIter,
2429 const list<Definition>::iterator& tokenEnd, Definition* markupChild) {
2430 SkASSERT(Definition::Type::kWord == tokenIter->fType); // should be enum name
2431 tokenIter = std::next(tokenIter);
2432 SkASSERT(tokenEnd != tokenIter);
2433 if (Definition::Type::kKeyWord == tokenIter->fType) {
2434 SkASSERT((unsigned) tokenIter->fKeyWord < SK_ARRAY_COUNT(kKeyWords));
2435 SkASSERT(KeyProperty::kNumber == kKeyWords[(int) tokenIter->fKeyWord].fProperty);
2436 tokenIter = std::next(tokenIter);
2437 SkASSERT(tokenEnd != tokenIter);
2438 }
2439 SkASSERT(Punctuation::kLeftBrace == tokenIter->fPunctuation);
2440 tokenIter = std::next(tokenIter);
2441 SkASSERT(tokenEnd != tokenIter);
2442 SkASSERT(Bracket::kBrace == tokenIter->fBracket);
2443 return parseOneEnumConst(tokenIter->fTokens, markupChild, false);
2444}
2445
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002446bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002447 fParent = &fIncludeMap[name];
2448 fParent->fName = name;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002449 this->checkName(fParent);
Cary Clark8032b982017-07-28 11:04:54 -04002450 fParent->fFileName = fFileName;
2451 fParent->fType = Definition::Type::kFileType;
2452 fParent->fContentStart = fChar;
2453 fParent->fContentEnd = fEnd;
2454 // parse include file into tree
2455 while (fChar < fEnd) {
2456 if (!this->parseChar()) {
2457 return false;
2458 }
2459 }
2460 // parse tree and add named objects to maps
2461 fParent = &fIncludeMap[name];
2462 if (!this->parseObjects(fParent, nullptr)) {
2463 return false;
2464 }
2465 return true;
2466}
2467
2468bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2469 const char* typeStart = child->fChildren[0]->fContentStart;
2470 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002471 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002472 Definition* markupChild = &markupDef->fTokens.back();
2473 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002474 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002475 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2476 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2477 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2478 markupChild->fName = uniqueName;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002479 this->checkName(markupChild);
Cary Clark9174bda2017-09-19 17:39:32 -04002480 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clarkabaffd82018-11-15 08:25:12 -05002481 if (!markupChild->fUndocumented) {
2482 classDef.fMembers[uniqueName] = markupChild;
2483 }
Cary Clark8032b982017-07-28 11:04:54 -04002484 if (child->fParentIndex >= 2) {
2485 auto comment = child->fParent->fTokens.begin();
2486 std::advance(comment, child->fParentIndex - 2);
2487 if (Definition::Type::kBracket == comment->fType
2488 && (Bracket::kSlashStar == comment->fBracket
2489 || Bracket::kSlashSlash == comment->fBracket)) {
2490 TextParser parser(&*comment);
2491 do {
2492 parser.skipToAlpha();
2493 if (parser.eof()) {
2494 break;
2495 }
2496 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002497 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002498 if (Bracket::kSlashStar == comment->fBracket) {
2499 const char* commentEnd = parser.strnstr("*/", end);
2500 if (commentEnd) {
2501 end = commentEnd;
2502 }
2503 }
2504 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002505 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002506 Definition* commentChild = &markupDef->fTokens.back();
2507 markupChild->fChildren.emplace_back(commentChild);
2508 parser.skipTo(end);
2509 } while (!parser.eof());
2510 }
2511 }
2512 return true;
2513}
2514
2515bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2516 auto tokenIter = child->fParent->fTokens.begin();
2517 std::advance(tokenIter, child->fParentIndex);
2518 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002519 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002520 bool addConst = false;
2521 auto operatorCheck = tokenIter;
2522 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2523 operatorCheck = std::prev(tokenIter);
2524 }
2525 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002526 auto closeParen = std::next(tokenIter);
2527 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2528 '(' == closeParen->fContentStart[0]);
2529 nameEnd = closeParen->fContentEnd + 1;
2530 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002531 if (Definition::Type::kKeyWord == closeParen->fType &&
2532 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002533 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002534 }
Cary Clarka560c472017-11-27 10:44:06 -05002535 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002536 }
2537 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002538 if (addConst) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05002539 nameStr += " const";
Cary Clark154beea2017-10-26 07:58:48 -04002540 }
Cary Clark8032b982017-07-28 11:04:54 -04002541 while (tokenIter != child->fParent->fTokens.begin()) {
2542 auto testIter = std::prev(tokenIter);
2543 switch (testIter->fType) {
2544 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002545 if (testIter == child->fParent->fTokens.begin() &&
2546 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2547 KeyWord::kIfndef == child->fParent->fKeyWord ||
2548 KeyWord::kIf == child->fParent->fKeyWord)) {
2549 std::next(tokenIter);
2550 break;
2551 }
Cary Clark8032b982017-07-28 11:04:54 -04002552 goto keepGoing;
2553 case Definition::Type::kKeyWord: {
2554 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2555 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2556 goto keepGoing;
2557 }
2558 } break;
2559 case Definition::Type::kBracket:
2560 if (Bracket::kAngle == testIter->fBracket) {
2561 goto keepGoing;
2562 }
2563 break;
2564 case Definition::Type::kPunctuation:
2565 if (Punctuation::kSemicolon == testIter->fPunctuation
2566 || Punctuation::kLeftBrace == testIter->fPunctuation
2567 || Punctuation::kColon == testIter->fPunctuation) {
2568 break;
2569 }
2570 keepGoing:
2571 tokenIter = testIter;
2572 continue;
2573 default:
2574 break;
2575 }
2576 break;
2577 }
Cary Clark224c7002018-06-27 11:00:21 -04002578 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002579 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002580 tokenIter->fPrivate = string::npos != nameStr.find("::")
2581 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002582 this->checkName(&*tokenIter);
Cary Clark8032b982017-07-28 11:04:54 -04002583 auto testIter = child->fParent->fTokens.begin();
2584 SkASSERT(child->fParentIndex > 0);
2585 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002586 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2587 0 == tokenIter->fParentIndex) {
2588 tokenIter = std::next(tokenIter);
2589 }
Cary Clark8032b982017-07-28 11:04:54 -04002590 const char* start = tokenIter->fContentStart;
2591 const char* end = tokenIter->fContentEnd;
2592 const char kDebugCodeStr[] = "SkDEBUGCODE";
2593 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2594 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2595 std::advance(testIter, 1);
2596 start = testIter->fContentStart + 1;
2597 end = testIter->fContentEnd - 1;
2598 } else {
2599 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002600 do {
2601 std::advance(testIter, 1);
2602 if (testIter == child->fParent->fTokens.end()) {
2603 break;
2604 }
Cary Clark8032b982017-07-28 11:04:54 -04002605 switch (testIter->fType) {
2606 case Definition::Type::kPunctuation:
2607 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2608 || Punctuation::kLeftBrace == testIter->fPunctuation
2609 || Punctuation::kColon == testIter->fPunctuation);
2610 end = testIter->fStart;
2611 break;
2612 case Definition::Type::kKeyWord: {
2613 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2614 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2615 continue;
2616 }
2617 } break;
2618 default:
2619 continue;
2620 }
2621 break;
Cary Clark61313f32018-10-08 14:57:48 -04002622 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002623 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002624 while (end > start && ' ' >= end[-1]) {
2625 --end;
2626 }
Cary Clark9174bda2017-09-19 17:39:32 -04002627 if (!markupDef) {
2628 auto parentIter = child->fParent->fTokens.begin();
2629 SkASSERT(child->fParentIndex > 0);
2630 std::advance(parentIter, child->fParentIndex - 1);
2631 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002632 TextParser nameParser(methodName);
2633 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002634 return true; // expect this is inline class definition outside of class
2635 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002636 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2637 fParent, '\0');
2638 Definition* globalMarkupChild = &fGlobals.back();
2639 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2640 globalMarkupChild->fName = globalUniqueName;
2641 if (!this->findComments(*child, globalMarkupChild)) {
2642 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002643 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002644 if (globalMarkupChild->fUndocumented) {
2645 child->fUndocumented = true;
2646 } else {
2647 fIFunctionMap[globalUniqueName] = globalMarkupChild;
2648 }
Cary Clarka560c472017-11-27 10:44:06 -05002649 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002650 }
Cary Clark8032b982017-07-28 11:04:54 -04002651 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002652 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002653 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002654 {
2655 auto mapIter = fIClassMap.find(markupDef->fName);
2656 SkASSERT(fIClassMap.end() != mapIter);
2657 IClassDefinition& classDef = mapIter->second;
2658 SkASSERT(classDef.fStart);
2659 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2660 markupChild->fName = uniqueName;
2661 if (!this->findComments(*child, markupChild)) {
2662 return false;
2663 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002664 if (markupChild->fUndocumented) {
2665 tokenIter->fUndocumented = true;
2666 } else {
2667 classDef.fMethods[uniqueName] = markupChild;
2668 }
Cary Clark8032b982017-07-28 11:04:54 -04002669 }
Cary Clark8032b982017-07-28 11:04:54 -04002670 return true;
2671}
2672
Cary Clark8032b982017-07-28 11:04:54 -04002673bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002674 fPriorObject = nullptr;
2675 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002676 if (!this->parseObject(child, markupDef)) {
2677 return false;
2678 }
Cary Clark0d225392018-06-07 09:59:07 -04002679 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002680 }
2681 return true;
2682}
2683
2684bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2685 // set up for error reporting
2686 fLine = fChar = child->fStart;
2687 fEnd = child->fContentEnd;
2688 // todo: put original line number in child as well
2689 switch (child->fType) {
2690 case Definition::Type::kKeyWord:
2691 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002692 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002693 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002694 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002695 }
2696 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002697 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002698 case KeyWord::kConst:
2699 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002700 if (!this->parseConst(child, markupDef)) {
2701 return child->reportError<bool>("failed to parse const or constexpr");
2702 }
2703 break;
Cary Clark8032b982017-07-28 11:04:54 -04002704 case KeyWord::kEnum:
2705 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002706 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002707 }
2708 break;
2709 case KeyWord::kStruct:
2710 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002711 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002712 }
2713 break;
2714 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002715 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002716 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002717 }
2718 break;
2719 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002720 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002721 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002722 }
2723 break;
2724 case KeyWord::kUnion:
2725 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002726 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002727 }
2728 break;
Cary Clark61313f32018-10-08 14:57:48 -04002729 case KeyWord::kUsing:
2730 if (!this->parseUsing()) {
2731 return child->reportError<bool>("failed to parse using");
2732 }
2733 break;
Cary Clark8032b982017-07-28 11:04:54 -04002734 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002735 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002736 }
2737 break;
2738 case Definition::Type::kBracket:
2739 switch (child->fBracket) {
2740 case Bracket::kParen:
Cary Clark9174bda2017-09-19 17:39:32 -04002741 {
2742 auto tokenIter = child->fParent->fTokens.begin();
2743 std::advance(tokenIter, child->fParentIndex);
2744 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002745 TextParser previousToken(&*tokenIter);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002746 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002747 break;
2748 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002749 if (Bracket::kPound == child->fParent->fBracket &&
2750 KeyWord::kIf == child->fParent->fKeyWord) {
2751 // TODO: this will skip methods named defined() -- for the
2752 // moment there aren't any
2753 if (previousToken.startsWith("defined")) {
2754 break;
2755 }
2756 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002757 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2758 break;
2759 }
Cary Clark73fa9722017-08-29 17:36:51 -04002760 }
Cary Clark0d225392018-06-07 09:59:07 -04002761 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2762 break;
2763 }
Cary Clark8032b982017-07-28 11:04:54 -04002764 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002765 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002766 }
Cary Clark73fa9722017-08-29 17:36:51 -04002767 break;
Cary Clark8032b982017-07-28 11:04:54 -04002768 case Bracket::kSlashSlash:
2769 case Bracket::kSlashStar:
2770 // comments are picked up by parsing objects first
2771 break;
2772 case Bracket::kPound:
2773 // special-case the #xxx xxx_DEFINED entries
2774 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002775 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002776 case KeyWord::kIfndef:
2777 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002778 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002779 if (!this->parseObjects(child, markupDef)) {
2780 return false;
2781 }
2782 break;
2783 }
2784 goto preproError;
2785 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002786 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002787 break;
2788 }
2789 goto preproError;
2790 case KeyWord::kEndif:
2791 if (child->boilerplateEndIf()) {
2792 break;
2793 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002794 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002795 case KeyWord::kInclude:
2796 // ignored for now
2797 break;
2798 case KeyWord::kElse:
Cary Clarkabaffd82018-11-15 08:25:12 -05002799 if (!this->parseObjects(child, markupDef)) {
2800 return false;
2801 }
2802 break;
Cary Clark8032b982017-07-28 11:04:54 -04002803 case KeyWord::kElif:
2804 // todo: handle these
2805 break;
2806 default:
2807 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002808 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002809 }
2810 break;
2811 case Bracket::kAngle:
2812 // pick up templated function pieces when method is found
2813 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002814 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002815 if (!this->parseObjects(child, markupDef)) {
2816 return false;
2817 }
Cary Clark73fa9722017-08-29 17:36:51 -04002818 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002819 case Bracket::kSquare: {
2820 // check to see if parent is operator, the only case we handle so far
2821 auto prev = child->fParent->fTokens.begin();
2822 std::advance(prev, child->fParentIndex - 1);
2823 if (KeyWord::kOperator != prev->fKeyWord) {
2824 return child->reportError<bool>("expected operator overload");
2825 }
2826 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002827 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002828 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002829 }
2830 break;
2831 case Definition::Type::kWord:
2832 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002833 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002834 }
2835 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002836 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002837 }
2838 break;
2839 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002840 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002841 break;
2842 }
2843 return true;
2844}
2845
Cary Clarkbbfda252018-03-09 15:32:01 -05002846bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2847 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002848}
2849
Cary Clark2f466242017-12-11 16:03:17 -05002850bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2851 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002852 typedefParser.skipExact("typedef");
2853 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002854 string nameStr = typedefParser.typedefName();
2855 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002856 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2857 child->fLineCount, fParent, '\0');
2858 Definition* globalMarkupChild = &fGlobals.back();
2859 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2860 globalMarkupChild->fName = globalUniqueName;
2861 if (!this->findComments(*child, globalMarkupChild)) {
2862 return false;
2863 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002864 if (globalMarkupChild->fUndocumented) {
2865 child->fUndocumented = true;
2866 } else {
2867 fITypedefMap[globalUniqueName] = globalMarkupChild;
2868 }
Cary Clark0d225392018-06-07 09:59:07 -04002869 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002870 return true;
2871 }
2872 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002873 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002874 Definition* markupChild = &markupDef->fTokens.back();
2875 markupChild->fName = nameStr;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002876 this->checkName(markupChild);
Cary Clark2f466242017-12-11 16:03:17 -05002877 markupChild->fTerminator = markupChild->fContentEnd;
2878 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2879 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002880 child->fName = markupDef->fName + "::" + nameStr;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002881 this->checkName(child);
Cary Clarka90ea222018-10-16 10:30:28 -04002882 fITypedefMap[child->fName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002883 return true;
2884}
2885
2886bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04002887 // incomplete
2888 return true;
2889}
Cary Clark8032b982017-07-28 11:04:54 -04002890
Cary Clark61313f32018-10-08 14:57:48 -04002891bool IncludeParser::parseUsing() {
2892 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04002893 return true;
2894}
2895
2896bool IncludeParser::parseChar() {
2897 char test = *fChar;
2898 if ('\\' == fPrev) {
2899 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002900// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002901 fLine = fChar + 1;
2902 }
2903 goto done;
2904 }
2905 switch (test) {
2906 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002907// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002908 fLine = fChar + 1;
2909 if (fInChar) {
2910 return reportError<bool>("malformed char");
2911 }
2912 if (fInString) {
2913 return reportError<bool>("malformed string");
2914 }
2915 if (!this->checkForWord()) {
2916 return false;
2917 }
2918 if (Bracket::kPound == this->topBracket()) {
2919 KeyWord keyWord = fParent->fKeyWord;
2920 if (KeyWord::kNone == keyWord) {
2921 return this->reportError<bool>("unhandled preprocessor directive");
2922 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002923 if (fInDefine) {
2924 SkASSERT(KeyWord::kDefine == keyWord);
2925 fInDefine = false;
2926 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002927 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002928 this->popBracket();
2929 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002930 if (fInBrace) {
2931 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2932 fInBrace = nullptr;
2933 }
Cary Clark8032b982017-07-28 11:04:54 -04002934 } else if (Bracket::kSlashSlash == this->topBracket()) {
2935 this->popBracket();
2936 }
2937 break;
2938 case '*':
2939 if (!fInCharCommentString && '/' == fPrev) {
2940 this->pushBracket(Bracket::kSlashStar);
2941 }
2942 if (!this->checkForWord()) {
2943 return false;
2944 }
2945 if (!fInCharCommentString) {
2946 this->addPunctuation(Punctuation::kAsterisk);
2947 }
2948 break;
2949 case '/':
2950 if ('*' == fPrev) {
2951 if (!fInCharCommentString) {
2952 return reportError<bool>("malformed closing comment");
2953 }
2954 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002955 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002956 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002957 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002958 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002959 }
2960 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002961 }
Cary Clark8032b982017-07-28 11:04:54 -04002962 if (!fInCharCommentString && '/' == fPrev) {
2963 this->pushBracket(Bracket::kSlashSlash);
2964 break;
2965 }
2966 if (!this->checkForWord()) {
2967 return false;
2968 }
2969 break;
2970 case '\'':
2971 if (Bracket::kChar == this->topBracket()) {
2972 this->popBracket();
2973 } else if (!fInComment && !fInString) {
2974 if (fIncludeWord) {
2975 return this->reportError<bool>("word then single-quote");
2976 }
2977 this->pushBracket(Bracket::kChar);
2978 }
2979 break;
2980 case '\"':
2981 if (Bracket::kString == this->topBracket()) {
2982 this->popBracket();
2983 } else if (!fInComment && !fInChar) {
2984 if (fIncludeWord) {
2985 return this->reportError<bool>("word then double-quote");
2986 }
2987 this->pushBracket(Bracket::kString);
2988 }
2989 break;
Cary Clark8032b982017-07-28 11:04:54 -04002990 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002991 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002992 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2993 this->pushBracket(Bracket::kDebugCode);
2994 break;
2995 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002996 case ':':
2997 case '[':
2998 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002999 if (fInCharCommentString) {
3000 break;
3001 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003002 if (fInDefine && fInBrace) {
3003 break;
3004 }
Cary Clark8032b982017-07-28 11:04:54 -04003005 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
3006 break;
3007 }
Cary Clark0d225392018-06-07 09:59:07 -04003008 if (fConstExpr) {
3009 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3010 fConstExpr = nullptr;
3011 }
Cary Clark8032b982017-07-28 11:04:54 -04003012 if (!fInBrace) {
3013 if (!this->checkForWord()) {
3014 return false;
3015 }
3016 if (':' == test && !fInFunction) {
3017 break;
3018 }
3019 if ('{' == test) {
3020 this->addPunctuation(Punctuation::kLeftBrace);
3021 } else if (':' == test) {
3022 this->addPunctuation(Punctuation::kColon);
3023 }
3024 }
3025 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
3026 && Bracket::kColon == fInBrace->fBracket) {
3027 Definition* braceParent = fParent->fParent;
3028 braceParent->fChildren.pop_back();
3029 braceParent->fTokens.pop_back();
3030 fParent = braceParent;
3031 fInBrace = nullptr;
3032 }
3033 this->pushBracket(
3034 '(' == test ? Bracket::kParen :
3035 '[' == test ? Bracket::kSquare :
3036 '{' == test ? Bracket::kBrace :
3037 Bracket::kColon);
3038 if (!fInBrace
3039 && ('{' == test || (':' == test && ' ' >= fChar[1]))
3040 && fInFunction) {
3041 fInBrace = fParent;
3042 }
3043 } break;
3044 case '<':
3045 if (fInCharCommentString || fInBrace) {
3046 break;
3047 }
3048 if (!this->checkForWord()) {
3049 return false;
3050 }
3051 if (fInEnum) {
3052 break;
3053 }
3054 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04003055 // this angle bracket may be an operator or may be a bracket
3056 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04003057 break;
3058 case ')':
3059 case ']':
3060 case '}': {
3061 if (fInCharCommentString) {
3062 break;
3063 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003064 if (fInDefine && fInBrace) {
3065 break;
3066 }
Cary Clark8032b982017-07-28 11:04:54 -04003067 if (!fInBrace) {
3068 if (!this->checkForWord()) {
3069 return false;
3070 }
3071 }
3072 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04003073 Bracket match = ')' == test ? Bracket::kParen :
3074 ']' == test ? Bracket::kSquare : Bracket::kBrace;
3075 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04003076 this->popBracket();
3077 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003078 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04003079 } else {
3080 fInFunction = '}' != test;
3081 }
Cary Clark73fa9722017-08-29 17:36:51 -04003082 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
3083 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04003084 } else if (Bracket::kAngle == this->topBracket()
3085 && match == this->grandParentBracket()) {
3086 this->popBracket();
3087 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04003088 } else {
3089 return reportError<bool>("malformed close bracket");
3090 }
3091 if (popBraceParent) {
3092 Definition* braceParent = fInBrace->fParent;
3093 braceParent->fChildren.pop_back();
3094 braceParent->fTokens.pop_back();
3095 fInBrace = nullptr;
3096 }
3097 } break;
3098 case '>':
3099 if (fInCharCommentString || fInBrace) {
3100 break;
3101 }
3102 if (!this->checkForWord()) {
3103 return false;
3104 }
3105 if (fInEnum) {
3106 break;
3107 }
Cary Clarka560c472017-11-27 10:44:06 -05003108 if (Bracket::kPound == this->topBracket()) {
3109 break;
3110 }
Cary Clark8032b982017-07-28 11:04:54 -04003111 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04003112 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04003113 this->popBracket();
3114 } else {
3115 return reportError<bool>("malformed close angle bracket");
3116 }
3117 break;
3118 case '#': {
3119 if (fInCharCommentString || fInBrace) {
3120 break;
3121 }
3122 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3123 this->pushBracket(Bracket::kPound);
3124 break;
3125 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003126 case ' ':
3127 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3128 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3129 fInBrace = fParent;
3130 // delimiting brackets are space ... unescaped-linefeed
3131 }
Cary Clark8032b982017-07-28 11:04:54 -04003132 case '&':
3133 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003134 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003135 case '-':
3136 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003137 if (fInCharCommentString || fInBrace) {
3138 break;
3139 }
3140 if (!this->checkForWord()) {
3141 return false;
3142 }
3143 break;
Cary Clark0d225392018-06-07 09:59:07 -04003144 case '=':
3145 if (fInCharCommentString || fInBrace) {
3146 break;
3147 }
3148 if (!this->checkForWord()) {
3149 return false;
3150 }
Cary Clarkd7895502018-07-18 15:10:08 -04003151 if (!fParent->fTokens.size()) {
3152 break;
3153 }
Cary Clark0d225392018-06-07 09:59:07 -04003154 {
3155 const Definition& lastToken = fParent->fTokens.back();
3156 if (lastToken.fType != Definition::Type::kWord) {
3157 break;
3158 }
3159 string name(lastToken.fContentStart, lastToken.length());
3160 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3161 break;
3162 }
3163 // find token on start of line
3164 auto lineIter = fParent->fTokens.end();
3165 do {
Cary Clark80247e52018-07-11 16:18:41 -04003166 if (fParent->fTokens.begin() == lineIter) {
3167 break;
3168 }
Cary Clark0d225392018-06-07 09:59:07 -04003169 --lineIter;
3170 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003171 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003172 ++lineIter;
3173 }
3174 Definition* lineStart = &*lineIter;
3175 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3176 bool sawConst = false;
3177 bool sawStatic = false;
3178 bool sawTemplate = false;
3179 bool sawType = false;
3180 while (&lastToken != &*lineIter) {
3181 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3182 if (sawConst || sawStatic || sawTemplate) {
3183 sawConst = false;
3184 break;
3185 }
3186 if (&lastToken == &*++lineIter) {
3187 break;
3188 }
3189 if (KeyWord::kTypename != lineIter->fKeyWord) {
3190 break;
3191 }
3192 if (&lastToken == &*++lineIter) {
3193 break;
3194 }
3195 if (Definition::Type::kWord != lineIter->fType) {
3196 break;
3197 }
3198 sawTemplate = true;
3199 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3200 if (sawConst || sawStatic) {
3201 sawConst = false;
3202 break;
3203 }
3204 sawStatic = true;
3205 } else if (KeyWord::kConst == lineIter->fKeyWord
3206 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3207 if (sawConst) {
3208 sawConst = false;
3209 break;
3210 }
3211 sawConst = true;
3212 } else {
3213 if (sawType) {
3214 sawType = false;
3215 break;
3216 }
3217 if (Definition::Type::kKeyWord == lineIter->fType
3218 && KeyProperty::kNumber
3219 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3220 sawType = true;
3221 } else if (Definition::Type::kWord == lineIter->fType) {
3222 string typeName(lineIter->fContentStart, lineIter->length());
3223 if ("Sk" != name.substr(0, 2)) {
3224 sawType = true;
3225 }
3226 }
3227 }
3228 ++lineIter;
3229 }
3230 if (sawType && sawConst) {
3231 // if found, name first
3232 lineStart->fName = name;
3233 lineStart->fMarkType = MarkType::kConst;
Cary Clarkcb6bef02018-11-29 12:05:25 -05003234 this->checkName(lineStart);
Cary Clark0d225392018-06-07 09:59:07 -04003235 fParent->fChildren.emplace_back(lineStart);
3236 fConstExpr = lineStart;
3237 }
3238 }
3239 break;
Cary Clark8032b982017-07-28 11:04:54 -04003240 case ';':
3241 if (fInCharCommentString || fInBrace) {
3242 break;
3243 }
3244 if (!this->checkForWord()) {
3245 return false;
3246 }
Cary Clark0d225392018-06-07 09:59:07 -04003247 if (fConstExpr) {
3248 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3249 fConstExpr = nullptr;
3250 }
Cary Clark8032b982017-07-28 11:04:54 -04003251 if (Definition::Type::kKeyWord == fParent->fType
3252 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003253 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3254 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003255 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3256 this->popObject();
3257 }
Cary Clark8032b982017-07-28 11:04:54 -04003258 if (KeyWord::kEnum == fParent->fKeyWord) {
3259 fInEnum = false;
3260 }
Cary Clark61313f32018-10-08 14:57:48 -04003261 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003262 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003263 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3264 this->popObject();
3265 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003266 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003267 } else if (Definition::Type::kBracket == fParent->fType
3268 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3269 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3270 list<Definition>::iterator baseIter = fParent->fTokens.end();
3271 list<Definition>::iterator namedIter = fParent->fTokens.end();
3272 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003273 fParent->fTokens.begin() != tokenIter; ) {
3274 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003275 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003276 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003277 if (namedIter != fParent->fTokens.end()) {
3278 return reportError<bool>("found two named member tokens");
3279 }
3280 namedIter = tokenIter;
3281 }
3282 baseIter = tokenIter;
3283 } else {
3284 break;
3285 }
3286 }
3287 // FIXME: if a member definition spans multiple lines, this won't work
3288 if (namedIter != fParent->fTokens.end()) {
3289 if (baseIter == namedIter) {
3290 return this->reportError<bool>("expected type before named token");
3291 }
3292 Definition* member = &*namedIter;
3293 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003294 if (!member->fTerminator) {
3295 member->fTerminator = member->fContentEnd;
3296 }
Cary Clark8032b982017-07-28 11:04:54 -04003297 fParent->fChildren.push_back(member);
3298 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3299 member->fChildren.push_back(&*nameType);
3300 }
Cary Clark8032b982017-07-28 11:04:54 -04003301 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003302 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003303 } else if (fParent->fChildren.size() > 0) {
3304 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003305 Definition* priorEnum = fPriorEnum;
3306 fPriorEnum = nullptr;
3307 if (!priorEnum) {
3308 while (fParent->fChildren.begin() != lastIter) {
3309 std::advance(lastIter, -1);
3310 priorEnum = *lastIter;
3311 if (Definition::Type::kBracket != priorEnum->fType ||
3312 (Bracket::kSlashSlash != priorEnum->fBracket
3313 && Bracket::kSlashStar != priorEnum->fBracket)) {
3314 break;
3315 }
Cary Clark8032b982017-07-28 11:04:54 -04003316 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003317 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003318 }
3319 if (Definition::Type::kKeyWord == priorEnum->fType
3320 && KeyWord::kEnum == priorEnum->fKeyWord) {
3321 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003322 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003323 while (tokenWalker != fParent->fTokens.end()) {
3324 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003325 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003326 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3327 break;
3328 }
3329 }
3330 while (tokenWalker != fParent->fTokens.end()) {
3331 std::advance(tokenWalker, 1);
3332 const Definition* test = &*tokenWalker;
3333 if (Definition::Type::kBracket != test->fType ||
3334 (Bracket::kSlashSlash != test->fBracket
3335 && Bracket::kSlashStar != test->fBracket)) {
3336 break;
3337 }
3338 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003339 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003340 Definition* start = &*tokenWalker;
3341 bool foundExpected = true;
3342 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3343 const Definition* test = &*tokenWalker;
3344 if (expected != test->fKeyWord) {
3345 foundExpected = false;
3346 break;
3347 }
3348 if (tokenWalker == fParent->fTokens.end()) {
3349 break;
3350 }
3351 std::advance(tokenWalker, 1);
3352 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003353 if (!foundExpected) {
3354 foundExpected = true;
3355 tokenWalker = saveTokenWalker;
3356 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3357 const Definition* test = &*tokenWalker;
3358 if (expected != test->fKeyWord) {
3359 foundExpected = false;
3360 break;
3361 }
3362 if (tokenWalker == fParent->fTokens.end()) {
3363 break;
3364 }
3365 if (KeyWord::kNone != expected) {
3366 std::advance(tokenWalker, 1);
3367 }
3368 }
3369 if (foundExpected) {
3370 auto nameToken = priorEnum->fTokens.begin();
3371 string enumName = string(nameToken->fContentStart,
3372 nameToken->fContentEnd - nameToken->fContentStart);
3373 const Definition* test = &*tokenWalker;
3374 string constType = string(test->fContentStart,
3375 test->fContentEnd - test->fContentStart);
3376 if (enumName != constType) {
3377 foundExpected = false;
3378 } else {
3379 std::advance(tokenWalker, 1);
3380 }
3381 }
3382 }
Cary Clark8032b982017-07-28 11:04:54 -04003383 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3384 const char* nameStart = tokenWalker->fStart;
3385 std::advance(tokenWalker, 1);
3386 if (tokenWalker != fParent->fTokens.end()) {
3387 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003388 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003389 start->fName = string(nameStart, tp.fChar - nameStart);
Cary Clarkcb6bef02018-11-29 12:05:25 -05003390 this->checkName(start);
Cary Clark8032b982017-07-28 11:04:54 -04003391 start->fContentEnd = fChar;
3392 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003393 fPriorEnum = priorEnum;
3394 }
Cary Clark8032b982017-07-28 11:04:54 -04003395 }
3396 }
3397 }
3398 this->addPunctuation(Punctuation::kSemicolon);
3399 fInFunction = false;
3400 break;
3401 case '~':
3402 if (fInEnum) {
3403 break;
3404 }
3405 case '0': case '1': case '2': case '3': case '4':
3406 case '5': case '6': case '7': case '8': case '9':
3407 // TODO: don't want to parse numbers, but do need to track for enum defs
3408 // break;
3409 case 'A': case 'B': case 'C': case 'D': case 'E':
3410 case 'F': case 'G': case 'H': case 'I': case 'J':
3411 case 'K': case 'L': case 'M': case 'N': case 'O':
3412 case 'P': case 'Q': case 'R': case 'S': case 'T':
3413 case 'U': case 'V': case 'W': case 'X': case 'Y':
3414 case 'Z': case '_':
3415 case 'a': case 'b': case 'c': case 'd': case 'e':
3416 case 'f': case 'g': case 'h': case 'i': case 'j':
3417 case 'k': case 'l': case 'm': case 'n': case 'o':
3418 case 'p': case 'q': case 'r': case 's': case 't':
3419 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003420 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003421 if (fInCharCommentString || fInBrace) {
3422 break;
3423 }
3424 if (!fIncludeWord) {
3425 fIncludeWord = fChar;
3426 }
3427 break;
3428 }
3429done:
3430 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003431 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003432 return true;
3433}
3434
3435void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003436 IncludeParser::ValidateKeyWords();
3437}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003438
Cary Clark186d08f2018-04-03 08:43:27 -04003439bool IncludeParser::references(const SkString& file) const {
3440 // if includes weren't passed one at a time, assume all references are valid
3441 if (fIncludeMap.empty()) {
3442 return true;
3443 }
3444 SkASSERT(file.endsWith(".bmh") );
3445 string root(file.c_str(), file.size() - 4);
3446 string kReference("_Reference");
3447 if (string::npos != root.find(kReference)) {
3448 root = root.substr(0, root.length() - kReference.length());
3449 }
3450 if (fIClassMap.end() != fIClassMap.find(root)) {
3451 return true;
3452 }
3453 if (fIStructMap.end() != fIStructMap.find(root)) {
3454 return true;
3455 }
Cary Clark224c7002018-06-27 11:00:21 -04003456 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3457 return true;
3458 }
Cary Clarka90ea222018-10-16 10:30:28 -04003459 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3460 return true;
3461 }
Cary Clark224c7002018-06-27 11:00:21 -04003462 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3463 return true;
3464 }
Cary Clark186d08f2018-04-03 08:43:27 -04003465 return false;
3466}
3467
Cary Clark2dc84ad2018-01-26 12:56:22 -05003468void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3469 if (!sk_isdir(includes)) {
3470 IncludeParser::RemoveOneFile(docs, includes);
3471 } else {
3472 SkOSFile::Iter it(includes, ".h");
3473 for (SkString file; it.next(&file); ) {
3474 SkString p = SkOSPath::Join(includes, file.c_str());
3475 const char* hunk = p.c_str();
3476 if (!SkStrEndsWith(hunk, ".h")) {
3477 continue;
3478 }
3479 IncludeParser::RemoveOneFile(docs, hunk);
3480 }
3481 }
3482}
3483
3484void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3485 const char* lastForward = strrchr(includesFile, '/');
3486 const char* lastBackward = strrchr(includesFile, '\\');
3487 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3488 if (!last) {
3489 last = includesFile;
3490 } else {
3491 last += 1;
3492 }
3493 SkString baseName(last);
3494 SkASSERT(baseName.endsWith(".h"));
3495 baseName.remove(baseName.size() - 2, 2);
3496 baseName.append("_Reference.bmh");
Cary Clarkcb6bef02018-11-29 12:05:25 -05003497 SkString fullName = docs ? SkOSPath::Join(docs, baseName.c_str()) : baseName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05003498 remove(fullName.c_str());
3499}
Cary Clark224c7002018-06-27 11:00:21 -04003500
Cary Clarkfd32e722018-11-16 14:36:02 -05003501static const char kMethodMissingStr[] =
3502 "If the method requires documentation, add to "
3503 "%s at minimum:\n" // path to bmh file
3504 "\n"
3505 "#Method %s\n" // method declaration less implementation details
3506 "#In SomeSubtopicName\n"
3507 "#Line # add a one line description here ##\n"
3508 "#Populate\n"
3509 "#NoExample\n"
3510 "// or better yet, use #Example and put C++ code here\n"
3511 "##\n"
3512 "#SeeAlso optional related symbols\n"
3513 "#Method ##\n"
3514 "\n"
3515 "Add to %s, at minimum:\n" // path to include
3516 "\n"
3517 "/** (description) Starts with present tense action verb\n"
3518 " and end with a period.\n"
3519 "%s" // @param, @return if needed go here
3520 "*/\n"
3521 "%s ...\n" // method declaration
3522 "\n"
3523 "If the method does not require documentation,\n"
3524 "add \"private\" or \"experimental\", as in:\n"
3525 "\n"
3526 "/** Experimental, do not use. And so on...\n"
3527 "*/\n"
3528 "%s ...\n" // method declaration
3529 "\n"
3530 ;
3531
3532// bDef does not have #Populate
3533static const char kMethodDiffersNoPopStr[] =
3534 "In %s:\n" // path to bmh file
3535 "#Method %s\n" // method declaration less implementation details
3536 "does not match doxygen comment of:\n"
3537 "%s.\n" // method declaration
3538 "\n"
3539 ;
3540
3541static const char kMethodDiffersStr[] =
3542 "In %s:\n" // path to include
3543 "%s\n" // method declaration
3544 "does not match doxygen comment.\n"
3545 "\n"
3546 ;
3547
3548void IncludeParser::suggestFix(Suggest suggest, const Definition& iDef,
3549 const RootDefinition* root, const Definition* bDef) {
3550 string methodNameStr(iDef.fContentStart, iDef.length());
3551 const char* methodName = methodNameStr.c_str();
3552 TextParser lessImplParser(&iDef);
3553 if (lessImplParser.skipExact("static")) {
3554 lessImplParser.skipWhiteSpace();
3555 }
3556 // TODO : handle debug wrapper
3557 /* bool inDebugWrapper = */ Definition::SkipImplementationWords(lessImplParser);
3558 string lessImplStr(lessImplParser.fChar, lessImplParser.fEnd - lessImplParser.fChar);
3559 const char* methodNameLessImpl = lessImplStr.c_str();
3560 // return result, if any is substr from 0 to location of iDef.fName
3561 size_t namePos = methodNameStr.find(iDef.fName);
3562 SkASSERT(string::npos != namePos);
3563 size_t funcEnd = namePos;
3564 while (funcEnd > 0 && ' ' >= methodNameStr[funcEnd - 1]) {
3565 funcEnd -= 1;
3566 }
3567 string funcRet = methodNameStr.substr(0, funcEnd);
3568// parameters, if any, are delimited by () and separate by ,
3569 TextParser parser(&iDef);
3570 parser.fChar += namePos + iDef.fName.length();
3571 const char* start = parser.fChar;
3572 vector<string> paramStrs;
3573 if ('(' == start[0]) {
3574 parser.skipToBalancedEndBracket('(', ')');
3575 TextParser params(&iDef);
3576 params.fChar = start + 1;
3577 params.fEnd = parser.fChar;
3578 while (!params.eof()) {
3579 const char* paramEnd = params.anyOf("=,)");
3580 const char* paramStart = paramEnd;
3581 while (paramStart > params.fChar && ' ' >= paramStart[-1]) {
3582 paramStart -= 1;
3583 }
3584 while (paramStart > params.fChar && (isalnum(paramStart[-1])
3585 || '_' == paramStart[-1])) {
3586 paramStart -= 1;
3587 }
3588 string param(paramStart, paramEnd - paramStart);
3589 paramStrs.push_back(param);
3590 params.fChar = params.anyOf(",)") + 1;
3591 }
3592 }
3593 string bmhFile = root ? root->fFileName : bDef ? bDef->fFileName : "a *.bmh file";
3594 bool hasFuncReturn = "" != funcRet && "void" != funcRet;
3595 switch(suggest) {
3596 case Suggest::kMethodMissing: {
3597 // if include @param, @return are missing, request them as well
3598 string paramDox;
3599 bool firstParam = true;
3600 for (auto paramStr : paramStrs) {
3601 if (firstParam) {
3602 paramDox += "\n";
3603 firstParam = false;
3604 }
3605 paramDox += " @param " + paramStr + " descriptive phrase\n";
3606 }
3607 if (hasFuncReturn) {
3608 paramDox += "\n";
3609 paramDox += " @return descriptive phrase\n";
3610 }
3611 SkDebugf(kMethodMissingStr, bmhFile.c_str(), methodNameLessImpl, iDef.fFileName.c_str(),
3612 paramDox.c_str(), methodName, methodName);
3613 } break;
3614 case Suggest::kMethodDiffers: {
3615 bool hasPop = std::any_of(bDef->fChildren.begin(), bDef->fChildren.end(),
3616 [](Definition* def) { return MarkType::kPopulate == def->fMarkType; });
3617 if (!hasPop) {
3618 SkDebugf(kMethodDiffersNoPopStr, bmhFile.c_str(), methodNameLessImpl, methodName);
3619 }
3620 SkDebugf(kMethodDiffersStr, iDef.fFileName.c_str(), methodName);
3621 } break;
3622 default:
3623 SkASSERT(0);
3624 }
3625}
3626
Cary Clark224c7002018-06-27 11:00:21 -04003627Bracket IncludeParser::topBracket() const {
3628 Definition* parent = this->parentBracket(fParent);
3629 return parent ? parent->fBracket : Bracket::kNone;
3630}