summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLucie Gérard <lucie.gerard@qt.io>2020-02-18 11:44:41 +0100
committerLucie Gérard <lucie.gerard@qt.io>2020-02-18 14:33:02 +0100
commit4bfea4e31f4a3deec673f519c83081b284f7c556 (patch)
treea985c823175dc34677b1fa442f43677798c6d55f
parent60be66f17939ccbc6fcc73a16de45af6822851b8 (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.pri3
-rw-r--r--src/linguist/lupdate/clangtoolastreader.cpp157
-rw-r--r--src/linguist/lupdate/clangtoolastreader.h121
-rw-r--r--src/linguist/lupdate/cpp_clang.cpp9
-rw-r--r--src/linguist/lupdate/cpp_clang.h158
-rw-r--r--src/linguist/lupdate/lupdate.pro6
-rw-r--r--src/linguist/lupdate/lupdatepreprocessoraction.cpp142
-rw-r--r--src/linguist/lupdate/lupdatepreprocessoraction.h125
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 &macroNameTok,
- const clang::MacroDefinition &macroDefinition, 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 &macroNameTok,
- const clang::MacroDefinition &macroDefinition, 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 &macroDefinition, 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 &macroDefinition,
+ 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