summaryrefslogtreecommitdiffstats
path: root/build/clang-plugin/OverrideBaseCallChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--build/clang-plugin/OverrideBaseCallChecker.cpp109
1 files changed, 109 insertions, 0 deletions
diff --git a/build/clang-plugin/OverrideBaseCallChecker.cpp b/build/clang-plugin/OverrideBaseCallChecker.cpp
new file mode 100644
index 0000000000..600d431335
--- /dev/null
+++ b/build/clang-plugin/OverrideBaseCallChecker.cpp
@@ -0,0 +1,109 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OverrideBaseCallChecker.h"
+#include "CustomMatchers.h"
+
+void OverrideBaseCallChecker::registerMatchers(MatchFinder *AstMatcher) {
+ AstMatcher->addMatcher(cxxRecordDecl(hasBaseClasses()).bind("class"), this);
+}
+
+bool OverrideBaseCallChecker::isRequiredBaseMethod(
+ const CXXMethodDecl *Method) {
+ return hasCustomAttribute<moz_required_base_method>(Method);
+}
+
+void OverrideBaseCallChecker::evaluateExpression(
+ const Stmt *StmtExpr, std::list<const CXXMethodDecl *> &MethodList) {
+ // Continue while we have methods in our list
+ if (!MethodList.size()) {
+ return;
+ }
+
+ if (auto MemberFuncCall = dyn_cast<CXXMemberCallExpr>(StmtExpr)) {
+ if (auto Method =
+ dyn_cast<CXXMethodDecl>(MemberFuncCall->getDirectCallee())) {
+ findBaseMethodCall(Method, MethodList);
+ }
+ }
+
+ for (auto S : StmtExpr->children()) {
+ if (S) {
+ evaluateExpression(S, MethodList);
+ }
+ }
+}
+
+void OverrideBaseCallChecker::getRequiredBaseMethod(
+ const CXXMethodDecl *Method,
+ std::list<const CXXMethodDecl *> &MethodsList) {
+
+ if (isRequiredBaseMethod(Method)) {
+ MethodsList.push_back(Method);
+ } else {
+ // Loop through all it's base methods.
+ for (auto BaseMethod = Method->begin_overridden_methods();
+ BaseMethod != Method->end_overridden_methods(); BaseMethod++) {
+ getRequiredBaseMethod(*BaseMethod, MethodsList);
+ }
+ }
+}
+
+void OverrideBaseCallChecker::findBaseMethodCall(
+ const CXXMethodDecl *Method,
+ std::list<const CXXMethodDecl *> &MethodsList) {
+
+ MethodsList.remove(Method);
+ // Loop also through all it's base methods;
+ for (auto BaseMethod = Method->begin_overridden_methods();
+ BaseMethod != Method->end_overridden_methods(); BaseMethod++) {
+ findBaseMethodCall(*BaseMethod, MethodsList);
+ }
+}
+
+void OverrideBaseCallChecker::check(const MatchFinder::MatchResult &Result) {
+ const char *Error =
+ "Method %0 must be called in all overrides, but is not called in "
+ "this override defined for class %1";
+ const CXXRecordDecl *Decl = Result.Nodes.getNodeAs<CXXRecordDecl>("class");
+
+ // Loop through the methods and look for the ones that are overridden.
+ for (auto Method : Decl->methods()) {
+ // If this method doesn't override other methods or it doesn't have a body,
+ // continue to the next declaration.
+ if (!Method->size_overridden_methods() || !Method->hasBody()) {
+ continue;
+ }
+
+ // Preferred the usage of list instead of vector in order to avoid
+ // calling erase-remove when deleting items
+ std::list<const CXXMethodDecl *> MethodsList;
+ // For each overridden method push it to a list if it meets our
+ // criteria
+ for (auto BaseMethod = Method->begin_overridden_methods();
+ BaseMethod != Method->end_overridden_methods(); BaseMethod++) {
+ getRequiredBaseMethod(*BaseMethod, MethodsList);
+ }
+
+ // If no method has been found then no annotation was used
+ // so checking is not needed
+ if (!MethodsList.size()) {
+ continue;
+ }
+
+ // Loop through the body of our method and search for calls to
+ // base methods
+ evaluateExpression(Method->getBody(), MethodsList);
+
+ // If list is not empty pop up errors
+ for (auto BaseMethod : MethodsList) {
+ std::string QualName;
+ raw_string_ostream OS(QualName);
+ BaseMethod->printQualifiedName(OS);
+
+ diag(Method->getLocation(), Error, DiagnosticIDs::Error)
+ << OS.str() << Decl->getName();
+ }
+ }
+}