File indexing completed on 2024-05-12 05:41:01

0001 /*
0002     SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com
0003     SPDX-FileContributor: Nicolas Fella <nicolas.fella@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "jnisignatures.h"
0009 #include "HierarchyUtils.h"
0010 #include "StringUtils.h"
0011 #include "clazy_stl.h"
0012 
0013 #include <clang/AST/Decl.h>
0014 #include <clang/AST/DeclCXX.h>
0015 #include <clang/AST/Expr.h>
0016 #include <clang/AST/ExprCXX.h>
0017 #include <clang/AST/OperationKinds.h>
0018 #include <clang/AST/Stmt.h>
0019 #include <clang/Basic/LLVM.h>
0020 #include <clang/Basic/SourceLocation.h>
0021 #include <clang/Basic/SourceManager.h>
0022 #include <llvm/ADT/StringRef.h>
0023 #include <llvm/Support/Casting.h>
0024 
0025 class ClazyContext;
0026 
0027 using namespace clang;
0028 
0029 static const std::regex methodSignatureRegex("\\((\\[?([ZBCSIJFD]|L([a-zA-Z]+\\/)*[a-zA-Z]+;))*\\)\\[?([ZBCSIJFD]|L([a-zA-Z]+\\/)*[a-zA-Z]+;|V)");
0030 
0031 static const std::regex classNameRegex("([a-zA-Z]+\\/)*[a-zA-Z]+");
0032 
0033 static const std::regex methodNameRegex("[a-zA-Z]+");
0034 
0035 JniSignatures::JniSignatures(const std::string &name, ClazyContext *context)
0036     : CheckBase(name, context, Option_CanIgnoreIncludes)
0037 {
0038 }
0039 
0040 bool checkSignature(const std::string &signature, const std::regex &expr)
0041 {
0042     std::smatch match;
0043     return std::regex_match(signature, match, expr);
0044 }
0045 
0046 template<typename T>
0047 void JniSignatures::checkArgAt(T *call, unsigned int index, const std::regex &expr, const std::string &errorMessage)
0048 {
0049     if (call->getNumArgs() < index + 1) {
0050         return;
0051     }
0052 
0053     auto *stringLiteral = clazy::getFirstChildOfType2<StringLiteral>(call->getArg(index));
0054 
0055     if (!stringLiteral) {
0056         return;
0057     }
0058 
0059     if (stringLiteral->getCharByteWidth() != 1) {
0060         return;
0061     }
0062 
0063     const std::string signature = stringLiteral->getString().str();
0064 
0065     const bool valid = checkSignature(signature, expr);
0066 
0067     if (!valid) {
0068         emitWarning(call, errorMessage + ": '" + signature + "'");
0069     }
0070 }
0071 
0072 void JniSignatures::checkFunctionCall(Stmt *stm)
0073 {
0074     auto *callExpr = dyn_cast<CallExpr>(stm);
0075     if (!callExpr) {
0076         return;
0077     }
0078     auto *funDecl = callExpr->getDirectCallee();
0079     if (!funDecl) {
0080         return;
0081     }
0082 
0083     const std::string qualifiedName = funDecl->getQualifiedNameAsString();
0084     if (!clazy::startsWith(qualifiedName, "QAndroidJniObject::")) {
0085         return;
0086     }
0087 
0088     const std::string name = static_cast<std::string>(clazy::name(funDecl));
0089 
0090     if (name == "callObjectMethod" || name == "callMethod") {
0091         checkArgAt(callExpr, 0, methodNameRegex, "Invalid method name");
0092         checkArgAt(callExpr, 1, methodSignatureRegex, "Invalid method signature");
0093     } else if (name == "callStaticObjectMethod" || name == "callStaticMethod") {
0094         checkArgAt(callExpr, 0, classNameRegex, "Invalid class name");
0095         checkArgAt(callExpr, 1, methodNameRegex, "Invalid method name");
0096         checkArgAt(callExpr, 2, methodSignatureRegex, "Invalid method signature");
0097     }
0098 }
0099 
0100 void JniSignatures::checkConstructorCall(Stmt *stm)
0101 {
0102     auto *constructExpr = dyn_cast<CXXConstructExpr>(stm);
0103     if (!constructExpr) {
0104         return;
0105     }
0106     auto *funDecl = constructExpr->getConstructor();
0107 
0108     const std::string qualifiedName = funDecl->getQualifiedNameAsString();
0109     if (qualifiedName != "QAndroidJniObject::QAndroidJniObject") {
0110         return;
0111     }
0112 
0113     checkArgAt(constructExpr, 0, classNameRegex, "Invalid class name");
0114     checkArgAt(constructExpr, 1, methodSignatureRegex, "Invalid constructor signature");
0115 }
0116 
0117 void JniSignatures::VisitStmt(Stmt *stm)
0118 {
0119     checkConstructorCall(stm);
0120     checkFunctionCall(stm);
0121 }