blob: 82bbce58a75cd087216f1b7afecfda88c1f56bcd [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
Cary Clark05c1dcf2018-12-12 13:32:56 -05001831 if (string::npos == methodName.find('(')) {
1832 return nullptr;
1833 }
Cary Clark09d80c02018-10-31 12:14:03 -04001834 auto stripper = [](string s) -> string {
1835 bool last = false;
1836 string result;
1837 for (char c : s) {
1838 if (' ' >= c) {
1839 if (!last) {
1840 last = true;
1841 result += ' ';
1842 }
1843 continue;
1844 }
1845 result += c;
1846 last = false;
1847 }
1848 return result;
1849 };
1850 string strippedMethodName = stripper(methodName);
1851 if (strippedMethodName == methodName) {
1852 strippedMethodName = "";
1853 }
1854 const auto& cMethod = std::find_if(iTokens.begin(), iTokens.end(),
1855 [className, methodName, stripper, strippedMethodName](Definition& token) {
1856 if (MarkType::kMethod != token.fMarkType) {
1857 return false;
1858 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001859 if (token.fUndocumented) {
1860 return false;
1861 }
Cary Clark09d80c02018-10-31 12:14:03 -04001862 TextParser parser(&token);
1863 const char* match = parser.strnstr(className.c_str(), parser.fEnd);
1864 if (!match) {
1865 return false;
1866 }
1867 parser.skipTo(match);
1868 parser.skipExact(className.c_str());
1869 if ('(' != parser.peek()) {
1870 return false;
1871 }
1872 parser.skipToBalancedEndBracket('(', ')');
1873 string iMethodName(match, parser.fChar - match);
1874 if (methodName == iMethodName) {
1875 return true;
1876 }
Cary Clarkcb6bef02018-11-29 12:05:25 -05001877 if (strippedMethodName.empty()) {
Cary Clark09d80c02018-10-31 12:14:03 -04001878 return false;
1879 }
1880 string strippedIName = stripper(iMethodName);
1881 return strippedIName == strippedMethodName;
1882 } );
1883 SkAssertResult(iTokens.end() != cMethod);
1884 return &*cMethod;
Cary Clarka64e4ee2018-10-18 08:30:34 -04001885}
1886
Cary Clark224c7002018-06-27 11:00:21 -04001887Definition* IncludeParser::parentBracket(Definition* parent) const {
1888 while (parent && Definition::Type::kBracket != parent->fType) {
1889 parent = parent->fParent;
1890 }
1891 return parent;
1892}
1893
1894Bracket IncludeParser::grandParentBracket() const {
1895 Definition* parent = parentBracket(fParent);
1896 parent = parentBracket(parent ? parent->fParent : nullptr);
1897 return parent ? parent->fBracket : Bracket::kNone;
1898}
1899
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001900bool IncludeParser::inAlignAs() const {
1901 if (fParent->fTokens.size() < 2) {
1902 return false;
1903 }
1904 auto reverseIter = fParent->fTokens.end();
1905 bool checkForBracket = true;
1906 while (fParent->fTokens.begin() != reverseIter) {
1907 std::advance(reverseIter, -1);
1908 if (checkForBracket) {
1909 if (Definition::Type::kBracket != reverseIter->fType) {
1910 return false;
1911 }
1912 if (Bracket::kParen != reverseIter->fBracket) {
1913 return false;
1914 }
1915 checkForBracket = false;
1916 continue;
1917 }
1918 if (Definition::Type::kKeyWord != reverseIter->fType) {
1919 return false;
1920 }
1921 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1922 }
1923 return false;
1924}
1925
Cary Clark61313f32018-10-08 14:57:48 -04001926const Definition* IncludeParser::include(string match) const {
1927 for (auto& entry : fIncludeMap) {
1928 if (string::npos == entry.first.find(match)) {
1929 continue;
1930 }
1931 return &entry.second;
1932 }
1933 SkASSERT(0);
1934 return nullptr;
1935}
1936
Cary Clark137b8742018-05-30 09:21:49 -04001937// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001938bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1939 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001940 // parse class header
1941 auto iter = includeDef->fTokens.begin();
1942 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1943 // todo : documentation is ignoring this for now
1944 iter = std::next(iter);
1945 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001946 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1947 if (hasAlignAs) {
1948 iter = std::next(iter);
1949 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1950 return includeDef->reportError<bool>("expected alignas argument");
1951 }
1952 iter = std::next(iter);
1953 }
Cary Clark8032b982017-07-28 11:04:54 -04001954 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1955 includeDef->fName = nameStr;
Cary Clarkcb6bef02018-11-29 12:05:25 -05001956 this->checkName(includeDef);
Cary Clark9174bda2017-09-19 17:39:32 -04001957 iter = std::next(iter);
1958 if (iter == includeDef->fTokens.end()) {
1959 return true; // forward declaration only
1960 }
Cary Clark8032b982017-07-28 11:04:54 -04001961 do {
1962 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001963 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001964 }
1965 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1966 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001967 }
Cary Clark8032b982017-07-28 11:04:54 -04001968 } while (static_cast<void>(iter = std::next(iter)), true);
1969 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001970 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001971 }
1972 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1973 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001974 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001975 }
1976 markupDef->fStart = iter->fStart;
1977 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001978 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001979 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001980 if (markupDef->fUndocumented) {
1981 includeDef->fUndocumented = true;
1982 }
Cary Clark8032b982017-07-28 11:04:54 -04001983// if (1 != includeDef->fChildren.size()) {
1984// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1985// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001986 auto includeDefIter = includeDef->fChildren.begin();
1987 if (hasAlignAs) {
1988 SkASSERT(includeDef->fChildren.end() != includeDefIter);
1989 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
1990 std::advance(includeDefIter, 1);
1991 }
1992 if (includeDef->fChildren.end() != includeDefIter
1993 && Bracket::kAngle == (*includeDefIter)->fBracket) {
1994 std::advance(includeDefIter, 1);
1995 }
1996 includeDef = *includeDefIter;
1997 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04001998 iter = includeDef->fTokens.begin();
1999 // skip until public
2000 int publicIndex = 0;
2001 if (IsStruct::kNo == isStruct) {
2002 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2003 size_t publicLen = strlen(publicName);
2004 while (iter != includeDef->fTokens.end()
2005 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
2006 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04002007 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002008 iter = std::next(iter);
2009 ++publicIndex;
2010 }
2011 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002012 int keyIndex = publicIndex;
2013 KeyWord currentKey = KeyWord::kPublic;
2014 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2015 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04002016 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
2017 size_t protectedLen = strlen(protectedName);
2018 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
2019 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04002020 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002021 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04002022 std::advance(childIter, 1);
2023 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002024 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04002025 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04002026 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04002027 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04002028 const char* testStart = iter->fStart;
2029 size_t testLen = (size_t) (iter->fContentEnd - testStart);
2030 iter = std::next(iter);
2031 ++keyIndex;
2032 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2033 currentKey = KeyWord::kPublic;
2034 break;
2035 }
2036 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2037 currentKey = KeyWord::kProtected;
2038 break;
2039 }
2040 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2041 currentKey = KeyWord::kPrivate;
2042 break;
2043 }
2044 }
2045 fLastObject = nullptr;
2046 if (KeyWord::kPublic == currentKey) {
2047 if (!this->parseObject(child, markupDef)) {
2048 return false;
2049 }
Cary Clark8032b982017-07-28 11:04:54 -04002050 }
Cary Clark73fa9722017-08-29 17:36:51 -04002051 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002052 childIter = std::next(childIter);
2053 }
Cary Clark137b8742018-05-30 09:21:49 -04002054 while (iter != includeDef->fTokens.end()) {
2055 iter->fPrivate = KeyWord::kPublic != currentKey;
2056 iter = std::next(iter);
2057 }
Cary Clark8032b982017-07-28 11:04:54 -04002058 SkASSERT(fParent->fParent);
2059 fParent = fParent->fParent;
2060 return true;
2061}
2062
Cary Clarkabaffd82018-11-15 08:25:12 -05002063bool IncludeParser::isUndocumentable(string filename, const char* start, const char* end,
2064 int lineCount) {
Cary Clark8032b982017-07-28 11:04:54 -04002065 TextParser parser(filename, start, end, lineCount);
Cary Clarkcb6bef02018-11-29 12:05:25 -05002066 const vector<string> skipWords = { "deprecated", "experimental", "private" };
Cary Clarkabaffd82018-11-15 08:25:12 -05002067 const vector<string> butNot = { "to be deprecated", "may be deprecated" };
2068 const vector<string> alsoNot = { "todo" };
2069 string match = parser.anyWord(skipWords, 0);
2070 if ("" != match) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05002071 if (parser.anyWord(alsoNot, 0).empty()
2072 && ("deprecated" != match || parser.anyWord(butNot, 2).empty())) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002073 return true;
2074 }
2075 }
2076 return false;
2077}
2078
2079bool IncludeParser::parseComment(string filename, const char* start, const char* end,
2080 int lineCount, Definition* markupDef, bool* undocumentedPtr) {
2081 if (this->isUndocumentable(filename, start, end, lineCount)) {
2082 *undocumentedPtr = true;
2083 }
Cary Clark8032b982017-07-28 11:04:54 -04002084 // parse doxygen if present
Cary Clarkabaffd82018-11-15 08:25:12 -05002085 TextParser parser(filename, start, end, lineCount);
Cary Clark8032b982017-07-28 11:04:54 -04002086 if (parser.startsWith("**")) {
2087 parser.next();
2088 parser.next();
2089 parser.skipWhiteSpace();
2090 if ('\\' == parser.peek()) {
2091 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002092 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2093 if (parser.skipExact("file")) {
2094 if (Definition::Type::kFileType != fParent->fType) {
2095 return reportError<bool>("expected parent is file");
2096 }
2097 string filename = markupDef->fileName();
2098 if (!parser.skipWord(filename.c_str())) {
2099 return reportError<bool>("missing object type");
2100 }
2101 } else if (parser.skipExact("fn")) {
2102 SkASSERT(0); // incomplete
2103 } else {
2104 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2105 return reportError<bool>("missing object type");
2106 }
2107 if (!parser.skipWord(markupDef->fName.c_str()) &&
2108 KeyWord::kEnum != markupDef->fKeyWord) {
2109 return reportError<bool>("missing object name");
2110 }
Cary Clark8032b982017-07-28 11:04:54 -04002111 }
Cary Clark8032b982017-07-28 11:04:54 -04002112 }
2113 }
2114 // remove leading '*' if present
2115 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2116 while (!parser.eof() && parser.skipWhiteSpace()) {
2117 while ('*' == parser.peek()) {
2118 parser.next();
2119 if (parser.eof()) {
2120 break;
2121 }
2122 parser.skipWhiteSpace();
2123 }
2124 if (parser.eof()) {
2125 break;
2126 }
2127 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002128 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002129 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002130 parser.skipToEndBracket('\n');
2131 }
2132 return true;
2133}
2134
Cary Clarkabaffd82018-11-15 08:25:12 -05002135/*
2136 find comment either in front of or after the const def and then extract if the
2137 const is undocumented
2138 */
Cary Clarkd98f78c2018-04-26 08:32:37 -04002139bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002140 if (!markupDef) {
2141 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2142 child->fLineCount, fParent, '\0');
2143 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002144 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002145 globalMarkupChild->fName = globalUniqueName;
2146 if (!this->findComments(*child, globalMarkupChild)) {
2147 return false;
2148 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002149 if (!this->findCommentAfter(*child, globalMarkupChild)) {
2150 return false;
2151 }
2152 if (globalMarkupChild->fUndocumented) {
2153 child->fUndocumented = true;
2154 } else {
2155 fIConstMap[globalUniqueName] = globalMarkupChild;
2156 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002157 return true;
2158 }
2159 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2160 child->fLineCount, markupDef, '\0');
2161 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002162 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002163 markupChild->fTerminator = markupChild->fContentEnd;
2164 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002165 classDef.fConsts[child->fName] = markupChild;
Cary Clarkabaffd82018-11-15 08:25:12 -05002166 if (!this->findComments(*child, markupChild)) {
2167 return false;
2168 }
2169 if (!this->findCommentAfter(*child, markupChild)) {
2170 return false;
2171 }
2172 if (markupChild->fUndocumented) {
2173 child->fUndocumented = true;
2174 } else {
2175 fIConstMap[child->fName] = markupChild;
2176 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002177 return true;
2178}
2179
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002180bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2181 TextParser parser(child);
2182 if (!parser.skipExact("#define")) {
2183 return false;
2184 }
2185 if (!parser.skipSpace()) {
2186 return false;
2187 }
2188 const char* nameStart = parser.fChar;
2189 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2190 if (parser.eof()) {
2191 return true; // do nothing if #define doesn't define anything
2192 }
2193 string nameStr(nameStart, parser.fChar - nameStart);
2194 struct Param {
2195 const char* fStart;
2196 const char* fEnd;
2197 };
2198 vector<Param> params;
2199 if ('(' == parser.peek()) {
2200 parser.next();
2201 if (!parser.skipSpace()) {
2202 return false;
2203 }
2204 do {
2205 const char* paramStart = parser.fChar;
2206 if (!parser.skipExact("...")) {
2207 parser.skipToNonAlphaNum();
2208 }
2209 if (parser.eof()) {
2210 return false;
2211 }
2212 params.push_back({paramStart, parser.fChar});
2213 if (!parser.skipSpace()) {
2214 return false;
2215 }
2216 if (')' == parser.peek()) {
2217 parser.next();
2218 break;
2219 }
2220 if (',' != parser.next()) {
2221 return false;
2222 }
2223 if (!parser.skipSpace()) {
2224 return false;
2225 }
2226 } while (true);
2227 }
2228 if (!parser.skipSpace()) {
2229 return false;
2230 }
2231 if (!markupDef) {
2232 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2233 child->fLineCount, fParent, '\0');
2234 Definition* globalMarkupChild = &fGlobals.back();
2235 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2236 globalMarkupChild->fName = globalUniqueName;
2237 globalMarkupChild->fTerminator = child->fContentEnd;
2238 if (!this->findComments(*child, globalMarkupChild)) {
2239 return false;
2240 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002241 if (!globalMarkupChild->fUndocumented) {
2242 fIDefineMap[globalUniqueName] = globalMarkupChild;
2243 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002244 for (Param param : params) {
2245 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2246 child->fLineCount, globalMarkupChild, '\0');
2247 Definition* paramChild = &globalMarkupChild->fTokens.back();
2248 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
Cary Clarkcb6bef02018-11-29 12:05:25 -05002249 this->checkName(paramChild);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002250 paramChild->fTerminator = param.fEnd;
2251 }
2252 return true;
2253 }
2254 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2255 child->fLineCount, markupDef, '\0');
2256 Definition* markupChild = &markupDef->fTokens.back();
2257 markupChild->fName = nameStr;
2258 markupChild->fTerminator = markupChild->fContentEnd;
2259 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2260 if (!this->findComments(*child, markupChild)) {
2261 return false;
2262 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002263 if (markupChild->fUndocumented) {
2264 child->fUndocumented = true;
2265 } else {
2266 classDef.fDefines[nameStr] = markupChild;
2267 fIDefineMap[nameStr] = markupChild;
2268 }
Cary Clark8032b982017-07-28 11:04:54 -04002269 return true;
2270}
2271
2272bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002273 if (!child->fTokens.size()) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002274 return true; // if enum is a forward declaration, do nothing
2275 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002276 bool isEnumClass = false;
2277 Definition* parent = child;
2278 auto token = parent->fTokens.begin();
2279 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2280 isEnumClass = true;
2281 parent = &*token;
2282 token = parent->fTokens.begin();
Cary Clark8032b982017-07-28 11:04:54 -04002283 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002284 SkASSERT(Definition::Type::kWord == token->fType);
2285 string nameStr = string(token->fStart, token->fContentEnd - token->fStart);
Cary Clark2dc84ad2018-01-26 12:56:22 -05002286 Definition* markupChild;
2287 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002288 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2289 child->fLineCount, fParent, '\0');
2290 markupChild = &fGlobals.back();
2291 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2292 markupChild->fName = globalUniqueName;
2293 markupChild->fTerminator = child->fContentEnd;
Cary Clarkabaffd82018-11-15 08:25:12 -05002294 if (!markupChild->fUndocumented) {
2295 fIEnumMap[globalUniqueName] = markupChild;
2296 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002297 } else {
2298 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002299 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002300 markupChild = &markupDef->fTokens.back();
2301 }
Cary Clark8032b982017-07-28 11:04:54 -04002302 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2303 markupChild->fKeyWord = KeyWord::kEnum;
Cary Clarkabaffd82018-11-15 08:25:12 -05002304 if (isEnumClass) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04002305 markupChild->fMarkType = MarkType::kEnumClass;
2306 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002307 if (markupDef) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002308 markupChild->fName = markupDef->fName + "::" + nameStr;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002309 }
Cary Clark8032b982017-07-28 11:04:54 -04002310 if (!this->findComments(*child, markupChild)) {
2311 return false;
2312 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002313 if (markupChild->fUndocumented) {
2314 child->fUndocumented = true;
2315 }
2316 if (!this->parseEnumConst(token, parent->fTokens.end(), markupChild)) {
2317 return false;
2318 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002319 for (auto outsideMember : child->fChildren) {
2320 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002321 continue;
2322 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002323 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2324 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002325 continue;
2326 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002327 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2328 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002329 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002330 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002331 member->fName = outsideMember->fName;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002332 this->checkName(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002333 // FIXME: ? add comment as well ?
2334 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002335 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002336 if (markupDef) {
2337 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2338 SkASSERT(classDef.fStart);
2339 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002340 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002341 markupChild->fName = uniqueName;
2342 classDef.fEnums[uniqueName] = markupChild;
Cary Clarkabaffd82018-11-15 08:25:12 -05002343 if (!markupChild->fUndocumented) {
2344 fIEnumMap[fullName] = markupChild;
2345 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002346 }
Cary Clark8032b982017-07-28 11:04:54 -04002347 return true;
2348}
2349
Cary Clarkabaffd82018-11-15 08:25:12 -05002350bool IncludeParser::parseOneEnumConst(list<Definition>& constList,
2351 Definition* markupChild, bool skipWord) {
2352 auto memberIter = constList.begin();
2353 const auto memberIterEnd = constList.end();
2354 if (skipWord) {
2355 SkASSERT(Definition::Type::kWord == memberIter->fType);
2356 memberIter = std::next(memberIter);
2357 SkASSERT(memberIterEnd != memberIter);
2358 }
2359 // token array has parse atoms; child array has comments
2360 bool undocumented = false;
2361 while (memberIterEnd != memberIter) {
2362 while (Bracket::kSlashStar == memberIter->fBracket) {
2363 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2364 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2365 return false;
2366 }
2367 memberIter = std::next(memberIter);
2368 if (memberIterEnd == memberIter) {
2369 return false;
2370 }
2371 }
2372 if (Bracket::kPound == memberIter->fBracket) {
2373 KeyWord keyWord = memberIter->fKeyWord;
2374 bool sawIf = KeyWord::kIfdef == keyWord || KeyWord::kIf == keyWord
2375 || KeyWord::kElif == keyWord;
2376 if (sawIf || KeyWord::kElse == keyWord) {
2377 if (!parseOneEnumConst(memberIter->fTokens, markupChild, sawIf)) {
2378 return false;
2379 }
2380 } else {
2381 SkASSERT(KeyWord::kEndif == keyWord || KeyWord::kError == keyWord);
2382 }
2383 memberIter = std::next(memberIter);
2384 if (memberIterEnd == memberIter) {
2385 break;
2386 }
2387 continue;
2388 }
2389 while (Definition::Type::kWord != memberIter->fType) {
2390 memberIter = std::next(memberIter);
2391 if (memberIterEnd == memberIter) {
2392 return false;
2393 }
2394 }
2395 auto memberStart = memberIter;
2396 Definition* memberEnd = nullptr;
2397 const char* last;
2398 do {
2399 last = memberIter->fContentEnd;
2400 memberIter = std::next(memberIter);
2401 if (memberIterEnd == memberIter) {
2402 break;
2403 }
2404 memberEnd = &*memberIter;
2405 } while (string::npos == string(last, memberIter->fContentStart).find(','));
2406 if (!memberEnd) {
2407 return false;
2408 }
2409 if (memberIterEnd != memberIter && Bracket::kSlashSlash == memberIter->fBracket) {
2410 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2411 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2412 return false;
2413 }
2414 memberIter = std::next(memberIter);
2415 }
2416 markupChild->fTokens.emplace_back(MarkType::kMember, memberStart->fContentStart,
2417 memberEnd->fContentEnd, memberStart->fLineCount, markupChild, '\0');
2418 Definition* markupMember = &markupChild->fTokens.back();
2419 string name = string(memberStart->fContentStart, memberStart->length());
2420 memberStart->fName = name;
2421 markupMember->fName = name;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002422 this->checkName(markupMember);
2423 memberStart->fUndocumented = markupMember->fUndocumented;
Cary Clarkabaffd82018-11-15 08:25:12 -05002424 memberStart->fMarkType = MarkType::kMember;
2425 undocumented = false;
2426 }
2427 return true;
2428}
2429
2430bool IncludeParser::parseEnumConst(list<Definition>::iterator& tokenIter,
2431 const list<Definition>::iterator& tokenEnd, Definition* markupChild) {
2432 SkASSERT(Definition::Type::kWord == tokenIter->fType); // should be enum name
2433 tokenIter = std::next(tokenIter);
2434 SkASSERT(tokenEnd != tokenIter);
2435 if (Definition::Type::kKeyWord == tokenIter->fType) {
2436 SkASSERT((unsigned) tokenIter->fKeyWord < SK_ARRAY_COUNT(kKeyWords));
2437 SkASSERT(KeyProperty::kNumber == kKeyWords[(int) tokenIter->fKeyWord].fProperty);
2438 tokenIter = std::next(tokenIter);
2439 SkASSERT(tokenEnd != tokenIter);
2440 }
2441 SkASSERT(Punctuation::kLeftBrace == tokenIter->fPunctuation);
2442 tokenIter = std::next(tokenIter);
2443 SkASSERT(tokenEnd != tokenIter);
2444 SkASSERT(Bracket::kBrace == tokenIter->fBracket);
2445 return parseOneEnumConst(tokenIter->fTokens, markupChild, false);
2446}
2447
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002448bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002449 fParent = &fIncludeMap[name];
2450 fParent->fName = name;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002451 this->checkName(fParent);
Cary Clark8032b982017-07-28 11:04:54 -04002452 fParent->fFileName = fFileName;
2453 fParent->fType = Definition::Type::kFileType;
2454 fParent->fContentStart = fChar;
2455 fParent->fContentEnd = fEnd;
2456 // parse include file into tree
2457 while (fChar < fEnd) {
2458 if (!this->parseChar()) {
2459 return false;
2460 }
2461 }
2462 // parse tree and add named objects to maps
2463 fParent = &fIncludeMap[name];
2464 if (!this->parseObjects(fParent, nullptr)) {
2465 return false;
2466 }
2467 return true;
2468}
2469
2470bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2471 const char* typeStart = child->fChildren[0]->fContentStart;
2472 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002473 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002474 Definition* markupChild = &markupDef->fTokens.back();
2475 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002476 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002477 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2478 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2479 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2480 markupChild->fName = uniqueName;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002481 this->checkName(markupChild);
Cary Clark9174bda2017-09-19 17:39:32 -04002482 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clarkabaffd82018-11-15 08:25:12 -05002483 if (!markupChild->fUndocumented) {
2484 classDef.fMembers[uniqueName] = markupChild;
2485 }
Cary Clark8032b982017-07-28 11:04:54 -04002486 if (child->fParentIndex >= 2) {
2487 auto comment = child->fParent->fTokens.begin();
2488 std::advance(comment, child->fParentIndex - 2);
2489 if (Definition::Type::kBracket == comment->fType
2490 && (Bracket::kSlashStar == comment->fBracket
2491 || Bracket::kSlashSlash == comment->fBracket)) {
2492 TextParser parser(&*comment);
2493 do {
2494 parser.skipToAlpha();
2495 if (parser.eof()) {
2496 break;
2497 }
2498 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002499 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002500 if (Bracket::kSlashStar == comment->fBracket) {
2501 const char* commentEnd = parser.strnstr("*/", end);
2502 if (commentEnd) {
2503 end = commentEnd;
2504 }
2505 }
2506 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002507 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002508 Definition* commentChild = &markupDef->fTokens.back();
2509 markupChild->fChildren.emplace_back(commentChild);
2510 parser.skipTo(end);
2511 } while (!parser.eof());
2512 }
2513 }
2514 return true;
2515}
2516
2517bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2518 auto tokenIter = child->fParent->fTokens.begin();
2519 std::advance(tokenIter, child->fParentIndex);
2520 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002521 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002522 bool addConst = false;
2523 auto operatorCheck = tokenIter;
2524 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2525 operatorCheck = std::prev(tokenIter);
2526 }
2527 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002528 auto closeParen = std::next(tokenIter);
2529 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2530 '(' == closeParen->fContentStart[0]);
2531 nameEnd = closeParen->fContentEnd + 1;
2532 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002533 if (Definition::Type::kKeyWord == closeParen->fType &&
2534 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002535 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002536 }
Cary Clarka560c472017-11-27 10:44:06 -05002537 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002538 }
2539 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002540 if (addConst) {
Cary Clarkcb6bef02018-11-29 12:05:25 -05002541 nameStr += " const";
Cary Clark154beea2017-10-26 07:58:48 -04002542 }
Cary Clark8032b982017-07-28 11:04:54 -04002543 while (tokenIter != child->fParent->fTokens.begin()) {
2544 auto testIter = std::prev(tokenIter);
2545 switch (testIter->fType) {
2546 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002547 if (testIter == child->fParent->fTokens.begin() &&
2548 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2549 KeyWord::kIfndef == child->fParent->fKeyWord ||
2550 KeyWord::kIf == child->fParent->fKeyWord)) {
2551 std::next(tokenIter);
2552 break;
2553 }
Cary Clark8032b982017-07-28 11:04:54 -04002554 goto keepGoing;
2555 case Definition::Type::kKeyWord: {
2556 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2557 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2558 goto keepGoing;
2559 }
2560 } break;
2561 case Definition::Type::kBracket:
2562 if (Bracket::kAngle == testIter->fBracket) {
2563 goto keepGoing;
2564 }
2565 break;
2566 case Definition::Type::kPunctuation:
2567 if (Punctuation::kSemicolon == testIter->fPunctuation
2568 || Punctuation::kLeftBrace == testIter->fPunctuation
2569 || Punctuation::kColon == testIter->fPunctuation) {
2570 break;
2571 }
2572 keepGoing:
2573 tokenIter = testIter;
2574 continue;
2575 default:
2576 break;
2577 }
2578 break;
2579 }
Cary Clark224c7002018-06-27 11:00:21 -04002580 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002581 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002582 tokenIter->fPrivate = string::npos != nameStr.find("::")
2583 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002584 this->checkName(&*tokenIter);
Cary Clark8032b982017-07-28 11:04:54 -04002585 auto testIter = child->fParent->fTokens.begin();
2586 SkASSERT(child->fParentIndex > 0);
2587 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002588 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2589 0 == tokenIter->fParentIndex) {
2590 tokenIter = std::next(tokenIter);
2591 }
Cary Clark8032b982017-07-28 11:04:54 -04002592 const char* start = tokenIter->fContentStart;
2593 const char* end = tokenIter->fContentEnd;
2594 const char kDebugCodeStr[] = "SkDEBUGCODE";
2595 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2596 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2597 std::advance(testIter, 1);
2598 start = testIter->fContentStart + 1;
2599 end = testIter->fContentEnd - 1;
2600 } else {
2601 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002602 do {
2603 std::advance(testIter, 1);
2604 if (testIter == child->fParent->fTokens.end()) {
2605 break;
2606 }
Cary Clark8032b982017-07-28 11:04:54 -04002607 switch (testIter->fType) {
2608 case Definition::Type::kPunctuation:
2609 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2610 || Punctuation::kLeftBrace == testIter->fPunctuation
2611 || Punctuation::kColon == testIter->fPunctuation);
2612 end = testIter->fStart;
2613 break;
2614 case Definition::Type::kKeyWord: {
2615 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2616 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2617 continue;
2618 }
2619 } break;
2620 default:
2621 continue;
2622 }
2623 break;
Cary Clark61313f32018-10-08 14:57:48 -04002624 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002625 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002626 while (end > start && ' ' >= end[-1]) {
2627 --end;
2628 }
Cary Clark9174bda2017-09-19 17:39:32 -04002629 if (!markupDef) {
2630 auto parentIter = child->fParent->fTokens.begin();
2631 SkASSERT(child->fParentIndex > 0);
2632 std::advance(parentIter, child->fParentIndex - 1);
2633 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002634 TextParser nameParser(methodName);
2635 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002636 return true; // expect this is inline class definition outside of class
2637 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002638 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2639 fParent, '\0');
2640 Definition* globalMarkupChild = &fGlobals.back();
2641 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2642 globalMarkupChild->fName = globalUniqueName;
2643 if (!this->findComments(*child, globalMarkupChild)) {
2644 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002645 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002646 if (globalMarkupChild->fUndocumented) {
2647 child->fUndocumented = true;
2648 } else {
2649 fIFunctionMap[globalUniqueName] = globalMarkupChild;
2650 }
Cary Clarka560c472017-11-27 10:44:06 -05002651 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002652 }
Cary Clark8032b982017-07-28 11:04:54 -04002653 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002654 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002655 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002656 {
2657 auto mapIter = fIClassMap.find(markupDef->fName);
2658 SkASSERT(fIClassMap.end() != mapIter);
2659 IClassDefinition& classDef = mapIter->second;
2660 SkASSERT(classDef.fStart);
2661 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2662 markupChild->fName = uniqueName;
2663 if (!this->findComments(*child, markupChild)) {
2664 return false;
2665 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002666 if (markupChild->fUndocumented) {
2667 tokenIter->fUndocumented = true;
2668 } else {
2669 classDef.fMethods[uniqueName] = markupChild;
2670 }
Cary Clark8032b982017-07-28 11:04:54 -04002671 }
Cary Clark8032b982017-07-28 11:04:54 -04002672 return true;
2673}
2674
Cary Clark8032b982017-07-28 11:04:54 -04002675bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002676 fPriorObject = nullptr;
2677 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002678 if (!this->parseObject(child, markupDef)) {
2679 return false;
2680 }
Cary Clark0d225392018-06-07 09:59:07 -04002681 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002682 }
2683 return true;
2684}
2685
2686bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2687 // set up for error reporting
2688 fLine = fChar = child->fStart;
2689 fEnd = child->fContentEnd;
2690 // todo: put original line number in child as well
2691 switch (child->fType) {
2692 case Definition::Type::kKeyWord:
2693 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002694 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002695 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002696 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002697 }
2698 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002699 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002700 case KeyWord::kConst:
2701 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002702 if (!this->parseConst(child, markupDef)) {
2703 return child->reportError<bool>("failed to parse const or constexpr");
2704 }
2705 break;
Cary Clark8032b982017-07-28 11:04:54 -04002706 case KeyWord::kEnum:
2707 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002708 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002709 }
2710 break;
2711 case KeyWord::kStruct:
2712 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002713 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002714 }
2715 break;
2716 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002717 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002718 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002719 }
2720 break;
2721 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002722 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002723 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002724 }
2725 break;
2726 case KeyWord::kUnion:
2727 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002728 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002729 }
2730 break;
Cary Clark61313f32018-10-08 14:57:48 -04002731 case KeyWord::kUsing:
2732 if (!this->parseUsing()) {
2733 return child->reportError<bool>("failed to parse using");
2734 }
2735 break;
Cary Clark8032b982017-07-28 11:04:54 -04002736 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002737 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002738 }
2739 break;
2740 case Definition::Type::kBracket:
2741 switch (child->fBracket) {
2742 case Bracket::kParen:
Cary Clark9174bda2017-09-19 17:39:32 -04002743 {
2744 auto tokenIter = child->fParent->fTokens.begin();
2745 std::advance(tokenIter, child->fParentIndex);
2746 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002747 TextParser previousToken(&*tokenIter);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002748 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002749 break;
2750 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002751 if (Bracket::kPound == child->fParent->fBracket &&
2752 KeyWord::kIf == child->fParent->fKeyWord) {
2753 // TODO: this will skip methods named defined() -- for the
2754 // moment there aren't any
2755 if (previousToken.startsWith("defined")) {
2756 break;
2757 }
2758 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002759 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2760 break;
2761 }
Cary Clark73fa9722017-08-29 17:36:51 -04002762 }
Cary Clark0d225392018-06-07 09:59:07 -04002763 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2764 break;
2765 }
Cary Clark8032b982017-07-28 11:04:54 -04002766 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002767 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002768 }
Cary Clark73fa9722017-08-29 17:36:51 -04002769 break;
Cary Clark8032b982017-07-28 11:04:54 -04002770 case Bracket::kSlashSlash:
2771 case Bracket::kSlashStar:
2772 // comments are picked up by parsing objects first
2773 break;
2774 case Bracket::kPound:
2775 // special-case the #xxx xxx_DEFINED entries
2776 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002777 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002778 case KeyWord::kIfndef:
2779 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002780 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002781 if (!this->parseObjects(child, markupDef)) {
2782 return false;
2783 }
2784 break;
2785 }
2786 goto preproError;
2787 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002788 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002789 break;
2790 }
2791 goto preproError;
2792 case KeyWord::kEndif:
2793 if (child->boilerplateEndIf()) {
2794 break;
2795 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002796 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002797 case KeyWord::kInclude:
2798 // ignored for now
2799 break;
2800 case KeyWord::kElse:
Cary Clarkabaffd82018-11-15 08:25:12 -05002801 if (!this->parseObjects(child, markupDef)) {
2802 return false;
2803 }
2804 break;
Cary Clark8032b982017-07-28 11:04:54 -04002805 case KeyWord::kElif:
2806 // todo: handle these
2807 break;
2808 default:
2809 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002810 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002811 }
2812 break;
2813 case Bracket::kAngle:
2814 // pick up templated function pieces when method is found
2815 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002816 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002817 if (!this->parseObjects(child, markupDef)) {
2818 return false;
2819 }
Cary Clark73fa9722017-08-29 17:36:51 -04002820 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002821 case Bracket::kSquare: {
2822 // check to see if parent is operator, the only case we handle so far
2823 auto prev = child->fParent->fTokens.begin();
2824 std::advance(prev, child->fParentIndex - 1);
2825 if (KeyWord::kOperator != prev->fKeyWord) {
2826 return child->reportError<bool>("expected operator overload");
2827 }
2828 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002829 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002830 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002831 }
2832 break;
2833 case Definition::Type::kWord:
2834 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002835 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002836 }
2837 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002838 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002839 }
2840 break;
2841 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002842 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002843 break;
2844 }
2845 return true;
2846}
2847
Cary Clarkbbfda252018-03-09 15:32:01 -05002848bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2849 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002850}
2851
Cary Clark2f466242017-12-11 16:03:17 -05002852bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2853 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002854 typedefParser.skipExact("typedef");
2855 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002856 string nameStr = typedefParser.typedefName();
2857 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002858 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2859 child->fLineCount, fParent, '\0');
2860 Definition* globalMarkupChild = &fGlobals.back();
2861 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2862 globalMarkupChild->fName = globalUniqueName;
2863 if (!this->findComments(*child, globalMarkupChild)) {
2864 return false;
2865 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002866 if (globalMarkupChild->fUndocumented) {
2867 child->fUndocumented = true;
2868 } else {
2869 fITypedefMap[globalUniqueName] = globalMarkupChild;
2870 }
Cary Clark0d225392018-06-07 09:59:07 -04002871 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002872 return true;
2873 }
2874 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002875 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002876 Definition* markupChild = &markupDef->fTokens.back();
2877 markupChild->fName = nameStr;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002878 this->checkName(markupChild);
Cary Clark2f466242017-12-11 16:03:17 -05002879 markupChild->fTerminator = markupChild->fContentEnd;
2880 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2881 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002882 child->fName = markupDef->fName + "::" + nameStr;
Cary Clarkcb6bef02018-11-29 12:05:25 -05002883 this->checkName(child);
Cary Clarka90ea222018-10-16 10:30:28 -04002884 fITypedefMap[child->fName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002885 return true;
2886}
2887
2888bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04002889 // incomplete
2890 return true;
2891}
Cary Clark8032b982017-07-28 11:04:54 -04002892
Cary Clark61313f32018-10-08 14:57:48 -04002893bool IncludeParser::parseUsing() {
2894 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04002895 return true;
2896}
2897
2898bool IncludeParser::parseChar() {
2899 char test = *fChar;
2900 if ('\\' == fPrev) {
2901 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002902// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002903 fLine = fChar + 1;
2904 }
2905 goto done;
2906 }
2907 switch (test) {
2908 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002909// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002910 fLine = fChar + 1;
2911 if (fInChar) {
2912 return reportError<bool>("malformed char");
2913 }
2914 if (fInString) {
2915 return reportError<bool>("malformed string");
2916 }
2917 if (!this->checkForWord()) {
2918 return false;
2919 }
2920 if (Bracket::kPound == this->topBracket()) {
2921 KeyWord keyWord = fParent->fKeyWord;
2922 if (KeyWord::kNone == keyWord) {
2923 return this->reportError<bool>("unhandled preprocessor directive");
2924 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002925 if (fInDefine) {
2926 SkASSERT(KeyWord::kDefine == keyWord);
2927 fInDefine = false;
2928 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002929 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002930 this->popBracket();
2931 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002932 if (fInBrace) {
2933 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2934 fInBrace = nullptr;
2935 }
Cary Clark8032b982017-07-28 11:04:54 -04002936 } else if (Bracket::kSlashSlash == this->topBracket()) {
2937 this->popBracket();
2938 }
2939 break;
2940 case '*':
2941 if (!fInCharCommentString && '/' == fPrev) {
2942 this->pushBracket(Bracket::kSlashStar);
2943 }
2944 if (!this->checkForWord()) {
2945 return false;
2946 }
2947 if (!fInCharCommentString) {
2948 this->addPunctuation(Punctuation::kAsterisk);
2949 }
2950 break;
2951 case '/':
2952 if ('*' == fPrev) {
2953 if (!fInCharCommentString) {
2954 return reportError<bool>("malformed closing comment");
2955 }
2956 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002957 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002958 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002959 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002960 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002961 }
2962 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002963 }
Cary Clark8032b982017-07-28 11:04:54 -04002964 if (!fInCharCommentString && '/' == fPrev) {
2965 this->pushBracket(Bracket::kSlashSlash);
2966 break;
2967 }
2968 if (!this->checkForWord()) {
2969 return false;
2970 }
2971 break;
2972 case '\'':
2973 if (Bracket::kChar == this->topBracket()) {
2974 this->popBracket();
2975 } else if (!fInComment && !fInString) {
2976 if (fIncludeWord) {
2977 return this->reportError<bool>("word then single-quote");
2978 }
2979 this->pushBracket(Bracket::kChar);
2980 }
2981 break;
2982 case '\"':
2983 if (Bracket::kString == this->topBracket()) {
2984 this->popBracket();
2985 } else if (!fInComment && !fInChar) {
2986 if (fIncludeWord) {
2987 return this->reportError<bool>("word then double-quote");
2988 }
2989 this->pushBracket(Bracket::kString);
2990 }
2991 break;
Cary Clark8032b982017-07-28 11:04:54 -04002992 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002993 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002994 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2995 this->pushBracket(Bracket::kDebugCode);
2996 break;
2997 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002998 case ':':
2999 case '[':
3000 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04003001 if (fInCharCommentString) {
3002 break;
3003 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003004 if (fInDefine && fInBrace) {
3005 break;
3006 }
Cary Clark8032b982017-07-28 11:04:54 -04003007 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
3008 break;
3009 }
Cary Clark0d225392018-06-07 09:59:07 -04003010 if (fConstExpr) {
3011 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3012 fConstExpr = nullptr;
3013 }
Cary Clark8032b982017-07-28 11:04:54 -04003014 if (!fInBrace) {
3015 if (!this->checkForWord()) {
3016 return false;
3017 }
3018 if (':' == test && !fInFunction) {
3019 break;
3020 }
3021 if ('{' == test) {
3022 this->addPunctuation(Punctuation::kLeftBrace);
3023 } else if (':' == test) {
3024 this->addPunctuation(Punctuation::kColon);
3025 }
3026 }
3027 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
3028 && Bracket::kColon == fInBrace->fBracket) {
3029 Definition* braceParent = fParent->fParent;
3030 braceParent->fChildren.pop_back();
3031 braceParent->fTokens.pop_back();
3032 fParent = braceParent;
3033 fInBrace = nullptr;
3034 }
3035 this->pushBracket(
3036 '(' == test ? Bracket::kParen :
3037 '[' == test ? Bracket::kSquare :
3038 '{' == test ? Bracket::kBrace :
3039 Bracket::kColon);
3040 if (!fInBrace
3041 && ('{' == test || (':' == test && ' ' >= fChar[1]))
3042 && fInFunction) {
3043 fInBrace = fParent;
3044 }
3045 } break;
3046 case '<':
3047 if (fInCharCommentString || fInBrace) {
3048 break;
3049 }
3050 if (!this->checkForWord()) {
3051 return false;
3052 }
3053 if (fInEnum) {
3054 break;
3055 }
3056 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04003057 // this angle bracket may be an operator or may be a bracket
3058 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04003059 break;
3060 case ')':
3061 case ']':
3062 case '}': {
3063 if (fInCharCommentString) {
3064 break;
3065 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003066 if (fInDefine && fInBrace) {
3067 break;
3068 }
Cary Clark8032b982017-07-28 11:04:54 -04003069 if (!fInBrace) {
3070 if (!this->checkForWord()) {
3071 return false;
3072 }
3073 }
3074 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04003075 Bracket match = ')' == test ? Bracket::kParen :
3076 ']' == test ? Bracket::kSquare : Bracket::kBrace;
3077 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04003078 this->popBracket();
3079 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003080 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04003081 } else {
3082 fInFunction = '}' != test;
3083 }
Cary Clark73fa9722017-08-29 17:36:51 -04003084 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
3085 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04003086 } else if (Bracket::kAngle == this->topBracket()
3087 && match == this->grandParentBracket()) {
3088 this->popBracket();
3089 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04003090 } else {
3091 return reportError<bool>("malformed close bracket");
3092 }
3093 if (popBraceParent) {
3094 Definition* braceParent = fInBrace->fParent;
3095 braceParent->fChildren.pop_back();
3096 braceParent->fTokens.pop_back();
3097 fInBrace = nullptr;
3098 }
3099 } break;
3100 case '>':
3101 if (fInCharCommentString || fInBrace) {
3102 break;
3103 }
3104 if (!this->checkForWord()) {
3105 return false;
3106 }
3107 if (fInEnum) {
3108 break;
3109 }
Cary Clarka560c472017-11-27 10:44:06 -05003110 if (Bracket::kPound == this->topBracket()) {
3111 break;
3112 }
Cary Clark8032b982017-07-28 11:04:54 -04003113 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04003114 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04003115 this->popBracket();
3116 } else {
3117 return reportError<bool>("malformed close angle bracket");
3118 }
3119 break;
3120 case '#': {
3121 if (fInCharCommentString || fInBrace) {
3122 break;
3123 }
3124 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3125 this->pushBracket(Bracket::kPound);
3126 break;
3127 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003128 case ' ':
3129 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3130 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3131 fInBrace = fParent;
3132 // delimiting brackets are space ... unescaped-linefeed
3133 }
Cary Clark8032b982017-07-28 11:04:54 -04003134 case '&':
3135 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003136 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003137 case '-':
3138 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003139 if (fInCharCommentString || fInBrace) {
3140 break;
3141 }
3142 if (!this->checkForWord()) {
3143 return false;
3144 }
3145 break;
Cary Clark0d225392018-06-07 09:59:07 -04003146 case '=':
3147 if (fInCharCommentString || fInBrace) {
3148 break;
3149 }
3150 if (!this->checkForWord()) {
3151 return false;
3152 }
Cary Clarkd7895502018-07-18 15:10:08 -04003153 if (!fParent->fTokens.size()) {
3154 break;
3155 }
Cary Clark0d225392018-06-07 09:59:07 -04003156 {
3157 const Definition& lastToken = fParent->fTokens.back();
3158 if (lastToken.fType != Definition::Type::kWord) {
3159 break;
3160 }
3161 string name(lastToken.fContentStart, lastToken.length());
3162 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3163 break;
3164 }
3165 // find token on start of line
3166 auto lineIter = fParent->fTokens.end();
3167 do {
Cary Clark80247e52018-07-11 16:18:41 -04003168 if (fParent->fTokens.begin() == lineIter) {
3169 break;
3170 }
Cary Clark0d225392018-06-07 09:59:07 -04003171 --lineIter;
3172 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003173 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003174 ++lineIter;
3175 }
3176 Definition* lineStart = &*lineIter;
3177 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3178 bool sawConst = false;
3179 bool sawStatic = false;
3180 bool sawTemplate = false;
3181 bool sawType = false;
3182 while (&lastToken != &*lineIter) {
3183 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3184 if (sawConst || sawStatic || sawTemplate) {
3185 sawConst = false;
3186 break;
3187 }
3188 if (&lastToken == &*++lineIter) {
3189 break;
3190 }
3191 if (KeyWord::kTypename != lineIter->fKeyWord) {
3192 break;
3193 }
3194 if (&lastToken == &*++lineIter) {
3195 break;
3196 }
3197 if (Definition::Type::kWord != lineIter->fType) {
3198 break;
3199 }
3200 sawTemplate = true;
3201 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3202 if (sawConst || sawStatic) {
3203 sawConst = false;
3204 break;
3205 }
3206 sawStatic = true;
3207 } else if (KeyWord::kConst == lineIter->fKeyWord
3208 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3209 if (sawConst) {
3210 sawConst = false;
3211 break;
3212 }
3213 sawConst = true;
3214 } else {
3215 if (sawType) {
3216 sawType = false;
3217 break;
3218 }
3219 if (Definition::Type::kKeyWord == lineIter->fType
3220 && KeyProperty::kNumber
3221 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3222 sawType = true;
3223 } else if (Definition::Type::kWord == lineIter->fType) {
3224 string typeName(lineIter->fContentStart, lineIter->length());
3225 if ("Sk" != name.substr(0, 2)) {
3226 sawType = true;
3227 }
3228 }
3229 }
3230 ++lineIter;
3231 }
3232 if (sawType && sawConst) {
3233 // if found, name first
3234 lineStart->fName = name;
3235 lineStart->fMarkType = MarkType::kConst;
Cary Clarkcb6bef02018-11-29 12:05:25 -05003236 this->checkName(lineStart);
Cary Clark0d225392018-06-07 09:59:07 -04003237 fParent->fChildren.emplace_back(lineStart);
3238 fConstExpr = lineStart;
3239 }
3240 }
3241 break;
Cary Clark8032b982017-07-28 11:04:54 -04003242 case ';':
3243 if (fInCharCommentString || fInBrace) {
3244 break;
3245 }
3246 if (!this->checkForWord()) {
3247 return false;
3248 }
Cary Clark0d225392018-06-07 09:59:07 -04003249 if (fConstExpr) {
3250 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3251 fConstExpr = nullptr;
3252 }
Cary Clark8032b982017-07-28 11:04:54 -04003253 if (Definition::Type::kKeyWord == fParent->fType
3254 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003255 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3256 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003257 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3258 this->popObject();
3259 }
Cary Clark8032b982017-07-28 11:04:54 -04003260 if (KeyWord::kEnum == fParent->fKeyWord) {
3261 fInEnum = false;
3262 }
Cary Clark61313f32018-10-08 14:57:48 -04003263 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003264 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003265 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3266 this->popObject();
3267 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003268 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003269 } else if (Definition::Type::kBracket == fParent->fType
3270 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3271 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3272 list<Definition>::iterator baseIter = fParent->fTokens.end();
3273 list<Definition>::iterator namedIter = fParent->fTokens.end();
3274 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003275 fParent->fTokens.begin() != tokenIter; ) {
3276 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003277 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003278 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003279 if (namedIter != fParent->fTokens.end()) {
3280 return reportError<bool>("found two named member tokens");
3281 }
3282 namedIter = tokenIter;
3283 }
3284 baseIter = tokenIter;
3285 } else {
3286 break;
3287 }
3288 }
3289 // FIXME: if a member definition spans multiple lines, this won't work
3290 if (namedIter != fParent->fTokens.end()) {
3291 if (baseIter == namedIter) {
3292 return this->reportError<bool>("expected type before named token");
3293 }
3294 Definition* member = &*namedIter;
3295 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003296 if (!member->fTerminator) {
3297 member->fTerminator = member->fContentEnd;
3298 }
Cary Clark8032b982017-07-28 11:04:54 -04003299 fParent->fChildren.push_back(member);
3300 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3301 member->fChildren.push_back(&*nameType);
3302 }
Cary Clark8032b982017-07-28 11:04:54 -04003303 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003304 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003305 } else if (fParent->fChildren.size() > 0) {
3306 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003307 Definition* priorEnum = fPriorEnum;
3308 fPriorEnum = nullptr;
3309 if (!priorEnum) {
3310 while (fParent->fChildren.begin() != lastIter) {
3311 std::advance(lastIter, -1);
3312 priorEnum = *lastIter;
3313 if (Definition::Type::kBracket != priorEnum->fType ||
3314 (Bracket::kSlashSlash != priorEnum->fBracket
3315 && Bracket::kSlashStar != priorEnum->fBracket)) {
3316 break;
3317 }
Cary Clark8032b982017-07-28 11:04:54 -04003318 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003319 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003320 }
3321 if (Definition::Type::kKeyWord == priorEnum->fType
3322 && KeyWord::kEnum == priorEnum->fKeyWord) {
3323 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003324 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003325 while (tokenWalker != fParent->fTokens.end()) {
3326 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003327 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003328 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3329 break;
3330 }
3331 }
3332 while (tokenWalker != fParent->fTokens.end()) {
3333 std::advance(tokenWalker, 1);
3334 const Definition* test = &*tokenWalker;
3335 if (Definition::Type::kBracket != test->fType ||
3336 (Bracket::kSlashSlash != test->fBracket
3337 && Bracket::kSlashStar != test->fBracket)) {
3338 break;
3339 }
3340 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003341 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003342 Definition* start = &*tokenWalker;
3343 bool foundExpected = true;
3344 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3345 const Definition* test = &*tokenWalker;
3346 if (expected != test->fKeyWord) {
3347 foundExpected = false;
3348 break;
3349 }
3350 if (tokenWalker == fParent->fTokens.end()) {
3351 break;
3352 }
3353 std::advance(tokenWalker, 1);
3354 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003355 if (!foundExpected) {
3356 foundExpected = true;
3357 tokenWalker = saveTokenWalker;
3358 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3359 const Definition* test = &*tokenWalker;
3360 if (expected != test->fKeyWord) {
3361 foundExpected = false;
3362 break;
3363 }
3364 if (tokenWalker == fParent->fTokens.end()) {
3365 break;
3366 }
3367 if (KeyWord::kNone != expected) {
3368 std::advance(tokenWalker, 1);
3369 }
3370 }
3371 if (foundExpected) {
3372 auto nameToken = priorEnum->fTokens.begin();
3373 string enumName = string(nameToken->fContentStart,
3374 nameToken->fContentEnd - nameToken->fContentStart);
3375 const Definition* test = &*tokenWalker;
3376 string constType = string(test->fContentStart,
3377 test->fContentEnd - test->fContentStart);
3378 if (enumName != constType) {
3379 foundExpected = false;
3380 } else {
3381 std::advance(tokenWalker, 1);
3382 }
3383 }
3384 }
Cary Clark8032b982017-07-28 11:04:54 -04003385 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3386 const char* nameStart = tokenWalker->fStart;
3387 std::advance(tokenWalker, 1);
3388 if (tokenWalker != fParent->fTokens.end()) {
3389 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003390 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003391 start->fName = string(nameStart, tp.fChar - nameStart);
Cary Clarkcb6bef02018-11-29 12:05:25 -05003392 this->checkName(start);
Cary Clark8032b982017-07-28 11:04:54 -04003393 start->fContentEnd = fChar;
3394 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003395 fPriorEnum = priorEnum;
3396 }
Cary Clark8032b982017-07-28 11:04:54 -04003397 }
3398 }
3399 }
3400 this->addPunctuation(Punctuation::kSemicolon);
3401 fInFunction = false;
3402 break;
3403 case '~':
3404 if (fInEnum) {
3405 break;
3406 }
3407 case '0': case '1': case '2': case '3': case '4':
3408 case '5': case '6': case '7': case '8': case '9':
3409 // TODO: don't want to parse numbers, but do need to track for enum defs
3410 // break;
3411 case 'A': case 'B': case 'C': case 'D': case 'E':
3412 case 'F': case 'G': case 'H': case 'I': case 'J':
3413 case 'K': case 'L': case 'M': case 'N': case 'O':
3414 case 'P': case 'Q': case 'R': case 'S': case 'T':
3415 case 'U': case 'V': case 'W': case 'X': case 'Y':
3416 case 'Z': case '_':
3417 case 'a': case 'b': case 'c': case 'd': case 'e':
3418 case 'f': case 'g': case 'h': case 'i': case 'j':
3419 case 'k': case 'l': case 'm': case 'n': case 'o':
3420 case 'p': case 'q': case 'r': case 's': case 't':
3421 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003422 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003423 if (fInCharCommentString || fInBrace) {
3424 break;
3425 }
3426 if (!fIncludeWord) {
3427 fIncludeWord = fChar;
3428 }
3429 break;
3430 }
3431done:
3432 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003433 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003434 return true;
3435}
3436
3437void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003438 IncludeParser::ValidateKeyWords();
3439}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003440
Cary Clark186d08f2018-04-03 08:43:27 -04003441bool IncludeParser::references(const SkString& file) const {
3442 // if includes weren't passed one at a time, assume all references are valid
3443 if (fIncludeMap.empty()) {
3444 return true;
3445 }
3446 SkASSERT(file.endsWith(".bmh") );
3447 string root(file.c_str(), file.size() - 4);
3448 string kReference("_Reference");
3449 if (string::npos != root.find(kReference)) {
3450 root = root.substr(0, root.length() - kReference.length());
3451 }
3452 if (fIClassMap.end() != fIClassMap.find(root)) {
3453 return true;
3454 }
3455 if (fIStructMap.end() != fIStructMap.find(root)) {
3456 return true;
3457 }
Cary Clark224c7002018-06-27 11:00:21 -04003458 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3459 return true;
3460 }
Cary Clarka90ea222018-10-16 10:30:28 -04003461 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3462 return true;
3463 }
Cary Clark224c7002018-06-27 11:00:21 -04003464 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3465 return true;
3466 }
Cary Clark186d08f2018-04-03 08:43:27 -04003467 return false;
3468}
3469
Cary Clark2dc84ad2018-01-26 12:56:22 -05003470void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3471 if (!sk_isdir(includes)) {
3472 IncludeParser::RemoveOneFile(docs, includes);
3473 } else {
3474 SkOSFile::Iter it(includes, ".h");
3475 for (SkString file; it.next(&file); ) {
3476 SkString p = SkOSPath::Join(includes, file.c_str());
3477 const char* hunk = p.c_str();
3478 if (!SkStrEndsWith(hunk, ".h")) {
3479 continue;
3480 }
3481 IncludeParser::RemoveOneFile(docs, hunk);
3482 }
3483 }
3484}
3485
3486void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3487 const char* lastForward = strrchr(includesFile, '/');
3488 const char* lastBackward = strrchr(includesFile, '\\');
3489 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3490 if (!last) {
3491 last = includesFile;
3492 } else {
3493 last += 1;
3494 }
3495 SkString baseName(last);
3496 SkASSERT(baseName.endsWith(".h"));
3497 baseName.remove(baseName.size() - 2, 2);
3498 baseName.append("_Reference.bmh");
Cary Clarkcb6bef02018-11-29 12:05:25 -05003499 SkString fullName = docs ? SkOSPath::Join(docs, baseName.c_str()) : baseName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05003500 remove(fullName.c_str());
3501}
Cary Clark224c7002018-06-27 11:00:21 -04003502
Cary Clarkfd32e722018-11-16 14:36:02 -05003503static const char kMethodMissingStr[] =
3504 "If the method requires documentation, add to "
3505 "%s at minimum:\n" // path to bmh file
3506 "\n"
3507 "#Method %s\n" // method declaration less implementation details
3508 "#In SomeSubtopicName\n"
3509 "#Line # add a one line description here ##\n"
3510 "#Populate\n"
3511 "#NoExample\n"
3512 "// or better yet, use #Example and put C++ code here\n"
3513 "##\n"
3514 "#SeeAlso optional related symbols\n"
3515 "#Method ##\n"
3516 "\n"
3517 "Add to %s, at minimum:\n" // path to include
3518 "\n"
3519 "/** (description) Starts with present tense action verb\n"
3520 " and end with a period.\n"
3521 "%s" // @param, @return if needed go here
3522 "*/\n"
3523 "%s ...\n" // method declaration
3524 "\n"
3525 "If the method does not require documentation,\n"
3526 "add \"private\" or \"experimental\", as in:\n"
3527 "\n"
3528 "/** Experimental, do not use. And so on...\n"
3529 "*/\n"
3530 "%s ...\n" // method declaration
3531 "\n"
3532 ;
3533
3534// bDef does not have #Populate
3535static const char kMethodDiffersNoPopStr[] =
3536 "In %s:\n" // path to bmh file
3537 "#Method %s\n" // method declaration less implementation details
3538 "does not match doxygen comment of:\n"
3539 "%s.\n" // method declaration
3540 "\n"
3541 ;
3542
3543static const char kMethodDiffersStr[] =
3544 "In %s:\n" // path to include
3545 "%s\n" // method declaration
3546 "does not match doxygen comment.\n"
3547 "\n"
3548 ;
3549
3550void IncludeParser::suggestFix(Suggest suggest, const Definition& iDef,
3551 const RootDefinition* root, const Definition* bDef) {
3552 string methodNameStr(iDef.fContentStart, iDef.length());
3553 const char* methodName = methodNameStr.c_str();
3554 TextParser lessImplParser(&iDef);
3555 if (lessImplParser.skipExact("static")) {
3556 lessImplParser.skipWhiteSpace();
3557 }
3558 // TODO : handle debug wrapper
3559 /* bool inDebugWrapper = */ Definition::SkipImplementationWords(lessImplParser);
3560 string lessImplStr(lessImplParser.fChar, lessImplParser.fEnd - lessImplParser.fChar);
3561 const char* methodNameLessImpl = lessImplStr.c_str();
3562 // return result, if any is substr from 0 to location of iDef.fName
3563 size_t namePos = methodNameStr.find(iDef.fName);
3564 SkASSERT(string::npos != namePos);
3565 size_t funcEnd = namePos;
3566 while (funcEnd > 0 && ' ' >= methodNameStr[funcEnd - 1]) {
3567 funcEnd -= 1;
3568 }
3569 string funcRet = methodNameStr.substr(0, funcEnd);
3570// parameters, if any, are delimited by () and separate by ,
3571 TextParser parser(&iDef);
3572 parser.fChar += namePos + iDef.fName.length();
3573 const char* start = parser.fChar;
3574 vector<string> paramStrs;
3575 if ('(' == start[0]) {
3576 parser.skipToBalancedEndBracket('(', ')');
3577 TextParser params(&iDef);
3578 params.fChar = start + 1;
3579 params.fEnd = parser.fChar;
3580 while (!params.eof()) {
3581 const char* paramEnd = params.anyOf("=,)");
3582 const char* paramStart = paramEnd;
3583 while (paramStart > params.fChar && ' ' >= paramStart[-1]) {
3584 paramStart -= 1;
3585 }
3586 while (paramStart > params.fChar && (isalnum(paramStart[-1])
3587 || '_' == paramStart[-1])) {
3588 paramStart -= 1;
3589 }
3590 string param(paramStart, paramEnd - paramStart);
3591 paramStrs.push_back(param);
3592 params.fChar = params.anyOf(",)") + 1;
3593 }
3594 }
3595 string bmhFile = root ? root->fFileName : bDef ? bDef->fFileName : "a *.bmh file";
3596 bool hasFuncReturn = "" != funcRet && "void" != funcRet;
3597 switch(suggest) {
3598 case Suggest::kMethodMissing: {
3599 // if include @param, @return are missing, request them as well
3600 string paramDox;
3601 bool firstParam = true;
3602 for (auto paramStr : paramStrs) {
3603 if (firstParam) {
3604 paramDox += "\n";
3605 firstParam = false;
3606 }
3607 paramDox += " @param " + paramStr + " descriptive phrase\n";
3608 }
3609 if (hasFuncReturn) {
3610 paramDox += "\n";
3611 paramDox += " @return descriptive phrase\n";
3612 }
3613 SkDebugf(kMethodMissingStr, bmhFile.c_str(), methodNameLessImpl, iDef.fFileName.c_str(),
3614 paramDox.c_str(), methodName, methodName);
3615 } break;
3616 case Suggest::kMethodDiffers: {
3617 bool hasPop = std::any_of(bDef->fChildren.begin(), bDef->fChildren.end(),
3618 [](Definition* def) { return MarkType::kPopulate == def->fMarkType; });
3619 if (!hasPop) {
3620 SkDebugf(kMethodDiffersNoPopStr, bmhFile.c_str(), methodNameLessImpl, methodName);
3621 }
3622 SkDebugf(kMethodDiffersStr, iDef.fFileName.c_str(), methodName);
3623 } break;
3624 default:
3625 SkASSERT(0);
3626 }
3627}
3628
Cary Clark224c7002018-06-27 11:00:21 -04003629Bracket IncludeParser::topBracket() const {
3630 Definition* parent = this->parentBracket(fParent);
3631 return parent ? parent->fBracket : Bracket::kNone;
3632}