diff options
author | Lucie Gérard <lucie.gerard@qt.io> | 2020-02-18 11:44:41 +0100 |
---|---|---|
committer | Lucie Gérard <lucie.gerard@qt.io> | 2020-02-18 14:33:02 +0100 |
commit | 4bfea4e31f4a3deec673f519c83081b284f7c556 (patch) | |
tree | a985c823175dc34677b1fa442f43677798c6d55f | |
parent | 60be66f17939ccbc6fcc73a16de45af6822851b8 (diff) |
Split the PP handling out of the AST parsing
Change-Id: Id36ef1a920fb21442263df975e8bde0e3a76bd5b
Reviewed-by: Karsten Heimrich <karsten.heimrich@qt.io>
-rw-r--r-- | src/global/configure.pri | 3 | ||||
-rw-r--r-- | src/linguist/lupdate/clangtoolastreader.cpp | 157 | ||||
-rw-r--r-- | src/linguist/lupdate/clangtoolastreader.h | 121 | ||||
-rw-r--r-- | src/linguist/lupdate/cpp_clang.cpp | 9 | ||||
-rw-r--r-- | src/linguist/lupdate/cpp_clang.h | 158 | ||||
-rw-r--r-- | src/linguist/lupdate/lupdate.pro | 6 | ||||
-rw-r--r-- | src/linguist/lupdate/lupdatepreprocessoraction.cpp | 142 | ||||
-rw-r--r-- | src/linguist/lupdate/lupdatepreprocessoraction.h | 125 |
8 files changed, 440 insertions, 281 deletions
diff --git a/src/global/configure.pri b/src/global/configure.pri index 2d0601245..4dd81c623 100644 --- a/src/global/configure.pri +++ b/src/global/configure.pri @@ -191,8 +191,7 @@ defineReplace(CheckClangLlvmLibForLupdateParser) { } } !equals(QMAKE_HOST.os, Windows): { - equals(QMAKE_HOST.os, Darwin): CLANG_LLVM_LIBS += -lz -lcurses - else: CLANG_LLVM_LIBS += -lz -ltinfo + CLANG_LLVM_LIBS += -lz -ltinfo } return($$CLANG_LLVM_LIBS) } diff --git a/src/linguist/lupdate/clangtoolastreader.cpp b/src/linguist/lupdate/clangtoolastreader.cpp index bd3147838..da0fcd5d8 100644 --- a/src/linguist/lupdate/clangtoolastreader.cpp +++ b/src/linguist/lupdate/clangtoolastreader.cpp @@ -25,12 +25,9 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include "clangtoolastreader.h" - -#include <QtCore/qregularexpression.h> -#include <clang/Lex/MacroArgs.h> -#include <clang/Basic/TokenKinds.h> +#include "clangtoolastreader.h" +#include "translator.h" QT_BEGIN_NAMESPACE @@ -50,67 +47,6 @@ namespace LupdatePrivate return QString::fromStdString(context.substr(0, context.find("::" + funcName, 0))); } - enum QuoteCompulsary - { - None = 0x01, - Left = 0x02, // Left quote is mandatory - Right = 0x04, // Right quote is mandatory - LeftAndRight = Left | Right // Both quotes are mandatory - }; - - /* - Removes the quotes around the lupdate extra, ID meta data, magic and - ID prefix comments and source string literals. - Depending on the given compulsory option, quotes can be unbalanced and - still some text is returned. This is to mimic the old lupdate behavior. - */ - QString cleanQuote(llvm::StringRef s, QuoteCompulsary quote) - { - if (s.empty()) - return {}; - s = s.trim(); - if (!s.consume_front("\"") && ((quote & Left) != 0)) - return {}; - if (!s.consume_back("\"") && ((quote & Right) != 0)) - return {}; - return QString::fromStdString(s); - } - - /* - Removes the quotes and a possible existing string literal prefix - for a given string literal coming from the source code. Do not use - to clean the quotes around the lupdate translator specific comments. - */ - QString cleanQuote(const std::string &token) - { - if (token.empty()) - return {}; - - const QString string = QString::fromStdString(token).trimmed(); - const int index = string.indexOf(QLatin1Char('"')); - if (index <= 0) - return LupdatePrivate::cleanQuote(token, QuoteCompulsary::LeftAndRight); - - QRegularExpressionMatch result; - if (string.at(index - 1) == QLatin1Char('R')) { - static const QRegularExpression rawStringLiteral { - QStringLiteral( - "(?:\\bu8|\\b[LuU])??R\\\"([^\\(\\)\\\\ ]{0,16})\\((?<characters>.*)\\)\\1\\\"" - ), QRegularExpression::DotMatchesEverythingOption }; - result = rawStringLiteral.match(string); - } else { - static const QRegularExpression stringLiteral { - QStringLiteral( - "(?:\\bu8|\\b[LuU])+?\\\"(?<characters>[^\\\"\\\\]*(?:\\\\.[^\\\"\\\\]*)*)\\\"" - ) - }; - result = stringLiteral.match(string); - } - if (result.hasMatch()) - return result.captured(QStringLiteral("characters")); - return string; - } - static bool capture(const QRegularExpression &exp, const QString &line, QString *i, QString *c) { i->clear(), c->clear(); @@ -622,93 +558,4 @@ void LupdateVisitor::processPreprocessorCall(TranslationRelatedStore store) } } -void LupdatePPCallbacks::MacroExpands(const clang::Token ¯oNameTok, - const clang::MacroDefinition ¯oDefinition, clang::SourceRange range, - const clang::MacroArgs *args) -{ - if (!args) - return; - const auto &sm = m_preprocessor.getSourceManager(); - llvm::StringRef fileName = sm.getFilename(range.getBegin()); - if (fileName != m_inputFile) - return; - - const QString funcName = QString::fromStdString(m_preprocessor.getSpelling(macroNameTok)); - qCDebug(lcClang) << "func Name " << funcName; - if (!funcName.contains(QStringLiteral("NOOP")) - && !funcName.contains(QStringLiteral("Q_DECLARE_TR_FUNCTIONS"))) { - return; - } - - TranslationRelatedStore store; - store.callType = QStringLiteral("MacroExpands"); - store.funcName = funcName; - store.lupdateLocationFile = QString::fromStdString(fileName); - store.lupdateLocationLine = sm.getExpansionLineNumber(range.getBegin()); - store.locationCol = sm.getExpansionColumnNumber(range.getBegin()); - store.callLocation = range.getBegin(); - - std::vector<QString> arguments(args->getNumMacroArguments()); - for (unsigned i = 0; i < args->getNumMacroArguments(); i++) { - auto preExpArguments = const_cast<clang::MacroArgs*>(args)->getPreExpArgument(i, - m_preprocessor); - QString temp; - for (const auto &preExpArgument : preExpArguments) { - const auto kind = preExpArgument.getKind(); - if (kind == clang::tok::TokenKind::identifier) - temp = QString::fromStdString(m_preprocessor.getSpelling(preExpArgument)); - else if (clang::tok::isStringLiteral(kind)) - temp += LupdatePrivate::cleanQuote(m_preprocessor.getSpelling(preExpArgument)); - } - arguments[i] = temp; - } - storeMacroArguments(arguments, &store); - if (store.isValid()) - m_translationStores.push_back(store); -} - -void LupdatePPCallbacks::storeMacroArguments(const std::vector<QString> &args, - TranslationRelatedStore *store) -{ - switch (trFunctionAliasManager.trFunctionByName(store->funcName)) { - // only one argument: the context with no " - case TrFunctionAliasManager::Function_Q_DECLARE_TR_FUNCTIONS: - if (args.size() != 1) - break; - store->contextArg = args[0]; - break; - // only one argument: the source - case TrFunctionAliasManager::Function_QT_TR_N_NOOP: - Q_FALLTHROUGH(); - case TrFunctionAliasManager::Function_QT_TR_NOOP: - case TrFunctionAliasManager::Function_QT_TR_NOOP_UTF8: - if (args.size() != 1) - break; - store->lupdateSource = args[0]; - break; - // two arguments: the context and the source - case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP: - case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3: - Q_FALLTHROUGH(); - case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP: - case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP_UTF8: - case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3: - case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3_UTF8: - if (args.size() != 2) - break; - store->contextArg = args[0]; - store->lupdateSource = args[1]; - break; - // only one argument (?) the message Id - case TrFunctionAliasManager::Function_QT_TRID_N_NOOP: - Q_FALLTHROUGH(); - case TrFunctionAliasManager::Function_qtTrId: - case TrFunctionAliasManager::Function_QT_TRID_NOOP: - if (args.size() != 1) - break; - store->lupdateId = args[0]; - break; - } -} - QT_END_NAMESPACE diff --git a/src/linguist/lupdate/clangtoolastreader.h b/src/linguist/lupdate/clangtoolastreader.h index d7f6fce3b..ea544bdad 100644 --- a/src/linguist/lupdate/clangtoolastreader.h +++ b/src/linguist/lupdate/clangtoolastreader.h @@ -29,136 +29,33 @@ #ifndef CLANG_TOOL_AST_READER_H #define CLANG_TOOL_AST_READER_H -#include "lupdate.h" -#include "translator.h" -#include "translatormessage.h" - -#include <QtCore/qloggingcategory.h> +#include "cpp_clang.h" #if defined(Q_CC_MSVC) +# pragma warning(push) # pragma warning(disable: 4100) # pragma warning(disable: 4146) # pragma warning(disable: 4267) # pragma warning(disable: 4624) #endif -#include <llvm/ADT/APInt.h> #include <clang/AST/RecursiveASTVisitor.h> #include <clang/Frontend/CompilerInstance.h> #include <clang/Frontend/FrontendActions.h> #include <clang/Tooling/Tooling.h> -#include <clang/Lex/PPCallbacks.h> -#include <clang/Lex/Preprocessor.h> #if defined(Q_CC_MSVC) -# pragma warning(default: 4100) -# pragma warning(default: 4146) -# pragma warning(default: 4267) -# pragma warning(default: 4624) +# pragma warning(pop) #endif #include <memory> -#include <string> -#include <utility> -#include <vector> QT_BEGIN_NAMESPACE -inline QDebug operator<<(QDebug out, const std::string& str) -{ - out << QString::fromStdString(str); - return out; -} -Q_DECLARE_LOGGING_CATEGORY(lcClang) - -#define LUPDATE_CLANG_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) -#define LUPDATE_CLANG_VERSION LUPDATE_CLANG_VERSION_CHECK(LUPDATE_CLANG_VERSION_MAJOR, \ - LUPDATE_CLANG_VERSION_MINOR, LUPDATE_CLANG_VERSION_PATCH) - -// Local storage of translation information (information from the AST and -// linguist side) -struct TranslationRelatedStore -{ - QString callType; - QString rawCode; - QString funcName; - qint64 locationCol = -1; - QString contextArg; - QString contextRetrieved; - QString lupdateSource; - QString lupdateLocationFile; - qint64 lupdateLocationLine = -1; - QString lupdateId; - QString lupdateSourceWhenId; - QString lupdateIdMetaData; - QString lupdateMagicMetaData; - QHash<QString, QString> lupdateAllMagicMetaData; - QString lupdateComment; - QString lupdateExtraComment; - QString lupdatePlural; - clang::SourceLocation callLocation; - - bool isValid() const - { - return !lupdateLocationFile.isEmpty() && (lupdateLocationLine > -1) && (locationCol > -1); - } - - void printStore() const - { - qCDebug(lcClang) << "------------------ Printing Store----------------------------------\n"; - qCDebug(lcClang) - << "callType : " << callType << "\n" - << "rawCode : \n" << rawCode << "\n" - << "funcName : " << funcName << "\n" - << "LocationCol : " << locationCol << "\n" - << "contextArg : " << contextArg << "\n" - << "contextRetrieved : " << contextRetrieved << "\n" - << "lupdateSource : " << lupdateSource << "\n" - << "lupdateLocationFile : " << lupdateLocationFile << "\n" - << "lupdateLocationLine : " << lupdateLocationLine << "\n" - << "lupdateId : " << lupdateId << "\n" - << "lupdateIdMetaData : " << lupdateIdMetaData << "\n" - << "lupdateMagicMetaData: " << lupdateMagicMetaData << "\n" - << "lupdateComment : " << lupdateComment << "\n" - << "lupdateExtraComment : " << lupdateExtraComment << "\n" - << "lupdatePlural : " << lupdatePlural; - qCDebug(lcClang) << "-------------------------------------------------------------------\n"; - } -}; - -using TranslationStores = std::vector<TranslationRelatedStore>; - -class LupdatePPCallbacks : public clang::PPCallbacks -{ -public: - LupdatePPCallbacks(TranslationStores &translationStores, clang::Preprocessor &preprocessor) - : m_translationStores(translationStores), - m_preprocessor(preprocessor) - { - const auto &sm = m_preprocessor.getSourceManager(); - m_inputFile = sm.getFileEntryForID(sm.getMainFileID())->getName(); - } - - ~LupdatePPCallbacks() override - {} - - // Overridden callback functions. - void MacroExpands(const clang::Token ¯oNameTok, - const clang::MacroDefinition ¯oDefinition, clang::SourceRange range, - const clang::MacroArgs *args) override; - -private: - void storeMacroArguments(const std::vector<QString> &args, TranslationRelatedStore *store); - - TranslationStores &m_translationStores; - clang::Preprocessor &m_preprocessor; - std::string m_inputFile; -}; +class Translator; class LupdateVisitor : public clang::RecursiveASTVisitor<LupdateVisitor> { - friend class LupdateASTConsumer; - public: explicit LupdateVisitor(clang::ASTContext *context, Translator *tor) : m_context(context), @@ -214,11 +111,6 @@ public: m_visitor.fillTranslator(); } - TranslationStores &preprocessorStores() - { - return m_visitor.m_translationStoresFromPP; - } - private: LupdateVisitor m_visitor; }; @@ -234,11 +126,6 @@ public: clang::CompilerInstance &compiler, llvm::StringRef /* inFile */) override { LupdateASTConsumer *consumer = new LupdateASTConsumer(&compiler.getASTContext(), m_tor); - clang::Preprocessor &preprocessor = compiler.getPreprocessor(); - LupdatePPCallbacks *callbacks = new LupdatePPCallbacks(consumer->preprocessorStores(), - preprocessor); - preprocessor.addPPCallbacks(std::unique_ptr<clang::PPCallbacks>(callbacks)); - return std::unique_ptr<clang::ASTConsumer>(consumer); } diff --git a/src/linguist/lupdate/cpp_clang.cpp b/src/linguist/lupdate/cpp_clang.cpp index 598ac94ca..6b8162c59 100644 --- a/src/linguist/lupdate/cpp_clang.cpp +++ b/src/linguist/lupdate/cpp_clang.cpp @@ -25,9 +25,11 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include "cpp_clang.h" -#include <translator.h> +#include "cpp_clang.h" +#include "clangtoolastreader.h" +#include "lupdatepreprocessoraction.h" +#include "translator.h" #include <clang/Tooling/CommonOptionsParser.h> #include <llvm/Option/Option.h> @@ -77,6 +79,9 @@ void ClangCppParser::loadCPP(Translator &translator, const QStringList &filename clang::tooling::ClangTool tool(OptionsParser.getCompilations(), sources); tool.appendArgumentsAdjuster(getClangArgumentAdjuster()); + Stores stores; + tool.run(new LupdatePreprocessorActionFactory(stores.Preprocessor)); + Translator *tor = new Translator(); // A ClangTool needs a new FrontendAction for each translation unit it runs on diff --git a/src/linguist/lupdate/cpp_clang.h b/src/linguist/lupdate/cpp_clang.h index 16e03fb70..c9d512c5e 100644 --- a/src/linguist/lupdate/cpp_clang.h +++ b/src/linguist/lupdate/cpp_clang.h @@ -29,16 +29,168 @@ #ifndef CLANG_CPP_H #define CLANG_CPP_H -#include "clangtoolastreader.h" #include "lupdate.h" +#include <QtCore/qloggingcategory.h> +#include <QtCore/qregularexpression.h> + +#if defined(Q_CC_MSVC) +# pragma warning(push) +# pragma warning(disable: 4100) +# pragma warning(disable: 4146) +# pragma warning(disable: 4267) +# pragma warning(disable: 4624) +#endif + +#include <clang/Basic/SourceLocation.h> + +#if defined(Q_CC_MSVC) +# pragma warning(pop) +#endif + +#include <vector> + QT_BEGIN_NAMESPACE -namespace ClangCppParser { +inline QDebug operator<<(QDebug out, const std::string& str) +{ + out << QString::fromStdString(str); + return out; +} +Q_DECLARE_LOGGING_CATEGORY(lcClang) + +#define LUPDATE_CLANG_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) +#define LUPDATE_CLANG_VERSION LUPDATE_CLANG_VERSION_CHECK(LUPDATE_CLANG_VERSION_MAJOR, \ + LUPDATE_CLANG_VERSION_MINOR, LUPDATE_CLANG_VERSION_PATCH) + +// Local storage of translation information (information from the AST and linguist side) +struct TranslationRelatedStore +{ + QString callType; + QString rawCode; + QString funcName; + qint64 locationCol = -1; + QString contextArg; + QString contextRetrieved; + QString lupdateSource; + QString lupdateLocationFile; + qint64 lupdateLocationLine = -1; + QString lupdateId; + QString lupdateSourceWhenId; + QString lupdateIdMetaData; + QString lupdateMagicMetaData; + QHash<QString, QString> lupdateAllMagicMetaData; + QString lupdateComment; + QString lupdateExtraComment; + QString lupdatePlural; + clang::SourceLocation callLocation; + + bool isValid() const + { + return !lupdateLocationFile.isEmpty() && (lupdateLocationLine > -1) && (locationCol > -1); + } + + void printStore() const + { + qCDebug(lcClang) << "------------------ Printing Store----------------------------------\n"; + qCDebug(lcClang) + << "callType : " << callType << "\n" + << "rawCode : \n" << rawCode << "\n" + << "funcName : " << funcName << "\n" + << "LocationCol : " << locationCol << "\n" + << "contextArg : " << contextArg << "\n" + << "contextRetrieved : " << contextRetrieved << "\n" + << "lupdateSource : " << lupdateSource << "\n" + << "lupdateLocationFile : " << lupdateLocationFile << "\n" + << "lupdateLocationLine : " << lupdateLocationLine << "\n" + << "lupdateId : " << lupdateId << "\n" + << "lupdateIdMetaData : " << lupdateIdMetaData << "\n" + << "lupdateMagicMetaData: " << lupdateMagicMetaData << "\n" + << "lupdateComment : " << lupdateComment << "\n" + << "lupdateExtraComment : " << lupdateExtraComment << "\n" + << "lupdatePlural : " << lupdatePlural; + qCDebug(lcClang) << "-------------------------------------------------------------------\n"; + } +}; +using TranslationStores = std::vector<TranslationRelatedStore>; + +struct Stores +{ + TranslationStores Preprocessor; + TranslationStores AST; + TranslationStores QDeclareTrWithContext; + TranslationStores QNoopTranlsationWithContext; +}; + +namespace LupdatePrivate +{ + enum QuoteCompulsary + { + None = 0x01, + Left = 0x02, // Left quote is mandatory + Right = 0x04, // Right quote is mandatory + LeftAndRight = Left | Right // Both quotes are mandatory + }; + + /* + Removes the quotes around the lupdate extra, ID meta data, magic and + ID prefix comments and source string literals. + Depending on the given compulsory option, quotes can be unbalanced and + still some text is returned. This is to mimic the old lupdate behavior. + */ + static QString cleanQuote(llvm::StringRef s, QuoteCompulsary quote) + { + if (s.empty()) + return {}; + s = s.trim(); + if (!s.consume_front("\"") && ((quote & Left) != 0)) + return {}; + if (!s.consume_back("\"") && ((quote & Right) != 0)) + return {}; + return QString::fromStdString(s); + } + + /* + Removes the quotes and a possible existing string literal prefix + for a given string literal coming from the source code. Do not use + to clean the quotes around the lupdate translator specific comments. + */ + static QString cleanQuote(const std::string &token) + { + if (token.empty()) + return {}; + + const QString string = QString::fromStdString(token).trimmed(); + const int index = string.indexOf(QLatin1Char('"')); + if (index <= 0) + return LupdatePrivate::cleanQuote(token, QuoteCompulsary::LeftAndRight); + + QRegularExpressionMatch result; + if (string.at(index - 1) == QLatin1Char('R')) { + static const QRegularExpression rawStringLiteral { + QStringLiteral( + "(?:\\bu8|\\b[LuU])??R\\\"([^\\(\\)\\\\ ]{0,16})\\((?<characters>.*)\\)\\1\\\"" + ), QRegularExpression::DotMatchesEverythingOption }; + result = rawStringLiteral.match(string); + } else { + static const QRegularExpression stringLiteral { + QStringLiteral( + "(?:\\bu8|\\b[LuU])+?\\\"(?<characters>[^\\\"\\\\]*(?:\\\\.[^\\\"\\\\]*)*)\\\"" + ) + }; + result = stringLiteral.match(string); + } + if (result.hasMatch()) + return result.captured(QStringLiteral("characters")); + return string; + } +} + +namespace ClangCppParser +{ void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd); } QT_END_NAMESPACE - #endif diff --git a/src/linguist/lupdate/lupdate.pro b/src/linguist/lupdate/lupdate.pro index 8f1826eee..c653a4e46 100644 --- a/src/linguist/lupdate/lupdate.pro +++ b/src/linguist/lupdate/lupdate.pro @@ -53,10 +53,12 @@ HEADERS += \ qtConfig(clangcpp) { SOURCES += \ cpp_clang.cpp \ - clangtoolastreader.cpp + clangtoolastreader.cpp \ + lupdatepreprocessoraction.cpp HEADERS += \ cpp_clang.h \ - clangtoolastreader.h + clangtoolastreader.h \ + lupdatepreprocessoraction.h } mingw { diff --git a/src/linguist/lupdate/lupdatepreprocessoraction.cpp b/src/linguist/lupdate/lupdatepreprocessoraction.cpp new file mode 100644 index 000000000..3e48ffdfe --- /dev/null +++ b/src/linguist/lupdate/lupdatepreprocessoraction.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Linguist of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lupdatepreprocessoraction.h" + +#include <clang/Lex/MacroArgs.h> +#include <clang/Basic/TokenKinds.h> + +QT_BEGIN_NAMESPACE + +void LupdatePPCallbacks::MacroExpands(const clang::Token &token, + const clang::MacroDefinition ¯oDefinition, clang::SourceRange sourceRange, + const clang::MacroArgs *macroArgs) +{ + const auto &sm = m_preprocessor.getSourceManager(); + llvm::StringRef fileName = sm.getFilename(sourceRange.getBegin()); + if (fileName != m_inputFile) + return; + + const QString funcName = QString::fromStdString(m_preprocessor.getSpelling(token)); + switch (trFunctionAliasManager.trFunctionByName(funcName)) { + default: + return; + case TrFunctionAliasManager::Function_Q_DECLARE_TR_FUNCTIONS: + case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP: + case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3: + case TrFunctionAliasManager::Function_QT_TRID_NOOP: + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP: + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3: + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP_UTF8: + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3_UTF8: + qCDebug(lcClang) << "MacroExpands: Function name:" << funcName; + break; + } + + TranslationRelatedStore store; + store.callType = QStringLiteral("MacroExpands"); + store.funcName = funcName; + store.lupdateLocationFile = QString::fromStdString(fileName); + store.lupdateLocationLine = sm.getExpansionLineNumber(sourceRange.getBegin()); + store.locationCol = sm.getExpansionColumnNumber(sourceRange.getBegin()); + store.callLocation = sourceRange.getBegin(); + + if (macroArgs) { + std::vector<QString> arguments(macroArgs->getNumMacroArguments()); + for (unsigned i = 0; i < macroArgs->getNumMacroArguments(); i++) { + auto preExpArguments = const_cast<clang::MacroArgs*>(macroArgs)->getPreExpArgument(i, + m_preprocessor); + QString temp; + bool errorArgument = false; + for (const auto &preExpArgument : preExpArguments) { + const auto kind = preExpArgument.getKind(); + switch (trFunctionAliasManager.trFunctionByName(funcName)) { + default: + break; + case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP: + case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3: + case TrFunctionAliasManager::Function_QT_TRID_NOOP: + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP: + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3: + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP_UTF8: + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3_UTF8: + if (!clang::tok::isStringLiteral(kind)) + errorArgument = true; + break; + } + if (errorArgument) + break; + if (clang::tok::isStringLiteral(kind)) + temp += LupdatePrivate::cleanQuote(m_preprocessor.getSpelling(preExpArgument)); + else + temp += QString::fromStdString(m_preprocessor.getSpelling(preExpArgument)); + } + arguments[i] = temp; + } + storeMacroArguments(arguments, &store); + } + if (store.isValid()) + m_stores.push_back(store); +} + +void LupdatePPCallbacks::storeMacroArguments(const std::vector<QString> &args, + TranslationRelatedStore *store) +{ + switch (trFunctionAliasManager.trFunctionByName(store->funcName)) { + // only one argument: the context with no " + case TrFunctionAliasManager::Function_Q_DECLARE_TR_FUNCTIONS: + if (args.size() == 1) + store->contextArg = args[0]; + break; + // two arguments: the context and the source + case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP: + case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3: + Q_FALLTHROUGH(); + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP: + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP_UTF8: + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3: + case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3_UTF8: + if (args.size() >= 2) { + store->contextArg = args[0]; + store->lupdateSource = args[1]; + } + if (args.size() == 3) + store->lupdateComment = args[2]; + break; + // only one argument (?) the message Id + case TrFunctionAliasManager::Function_QT_TRID_N_NOOP: + Q_FALLTHROUGH(); + case TrFunctionAliasManager::Function_qtTrId: + case TrFunctionAliasManager::Function_QT_TRID_NOOP: + if (args.size() == 1) + store->lupdateId = args[0]; + break; + } +} + +QT_END_NAMESPACE diff --git a/src/linguist/lupdate/lupdatepreprocessoraction.h b/src/linguist/lupdate/lupdatepreprocessoraction.h new file mode 100644 index 000000000..21c7baee1 --- /dev/null +++ b/src/linguist/lupdate/lupdatepreprocessoraction.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Linguist of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LUPDATEPREPROCESSORACTION_H +#define LUPDATEPREPROCESSORACTION_H + +#include "cpp_clang.h" + +#if defined(Q_CC_MSVC) +# pragma warning(push) +# pragma warning(disable: 4100) +# pragma warning(disable: 4146) +# pragma warning(disable: 4267) +# pragma warning(disable: 4624) +#endif + +#include <clang/Frontend/CompilerInstance.h> +#include <clang/Frontend/FrontendActions.h> +#include <clang/Tooling/Tooling.h> +#include <clang/Lex/PPCallbacks.h> +#include <clang/Lex/Preprocessor.h> + +#if defined(Q_CC_MSVC) +# pragma warning(pop) +#endif + +#include <memory> + +QT_BEGIN_NAMESPACE + +class LupdatePPCallbacks : public clang::PPCallbacks +{ +public: + LupdatePPCallbacks(TranslationStores &stores, clang::Preprocessor &pp) + : m_stores(stores) + , m_preprocessor(pp) + { + const auto &sm = m_preprocessor.getSourceManager(); + m_inputFile = sm.getFileEntryForID(sm.getMainFileID())->getName(); + } + +private: + void MacroExpands(const clang::Token &token, const clang::MacroDefinition ¯oDefinition, + clang::SourceRange sourceRange, const clang::MacroArgs *macroArgs) override; + + void storeMacroArguments(const std::vector<QString> &args, TranslationRelatedStore *store); + + std::string m_inputFile; + TranslationStores &m_stores; + clang::Preprocessor &m_preprocessor; +}; + +class LupdatePreprocessorAction : public clang::PreprocessOnlyAction +{ +public: + LupdatePreprocessorAction(TranslationStores &stores) + : m_stores(stores) + {} + +private: + void ExecuteAction() override + { + auto &preprocessor = getCompilerInstance().getPreprocessor(); + preprocessor.SetSuppressIncludeNotFoundError(true); + auto callbacks = new LupdatePPCallbacks(m_stores, preprocessor); + preprocessor.addPPCallbacks(std::unique_ptr<clang::PPCallbacks>(callbacks)); + + clang::PreprocessOnlyAction::ExecuteAction(); + } + +private: + TranslationStores &m_stores; +}; + +class LupdatePreprocessorActionFactory : public clang::tooling::FrontendActionFactory +{ +public: + LupdatePreprocessorActionFactory(TranslationStores &stores) + : m_stores(stores) + {} + +#if (LUPDATE_CLANG_VERSION >= LUPDATE_CLANG_VERSION_CHECK(10,0,0)) + std::unique_ptr<clang::FrontendAction> create() override + { + return std::make_unique<LupdatePreprocessorAction>(m_stores); + } +#else + clang::FrontendAction *create() override + { + return new LupdatePreprocessorAction(m_stores); + } +#endif + +private: + TranslationStores &m_stores; +}; + +QT_END_NAMESPACE + +#endif |