summaryrefslogtreecommitdiff
path: root/src/linguist
diff options
context:
space:
mode:
Diffstat (limited to 'src/linguist')
-rw-r--r--src/linguist/Qt5LinguistToolsConfig.cmake.in11
-rw-r--r--src/linguist/Qt5LinguistToolsMacros.cmake22
-rw-r--r--src/linguist/linguist/linguist.pro2
-rw-r--r--src/linguist/linguist/mainwindow.cpp9
-rw-r--r--src/linguist/linguist/messageeditorwidgets.cpp4
-rw-r--r--src/linguist/linguist/messagemodel.h2
-rw-r--r--src/linguist/linguist/phrase.cpp80
-rw-r--r--src/linguist/lupdate/clangtoolastreader.cpp407
-rw-r--r--src/linguist/lupdate/clangtoolastreader.h171
-rw-r--r--src/linguist/lupdate/cpp_clang.cpp183
-rw-r--r--src/linguist/lupdate/cpp_clang.h189
-rw-r--r--src/linguist/lupdate/lupdate.pro6
-rw-r--r--src/linguist/lupdate/lupdatepreprocessoraction.cpp141
-rw-r--r--src/linguist/lupdate/lupdatepreprocessoraction.h125
-rw-r--r--src/linguist/lupdate/main.cpp2
-rw-r--r--src/linguist/lupdate/qdeclarative.cpp17
-rw-r--r--src/linguist/lupdate/ui.cpp99
-rw-r--r--src/linguist/shared/formats.pri10
-rw-r--r--src/linguist/shared/po.cpp6
-rw-r--r--src/linguist/shared/proitems.h1
-rw-r--r--src/linguist/shared/qm.cpp21
-rw-r--r--src/linguist/shared/qmakebuiltins.cpp6
-rw-r--r--src/linguist/shared/qmakeevaluator.cpp4
-rw-r--r--src/linguist/shared/qmakeglobals.cpp2
-rw-r--r--src/linguist/shared/qmakeparser.h1
-rw-r--r--src/linguist/shared/xliff.cpp122
-rw-r--r--src/linguist/shared/xmlparser.cpp109
-rw-r--r--src/linguist/shared/xmlparser.h63
28 files changed, 1202 insertions, 613 deletions
diff --git a/src/linguist/Qt5LinguistToolsConfig.cmake.in b/src/linguist/Qt5LinguistToolsConfig.cmake.in
index 4318b16fa..2e99bc762 100644
--- a/src/linguist/Qt5LinguistToolsConfig.cmake.in
+++ b/src/linguist/Qt5LinguistToolsConfig.cmake.in
@@ -89,4 +89,15 @@ endif()
set(Qt5_LRELEASE_EXECUTABLE Qt5::lrelease)
set(Qt5_LUPDATE_EXECUTABLE Qt5::lupdate)
+# Create versionless tool targets.
+foreach(__qt_tool lrelease lupdate lconvert)
+ if(NOT \"${QT_NO_CREATE_VERSIONLESS_TARGETS}\" AND NOT TARGET Qt::${__qt_tool}
+ AND TARGET Qt5::${__qt_tool})
+ add_executable(Qt::${__qt_tool} IMPORTED)
+ get_target_property(__qt_imported_location Qt5::${__qt_tool} IMPORTED_LOCATION)
+ set_target_properties(Qt::${__qt_tool}
+ PROPERTIES IMPORTED_LOCATION \"${__qt_imported_location}\")
+ endif()
+endforeach()
+
include(\"${CMAKE_CURRENT_LIST_DIR}/Qt5LinguistToolsMacros.cmake\")
diff --git a/src/linguist/Qt5LinguistToolsMacros.cmake b/src/linguist/Qt5LinguistToolsMacros.cmake
index 23beeb397..ab271d56a 100644
--- a/src/linguist/Qt5LinguistToolsMacros.cmake
+++ b/src/linguist/Qt5LinguistToolsMacros.cmake
@@ -81,6 +81,17 @@ function(QT5_CREATE_TRANSLATION _qm_files)
set(${_qm_files} ${${_qm_files}} PARENT_SCOPE)
endfunction()
+if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
+ function(qt_create_translation _qm_files)
+ if(QT_DEFAULT_MAJOR_VERSION EQUAL 5)
+ qt5_create_translation("${_qm_files}" ${ARGN})
+ elseif(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_create_translation("${_qm_files}" ${ARGN})
+ endif()
+ set("${_qm_files}" "${${_qm_files}}" PARENT_SCOPE)
+ endfunction()
+endif()
+
function(QT5_ADD_TRANSLATION _qm_files)
set(options)
@@ -112,3 +123,14 @@ function(QT5_ADD_TRANSLATION _qm_files)
endforeach()
set(${_qm_files} ${${_qm_files}} PARENT_SCOPE)
endfunction()
+
+if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
+ function(qt_add_translation _qm_files)
+ if(QT_DEFAULT_MAJOR_VERSION EQUAL 5)
+ qt5_add_translation("${_qm_files}" ${ARGN})
+ elseif(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_add_translation("${_qm_files}" ${ARGN})
+ endif()
+ set("${_qm_files}" "${${_qm_files}}" PARENT_SCOPE)
+ endfunction()
+endif()
diff --git a/src/linguist/linguist/linguist.pro b/src/linguist/linguist/linguist.pro
index af74df8ec..d083896c3 100644
--- a/src/linguist/linguist/linguist.pro
+++ b/src/linguist/linguist/linguist.pro
@@ -1,4 +1,4 @@
-QT += core-private gui-private widgets xml uitools-private printsupport
+QT += core-private gui-private widgets uitools-private printsupport
DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
diff --git a/src/linguist/linguist/mainwindow.cpp b/src/linguist/linguist/mainwindow.cpp
index 35ce52891..26a3d03f2 100644
--- a/src/linguist/linguist/mainwindow.cpp
+++ b/src/linguist/linguist/mainwindow.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -299,7 +299,6 @@ MainWindow::MainWindow()
m_contextDock = new QDockWidget(this);
m_contextDock->setObjectName(QLatin1String("ContextDockWidget"));
m_contextDock->setAllowedAreas(Qt::AllDockWidgetAreas);
- m_contextDock->setFeatures(QDockWidget::AllDockWidgetFeatures);
m_contextDock->setWindowTitle(tr("Context"));
m_contextDock->setAcceptDrops(true);
m_contextDock->installEventFilter(this);
@@ -329,7 +328,6 @@ MainWindow::MainWindow()
m_messagesDock = new QDockWidget(this);
m_messagesDock->setObjectName(QLatin1String("StringsDockWidget"));
m_messagesDock->setAllowedAreas(Qt::AllDockWidgetAreas);
- m_messagesDock->setFeatures(QDockWidget::AllDockWidgetFeatures);
m_messagesDock->setWindowTitle(tr("Strings"));
m_messagesDock->setAcceptDrops(true);
m_messagesDock->installEventFilter(this);
@@ -366,7 +364,6 @@ MainWindow::MainWindow()
m_phrasesDock = new QDockWidget(this);
m_phrasesDock->setObjectName(QLatin1String("PhrasesDockwidget"));
m_phrasesDock->setAllowedAreas(Qt::AllDockWidgetAreas);
- m_phrasesDock->setFeatures(QDockWidget::AllDockWidgetFeatures);
m_phrasesDock->setWindowTitle(tr("Phrases and guesses"));
m_phraseView = new PhraseView(m_dataModel, &m_phraseDict, this);
@@ -376,7 +373,6 @@ MainWindow::MainWindow()
m_sourceAndFormDock = new QDockWidget(this);
m_sourceAndFormDock->setObjectName(QLatin1String("SourceAndFormDock"));
m_sourceAndFormDock->setAllowedAreas(Qt::AllDockWidgetAreas);
- m_sourceAndFormDock->setFeatures(QDockWidget::AllDockWidgetFeatures);
m_sourceAndFormDock->setWindowTitle(tr("Sources and Forms"));
m_sourceAndFormView = new QStackedWidget(this);
m_sourceAndFormDock->setWidget(m_sourceAndFormView);
@@ -391,7 +387,6 @@ MainWindow::MainWindow()
m_errorsDock = new QDockWidget(this);
m_errorsDock->setObjectName(QLatin1String("ErrorsDockWidget"));
m_errorsDock->setAllowedAreas(Qt::AllDockWidgetAreas);
- m_errorsDock->setFeatures(QDockWidget::AllDockWidgetFeatures);
m_errorsDock->setWindowTitle(tr("Warnings"));
m_errorsView = new ErrorsView(m_dataModel, this);
m_errorsDock->setWidget(m_errorsView);
@@ -1360,7 +1355,7 @@ void MainWindow::about()
const QString description
= tr("Qt Linguist is a tool for adding translations to Qt applications.");
const QString copyright
- = tr("Copyright (C) %1 The Qt Company Ltd.").arg(QStringLiteral("2019"));
+ = tr("Copyright (C) %1 The Qt Company Ltd.").arg(QStringLiteral("2020"));
box.setText(QStringLiteral("<center><img src=\":/images/icons/linguist-128-32.png\"/></img><p>%1</p></center>"
"<p>%2</p>"
"<p>%3</p>").arg(version, description, copyright));
diff --git a/src/linguist/linguist/messageeditorwidgets.cpp b/src/linguist/linguist/messageeditorwidgets.cpp
index 6c16b36fd..6880f50fc 100644
--- a/src/linguist/linguist/messageeditorwidgets.cpp
+++ b/src/linguist/linguist/messageeditorwidgets.cpp
@@ -372,7 +372,7 @@ void FormMultiWidget::slotSelectionChanged()
void FormMultiWidget::setTranslation(const QString &text, bool userAction)
{
- QStringList texts = text.split(QChar(Translator::BinaryVariantSeparator), QString::KeepEmptyParts);
+ QStringList texts = text.split(QChar(Translator::BinaryVariantSeparator), Qt::KeepEmptyParts);
while (m_editors.count() > texts.count()) {
delete m_minusButtons.takeLast();
@@ -418,7 +418,7 @@ QString FormMultiWidget::getTranslation() const
for (int i = 0; i < m_editors.count(); ++i) {
if (i)
ret += QChar(Translator::BinaryVariantSeparator);
- ret += toPlainText(m_editors.at(i)->document()->docHandle()->plainText());
+ ret += toPlainText(m_editors.at(i)->document()->toRawText());
}
return ret;
}
diff --git a/src/linguist/linguist/messagemodel.h b/src/linguist/linguist/messagemodel.h
index 5c2d95ec2..32f868d51 100644
--- a/src/linguist/linguist/messagemodel.h
+++ b/src/linguist/linguist/messagemodel.h
@@ -37,8 +37,6 @@
#include <QtCore/QLocale>
#include <QtGui/QColor>
#include <QtGui/QBitmap>
-#include <QtXml/QXmlDefaultHandler>
-
QT_BEGIN_NAMESPACE
diff --git a/src/linguist/linguist/phrase.cpp b/src/linguist/linguist/phrase.cpp
index b7b9835f5..064e79f40 100644
--- a/src/linguist/linguist/phrase.cpp
+++ b/src/linguist/linguist/phrase.cpp
@@ -28,6 +28,7 @@
#include "phrase.h"
#include "translator.h"
+#include "xmlparser.h"
#include <QApplication>
#include <QFile>
@@ -36,9 +37,7 @@
#include <QRegExp>
#include <QTextCodec>
#include <QTextStream>
-#include <QXmlAttributes>
-#include <QXmlDefaultHandler>
-#include <QXmlParseException>
+#include <QXmlStreamReader>
QT_BEGIN_NAMESPACE
@@ -104,24 +103,26 @@ bool operator==(const Phrase &p, const Phrase &q)
p.definition() == q.definition() && p.phraseBook() == q.phraseBook();
}
-class QphHandler : public QXmlDefaultHandler
+class QphHandler : public XmlParser
{
public:
- QphHandler(PhraseBook *phraseBook)
- : pb(phraseBook), ferrorCount(0) { }
-
- virtual bool startElement(const QString &namespaceURI,
- const QString &localName, const QString &qName,
- const QXmlAttributes &atts);
- virtual bool endElement(const QString &namespaceURI,
- const QString &localName, const QString &qName);
- virtual bool characters(const QString &ch);
- virtual bool fatalError(const QXmlParseException &exception);
+ QphHandler(PhraseBook *phraseBook, QXmlStreamReader &reader)
+ : XmlParser(reader), pb(phraseBook), ferrorCount(0)
+ {
+ }
+ ~QphHandler() override = default;
QString language() const { return m_language; }
QString sourceLanguage() const { return m_sourceLanguage; }
private:
+ bool startElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName, const QXmlStreamAttributes &atts) override;
+ bool endElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName) override;
+ bool characters(const QStringRef &ch) override;
+ bool fatalError(qint64 line, qint64 column, const QString &message) override;
+
PhraseBook *pb;
QString source;
QString target;
@@ -133,14 +134,15 @@ private:
int ferrorCount;
};
-bool QphHandler::startElement(const QString & /* namespaceURI */,
- const QString & /* localName */,
- const QString &qName,
- const QXmlAttributes &atts)
+bool QphHandler::startElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName, const QXmlStreamAttributes &atts)
{
+ Q_UNUSED(namespaceURI)
+ Q_UNUSED(localName)
+
if (qName == QLatin1String("QPH")) {
- m_language = atts.value(QLatin1String("language"));
- m_sourceLanguage = atts.value(QLatin1String("sourcelanguage"));
+ m_language = atts.value(QLatin1String("language")).toString();
+ m_sourceLanguage = atts.value(QLatin1String("sourcelanguage")).toString();
} else if (qName == QLatin1String("phrase")) {
source.truncate(0);
target.truncate(0);
@@ -150,10 +152,12 @@ bool QphHandler::startElement(const QString & /* namespaceURI */,
return true;
}
-bool QphHandler::endElement(const QString & /* namespaceURI */,
- const QString & /* localName */,
- const QString &qName)
+bool QphHandler::endElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName)
{
+ Q_UNUSED(namespaceURI)
+ Q_UNUSED(localName)
+
if (qName == QLatin1String("source"))
source = accum;
else if (qName == QLatin1String("target"))
@@ -165,20 +169,20 @@ bool QphHandler::endElement(const QString & /* namespaceURI */,
return true;
}
-bool QphHandler::characters(const QString &ch)
+bool QphHandler::characters(const QStringRef &ch)
{
accum += ch;
return true;
}
-bool QphHandler::fatalError(const QXmlParseException &exception)
+bool QphHandler::fatalError(qint64 line, qint64 column, const QString &message)
{
if (ferrorCount++ == 0) {
QString msg = PhraseBook::tr("Parse error at line %1, column %2 (%3).")
- .arg(exception.lineNumber()).arg(exception.columnNumber())
- .arg(exception.message());
- QMessageBox::information(0,
- QObject::tr("Qt Linguist"), msg);
+ .arg(line)
+ .arg(column)
+ .arg(message);
+ QMessageBox::information(nullptr, QObject::tr("Qt Linguist"), msg);
}
return false;
}
@@ -223,20 +227,10 @@ bool PhraseBook::load(const QString &fileName, bool *langGuessed)
m_fileName = fileName;
- QXmlInputSource in(&f);
- QXmlSimpleReader reader;
- // don't click on these!
- reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), false);
- reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), true);
- reader.setFeature(QLatin1String("http://trolltech.com/xml/features/report-whitespace"
- "-only-CharData"), false);
- QphHandler *hand = new QphHandler(this);
- reader.setContentHandler(hand);
- reader.setErrorHandler(hand);
-
- bool ok = reader.parse(in);
- reader.setContentHandler(0);
- reader.setErrorHandler(0);
+ QXmlStreamReader reader(&f);
+ QphHandler *hand = new QphHandler(this, reader);
+ reader.setNamespaceProcessing(false);
+ bool ok = hand->parse();
Translator::languageAndCountry(hand->language(), &m_language, &m_country);
*langGuessed = false;
diff --git a/src/linguist/lupdate/clangtoolastreader.cpp b/src/linguist/lupdate/clangtoolastreader.cpp
index bd3147838..ceff09523 100644
--- a/src/linguist/lupdate/clangtoolastreader.cpp
+++ b/src/linguist/lupdate/clangtoolastreader.cpp
@@ -25,17 +25,42 @@
** $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
namespace LupdatePrivate
{
+ /*
+ Retrieves the context for the NOOP macros using the context of
+ the NamedDeclaration within which the Macro is.
+ The context is stripped of the function or method part as it used not to be retrieved
+ in the previous cpp parser.
+ */
+ QString contextForNoopMacro(clang::NamedDecl *namedDecl)
+ {
+ QStringList context;
+ const clang::DeclContext *decl = namedDecl->getDeclContext();
+ while (decl) {
+ if (clang::isa<clang::NamedDecl>(decl) && !decl->isFunctionOrMethod()) {
+ if (const auto *namespaceDecl = clang::dyn_cast<clang::NamespaceDecl>(decl)) {
+ context.prepend(namespaceDecl->isAnonymousNamespace()
+ ? QStringLiteral("(anonymous namespace)")
+ : QString::fromStdString(namespaceDecl->getDeclName().getAsString()));
+ } else if (const auto *recordDecl = clang::dyn_cast<clang::RecordDecl>(decl)) {
+ static const QString anonymous = QStringLiteral("(anonymous %1)");
+ context.prepend(recordDecl->getIdentifier()
+ ? QString::fromStdString(recordDecl->getDeclName().getAsString())
+ : anonymous.arg(QLatin1String(recordDecl->getKindName().data())));
+ }
+ }
+ decl = decl->getParent();
+ }
+ return context.join(QStringLiteral("::"));
+ }
+
QString contextForFunctionDecl(clang::FunctionDecl *func, const std::string &funcName)
{
std::string context;
@@ -50,67 +75,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();
@@ -168,6 +132,15 @@ namespace LupdatePrivate
return true;
return false;
}
+
+ bool isPointWithin(const clang::SourceRange &sourceRange, const clang::SourceLocation &point,
+ const clang::SourceManager &sm)
+ {
+ clang::SourceLocation start = sourceRange.getBegin();
+ clang::SourceLocation end = sourceRange.getEnd();
+ return point == start || point == end || (sm.isBeforeInTranslationUnit(start, point)
+ && sm.isBeforeInTranslationUnit(point, end));
+ }
}
/*
@@ -270,7 +243,7 @@ bool LupdateVisitor::VisitCallExpr(clang::CallExpr *callExpression)
qCDebug(lcClang) << "Plural : " << store.lupdatePlural;
break;
}
- m_translationStoresFromAST.push_back(store);
+ m_stores.AST.push_back(store);
return true;
}
@@ -487,227 +460,137 @@ void LupdateVisitor::setInfoFromRawComment(const QString &commentString,
}
}
-/*
- Fill the Translator with the retrieved information after traversing the AST.
-*/
-void LupdateVisitor::fillTranslator()
-{
- for (const auto &store : qAsConst(m_translationStoresFromAST))
- fillTranslator(store);
- // Here also need to fill the translator with the information retrieved from the PreProcessor
-}
-
-void LupdateVisitor::fillTranslator(TranslationRelatedStore store)
-{
- bool forcePlural = false;
- switch (trFunctionAliasManager.trFunctionByName(store.funcName)) {
- case TrFunctionAliasManager::Function_Q_DECLARE_TR_FUNCTIONS:
- // If there is a Q_DECLARE_TR_FUNCTION the context given takes priority
- // over the retrieved context.
- // The retrieved context for Q_DECLARE_TR_FUNCTION (where the macro was)
- // has to fit the start of the retrieved context of the tr function or
- // NOOP macro if there is already a argument giving the context, it has
- // priority.
- //handleDeclareTrFunctions(); // TODO: Implement.
- break;
- case TrFunctionAliasManager::Function_QT_TR_N_NOOP:
- forcePlural = true;
- Q_FALLTHROUGH();
- case TrFunctionAliasManager::Function_tr:
- case TrFunctionAliasManager::Function_trUtf8:
- case TrFunctionAliasManager::Function_QT_TR_NOOP:
- case TrFunctionAliasManager::Function_QT_TR_NOOP_UTF8:
- handleTr(store, forcePlural);
- break;
- case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP:
- case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3:
- forcePlural = true;
- Q_FALLTHROUGH();
- case TrFunctionAliasManager::Function_translate:
- case TrFunctionAliasManager::Function_findMessage:
- 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:
- handleTranslate(store, forcePlural);
- break;
- case TrFunctionAliasManager::Function_QT_TRID_N_NOOP:
- forcePlural = true;
- Q_FALLTHROUGH();
- case TrFunctionAliasManager::Function_qtTrId:
- case TrFunctionAliasManager::Function_QT_TRID_NOOP:
- handleTrId(store, forcePlural);
- break;
- }
-}
-
-TranslatorMessage LupdateVisitor::fillTranslatorMessage(const TranslationRelatedStore &store,
- bool forcePlural, bool isId)
-{
- QString context;
- if (!isId) {
- context = ParserTool::transcode(store.contextArg.isEmpty() ? store.contextRetrieved
- : store.contextArg);
- }
-
- TranslatorMessage msg(context,
- ParserTool::transcode(isId ? store.lupdateSourceWhenId
- : store.lupdateSource),
- ParserTool::transcode(store.lupdateComment),
- QString(),
- store.lupdateLocationFile,
- store.lupdateLocationLine,
- QStringList(),
- TranslatorMessage::Type::Unfinished,
- (forcePlural ? forcePlural : !store.lupdatePlural.isEmpty()));
-
- if (!store.lupdateAllMagicMetaData.empty())
- msg.setExtras(store.lupdateAllMagicMetaData);
- msg.setExtraComment(ParserTool::transcode(store.lupdateExtraComment));
- return msg;
-}
-
-void LupdateVisitor::handleTranslate(const TranslationRelatedStore &store, bool forcePlural)
-{
- if (!store.lupdateSourceWhenId.isEmpty())
- qCDebug(lcClang) << "//% is ignored when using translate function\n";
-
- TranslatorMessage msg = fillTranslatorMessage(store, forcePlural);
- msg.setId(ParserTool::transcode(store.lupdateIdMetaData)); // //= NOT to be used with qTrId
- m_tor->append(msg);
-}
-
-void LupdateVisitor::handleTr(const TranslationRelatedStore &store, bool forcePlural)
-{
- if (!store.lupdateSourceWhenId.isEmpty())
- qCDebug(lcClang) << "//% is ignored when using tr function\n";
- if (store.contextRetrieved.isEmpty() && store.contextArg.isEmpty()) {
- qCDebug(lcClang) << "tr() cannot be called without context \n";
- return;
- }
-
- TranslatorMessage msg = fillTranslatorMessage(store, forcePlural);
- msg.setId(ParserTool::transcode(store.lupdateIdMetaData)); // //= NOT to be used with qTrId
- m_tor->append(msg);
-}
-
-void LupdateVisitor::handleTrId(const TranslationRelatedStore &store, bool forcePlural)
-{
- if (!store.lupdateIdMetaData.isEmpty())
- qCDebug(lcClang) << "//= is ignored when using qtTrId function \n";
-
- TranslatorMessage msg = fillTranslatorMessage(store, forcePlural, true);
- msg.setId(ParserTool::transcode(store.lupdateId));
- m_tor->append(msg);
-}
-
void LupdateVisitor::processPreprocessorCalls()
{
- for (const auto &store : qAsConst(m_translationStoresFromPP))
+ m_macro = (m_stores.Preprocessor.size() > 0);
+ for (const auto &store : qAsConst(m_stores.Preprocessor))
processPreprocessorCall(store);
}
void LupdateVisitor::processPreprocessorCall(TranslationRelatedStore store)
{
- const std::vector<QString> rawComments = rawCommentsFromSourceLocation(store.callLocation);
+ const std::vector<QString> rawComments = rawCommentsFromSourceLocation(store
+ .callLocation(m_context->getSourceManager()));
for (const auto &rawComment : rawComments)
setInfoFromRawComment(rawComment, &store);
if (store.isValid()) {
if (store.funcName.contains(QStringLiteral("Q_DECLARE_TR_FUNCTIONS")))
- m_qDeclateTrFunctionContext.push_back(store);
+ m_qDeclareTrMacroAll.push_back(store);
else
- m_noopTranslationStores.push_back(store);
+ m_noopTranslationMacroAll.push_back(store);
store.printStore();
}
}
-void LupdatePPCallbacks::MacroExpands(const clang::Token &macroNameTok,
- const clang::MacroDefinition &macroDefinition, clang::SourceRange range,
- const clang::MacroArgs *args)
+bool LupdateVisitor::VisitNamedDecl(clang::NamedDecl *namedDeclaration)
{
- if (!args)
- return;
- const auto &sm = m_preprocessor.getSourceManager();
- llvm::StringRef fileName = sm.getFilename(range.getBegin());
- if (fileName != m_inputFile)
- return;
+ if (!m_macro)
+ return true;
+ auto fullLocation = m_context->getFullLoc(namedDeclaration->getBeginLoc());
+ if (!fullLocation.isValid() || !fullLocation.getFileEntry())
+ return true;
+
+ if (fullLocation.getFileEntry()->getName() != m_inputFile)
+ return true;
+
+ qCDebug(lcClang) << "NamedDecl Name: " << namedDeclaration->getQualifiedNameAsString();
+ qCDebug(lcClang) << "NamedDecl source: " << namedDeclaration->getSourceRange().printToString(
+ m_context->getSourceManager());
+ // Checks if there is a macro located within the range of this NamedDeclaration
+ // in order to find a context for the macro
+ findContextForTranslationStoresFromPP(namedDeclaration);
+ return true;
+}
- 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;
+void LupdateVisitor::findContextForTranslationStoresFromPP(clang::NamedDecl *namedDeclaration)
+{
+ qCDebug(lcClang) << "=================findContextForTranslationStoresFromPP===================";
+ qCDebug(lcClang) << "m_noopTranslationMacroAll " << m_noopTranslationMacroAll.size();
+ qCDebug(lcClang) << "m_qDeclareTrMacroAll " << m_qDeclareTrMacroAll.size();
+ clang::SourceManager &sm = m_context->getSourceManager();
+
+ // Looking for NOOP context only in the input file
+ // because we are not interested in the NOOP from all related file
+ // Once QT_TR_NOOP are gone this step can be removes because the only
+ // QT_...NOOP left will have an context as argument
+ for (TranslationRelatedStore &store : m_noopTranslationMacroAll) {
+ if (!store.contextArg.isEmpty())
+ continue;
+ clang::SourceLocation sourceLoc = store.callLocation(sm);
+ if (!sourceLoc.isValid())
+ continue;
+ if (LupdatePrivate::isPointWithin(namedDeclaration->getSourceRange(), sourceLoc, sm)) {
+ /*
+ void N3::C1::C12::C121::f2()
+ {
+ const char test_NOOP[] = QT_TR_NOOP("A QT_TR_NOOP N3::C1::C13");
+ }
+ In such case namedDeclaration->getQualifiedNameAsString() will give only
+ test_NOOP as context.
+ This is why the following function is needed
+ */
+ store.contextRetrievedTempNOOP = LupdatePrivate::contextForNoopMacro(namedDeclaration);
+ qCDebug(lcClang) << "------------------------------------------NOOP Macro in range ---";
+ qCDebug(lcClang) << "Range " << namedDeclaration->getSourceRange().printToString(sm);
+ qCDebug(lcClang) << "Point " << sourceLoc.printToString(sm);
+ qCDebug(lcClang) << "=========== Visit Named Declaration =============================";
+ qCDebug(lcClang) << " Declaration Location " <<
+ namedDeclaration->getSourceRange().printToString(sm);
+ qCDebug(lcClang) << " Macro Location "
+ << sourceLoc.printToString(sm);
+ qCDebug(lcClang) << " Context namedDeclaration->getQualifiedNameAsString() "
+ << namedDeclaration->getQualifiedNameAsString();
+ qCDebug(lcClang) << " Context LupdatePrivate::contextForNoopMacro "
+ << store.contextRetrievedTempNOOP;
+ qCDebug(lcClang) << " Context Retrieved " << store.contextRetrievedTempNOOP;
+ qCDebug(lcClang) << "=================================================================";
+ store.printStore();
+ }
}
- 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));
+ for (TranslationRelatedStore &store : m_qDeclareTrMacroAll) {
+ clang::SourceLocation sourceLoc = store.callLocation(sm);
+ if (!sourceLoc.isValid())
+ continue;
+ if (LupdatePrivate::isPointWithin(namedDeclaration->getSourceRange(), sourceLoc, sm)) {
+ store.contextRetrieved = QString::fromStdString(
+ namedDeclaration->getQualifiedNameAsString());
+ qCDebug(lcClang) << "------------------------------------------DECL Macro in range ---";
+ qCDebug(lcClang) << "Range " << namedDeclaration->getSourceRange().printToString(sm);
+ qCDebug(lcClang) << "Point " << sourceLoc.printToString(sm);
+ qCDebug(lcClang) << "=========== Visit Named Declaration =============================";
+ qCDebug(lcClang) << " Declaration Location " <<
+ namedDeclaration->getSourceRange().printToString(sm);
+ qCDebug(lcClang) << " Macro Location "
+ << sourceLoc.printToString(sm);
+ qCDebug(lcClang) << " Context namedDeclaration->getQualifiedNameAsString() "
+ << store.contextRetrieved;
+ qCDebug(lcClang) << " Context LupdatePrivate::contextForNoopMacro "
+ << LupdatePrivate::contextForNoopMacro(namedDeclaration);
+ qCDebug(lcClang) << " Context Retrieved " << store.contextRetrieved;
+ qCDebug(lcClang) << "=================================================================";
+ store.printStore();
}
- arguments[i] = temp;
}
- storeMacroArguments(arguments, &store);
- if (store.isValid())
- m_translationStores.push_back(store);
}
-void LupdatePPCallbacks::storeMacroArguments(const std::vector<QString> &args,
- TranslationRelatedStore *store)
+void LupdateVisitor::generateOuput()
{
- 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;
+ qCDebug(lcClang) << "===================generateOuput============================";
+
+ for (TranslationRelatedStore &store : m_noopTranslationMacroAll) {
+ if (store.contextRetrievedTempNOOP.isEmpty() && store.contextArg.isEmpty())
+ continue;
+ // only fill if a context has been retrieved in the file we're currently visiting
+ m_stores.QNoopTranlsationWithContext.push_back(store);
+ }
+
+ for (TranslationRelatedStore &store : m_qDeclareTrMacroAll) {
+ if (store.contextRetrieved.isEmpty())
+ continue;
+ // only fill if a context has been retrieved in the file we're currently visiting
+ m_stores.QDeclareTrWithContext.push_back(store);
}
}
diff --git a/src/linguist/lupdate/clangtoolastreader.h b/src/linguist/lupdate/clangtoolastreader.h
index d7f6fce3b..8f9a54565 100644
--- a/src/linguist/lupdate/clangtoolastreader.h
+++ b/src/linguist/lupdate/clangtoolastreader.h
@@ -29,148 +29,47 @@
#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),
- m_tor(tor)
+ explicit LupdateVisitor(clang::ASTContext *context, Stores &stores)
+ : m_context(context)
+ , m_stores(stores)
{
m_inputFile = m_context->getSourceManager().getFileEntryForID(
m_context->getSourceManager().getMainFileID())->getName();
}
bool VisitCallExpr(clang::CallExpr *callExpression);
- void fillTranslator();
void processPreprocessorCalls();
+ bool VisitNamedDecl(clang::NamedDecl *namedDeclaration);
+ void findContextForTranslationStoresFromPP(clang::NamedDecl *namedDeclaration);
+ void generateOuput();
private:
std::vector<QString> rawCommentsForCallExpr(const clang::CallExpr *callExpr) const;
@@ -178,30 +77,24 @@ private:
void setInfoFromRawComment(const QString &commentString, TranslationRelatedStore *store);
- void fillTranslator(TranslationRelatedStore store);
- TranslatorMessage fillTranslatorMessage(const TranslationRelatedStore &store,
- bool forcePlural, bool isID = false);
- void handleTr(const TranslationRelatedStore &store, bool forcePlural);
- void handleTrId(const TranslationRelatedStore &store, bool forcePlural);
- void handleTranslate(const TranslationRelatedStore &store, bool forcePlural);
-
void processPreprocessorCall(TranslationRelatedStore store);
clang::ASTContext *m_context { nullptr };
Translator *m_tor { nullptr };
std::string m_inputFile;
- TranslationStores m_translationStoresFromAST;
- TranslationStores m_qDeclateTrFunctionContext;
- TranslationStores m_noopTranslationStores;
- TranslationStores m_translationStoresFromPP;
+ Stores &m_stores;
+
+ TranslationStores m_qDeclareTrMacroAll;
+ TranslationStores m_noopTranslationMacroAll;
+ bool m_macro = false;
};
class LupdateASTConsumer : public clang::ASTConsumer
{
public:
- explicit LupdateASTConsumer(clang::ASTContext *context, Translator *tor)
- : m_visitor(context, tor)
+ explicit LupdateASTConsumer(clang::ASTContext *context, Stores &stores)
+ : m_visitor(context, stores)
{}
// This method is called when the ASTs for entire translation unit have been
@@ -211,12 +104,7 @@ public:
m_visitor.processPreprocessorCalls();
bool traverse = m_visitor.TraverseAST(context);
qCDebug(lcClang) << "TraverseAST: " << traverse;
- m_visitor.fillTranslator();
- }
-
- TranslationStores &preprocessorStores()
- {
- return m_visitor.m_translationStoresFromPP;
+ m_visitor.generateOuput();
}
private:
@@ -226,47 +114,42 @@ private:
class LupdateFrontendAction : public clang::ASTFrontendAction
{
public:
- LupdateFrontendAction(Translator *tor)
- : m_tor(tor)
+ LupdateFrontendAction(Stores &outputStoresWithContext)
+ : m_stores(outputStoresWithContext)
{}
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
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));
-
+ auto consumer = new LupdateASTConsumer(&compiler.getASTContext(), m_stores);
return std::unique_ptr<clang::ASTConsumer>(consumer);
}
private:
- Translator *m_tor { nullptr };
+ Stores &m_stores;
};
class LupdateToolActionFactory : public clang::tooling::FrontendActionFactory
{
public:
- LupdateToolActionFactory(Translator *tor)
- : m_tor(tor)
+ LupdateToolActionFactory(Stores &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<LupdateFrontendAction>(m_tor);
+ return std::make_unique<LupdateFrontendAction>(m_stores);
}
#else
clang::FrontendAction *create() override
{
- return new LupdateFrontendAction(m_tor);
+ return new LupdateFrontendAction(m_stores);
}
#endif
private:
- Translator *m_tor { nullptr };
+ Stores &m_stores;
};
QT_END_NAMESPACE
diff --git a/src/linguist/lupdate/cpp_clang.cpp b/src/linguist/lupdate/cpp_clang.cpp
index 598ac94ca..fc11253bd 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>
@@ -66,8 +68,6 @@ void ClangCppParser::loadCPP(Translator &translator, const QStringList &filename
for (const QString &filename : filenames)
sources.push_back(filename.toStdString());
- // The ClangTool is to be created and run from this function.
-
int argc = 4;
// NEED 2 empty one to start!!! otherwise: LLVM::ERROR
const QByteArray jsonPath = cd.m_compileCommandsPath.toLocal8Bit();
@@ -77,11 +77,12 @@ void ClangCppParser::loadCPP(Translator &translator, const QStringList &filename
clang::tooling::ClangTool tool(OptionsParser.getCompilations(), sources);
tool.appendArgumentsAdjuster(getClangArgumentAdjuster());
- Translator *tor = new Translator();
+ Stores stores;
+ tool.run(new LupdatePreprocessorActionFactory(stores.Preprocessor));
+ tool.run(new LupdateToolActionFactory(stores));
- // A ClangTool needs a new FrontendAction for each translation unit it runs on
- // A Customized FrontendActionFactory is building a customized FrondendAction
- tool.run(new LupdateToolActionFactory(tor));
+ Translator *tor = new Translator();
+ ClangCppParser::fillTranslator(tor, stores);
if (QLoggingCategory("qt.lupdate.clang").isDebugEnabled())
tor->dump();
@@ -90,4 +91,170 @@ void ClangCppParser::loadCPP(Translator &translator, const QStringList &filename
translator.extend(msg, cd);
}
+/*
+ Fill the Translator with the retrieved information after traversing the AST.
+*/
+void ClangCppParser::fillTranslator(Translator *tor, Stores &stores)
+{
+ correctAstTranslationContext(stores);
+ for (auto &store : stores.AST)
+ fillTranslator(tor, store);
+
+ correctNoopTanslationContext(stores);
+ for (auto &store : stores.QNoopTranlsationWithContext)
+ fillTranslator(tor, store);
+}
+
+void ClangCppParser::fillTranslator(Translator *tor, TranslationRelatedStore store)
+{
+ bool plural = false;
+ switch (trFunctionAliasManager.trFunctionByName(store.funcName)) {
+ case TrFunctionAliasManager::Function_Q_DECLARE_TR_FUNCTIONS:
+ break;
+ case TrFunctionAliasManager::Function_QT_TR_N_NOOP:
+ plural = true;
+ Q_FALLTHROUGH();
+ case TrFunctionAliasManager::Function_tr:
+ case TrFunctionAliasManager::Function_trUtf8:
+ case TrFunctionAliasManager::Function_QT_TR_NOOP:
+ case TrFunctionAliasManager::Function_QT_TR_NOOP_UTF8:
+ handleTr(tor, store, plural);
+ break;
+ case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP:
+ case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3:
+ plural = true;
+ Q_FALLTHROUGH();
+ case TrFunctionAliasManager::Function_translate:
+ case TrFunctionAliasManager::Function_findMessage:
+ 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:
+ handleTranslate(tor, store, plural);
+ break;
+ case TrFunctionAliasManager::Function_QT_TRID_N_NOOP:
+ plural = true;
+ Q_FALLTHROUGH();
+ case TrFunctionAliasManager::Function_qtTrId:
+ case TrFunctionAliasManager::Function_QT_TRID_NOOP:
+ handleTrId(tor, store, plural);
+ break;
+ }
+}
+
+TranslatorMessage ClangCppParser::fillTranslatorMessage(const TranslationRelatedStore &store,
+ const QString &id, bool plural, bool isId)
+{
+ QString context;
+ if (!isId) {
+ context = ParserTool::transcode(store.contextArg.isEmpty() ? store.contextRetrieved
+ : store.contextArg);
+ }
+
+ TranslatorMessage msg(context,
+ ParserTool::transcode(isId ? store.lupdateSourceWhenId
+ : store.lupdateSource),
+ ParserTool::transcode(store.lupdateComment),
+ QString(),
+ store.lupdateLocationFile,
+ store.lupdateLocationLine,
+ QStringList(),
+ TranslatorMessage::Type::Unfinished,
+ (plural ? plural : !store.lupdatePlural.isEmpty()));
+
+ if (!store.lupdateAllMagicMetaData.empty())
+ msg.setExtras(store.lupdateAllMagicMetaData);
+ msg.setExtraComment(ParserTool::transcode(store.lupdateExtraComment));
+ msg.setId(ParserTool::transcode(id));
+ return msg;
+}
+
+void ClangCppParser::handleTranslate(Translator *tor, const TranslationRelatedStore &store,
+ bool plural)
+{
+ if (!store.lupdateSourceWhenId.isEmpty())
+ qCDebug(lcClang) << "//% is ignored when using translate function\n";
+ tor->append(fillTranslatorMessage(store, store.lupdateIdMetaData, plural, false));
+}
+
+void ClangCppParser::handleTr(Translator *tor, const TranslationRelatedStore &store, bool plural)
+{
+ if (!store.lupdateSourceWhenId.isEmpty())
+ qCDebug(lcClang) << "//% is ignored when using tr function\n";
+ if (store.contextRetrieved.isEmpty() && store.contextArg.isEmpty()) {
+ qCDebug(lcClang) << "tr() cannot be called without context \n";
+ return;
+ }
+ tor->append(fillTranslatorMessage(store, store.lupdateIdMetaData, plural, false));
+}
+
+void ClangCppParser::handleTrId(Translator *tor, const TranslationRelatedStore &store, bool plural)
+{
+ if (!store.lupdateIdMetaData.isEmpty())
+ qCDebug(lcClang) << "//= is ignored when using qtTrId function \n";
+ tor->append(fillTranslatorMessage(store, store.lupdateId, plural, true));
+}
+
+void ClangCppParser::correctAstTranslationContext(Stores &stores)
+{
+ for (auto &store : stores.AST) {
+ if (!store.contextArg.isEmpty())
+ continue;
+
+ // If there is a Q_DECLARE_TR_FUNCTION the context given there takes
+ // priority over the retrieved context. The retrieved context for
+ // Q_DECLARE_TR_FUNCTION (where the macro was) has to fit the retrieved
+ // context of the tr function if there is already a argument giving the
+ // context, it has priority
+ for (auto &declareStore : stores.QDeclareTrWithContext) {
+ qCDebug(lcClang) << "----------------------------";
+ qCDebug(lcClang) << "Tr call context retrieved " << store.contextRetrieved;
+ qCDebug(lcClang) << "Tr call source " << store.lupdateSource;
+ qCDebug(lcClang) << "- DECLARE context retrieved " << declareStore.contextRetrieved;
+ qCDebug(lcClang) << "- DECLARE context Arg " << declareStore.contextArg;
+ if (declareStore.contextRetrieved.isEmpty())
+ continue;
+ if (!declareStore.contextRetrieved.startsWith(store.contextRetrieved))
+ continue;
+ if (store.contextRetrieved.size() == declareStore.contextRetrieved.size()) {
+ qCDebug(lcClang) << "* Tr call context retrieved " << store.contextRetrieved;
+ qCDebug(lcClang) << "* DECLARE context retrieved " << declareStore.contextRetrieved;
+ qCDebug(lcClang) << "* DECLARE context Arg " << declareStore.contextArg;
+ store.contextArg = declareStore.contextArg;
+ }
+ }
+ }
+}
+
+void ClangCppParser::correctNoopTanslationContext(Stores &stores)
+{
+ for (auto &store : stores.QNoopTranlsationWithContext) {
+ if (!store.contextArg.isEmpty())
+ continue;
+ qCDebug(lcClang) << "----------------------------";
+ qCDebug(lcClang) << "NOOP call context retrieved Temp" << store.contextRetrievedTempNOOP;
+ qCDebug(lcClang) << "NOOP call source " << store.lupdateSource;
+
+ for (const auto &qDeclare : stores.QDeclareTrWithContext) {
+ bool firstCheck = false;
+ bool secondCheck = false;
+ qCDebug(lcClang) << "- DECLARE context retrieved " << qDeclare.contextRetrieved;
+ qCDebug(lcClang) << "- DECLARE context Arg " << qDeclare.contextArg;
+ if (store.contextRetrievedTempNOOP.startsWith(qDeclare.contextRetrieved)) {
+ firstCheck = (store.contextRetrievedTempNOOP.size() == qDeclare.contextRetrieved.size()
+ || (store.contextRetrievedTempNOOP.at(qDeclare.contextRetrieved.size() + 1)
+ == QLatin1Char(':')));
+ secondCheck = qDeclare.contextRetrieved.size() > store.contextRetrieved.size();
+ if (firstCheck && secondCheck) {
+ store.contextRetrieved = qDeclare.contextRetrieved;
+ store.contextArg = qDeclare.contextArg;
+ qCDebug(lcClang) << "* NOOP call context retrieved " << store.contextRetrieved;
+ qCDebug(lcClang) << "* DECLARE context retrieved " << qDeclare.contextRetrieved;
+ qCDebug(lcClang) << "* DECLARE context Arg " << qDeclare.contextArg;
+ }
+ }
+ }
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/linguist/lupdate/cpp_clang.h b/src/linguist/lupdate/cpp_clang.h
index 16e03fb70..eabf2d830 100644
--- a/src/linguist/lupdate/cpp_clang.h
+++ b/src/linguist/lupdate/cpp_clang.h
@@ -29,16 +29,199 @@
#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>
+#include <clang/Basic/SourceManager.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 contextRetrievedTempNOOP;
+ 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 sourceLocation;
+
+ bool isValid() const
+ {
+ return !lupdateLocationFile.isEmpty() && (lupdateLocationLine > -1) && (locationCol > -1);
+ }
+
+ clang::SourceLocation callLocation(const clang::SourceManager &sourceManager)
+ {
+ if (sourceLocation.isInvalid()) {
+ auto sourceFile = sourceManager.getFileManager()
+ .getFile(lupdateLocationFile.toStdString());
+#if (LUPDATE_CLANG_VERSION >= LUPDATE_CLANG_VERSION_CHECK(10,0,0))
+ sourceLocation = sourceManager.translateFileLineCol(sourceFile.get(),
+ lupdateLocationLine, locationCol);
+#else
+ sourceLocation = sourceManager.translateFileLineCol(sourceFile, lupdateLocationLine,
+ locationCol);
+#endif
+ }
+ return sourceLocation;
+ }
+
+ 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);
+
+ void fillTranslator(Translator *tor, Stores &stores);
+ void fillTranslator(Translator *tor, TranslationRelatedStore store);
+
+ TranslatorMessage fillTranslatorMessage(const TranslationRelatedStore &store,
+ const QString &id, bool plural, bool isID);
+
+ void handleTr(Translator *tor, const TranslationRelatedStore &store, bool plural);
+ void handleTrId(Translator *tor, const TranslationRelatedStore &store, bool plural);
+ void handleTranslate(Translator *tor, const TranslationRelatedStore &store, bool plural);
+
+ void correctAstTranslationContext(Stores &stores);
+ void correctNoopTanslationContext(Stores &stores);
}
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..feba49942
--- /dev/null
+++ b/src/linguist/lupdate/lupdatepreprocessoraction.cpp
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** 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());
+
+ 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
diff --git a/src/linguist/lupdate/main.cpp b/src/linguist/lupdate/main.cpp
index 6c4e3db9c..3a3ff76a8 100644
--- a/src/linguist/lupdate/main.cpp
+++ b/src/linguist/lupdate/main.cpp
@@ -299,7 +299,7 @@ static void printUsage()
static bool handleTrFunctionAliases(const QString &arg)
{
- foreach (const QString &pair, arg.split(QLatin1Char(','), QString::SkipEmptyParts)) {
+ foreach (const QString &pair, arg.split(QLatin1Char(','), Qt::SkipEmptyParts)) {
const int equalSign = pair.indexOf(QLatin1Char('='));
if (equalSign < 0) {
printErr(LU::tr("tr-function mapping '%1' in -tr-function-alias is missing the '='.\n").arg(pair));
diff --git a/src/linguist/lupdate/qdeclarative.cpp b/src/linguist/lupdate/qdeclarative.cpp
index 7a453aa32..7d995ae86 100644
--- a/src/linguist/lupdate/qdeclarative.cpp
+++ b/src/linguist/lupdate/qdeclarative.cpp
@@ -39,6 +39,7 @@
#include <private/qqmljslexer_p.h>
#include <private/qqmljsastvisitor_p.h>
#include <private/qqmljsast_p.h>
+#include <private/qqmlapiversion_p.h>
#include <QCoreApplication>
#include <QFile>
@@ -52,6 +53,12 @@
QT_BEGIN_NAMESPACE
+#if Q_QML_PRIVATE_API_VERSION < 8
+namespace QQmlJS {
+ using SourceLocation = AST::SourceLocation;
+}
+#endif
+
using namespace QQmlJS;
static QString MagicComment(QLatin1String("TRANSLATOR"));
@@ -229,7 +236,7 @@ private:
void processComments(quint32 offset, bool flush = false);
- void processComment(const AST::SourceLocation &loc);
+ void processComment(const SourceLocation &loc);
void consumeComment();
bool createString(AST::ExpressionNode *ast, QString *out)
@@ -259,7 +266,7 @@ private:
TranslatorMessage::ExtraData extra;
QString sourcetext;
QString trcontext;
- QList<AST::SourceLocation> m_todo;
+ QList<SourceLocation> m_todo;
};
QString createErrorString(const QString &filename, const QString &code, Parser &parser)
@@ -274,7 +281,7 @@ QString createErrorString(const QString &filename, const QString &code, Parser &
if (m.isWarning())
continue;
-#if Q_QML_PRIVATE_API_VERSION < 5
+#if Q_QML_PRIVATE_API_VERSION >= 8
const int line = m.loc.startLine;
const int column = m.loc.startColumn;
#else
@@ -315,7 +322,7 @@ void FindTrCalls::postVisit(AST::Node *node)
void FindTrCalls::processComments(quint32 offset, bool flush)
{
for (; !m_todo.isEmpty(); m_todo.removeFirst()) {
- AST::SourceLocation loc = m_todo.first();
+ SourceLocation loc = m_todo.first();
if (! flush && (loc.begin() >= offset))
break;
@@ -332,7 +339,7 @@ void FindTrCalls::consumeComment()
sourcetext.clear();
}
-void FindTrCalls::processComment(const AST::SourceLocation &loc)
+void FindTrCalls::processComment(const SourceLocation &loc)
{
if (!loc.length)
return;
diff --git a/src/linguist/lupdate/ui.cpp b/src/linguist/lupdate/ui.cpp
index ce4ecc045..f91ffac5b 100644
--- a/src/linguist/lupdate/ui.cpp
+++ b/src/linguist/lupdate/ui.cpp
@@ -29,40 +29,41 @@
#include "lupdate.h"
#include <translator.h>
+#include <xmlparser.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QString>
-
-#include <QtXml/QXmlAttributes>
-#include <QtXml/QXmlDefaultHandler>
-#include <QtXml/QXmlLocator>
-#include <QtXml/QXmlParseException>
-
+#include <QtCore/QXmlStreamReader>
QT_BEGIN_NAMESPACE
-class UiReader : public QXmlDefaultHandler
+class UiReader : public XmlParser
{
public:
- UiReader(Translator &translator, ConversionData &cd)
- : m_translator(translator), m_cd(cd), m_lineNumber(-1), m_isTrString(false),
- m_insideStringList(false), m_idBasedTranslations(false)
- {}
-
- bool startElement(const QString &namespaceURI, const QString &localName,
- const QString &qName, const QXmlAttributes &atts);
- bool endElement(const QString &namespaceURI, const QString &localName,
- const QString &qName);
- bool characters(const QString &ch);
- bool fatalError(const QXmlParseException &exception);
-
- void setDocumentLocator(QXmlLocator *locator) { m_locator = locator; }
+ UiReader(Translator &translator, ConversionData &cd, QXmlStreamReader &reader)
+ : XmlParser(reader),
+ m_translator(translator),
+ m_cd(cd),
+ m_lineNumber(-1),
+ m_isTrString(false),
+ m_insideStringList(false),
+ m_idBasedTranslations(false)
+ {
+ }
+ ~UiReader() override = default;
private:
+ bool startElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName, const QXmlStreamAttributes &atts) override;
+ bool endElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName) override;
+ bool characters(const QStringRef &ch) override;
+ bool fatalError(qint64 line, qint64 column, const QString &message) override;
+
void flush();
- void readTranslationAttributes(const QXmlAttributes &atts);
+ void readTranslationAttributes(const QXmlStreamAttributes &atts);
Translator &m_translator;
ConversionData &m_cd;
@@ -71,7 +72,6 @@ private:
QString m_comment;
QString m_extracomment;
QString m_id;
- QXmlLocator *m_locator;
QString m_accum;
int m_lineNumber;
@@ -80,8 +80,8 @@ private:
bool m_idBasedTranslations;
};
-bool UiReader::startElement(const QString &namespaceURI,
- const QString &localName, const QString &qName, const QXmlAttributes &atts)
+bool UiReader::startElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName, const QXmlStreamAttributes &atts)
{
Q_UNUSED(namespaceURI);
Q_UNUSED(localName);
@@ -95,16 +95,16 @@ bool UiReader::startElement(const QString &namespaceURI,
m_insideStringList = true;
readTranslationAttributes(atts);
} else if (qName == QLatin1String("ui")) { // UI "header"
- const int translationTypeIndex = atts.index(QStringLiteral("idbasedtr"));
- m_idBasedTranslations = translationTypeIndex >= 0
- && atts.value(translationTypeIndex) == QLatin1String("true");
+ const auto attr = QStringLiteral("idbasedtr");
+ m_idBasedTranslations =
+ atts.hasAttribute(attr) && atts.value(attr) == QLatin1String("true");
}
m_accum.clear();
return true;
}
-bool UiReader::endElement(const QString &namespaceURI,
- const QString &localName, const QString &qName)
+bool UiReader::endElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName)
{
Q_UNUSED(namespaceURI);
Q_UNUSED(localName);
@@ -127,17 +127,18 @@ bool UiReader::endElement(const QString &namespaceURI,
return true;
}
-bool UiReader::characters(const QString &ch)
+bool UiReader::characters(const QStringRef &ch)
{
- m_accum += ch;
+ m_accum += ch.toString();
return true;
}
-bool UiReader::fatalError(const QXmlParseException &exception)
+bool UiReader::fatalError(qint64 line, qint64 column, const QString &message)
{
QString msg = LU::tr("XML error: Parse error at line %1, column %2 (%3).")
- .arg(exception.lineNumber()).arg(exception.columnNumber())
- .arg(exception.message());
+ .arg(line)
+ .arg(column)
+ .arg(message);
m_cd.appendError(msg);
return false;
}
@@ -160,17 +161,17 @@ void UiReader::flush()
}
}
-void UiReader::readTranslationAttributes(const QXmlAttributes &atts)
+void UiReader::readTranslationAttributes(const QXmlStreamAttributes &atts)
{
- const QString notr = atts.value(QStringLiteral("notr"));
+ const auto notr = atts.value(QStringLiteral("notr"));
if (notr.isEmpty() || notr != QStringLiteral("true")) {
m_isTrString = true;
- m_comment = atts.value(QStringLiteral("comment"));
- m_extracomment = atts.value(QStringLiteral("extracomment"));
+ m_comment = atts.value(QStringLiteral("comment")).toString();
+ m_extracomment = atts.value(QStringLiteral("extracomment")).toString();
if (m_idBasedTranslations)
- m_id = atts.value(QStringLiteral("id"));
+ m_id = atts.value(QStringLiteral("id")).toString();
if (!m_cd.m_noUiLines)
- m_lineNumber = m_locator->lineNumber();
+ m_lineNumber = static_cast<int>(reader.lineNumber());
} else {
m_isTrString = false;
}
@@ -184,20 +185,14 @@ bool loadUI(Translator &translator, const QString &filename, ConversionData &cd)
cd.appendError(LU::tr("Cannot open %1: %2").arg(filename, file.errorString()));
return false;
}
- QXmlInputSource in(&file);
- QXmlSimpleReader reader;
- reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), false);
- reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), true);
- reader.setFeature(QLatin1String(
- "http://trolltech.com/xml/features/report-whitespace-only-CharData"), false);
- UiReader handler(translator, cd);
- reader.setContentHandler(&handler);
- reader.setErrorHandler(&handler);
- bool result = reader.parse(in);
+
+ QXmlStreamReader reader(&file);
+ reader.setNamespaceProcessing(false);
+
+ UiReader uiReader(translator, cd, reader);
+ bool result = uiReader.parse();
if (!result)
cd.appendError(LU::tr("Parse error in UI file"));
- reader.setContentHandler(0);
- reader.setErrorHandler(0);
return result;
}
diff --git a/src/linguist/shared/formats.pri b/src/linguist/shared/formats.pri
index e5f388f2a..876fe50cc 100644
--- a/src/linguist/shared/formats.pri
+++ b/src/linguist/shared/formats.pri
@@ -1,17 +1,15 @@
-
-# infrastructure
-QT *= xml
-
INCLUDEPATH *= $$PWD
SOURCES += \
$$PWD/numerus.cpp \
$$PWD/translator.cpp \
- $$PWD/translatormessage.cpp
+ $$PWD/translatormessage.cpp \
+ $$PWD/xmlparser.cpp
HEADERS += \
$$PWD/translator.h \
- $$PWD/translatormessage.h
+ $$PWD/translatormessage.h \
+ $$PWD/xmlparser.h
# "real" formats readers and writers
SOURCES += \
diff --git a/src/linguist/shared/po.cpp b/src/linguist/shared/po.cpp
index 19ba12b96..69062b772 100644
--- a/src/linguist/shared/po.cpp
+++ b/src/linguist/shared/po.cpp
@@ -548,7 +548,7 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
QString xrefs;
foreach (const QString &ref,
codec->toUnicode(item.references).split(
- QRegExp(QLatin1String("\\s")), QString::SkipEmptyParts)) {
+ QRegExp(QLatin1String("\\s")), Qt::SkipEmptyParts)) {
int pos = ref.indexOf(QLatin1Char(':'));
int lpos = ref.lastIndexOf(QLatin1Char(':'));
if (pos != -1 && pos == lpos) {
@@ -607,7 +607,7 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd)
case ',': {
QStringList flags =
QString::fromLatin1(line.mid(2)).split(
- QRegExp(QLatin1String("[, ]")), QString::SkipEmptyParts);
+ QRegExp(QLatin1String("[, ]")), Qt::SkipEmptyParts);
if (flags.removeOne(QLatin1String("fuzzy")))
item.isFuzzy = true;
flags.removeOne(QLatin1String("qt-format"));
@@ -750,7 +750,7 @@ bool savePO(const Translator &translator, QIODevice &dev, ConversionData &)
out << "msgid \"\"\n";
Translator::ExtraData headers = translator.extras();
QStringList hdrOrder = translator.extra(QLatin1String("po-headers")).split(
- QLatin1Char(','), QString::SkipEmptyParts);
+ QLatin1Char(','), Qt::SkipEmptyParts);
// Keep in sync with loadPO
addPoHeader(headers, hdrOrder, "MIME-Version", QLatin1String("1.0"));
addPoHeader(headers, hdrOrder, "Content-Type",
diff --git a/src/linguist/shared/proitems.h b/src/linguist/shared/proitems.h
index c7af53b1c..37a25196a 100644
--- a/src/linguist/shared/proitems.h
+++ b/src/linguist/shared/proitems.h
@@ -67,6 +67,7 @@ class ProString {
public:
ProString();
ProString(const ProString &other);
+ ProString &operator=(const ProString &) = default;
PROITEM_EXPLICIT ProString(const QString &str);
PROITEM_EXPLICIT ProString(const QStringRef &str);
PROITEM_EXPLICIT ProString(const char *str);
diff --git a/src/linguist/shared/qm.cpp b/src/linguist/shared/qm.cpp
index 6963ad6cb..288607824 100644
--- a/src/linguist/shared/qm.cpp
+++ b/src/linguist/shared/qm.cpp
@@ -151,9 +151,9 @@ public:
uint o;
};
- enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88, Dependencies = 0x96 };
+ enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88, Dependencies = 0x96, Language = 0xa7 };
- Releaser() {}
+ Releaser(const QString &language) : m_language(language) {}
bool save(QIODevice *iod);
@@ -179,6 +179,7 @@ private:
void writeMessage(const ByteTranslatorMessage & msg, QDataStream & stream,
TranslatorSaveMode strip, Prefix prefix) const;
+ QString m_language;
// for squeezed but non-file data, this is what needs to be deleted
QByteArray m_messageArray;
QByteArray m_offsetArray;
@@ -249,6 +250,12 @@ bool Releaser::save(QIODevice *iod)
QDataStream s(iod);
s.writeRawData((const char *)magic, MagicLength);
+ if (!m_language.isEmpty()) {
+ QByteArray lang = originalBytes(m_language);
+ quint32 las = quint32(lang.size());
+ s << quint8(Language) << las;
+ s.writeRawData(lang, las);
+ }
if (!m_dependencyArray.isEmpty()) {
quint32 das = quint32(m_dependencyArray.size());
s << quint8(Dependencies) << das;
@@ -465,7 +472,7 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd)
return false;
}
- enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88, Dependencies = 0x96 };
+ enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88, Dependencies = 0x96, Language = 0xa7 };
// for squeezed but non-file data, this is what needs to be deleted
const uchar *messageArray = 0;
@@ -473,6 +480,7 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd)
uint offsetLength = 0;
bool ok = true;
+ bool utf8Fail = false;
const uchar *end = data + len;
data += MagicLength;
@@ -505,6 +513,10 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd)
dependencies.append(dep);
}
translator.setDependencies(dependencies);
+ } else if (tag == Language) {
+ QString language;
+ fromBytes((const char *)data, blockLen, &language, &utf8Fail);
+ translator.setLanguageCode(language);
}
data += blockLen;
@@ -524,7 +536,6 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd)
guessPlurals = (numerusForms.count() == 1);
QString context, sourcetext, comment;
- bool utf8Fail = false;
QStringList translations;
for (const uchar *start = offsetArray; start != offsetArray + (numItems << 3); start += 8) {
@@ -632,7 +643,7 @@ static bool containsStripped(const Translator &translator, const TranslatorMessa
bool saveQM(const Translator &translator, QIODevice &dev, ConversionData &cd)
{
- Releaser releaser;
+ Releaser releaser(translator.languageCode());
QLocale::Language l;
QLocale::Country c;
Translator::languageAndCountry(translator.languageCode(), &l, &c);
diff --git a/src/linguist/shared/qmakebuiltins.cpp b/src/linguist/shared/qmakebuiltins.cpp
index 92366b9f7..180c1fa7a 100644
--- a/src/linguist/shared/qmakebuiltins.cpp
+++ b/src/linguist/shared/qmakebuiltins.cpp
@@ -768,7 +768,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand(
const auto vars = values(map(args.at(0)));
for (const ProString &var : vars) {
// FIXME: this is inconsistent with the "there are no empty strings" dogma.
- const auto splits = var.toQStringRef().split(sep, QString::KeepEmptyParts);
+ const auto splits = var.toQStringRef().split(sep, Qt::KeepEmptyParts);
for (const auto &splt : splits)
ret << ProString(splt).setSource(var);
}
@@ -1538,7 +1538,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
if (args.count() == 1)
return returnBool(isActiveConfig(args.at(0).toQStringRef()));
const auto &mutuals = args.at(1).toQStringRef().split(QLatin1Char('|'),
- QString::SkipEmptyParts);
+ Qt::SkipEmptyParts);
const ProStringList &configs = values(statics.strCONFIG);
for (int i = configs.size() - 1; i >= 0; i--) {
@@ -1572,7 +1572,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
}
} else {
const auto mutuals = args.at(2).toQStringRef().split(QLatin1Char('|'),
- QString::SkipEmptyParts);
+ Qt::SkipEmptyParts);
for (int i = l.size() - 1; i >= 0; i--) {
const ProString &val = l[i];
for (int mut = 0; mut < mutuals.count(); mut++) {
diff --git a/src/linguist/shared/qmakeevaluator.cpp b/src/linguist/shared/qmakeevaluator.cpp
index 639114d20..9e3fbf3e0 100644
--- a/src/linguist/shared/qmakeevaluator.cpp
+++ b/src/linguist/shared/qmakeevaluator.cpp
@@ -883,7 +883,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProVariable(
return ReturnTrue;
}
QChar sep = val.at(1);
- auto func = val.split(sep, QString::KeepEmptyParts);
+ auto func = val.split(sep, Qt::KeepEmptyParts);
if (func.count() < 3 || func.count() > 4) {
evalError(fL1S("The s/// function expects 3 or 4 arguments."));
return ReturnTrue;
@@ -1022,7 +1022,7 @@ static ProString msvcArchitecture(const QString &vcInstallDir, const QString &pa
QString vcBinDir = vcInstallDir;
if (vcBinDir.endsWith(QLatin1Char('\\')))
vcBinDir.chop(1);
- const auto dirs = pathVar.split(QLatin1Char(';'), QString::SkipEmptyParts);
+ const auto dirs = pathVar.split(QLatin1Char(';'), Qt::SkipEmptyParts);
for (const QString &dir : dirs) {
if (!dir.startsWith(vcBinDir, Qt::CaseInsensitive))
continue;
diff --git a/src/linguist/shared/qmakeglobals.cpp b/src/linguist/shared/qmakeglobals.cpp
index 6ba7abf28..0a58e9e29 100644
--- a/src/linguist/shared/qmakeglobals.cpp
+++ b/src/linguist/shared/qmakeglobals.cpp
@@ -261,7 +261,7 @@ QStringList QMakeGlobals::splitPathList(const QString &val) const
QStringList ret;
if (!val.isEmpty()) {
QString cwd(QDir::currentPath());
- const QStringList vals = val.split(dirlist_sep, QString::SkipEmptyParts);
+ const QStringList vals = val.split(dirlist_sep, Qt::SkipEmptyParts);
ret.reserve(vals.length());
for (const QString &it : vals)
ret << IoUtils::resolvePath(cwd, it);
diff --git a/src/linguist/shared/qmakeparser.h b/src/linguist/shared/qmakeparser.h
index ae76d8c46..22da3c69f 100644
--- a/src/linguist/shared/qmakeparser.h
+++ b/src/linguist/shared/qmakeparser.h
@@ -111,7 +111,6 @@ private:
struct BlockScope {
BlockScope() : start(nullptr), braceLevel(0), special(false), inBranch(false), nest(NestNone) {}
- BlockScope(const BlockScope &other) { *this = other; }
ushort *start; // Where this block started; store length here
int braceLevel; // Nesting of braces in scope
bool special; // Single-line conditionals inside loops, etc. cannot have else branches
diff --git a/src/linguist/shared/xliff.cpp b/src/linguist/shared/xliff.cpp
index c499e9ea5..bc47289b4 100644
--- a/src/linguist/shared/xliff.cpp
+++ b/src/linguist/shared/xliff.cpp
@@ -27,6 +27,7 @@
****************************************************************************/
#include "translator.h"
+#include "xmlparser.h"
#include <QtCore/QDebug>
#include <QtCore/QMap>
@@ -36,11 +37,6 @@
#include <QtCore/QTextCodec>
#include <QtCore/QTextStream>
-#include <QtXml/QXmlAttributes>
-#include <QtXml/QXmlDefaultHandler>
-#include <QtXml/QXmlParseException>
-
-
// The string value is historical and reflects the main purpose: Keeping
// obsolete entries separate from the magic file message (which both have
// no location information, but typically reside at opposite ends of the file).
@@ -368,22 +364,22 @@ static void writeMessage(QTextStream &ts, const TranslatorMessage &msg, const QR
}
}
-
-class XLIFFHandler : public QXmlDefaultHandler
+class XLIFFHandler : public XmlParser
{
public:
- XLIFFHandler(Translator &translator, ConversionData &cd);
+ XLIFFHandler(Translator &translator, ConversionData &cd, QXmlStreamReader &reader);
+ ~XLIFFHandler() override = default;
- bool startElement(const QString& namespaceURI, const QString &localName,
- const QString &qName, const QXmlAttributes &atts );
- bool endElement(const QString& namespaceURI, const QString &localName,
- const QString &qName );
- bool characters(const QString &ch);
- bool fatalError(const QXmlParseException &exception);
+private:
+ bool startElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName, const QXmlStreamAttributes &atts) override;
+ bool endElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName) override;
+ bool characters(const QStringRef &ch) override;
+ bool fatalError(qint64 line, qint64 column, const QString &message) override;
- bool endDocument();
+ bool endDocument() override;
-private:
enum XliffContext {
XC_xliff,
XC_group,
@@ -442,14 +438,16 @@ private:
QStack<int> m_contextStack;
};
-XLIFFHandler::XLIFFHandler(Translator &translator, ConversionData &cd)
- : m_translator(translator), m_cd(cd),
- m_translate(true),
- m_approved(true),
- m_lineNumber(-1),
- m_URITT(QLatin1String(TrollTsNamespaceURI)),
- m_URI(QLatin1String(XLIFF11namespaceURI)),
- m_URI12(QLatin1String(XLIFF12namespaceURI))
+XLIFFHandler::XLIFFHandler(Translator &translator, ConversionData &cd, QXmlStreamReader &reader)
+ : XmlParser(reader, true),
+ m_translator(translator),
+ m_cd(cd),
+ m_translate(true),
+ m_approved(true),
+ m_lineNumber(-1),
+ m_URITT(QLatin1String(TrollTsNamespaceURI)),
+ m_URI(QLatin1String(XLIFF11namespaceURI)),
+ m_URI12(QLatin1String(XLIFF12namespaceURI))
{}
@@ -485,33 +483,35 @@ bool XLIFFHandler::hasContext(XliffContext ctx) const
return false;
}
-bool XLIFFHandler::startElement(const QString& namespaceURI,
- const QString &localName, const QString &qName, const QXmlAttributes &atts )
+bool XLIFFHandler::startElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName, const QXmlStreamAttributes &atts)
{
Q_UNUSED(qName);
if (namespaceURI == m_URITT)
goto bail;
- if (namespaceURI != m_URI && namespaceURI != m_URI12)
- return false;
+ if (namespaceURI != m_URI && namespaceURI != m_URI12) {
+ return fatalError(reader.lineNumber(), reader.columnNumber(),
+ QLatin1String("Unknown namespace in the XLIFF file"));
+ }
if (localName == QLatin1String("xliff")) {
// make sure that the stack is not empty during parsing
pushContext(XC_xliff);
} else if (localName == QLatin1String("file")) {
- m_fileName = atts.value(QLatin1String("original"));
- m_language = atts.value(QLatin1String("target-language"));
+ m_fileName = atts.value(QLatin1String("original")).toString();
+ m_language = atts.value(QLatin1String("target-language")).toString();
m_language.replace(QLatin1Char('-'), QLatin1Char('_'));
- m_sourceLanguage = atts.value(QLatin1String("source-language"));
+ m_sourceLanguage = atts.value(QLatin1String("source-language")).toString();
m_sourceLanguage.replace(QLatin1Char('-'), QLatin1Char('_'));
if (m_sourceLanguage == QLatin1String("en"))
m_sourceLanguage.clear();
} else if (localName == QLatin1String("group")) {
if (atts.value(QLatin1String("restype")) == QLatin1String(restypeContext)) {
- m_context = atts.value(QLatin1String("resname"));
+ m_context = atts.value(QLatin1String("resname")).toString();
pushContext(XC_restype_context);
} else {
if (atts.value(QLatin1String("restype")) == QLatin1String(restypePlurals)) {
pushContext(XC_restype_plurals);
- m_id = atts.value(QLatin1String("id"));
+ m_id = atts.value(QLatin1String("id")).toString();
if (atts.value(QLatin1String("translate")) == QLatin1String("no"))
m_translate = false;
} else {
@@ -523,7 +523,7 @@ bool XLIFFHandler::startElement(const QString& namespaceURI,
if (atts.value(QLatin1String("translate")) == QLatin1String("no"))
m_translate = false;
if (!hasContext(XC_restype_plurals)) {
- m_id = atts.value(QLatin1String("id"));
+ m_id = atts.value(QLatin1String("id")).toString();
if (m_id.startsWith(QLatin1String("_msg")))
m_id.clear();
}
@@ -539,19 +539,18 @@ bool XLIFFHandler::startElement(const QString& namespaceURI,
if (atts.value(QLatin1String("restype")) != QLatin1String(restypeDummy))
pushContext(XC_restype_translation);
} else if (localName == QLatin1String("context-group")) {
- QString purpose = atts.value(QLatin1String("purpose"));
- if (purpose == QLatin1String("location"))
+ if (atts.value(QLatin1String("purpose")) == QLatin1String("location"))
pushContext(XC_context_group);
else
pushContext(XC_context_group_any);
} else if (currentContext() == XC_context_group && localName == QLatin1String("context")) {
- QString ctxtype = atts.value(QLatin1String("context-type"));
+ const auto ctxtype = atts.value(QLatin1String("context-type"));
if (ctxtype == QLatin1String("linenumber"))
pushContext(XC_context_linenumber);
else if (ctxtype == QLatin1String("sourcefile"))
pushContext(XC_context_filename);
} else if (currentContext() == XC_context_group_any && localName == QLatin1String("context")) {
- QString ctxtype = atts.value(QLatin1String("context-type"));
+ const auto ctxtype = atts.value(QLatin1String("context-type"));
if (ctxtype == QLatin1String(contextMsgctxt))
pushContext(XC_context_comment);
else if (ctxtype == QLatin1String(contextOldMsgctxt))
@@ -563,7 +562,7 @@ bool XLIFFHandler::startElement(const QString& namespaceURI,
else
pushContext(XC_translator_comment);
} else if (localName == QLatin1String("ph")) {
- QString ctype = atts.value(QLatin1String("ctype"));
+ QString ctype = atts.value(QLatin1String("ctype")).toString();
if (ctype.startsWith(QLatin1String("x-ch-")))
m_ctype = ctype.mid(5);
pushContext(XC_ph);
@@ -574,19 +573,21 @@ bail:
return true;
}
-bool XLIFFHandler::endElement(const QString &namespaceURI, const QString& localName,
- const QString &qName)
+bool XLIFFHandler::endElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName)
{
Q_UNUSED(qName);
if (namespaceURI == m_URITT) {
if (hasContext(XC_trans_unit) || hasContext(XC_restype_plurals))
- m_extra[localName] = accum;
+ m_extra[localName.toString()] = accum;
else
- m_translator.setExtra(localName, accum);
+ m_translator.setExtra(localName.toString(), accum);
return true;
}
- if (namespaceURI != m_URI && namespaceURI != m_URI12)
- return false;
+ if (namespaceURI != m_URI && namespaceURI != m_URI12) {
+ return fatalError(reader.lineNumber(), reader.columnNumber(),
+ QLatin1String("Unknown namespace in the XLIFF file"));
+ }
//qDebug() << "URI:" << namespaceURI << "QNAME:" << qName;
if (localName == QLatin1String("xliff")) {
popContext(XC_xliff);
@@ -640,15 +641,19 @@ bool XLIFFHandler::endElement(const QString &namespaceURI, const QString& localN
if (!m_hadAlt)
m_oldSources.append(QString());
if (!hasContext(XC_restype_plurals)) {
- if (!finalizeMessage(false))
- return false;
+ if (!finalizeMessage(false)) {
+ return fatalError(reader.lineNumber(), reader.columnNumber(),
+ QLatin1String("Element processing failed"));
+ }
}
} else if (localName == QLatin1String("alt-trans")) {
popContext(XC_alt_trans);
} else if (localName == QLatin1String("group")) {
if (popContext(XC_restype_plurals)) {
- if (!finalizeMessage(true))
- return false;
+ if (!finalizeMessage(true)) {
+ return fatalError(reader.lineNumber(), reader.columnNumber(),
+ QLatin1String("Element processing failed"));
+ }
} else if (popContext(XC_restype_context)) {
m_context.clear();
} else {
@@ -658,7 +663,7 @@ bool XLIFFHandler::endElement(const QString &namespaceURI, const QString& localN
return true;
}
-bool XLIFFHandler::characters(const QString &ch)
+bool XLIFFHandler::characters(const QStringRef &ch)
{
if (currentContext() == XC_ph) {
// handle the content of <ph> elements
@@ -670,7 +675,7 @@ bool XLIFFHandler::characters(const QString &ch)
accum.append(chr);
}
} else {
- QString t = ch;
+ QString t = ch.toString();
t.replace(QLatin1String("\r"), QLatin1String(""));
accum.append(t);
}
@@ -730,23 +735,20 @@ bool XLIFFHandler::finalizeMessage(bool isPlural)
return true;
}
-bool XLIFFHandler::fatalError(const QXmlParseException &exception)
+bool XLIFFHandler::fatalError(qint64 line, qint64 column, const QString &message)
{
QString msg = QString::asprintf("XML error: Parse error at line %d, column %d (%s).\n",
- exception.lineNumber(), exception.columnNumber(),
- exception.message().toLatin1().data());
+ static_cast<int>(line), static_cast<int>(column),
+ message.toLatin1().data());
m_cd.appendError(msg);
return false;
}
bool loadXLIFF(Translator &translator, QIODevice &dev, ConversionData &cd)
{
- QXmlInputSource in(&dev);
- QXmlSimpleReader reader;
- XLIFFHandler hand(translator, cd);
- reader.setContentHandler(&hand);
- reader.setErrorHandler(&hand);
- return reader.parse(in);
+ QXmlStreamReader reader(&dev);
+ XLIFFHandler hand(translator, cd, reader);
+ return hand.parse();
}
bool saveXLIFF(const Translator &translator, QIODevice &dev, ConversionData &cd)
diff --git a/src/linguist/shared/xmlparser.cpp b/src/linguist/shared/xmlparser.cpp
new file mode 100644
index 000000000..bcdca12bb
--- /dev/null
+++ b/src/linguist/shared/xmlparser.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "xmlparser.h"
+
+QT_BEGIN_NAMESPACE
+
+bool XmlParser::parse()
+{
+ while (!reader.atEnd()) {
+ reader.readNext();
+ if (reader.hasError()) {
+ fatalError(reader.lineNumber(), reader.columnNumber(), reader.errorString());
+ return false;
+ }
+
+ switch (reader.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ if (!startElement(reader.namespaceUri(), reader.name(), reader.qualifiedName(),
+ reader.attributes())) {
+ return false;
+ }
+ break;
+ case QXmlStreamReader::EndElement:
+ if (!endElement(reader.namespaceUri(), reader.name(), reader.qualifiedName())) {
+ return false;
+ }
+ break;
+ case QXmlStreamReader::Characters:
+ if (reportWhitespaceOnlyData
+ || (!reader.isWhitespace() && !reader.text().toString().trimmed().isEmpty())) {
+ if (!characters(reader.text()))
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (reader.isEndDocument() && !endDocument())
+ return false;
+
+ return true;
+}
+
+bool XmlParser::startElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName, const QXmlStreamAttributes &atts)
+{
+ Q_UNUSED(namespaceURI)
+ Q_UNUSED(localName)
+ Q_UNUSED(qName)
+ Q_UNUSED(atts)
+ return true;
+}
+
+bool XmlParser::endElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName)
+{
+ Q_UNUSED(namespaceURI)
+ Q_UNUSED(localName)
+ Q_UNUSED(qName)
+ return true;
+}
+
+bool XmlParser::characters(const QStringRef &text)
+{
+ Q_UNUSED(text)
+ return true;
+}
+
+bool XmlParser::fatalError(qint64 line, qint64 column, const QString &message)
+{
+ Q_UNUSED(line)
+ Q_UNUSED(column)
+ Q_UNUSED(message)
+ return true;
+}
+
+bool XmlParser::endDocument()
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/linguist/shared/xmlparser.h b/src/linguist/shared/xmlparser.h
new file mode 100644
index 000000000..4028c7067
--- /dev/null
+++ b/src/linguist/shared/xmlparser.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 XMLPARSER_H
+#define XMLPARSER_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qxmlstream.h>
+
+QT_BEGIN_NAMESPACE
+
+class XmlParser
+{
+public:
+ XmlParser(QXmlStreamReader &r, bool whitespaceOnlyData = false)
+ : reader(r), reportWhitespaceOnlyData(whitespaceOnlyData)
+ {
+ }
+ virtual ~XmlParser() = default;
+
+ bool parse();
+
+protected:
+ virtual bool startElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName, const QXmlStreamAttributes &atts);
+ virtual bool endElement(const QStringRef &namespaceURI, const QStringRef &localName,
+ const QStringRef &qName);
+ virtual bool characters(const QStringRef &text);
+ virtual bool endDocument();
+ virtual bool fatalError(qint64 line, qint64 column, const QString &message);
+
+ QXmlStreamReader &reader;
+ bool reportWhitespaceOnlyData;
+};
+
+QT_END_NAMESPACE
+
+#endif // XMLPARSER_H