blob: 0b28d16c70df433ead929858d8107c189fe994ad [file] [log] [blame]
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#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
// SkSurface::makeImageSnapShot should be referenced under SkImage 'creators'
class SelfChecker {
public:
SelfChecker(const BmhParser& bmh)
: fBmhParser(bmh)
{}
bool check() {
for (const auto& topic : fBmhParser.fTopicMap) {
Definition* topicDef = topic.second;
if (topicDef->fParent) {
continue;
}
if (!topicDef->isRoot()) {
return fBmhParser.reportError<bool>("expected root topic");
}
fRoot = topicDef->asRoot();
if (!this->checkMethodSummary()) {
return false;
}
if (!this->checkMethodSubtopic()) {
return false;
}
if (!this->checkSubtopicContents()) {
return false;
}
if (!this->checkConstructors()) {
return false;
}
if (!this->checkSeeAlso()) {
return false;
}
if (!this->checkCreators()) {
return false;
}
}
return true;
}
protected:
bool checkConstructors() {
return true;
}
bool checkCreators() {
return true;
}
bool checkMethodSubtopic() {
return true;
}
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 (!overview) {
return cs->reportError<bool>("missing #Topic Overview");
}
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");
}
Definition* memberTable = nullptr;
for (auto& memberChild : memberFunctions->fChildren) {
if (MarkType::kTable == memberChild->fMarkType &&
memberChild->fName == memberFunctions->fName) {
memberTable = memberChild;
break;
}
}
if (!memberTable) {
return memberFunctions->reportError<bool>("missing #Table in Member_Functions");
}
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 checkSeeAlso() {
return true;
}
bool checkSubtopicContents() {
return true;
}
private:
const BmhParser& fBmhParser;
RootDefinition* fRoot;
};
bool SelfCheck(const BmhParser& bmh) {
SelfChecker checker(bmh);
return checker.check();
}