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 }