jnisignatures.cpp 4.31 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
/*
    This file is part of the clazy static checker.

    Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
    Author: Nicolas Fella <nicolas.fella@kdab.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include "jnisignatures.h"
#include "MacroUtils.h"
#include "FunctionUtils.h"
#include "StringUtils.h"
#include "SourceCompatibilityHelpers.h"
#include "clazy_stl.h"

#include <clang/AST/Expr.h>
#include <clang/AST/Stmt.h>
#include <clang/AST/Decl.h>
#include <clang/AST/DeclCXX.h>
#include <clang/AST/ExprCXX.h>
#include <clang/AST/OperationKinds.h>
#include <clang/Basic/LLVM.h>
#include <clang/Basic/SourceLocation.h>
#include <clang/Basic/SourceManager.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/Casting.h>

#include <vector>

#include <iostream>

class ClazyContext;

using namespace clang;
using namespace std;

51 52 53 54 55 56
static const regex methodSignatureRegex("\\((\\[?([ZBCSIJFD]|L([a-zA-Z]+\\/)*[a-zA-Z]+;))*\\)\\[?([ZBCSIJFD]|L([a-zA-Z]+\\/)*[a-zA-Z]+;|V)");

static const regex classNameRegex("([a-zA-Z]+\\/)*[a-zA-Z]+");

static const regex methodNameRegex("[a-zA-Z]+");

57 58 59 60 61
JniSignatures::JniSignatures(const std::string &name, ClazyContext *context)
    : CheckBase(name, context, Option_CanIgnoreIncludes)
{
}

62
bool checkSignature(std::string signature, const regex &expr)
63 64
{
    smatch match;
65
    return regex_match(signature, match, expr);
66 67 68
}

template<typename T>
69
void JniSignatures::checkArgAt(T *call, unsigned int index, const regex &expr, const std::string &errorMessage)
70 71 72 73 74 75 76 77 78 79 80 81 82 83
{
    if (call->getNumArgs() < index + 1)
        return;

    StringLiteral *stringLiteral = clazy::getFirstChildOfType2<StringLiteral>(call->getArg(index));

    if (!stringLiteral)
        return;

    if (stringLiteral->getCharByteWidth() != 1)
        return;

    const std::string signature = stringLiteral->getString().str();

84
    const bool valid = checkSignature(signature, expr);
85 86

    if (!valid) {
87
        emitWarning(call, errorMessage + ": '" + signature + "'");
88 89 90 91 92 93 94 95 96 97 98 99 100
    }
}

void JniSignatures::checkFunctionCall(Stmt *stm)
{
    auto callExpr = dyn_cast<CallExpr>(stm);
    if (!callExpr)
        return;
    auto funDecl = callExpr->getDirectCallee();
    if (!funDecl) {
        return;
    }

101 102
    const std::string qualifiedName = funDecl->getQualifiedNameAsString();
    if (!clazy::startsWith(qualifiedName, "QAndroidJniObject::")) {
103 104 105
        return;
    }

106
    const std::string name = static_cast<std::string>(clazy::name(funDecl));
107 108 109 110 111 112 113 114 115

    if (name == "callObjectMethod" || name == "callMethod") {
        checkArgAt(callExpr, 0, methodNameRegex, "Invalid method name");
        checkArgAt(callExpr, 1, methodSignatureRegex, "Invalid method signature");
    } else if (name == "callStaticObjectMethod" || name == "callStaticMethod") {
        checkArgAt(callExpr, 0, classNameRegex, "Invalid class name");
        checkArgAt(callExpr, 1, methodNameRegex, "Invalid method name");
        checkArgAt(callExpr, 2, methodSignatureRegex, "Invalid method signature");
    }
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
}

void JniSignatures::checkConstructorCall(Stmt *stm)
{
    auto constructExpr = dyn_cast<CXXConstructExpr>(stm);
    if (!constructExpr) {
        return;
    }
    auto funDecl = constructExpr->getConstructor();

    const std::string qualifiedName = funDecl->getQualifiedNameAsString();
    if (qualifiedName != "QAndroidJniObject::QAndroidJniObject") {
        return;
    }

131 132
    checkArgAt(constructExpr, 0, classNameRegex, "Invalid class name");
    checkArgAt(constructExpr, 1, methodSignatureRegex, "Invalid constructor signature");
133 134 135 136 137 138 139
}

void JniSignatures::VisitStmt(Stmt *stm)
{
    checkConstructorCall(stm);
    checkFunctionCall(stm);
}