add subtopics to all methods

add self-check looking for #In markup on every method, pointing
to an existing #Subtopic to reference the method.

Docs-Preview: https://skia.org/?cl=104325
Bug: skia:6898
Change-Id: I749a25b9a43033ae68d193249b2c0b810dcf8fc8
Reviewed-on: https://skia-review.googlesource.com/104325
Commit-Queue: Cary Clark <caryclark@skia.org>
Reviewed-by: Cary Clark <caryclark@skia.org>
diff --git a/tools/bookmaker/selfCheck.cpp b/tools/bookmaker/selfCheck.cpp
index 3392945..5f1eb38 100644
--- a/tools/bookmaker/selfCheck.cpp
+++ b/tools/bookmaker/selfCheck.cpp
@@ -11,6 +11,27 @@
 #include <windows.h>
 #endif
 
+
+ /* SkDebugf works in both visual studio and git shell, but
+ in git shell output is not piped to grep.
+ printf does not generate output in visual studio, but
+ does in git shell and can be piped.
+ */
+#ifdef SK_BUILD_FOR_WIN
+#define PRINTF(...)                 \
+do {                                \
+    if (IsDebuggerPresent()) {      \
+        SkDebugf(__VA_ARGS__);      \
+    } else {                        \
+        printf(__VA_ARGS__);        \
+    }                               \
+} while (false)
+#else
+#define PRINTF(...)                 \
+        printf(__VA_ARGS__)
+#endif
+
+
 // Check that mutiple like-named methods are under one Subtopic
 
 // Check that SeeAlso reference each other
@@ -47,52 +68,62 @@
 
 protected:
 
-	bool checkRelatedFunctions() {
-		const Definition* cs = this->classOrStruct();
-		vector<string> methodNames;
-		if (cs) {
-			string prefix = cs->fName + "::";
-			for (auto& csChild : cs->fChildren) {
-				if (MarkType::kMethod != csChild->fMarkType) {
-					// only check methods for now
-					continue;
-				}
-				if (Definition::MethodType::kConstructor == csChild->fMethodType) {
-					continue;
-				}
-				if (Definition::MethodType::kDestructor == csChild->fMethodType) {
-					continue;
-				}
-				if (Definition::MethodType::kOperator == csChild->fMethodType) {
-					continue;
-				}
-				if (csChild->fClone) {
-					// FIXME: check to see if all cloned methods are in table
-					// since format of clones is in flux, defer this check for now
-					continue;
-				}
-                bool containsMarkTypeIn = csChild->fDeprecated;  // no markup for deprecated
-                for (auto child : csChild->fChildren) {
-                    if (MarkType::kIn == child->fMarkType) {
-                        containsMarkTypeIn = true;
-                        break;
+    void checkMethod(string topic, const Definition* csChild, vector<string>* reported) {
+        if (MarkType::kSubtopic == csChild->fMarkType) {
+            for (auto child : csChild->fChildren) {
+                checkMethod(topic, child, reported);
+            }
+            return;
+        } else if (MarkType::kMethod != csChild->fMarkType) {
+            // only check methods for now
+            return;
+        }
+        bool containsMarkTypeIn = csChild->fDeprecated  // no markup for deprecated
+                || Definition::MethodType::kConstructor == csChild->fMethodType
+                || Definition::MethodType::kDestructor == csChild->fMethodType
+                || Definition::MethodType::kOperator == csChild->fMethodType
+                || csChild->fClone;
+        for (auto child : csChild->fChildren) {
+            if (MarkType::kIn == child->fMarkType) {
+                containsMarkTypeIn = true;
+                string subtopic(child->fContentStart,
+                    child->fContentEnd - child->fContentStart);
+                string fullname = topic + '_' + subtopic;
+                auto topEnd = fBmhParser.fTopicMap.end();
+                auto topFind = fBmhParser.fTopicMap.find(fullname);
+                auto reportEnd = reported->end();
+                auto reportFind = std::find(reported->begin(), reported->end(), subtopic);
+                if (topEnd == topFind) {
+                    if (reportEnd == reportFind) {
+                        reported->push_back(subtopic);
                     }
                 }
-                if (!containsMarkTypeIn) {
-#ifdef SK_BUILD_FOR_WIN
-                    /* SkDebugf works in both visual studio and git shell, but
-                       in git shell output is not piped to grep.
-                       printf does not generate output in visual studio, but
-                       does in git shell and can be piped.
-                     */
-                    if (IsDebuggerPresent()) {
-                        SkDebugf("No #In: %s\n", csChild->fName.c_str());
-                    } else
-#endif
-                    printf("No #In: %s\n", csChild->fName.c_str());
-                }
-			}
+            }
+        }
+        if (!containsMarkTypeIn) {
+            PRINTF("No #In: %s\n", csChild->fName.c_str());
+        }
+    }
+
+	bool checkRelatedFunctions() {
+		const Definition* cs = this->classOrStruct();
+        if (!cs) {
+            return true;
+        }
+        const Definition* topic = cs->fParent;
+        SkASSERT(topic);
+        SkASSERT(MarkType::kTopic == topic->fMarkType);
+        string topicName = topic->fName;
+        vector<string> methodNames;
+        vector<string> reported;
+		string prefix = cs->fName + "::";
+		for (auto& csChild : cs->fChildren) {
+            checkMethod(topicName, csChild, &reported);
 		}
+        for (auto missing : reported) {
+            string fullname = topicName + '_' + missing;
+            PRINTF("No #Subtopic: %s\n", fullname.c_str());
+        }
 		return true;
 	}