void QPropertyTypeMismatch::VisitMacroExpands()

in src/checks/manuallevel/qproperty-type-mismatch.cpp [202:308]


void QPropertyTypeMismatch::VisitMacroExpands(const clang::Token &MacroNameTok, const clang::SourceRange &range, const MacroInfo *)
{
    IdentifierInfo *ii = MacroNameTok.getIdentifierInfo();
    if (!ii) {
        return;
    }

    constexpr llvm::StringLiteral q_property{"Q_PROPERTY"};

    if (ii->getName() != q_property) {
        return;
    }

    const CharSourceRange crange = Lexer::getAsCharRange(range, sm(), lo());

    llvm::StringRef text = Lexer::getSourceText(crange, sm(), lo());

    const auto openingLParen = std::find_if_not(text.begin() + q_property.size(), text.end(), [](char c) {
        return std::isspace(c);
    });
    if (*openingLParen != '(') {
        emitWarning(range.getBegin(), "Could not parse argument of the Q_PROPERTY macro");
        return;
    }

    const std::size_t contentBegin = openingLParen - text.begin() + 1;
    std::size_t contentLength = text.size() - contentBegin;
    if (!text.empty() && text.back() == ')') {
        --contentLength;
    }
    text = text.substr(contentBegin, contentLength);

    std::vector<std::string_view> split = clazy::splitStringBySpaces(text);
    if (split.size() < 2) {
        return;
    }

    Property p;
    p.loc = range.getBegin();

    std::size_t splitIndex = 0;
    using namespace std::string_view_literals;

    // Handle type (type string and any following modifiers)
    const auto isModifier = [](std::string_view str) {
        return str == "*"sv || str == "&"sv;
    };

    for (; isModifier(split[splitIndex]) || p.type.empty(); ++splitIndex) {
        p.type += split[splitIndex];
    }

    // Handle name
    p.name = split[splitIndex];

    std::size_t actualNameStartPos = 0;
    // FIXME: This is getting hairy, better use regexps
    for (unsigned int i = 0; i < p.name.size(); ++i) {
        if (p.name[i] == '*' || p.name[i] == '&') {
            p.type += p.name[i];
            ++actualNameStartPos;
        } else {
            break;
        }
    }

    if (actualNameStartPos) {
        p.name.erase(0, actualNameStartPos);
    }

    // Handle Q_PROPERTY functions
    enum { None, Read, Write, Notify } next = None;

    for (const std::string_view &token : split) {
        switch (next) {
        case None: {
            if (token == "READ"sv) {
                next = Read;
                continue;
            } else if (token == "WRITE"sv) {
                next = Write;
                continue;
            } else if (token == "NOTIFY"sv) {
                next = Notify;
                continue;
            } else if (token == "MEMBER"sv) {
                p.member = true;
                break;
            }
            break;
        }
        case Read:
            p.read = token;
            break;
        case Write:
            p.write = token;
            break;
        case Notify:
            p.notify = token;
            break;
        }

        next = None;
    }

    m_qproperties.push_back(std::move(p));
}