diff options
author | Cary Clark <caryclark@skia.org> | 2018-01-22 07:55:48 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-01-22 14:04:18 +0000 |
commit | 5081eede67601e5c5c0fc343b787490603e058cc (patch) | |
tree | 607f095f636eca498e62e14e3c6d760f477052d4 /tools/bookmaker/selfCheck.cpp | |
parent | 8a67c4c2aa4debca84a68fbc25f048ce55118916 (diff) |
self check and corrections
Add self-checking code that looks to see that overview is populated
and alphabetized. Eventually, this will self-check to see if
methods are collected into subtopics and have reciprocal 'see also'
data.
Standardize phrases so that they don't start with a capital or end
with a period.
Self-check is a work in progress, so it is not yet run by the
bookmaker bots. The self-check should run cleanly, however. To run
it:
./out/skia/bookmaker -b docs -k
The expected output is doc stats. Self-check errors such as missing
methods in the overview would be reported here if there are any.
TBR=caryclark@google.com
Docs-Preview: https://skia.org/?cl=93621
Bug: skia:6898
Change-Id: I8f1f817a7b083b13138ee33d1aa090445e9304c6
Reviewed-on: https://skia-review.googlesource.com/93621
Reviewed-by: Cary Clark <caryclark@skia.org>
Commit-Queue: Cary Clark <caryclark@skia.org>
Diffstat (limited to 'tools/bookmaker/selfCheck.cpp')
-rw-r--r-- | tools/bookmaker/selfCheck.cpp | 400 |
1 files changed, 317 insertions, 83 deletions
diff --git a/tools/bookmaker/selfCheck.cpp b/tools/bookmaker/selfCheck.cpp index 0b28d16c70..7f112697c0 100644 --- a/tools/bookmaker/selfCheck.cpp +++ b/tools/bookmaker/selfCheck.cpp @@ -7,15 +7,11 @@ #include "bookmaker.h" -// Check that summary contains all methods // Check that mutiple like-named methods are under one Subtopic // Check that all subtopics are in table of contents -// Check that all constructors are in a table of contents -// should be 'creators' instead of constructors? - // Check that SeeAlso reference each other // Would be nice to check if other classes have 'create' methods that are included @@ -43,10 +39,13 @@ public: if (!this->checkMethodSubtopic()) { return false; } - if (!this->checkSubtopicContents()) { + if (!this->checkSubtopicSummary()) { + return false; + } + if (!this->checkConstructorsSummary()) { return false; } - if (!this->checkConstructors()) { + if (!this->checkOperatorsSummary()) { return false; } if (!this->checkSeeAlso()) { @@ -60,7 +59,74 @@ public: } protected: - bool checkConstructors() { + // Check that all constructors are in a table of contents + // should be 'creators' instead of constructors? + bool checkConstructorsSummary() { + for (auto& rootChild : fRoot->fChildren) { + if (!this->isStructOrClass(rootChild)) { + continue; + } + auto& cs = rootChild; + auto overview = this->findOverview(cs); + if (!overview) { + return false; + } + Definition* constructors = nullptr; + for (auto& overChild : overview->fChildren) { + if ("Constructors" == overChild->fName) { + constructors = overChild; + break; + } + } + if (constructors && MarkType::kSubtopic != constructors->fMarkType) { + return constructors->reportError<bool>("expected #Subtopic Constructors"); + } + vector<string> constructorEntries; + if (constructors) { + if (!this->collectEntries(constructors, &constructorEntries)) { + return false; + } + } + // mark corresponding methods as visited (may be more than one per entry) + for (auto& csChild : cs->fChildren) { + if (MarkType::kMethod != csChild->fMarkType) { + // only check methods for now + continue; + } + string name; + if (!this->childName(csChild, &name)) { + return false; + } + string returnType; + if (Definition::MethodType::kConstructor != csChild->fMethodType && + Definition::MethodType::kDestructor != csChild->fMethodType) { + string makeCheck = name.substr(0, 4); + if ("Make" != makeCheck && "make" != makeCheck) { + continue; + } + // for now, assume return type of interest is first word to start Sk + string search(csChild->fStart, csChild->fContentStart - csChild->fStart); + auto end = search.find(makeCheck); + if (string::npos == end) { + return csChild->reportError<bool>("expected Make in content"); + } + search = search.substr(0, end); + if (string::npos == search.find(cs->fName)) { + // if return value doesn't match current struct or class, look in + // returned struct / class instead + auto sk = search.find("Sk"); + if (string::npos != sk) { + // todo: build class name, find it, search for match in its overview + continue; + } + } + } + if (constructorEntries.end() == + std::find(constructorEntries.begin(), constructorEntries.end(), name)) { + return csChild->reportError<bool>("missing constructor in Constructors"); + } + } + } return true; } @@ -72,105 +138,273 @@ protected: return true; } + // Check that summary contains all methods bool checkMethodSummary() { - SkDebugf(""); // look for struct or class in fChildren for (auto& rootChild : fRoot->fChildren) { - if (MarkType::kStruct == rootChild->fMarkType || - MarkType::kClass == rootChild->fMarkType) { - auto& cs = rootChild; - // expect Overview as Topic in every main class or struct - Definition* overview = nullptr; - for (auto& csChild : cs->fChildren) { - if ("Overview" == csChild->fName) { - if (!overview) { - return cs->reportError<bool>("expected only one Overview"); - } - overview = csChild; - } + if (!this->isStructOrClass(rootChild)) { + continue; + } + auto& cs = rootChild; + // expect Overview as Topic in every main class or struct + auto overview = this->findOverview(cs); + if (!overview) { + return false; + } + Definition* memberFunctions = nullptr; + for (auto& overChild : overview->fChildren) { + if ("Member_Functions" == overChild->fName) { + memberFunctions = overChild; + break; + } + } + if (!memberFunctions) { + return overview->reportError<bool>("missing #Subtopic Member_Functions"); + } + if (MarkType::kSubtopic != memberFunctions->fMarkType) { + return memberFunctions->reportError<bool>("expected #Subtopic Member_Functions"); + } + vector<string> overviewEntries; // build map of overview entries + if (!this->collectEntries(memberFunctions, &overviewEntries)) { + return false; + } + // mark corresponding methods as visited (may be more than one per entry) + for (auto& csChild : cs->fChildren) { + if (MarkType::kMethod != csChild->fMarkType) { + // only check methods for now + continue; + } + if (Definition::MethodType::kConstructor == csChild->fMethodType) { + continue; } - if (!overview) { - return cs->reportError<bool>("missing #Topic Overview"); + if (Definition::MethodType::kDestructor == csChild->fMethodType) { + continue; } - Definition* memberFunctions = nullptr; - for (auto& overChild : overview->fChildren) { - if ("Member_Functions" == overChild->fName) { - memberFunctions = overChild; + if (Definition::MethodType::kOperator == csChild->fMethodType) { + continue; + } + string name; + if (!this->childName(csChild, &name)) { + return false; + } + if (overviewEntries.end() == + std::find(overviewEntries.begin(), overviewEntries.end(), name)) { + return csChild->reportError<bool>("missing method in Member_Functions"); + } + } + } + return true; + } + + // Check that all operators are in a table of contents + bool checkOperatorsSummary() { + for (auto& rootChild : fRoot->fChildren) { + if (!this->isStructOrClass(rootChild)) { + continue; + } + auto& cs = rootChild; + auto overview = this->findOverview(cs); + if (!overview) { + return false; + } + Definition* operators = nullptr; + for (auto& overChild : overview->fChildren) { + if ("Operators" == overChild->fName) { + operators = overChild; + break; + } + } + if (operators && MarkType::kSubtopic != operators->fMarkType) { + return operators->reportError<bool>("expected #Subtopic Operators"); + } + vector<string> operatorEntries; + if (operators) { + if (!this->collectEntries(operators, &operatorEntries)) { + return false; + } + } + for (auto& csChild : cs->fChildren) { + if (Definition::MethodType::kOperator != csChild->fMethodType) { + continue; + } + string name; + if (!this->childName(csChild, &name)) { + return false; + } + bool found = false; + for (auto str : operatorEntries) { + if (string::npos != str.find(name)) { + found = true; break; } } - if (!memberFunctions) { - return overview->reportError<bool>("missing #Subtopic Member_Functions"); + if (!found) { + return csChild->reportError<bool>("missing operator in Operators"); + } + } + } + return true; + } + + bool checkSeeAlso() { + return true; + } + + bool checkSubtopicSummary() { + for (auto& rootChild : fRoot->fChildren) { + if (!this->isStructOrClass(rootChild)) { + continue; + } + auto& cs = rootChild; + auto overview = this->findOverview(cs); + if (!overview) { + return false; + } + Definition* subtopics = nullptr; + Definition* relatedFunctions = nullptr; + for (auto& overChild : overview->fChildren) { + if ("Subtopics" == overChild->fName) { + subtopics = overChild; + } else if ("Related_Functions" == overChild->fName) { + relatedFunctions = overChild; } - if (MarkType::kSubtopic != memberFunctions->fMarkType) { - return memberFunctions->reportError<bool>("expected #Subtopic Member_Functions"); + } + if (!subtopics) { + return overview->reportError<bool>("missing #Subtopic Subtopics"); + } + if (MarkType::kSubtopic != subtopics->fMarkType) { + return subtopics->reportError<bool>("expected #Subtopic Subtopics"); + } + if (relatedFunctions && MarkType::kSubtopic != relatedFunctions->fMarkType) { + return relatedFunctions->reportError<bool>("expected #Subtopic Related_Functions"); + } + vector<string> subtopicEntries; + if (!this->collectEntries(subtopics, &subtopicEntries)) { + return false; + } + if (relatedFunctions && !this->collectEntries(relatedFunctions, &subtopicEntries)) { + return false; + } + for (auto& csChild : cs->fChildren) { + if (MarkType::kSubtopic != csChild->fMarkType) { + continue; } - Definition* memberTable = nullptr; - for (auto& memberChild : memberFunctions->fChildren) { - if (MarkType::kTable == memberChild->fMarkType && - memberChild->fName == memberFunctions->fName) { - memberTable = memberChild; + string name; + if (!this->childName(csChild, &name)) { + return false; + } + bool found = false; + for (auto str : subtopicEntries) { + if (string::npos != str.find(name)) { + found = true; break; } } - if (!memberTable) { - return memberFunctions->reportError<bool>("missing #Table in Member_Functions"); + if (!found) { + return csChild->reportError<bool>("missing SubTopic in SubTopics"); } - vector<string> overviewEntries; // build map of overview entries - bool expectLegend = true; - string prior = " "; // expect entries to be alphabetical - for (auto& memberRow : memberTable->fChildren) { - if (MarkType::kLegend == memberRow->fMarkType) { - if (!expectLegend) { - return memberRow->reportError<bool>("expect #Legend only once"); - } - // todo: check if legend format matches table's rows' format - expectLegend = false; - } else if (expectLegend) { - return memberRow->reportError<bool>("expect #Legend first"); - } - if (MarkType::kRow != memberRow->fMarkType) { - continue; // let anything through for now; can tighten up in the future - } - // expect column 0 to point to function name - // todo: content end points past space; could tighten that up - Definition* column0 = memberRow->fChildren[0]; - string name = string(column0->fContentStart, - column0->fTerminator - column0->fContentStart); - if (prior > name) { - return memberRow->reportError<bool>("expect alphabetical order"); - } - if (prior == name) { - return memberRow->reportError<bool>("expect unique names"); - } - // todo: error if name is all lower case and doesn't end in () - overviewEntries.push_back(name); - prior = name; - } - // mark corresponding methods as visited (may be more than one per entry) - for (auto& csChild : cs->fChildren) { - if (MarkType::kMethod != csChild->fMarkType) { - // only check methods for now - continue; - } - auto start = csChild->fName.find_last_of(':'); - start = string::npos == start ? 0 : start + 1; - string name = csChild->fName.substr(start); - if (overviewEntries.end() == - std::find(overviewEntries.begin(), overviewEntries.end(), name)) { - return csChild->reportError<bool>("missing in Overview"); - } + } + } + return true; + } + + bool childName(const Definition* def, string* name) { + auto start = def->fName.find_last_of(':'); + start = string::npos == start ? 0 : start + 1; + *name = def->fName.substr(start); + if (def->fClone) { + auto lastUnderline = name->find_last_of('_'); + if (string::npos == lastUnderline) { + return def->reportError<bool>("expect _ in name"); + } + if (lastUnderline + 1 >= name->length()) { + return def->reportError<bool>("expect char after _ in name"); + } + for (auto index = lastUnderline + 1; index < name->length(); ++index) { + if (!isdigit((*name)[index])) { + return def->reportError<bool>("expect digit after _ in name"); } } + *name = name->substr(0, lastUnderline); + bool allLower = true; + for (auto ch : *name) { + allLower &= (bool) islower(ch); + } + if (allLower) { + *name += "()"; + } } return true; } - bool checkSeeAlso() { + const Definition* findOverview(const Definition* parent) { + // expect Overview as Topic in every main class or struct + Definition* overview = nullptr; + for (auto& csChild : parent->fChildren) { + if ("Overview" == csChild->fName) { + if (overview) { + return csChild->reportError<const Definition*>("expected only one Overview"); + } + overview = csChild; + } + } + if (!overview) { + return parent->reportError<const Definition*>("missing #Topic Overview"); + } + return overview; + } + + bool collectEntries(const Definition* entries, vector<string>* strings) { + const Definition* table = nullptr; + for (auto& child : entries->fChildren) { + if (MarkType::kTable == child->fMarkType && child->fName == entries->fName) { + table = child; + break; + } + } + if (!table) { + return entries->reportError<bool>("missing #Table in Overview Subtopic"); + } + bool expectLegend = true; + string prior = " "; // expect entries to be alphabetical + for (auto& row : table->fChildren) { + if (MarkType::kLegend == row->fMarkType) { + if (!expectLegend) { + return row->reportError<bool>("expect #Legend only once"); + } + // todo: check if legend format matches table's rows' format + expectLegend = false; + } else if (expectLegend) { + return row->reportError<bool>("expect #Legend first"); + } + if (MarkType::kRow != row->fMarkType) { + continue; // let anything through for now; can tighten up in the future + } + // expect column 0 to point to function name + Definition* column0 = row->fChildren[0]; + string name = string(column0->fContentStart, + column0->fContentEnd - column0->fContentStart); + if (prior > name) { + return row->reportError<bool>("expect alphabetical order"); + } + if (prior == name) { + return row->reportError<bool>("expect unique names"); + } + // todo: error if name is all lower case and doesn't end in () + strings->push_back(name); + prior = name; + } return true; } - bool checkSubtopicContents() { + bool isStructOrClass(const Definition* definition) { + if (MarkType::kStruct != definition->fMarkType && + MarkType::kClass != definition->fMarkType) { + return false; + } + if (string::npos != definition->fFileName.find("undocumented.bmh")) { + return false; + } return true; } |