diff options
Diffstat (limited to 'build/clang-plugin/OverrideBaseCallChecker.cpp')
-rw-r--r-- | build/clang-plugin/OverrideBaseCallChecker.cpp | 109 |
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(); + } + } +} |