/*
    SPDX-FileCopyrightText: 2018 Sergio Martins <smartins@kde.org>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "empty-qstringliteral.h"
#include "ClazyContext.h"
#include "HierarchyUtils.h"
#include "PreProcessorVisitor.h"
#include "QtUtils.h"

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

using namespace clang;

void EmptyQStringliteral::VisitStmt(clang::Stmt *stmt)
{
    if (!stmt->getBeginLoc().isMacroID()) {
        return;
    }

    const std::string filepath = sm().getFilename(sm().getExpansionLoc(stmt->getBeginLoc())).str();
    if (clazy::contains(filepath, ".rcc/qmlcache/")) {
        return; // This is an autogenerated file
    }
    if (auto splitted = clazy::splitString(filepath, '/'); !splitted.empty()) {
        if (std::string filename = splitted[splitted.size() - 1]; clazy::startsWith(filename, "ui_") && clazy::endsWith(filename, ".h")) {
            return; // Also ignore all ui files
        }
    }

    if (m_context->preprocessorVisitor && m_context->preprocessorVisitor->qtVersion() >= 60000) { // The AST looks very different in Qt6
        handleQt6StringLiteral(stmt);
    } else {
        handleQt5StringLiteral(stmt);
    }
}

void EmptyQStringliteral::handleQt6StringLiteral(clang::Stmt *stmt)
{
    if (auto c = dyn_cast<CallExpr>(stmt)) {
        if (clazy::qualifiedMethodName(c->getDirectCallee()) == qtNamespaced("QtPrivate::qMakeStringPrivate")) {
            if (auto lt = clazy::getFirstChildOfType<StringLiteral>(c); lt && lt->getByteLength() == 0) {
                emitWarning(stmt, "Use QLatin1String(\"\") or QString() instead of an empty QStringLiteral");
            }
        }
    }
}

void EmptyQStringliteral::handleQt5StringLiteral(clang::Stmt *stmt)
{
    auto *declstm = dyn_cast<DeclStmt>(stmt);
    if (!declstm || !declstm->isSingleDecl()) {
        return;
    }

    auto *vd = dyn_cast<VarDecl>(declstm->getSingleDecl());
    if (!vd || clazy::name(vd) != "qstring_literal") {
        return;
    }

    Expr *expr = vd->getInit();
    auto *initListExpr = expr ? dyn_cast<InitListExpr>(expr) : nullptr;
    if (!initListExpr || initListExpr->getNumInits() != 2) {
        return;
    }

    Expr *init = initListExpr->getInit(1);
    auto *literal = init ? dyn_cast<StringLiteral>(init) : nullptr;
    if (!literal || literal->getByteLength() != 0) {
        return;
    }

    emitWarning(stmt, "Use QLatin1String(\"\") or QString() instead of an empty QStringLiteral");
}
