summaryrefslogtreecommitdiffstats
path: root/src/linguist/lupdate
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2020-03-18 14:27:27 +0100
committerAlexandru Croitor <alexandru.croitor@qt.io>2020-03-18 14:27:27 +0100
commitd9a3e03934076aea8852f1c4b142a5ecfeea0297 (patch)
tree6832178b5dddb8bf4dc61590265a638133f8bcfd /src/linguist/lupdate
parent4c0ba237eabf66e028459e86620c448c4e21760a (diff)
parent64221433d7a82955b111e20178c9527cb0373914 (diff)
Merge remote-tracking branch 'origin/dev' into wip/cmake
Conflicts: dependencies.yaml Change-Id: Ia86822a3b64be4e17d525f6b055eadfab7215bc8
Diffstat (limited to 'src/linguist/lupdate')
-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
10 files changed, 863 insertions, 477 deletions
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;
}