1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#ifndef CLANG_CPP_H
5#define CLANG_CPP_H
6
7#include "lupdate.h"
8#include "synchronized.h"
9
10#include <QtCore/qloggingcategory.h>
11#include <QtCore/qregularexpression.h>
12#include <QtCore/qstring.h>
13
14QT_WARNING_PUSH
15QT_WARNING_DISABLE_MSVC(4100)
16QT_WARNING_DISABLE_MSVC(4146)
17QT_WARNING_DISABLE_MSVC(4267)
18QT_WARNING_DISABLE_MSVC(4624)
19QT_WARNING_DISABLE_GCC("-Wnonnull")
20
21#include <llvm/ADT/StringRef.h>
22#include <clang/Basic/SourceLocation.h>
23#include <clang/Basic/SourceManager.h>
24#include <clang/Basic/FileManager.h>
25
26QT_WARNING_POP
27
28#include <vector>
29#include <iostream>
30#include <sstream>
31
32QT_BEGIN_NAMESPACE
33
34Q_DECLARE_LOGGING_CATEGORY(lcClang)
35
36inline QString toQt(llvm::StringRef str)
37{
38 return QString::fromUtf8(utf8: str.data(), size: str.size());
39}
40
41#define LUPDATE_CLANG_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch))
42#define LUPDATE_CLANG_VERSION LUPDATE_CLANG_VERSION_CHECK(LUPDATE_CLANG_VERSION_MAJOR, \
43 LUPDATE_CLANG_VERSION_MINOR, LUPDATE_CLANG_VERSION_PATCH)
44
45// Local storage of translation information (information from the AST and linguist side)
46struct TranslationRelatedStore
47{
48 QString callType;
49 QString rawCode;
50 QString funcName;
51 qint64 locationCol = -1;
52 QString contextArg;
53 QString contextRetrieved;
54 QString lupdateSource;
55 QString lupdateLocationFile;
56 QString lupdateInputFile; // file associated to the running of the tool
57 qint64 lupdateLocationLine = -1;
58 QString lupdateId;
59 QString lupdateSourceWhenId;
60 QString lupdateIdMetaData;
61 QString lupdateMagicMetaData;
62 QHash<QString, QString> lupdateAllMagicMetaData;
63 QString lupdateComment;
64 QString lupdateExtraComment;
65 QString lupdatePlural;
66 QString lupdateWarning;
67 clang::SourceLocation sourceLocation;
68
69 bool isValid(bool printwarning = false)
70 {
71 switch (trFunctionAliasManager.trFunctionByName(trFunctionName: funcName)) {
72 // only one argument: the source
73 case TrFunctionAliasManager::Function_Q_DECLARE_TR_FUNCTIONS:
74 if (contextArg.isEmpty()) {
75 if (printwarning) {
76 std::stringstream warning;
77 warning << qPrintable(lupdateLocationFile) << ":"
78 << lupdateLocationLine << ":"
79 << locationCol << ": "
80 << " \'" << qPrintable(funcName)
81 << "\' cannot be called without context."
82 << " The call is ignored." <<std::endl;
83 lupdateWarning.append(s: QString::fromStdString(s: warning.str()));
84 }
85 return false;
86 }
87 break;
88 case TrFunctionAliasManager::Function_tr:
89 case TrFunctionAliasManager::Function_trUtf8:
90 if (lupdateSource.isEmpty()) {
91 if (printwarning) {
92 std::stringstream warning;
93 warning << qPrintable(lupdateLocationFile) << ":"
94 << lupdateLocationLine << ":"
95 << locationCol << ": "
96 << " \'" << qPrintable(funcName)
97 << "\' cannot be called without source."
98 << " The call is ignored." << std::endl;
99 lupdateWarning.append(s: QString::fromStdString(s: warning.str()));
100 }
101 return false;
102 }
103 break;
104 // two arguments: the context and the source
105 case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP:
106 case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3:
107 case TrFunctionAliasManager::Function_translate:
108 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP:
109 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP_UTF8:
110 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3:
111 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3_UTF8:
112 if (contextArg.isEmpty() || lupdateSource.isEmpty()) {
113 if (printwarning) {
114 std::stringstream warning;
115 warning << qPrintable(lupdateLocationFile) << ":"
116 << lupdateLocationLine << ":"
117 << locationCol << ": "
118 << " \'" << qPrintable(funcName)
119 << "\' cannot be called without context or source."
120 << " The call is ignored." << std::endl;
121 lupdateWarning.append(s: QString::fromStdString(s: warning.str()));
122 }
123 return false;
124 }
125 // not sure if the third argument is compulsory
126 break;
127 // only one argument (?) the message Id
128 case TrFunctionAliasManager::Function_QT_TRID_N_NOOP:
129 case TrFunctionAliasManager::Function_qtTrId:
130 case TrFunctionAliasManager::Function_QT_TRID_NOOP:
131 if (lupdateId.isEmpty()) {
132 if (printwarning) {
133 std::stringstream warning;
134 warning << qPrintable(lupdateLocationFile) << ":"
135 << lupdateLocationLine << ":"
136 << locationCol << ": "
137 << " \'" << qPrintable(funcName)
138 << "\' cannot be called without Id."
139 << " The call is ignored." << std::endl;
140 lupdateWarning.append(s: QString::fromStdString(s: warning.str()));
141 }
142 return false;
143 }
144 break;
145 default:
146 if (funcName == QStringLiteral("TRANSLATOR") && lupdateComment.isEmpty()) {
147 if (printwarning) {
148 std::stringstream warning;
149 warning << qPrintable(lupdateLocationFile) << ":"
150 << lupdateLocationLine << ":"
151 << locationCol << ": "
152 << qPrintable(funcName)
153 << " cannot be called without comment."
154 << " The call is ignored." << std::endl;
155 lupdateWarning.append(s: QString::fromStdString(s: warning.str()));
156 }
157 return false;
158 }
159 }
160 return !lupdateLocationFile.isEmpty() && (lupdateLocationLine > -1) && (locationCol > -1);
161 }
162
163 clang::SourceLocation callLocation(const clang::SourceManager &sourceManager)
164 {
165 if (sourceLocation.isInvalid()) {
166 auto sourceFile = sourceManager.getFileManager()
167 .getFile(Filename: lupdateLocationFile.toStdString());
168#if (LUPDATE_CLANG_VERSION >= LUPDATE_CLANG_VERSION_CHECK(10,0,0))
169 sourceLocation = sourceManager.translateFileLineCol(SourceFile: sourceFile.get(),
170 Line: lupdateLocationLine, Col: locationCol);
171#else
172 sourceLocation = sourceManager.translateFileLineCol(sourceFile, lupdateLocationLine,
173 locationCol);
174#endif
175 }
176 return sourceLocation;
177 }
178
179 void printStore() const
180 {
181 qCDebug(lcClang) << "------------------ Printing Store----------------------------------\n";
182 qCDebug(lcClang)
183 << "callType : " << callType << "\n"
184 << "rawCode : \n" << rawCode << "\n"
185 << "funcName : " << funcName << "\n"
186 << "LocationCol : " << locationCol << "\n"
187 << "contextArg : " << contextArg << "\n"
188 << "contextRetrieved : " << contextRetrieved << "\n"
189 << "lupdateSource : " << lupdateSource << "\n"
190 << "lupdateLocationFile : " << lupdateLocationFile << "\n"
191 << "lupdateLocationLine : " << lupdateLocationLine << "\n"
192 << "lupdateId : " << lupdateId << "\n"
193 << "lupdateIdMetaData : " << lupdateIdMetaData << "\n"
194 << "lupdateMagicMetaData: " << lupdateMagicMetaData << "\n"
195 << "lupdateComment : " << lupdateComment << "\n"
196 << "lupdateExtraComment : " << lupdateExtraComment << "\n"
197 << "lupdatePlural : " << lupdatePlural;
198 qCDebug(lcClang) << "-------------------------------------------------------------------\n";
199 }
200};
201using TranslationStores = std::vector<TranslationRelatedStore>;
202
203struct Stores
204{
205 Stores(TranslationStores &a, TranslationStores &qd, TranslationStores &qn)
206 : AST(a)
207 , QDeclareTrWithContext(qd)
208 , QNoopTranlsationWithContext(qn)
209 {}
210
211 TranslationStores Preprocessor;
212 WriteSynchronizedRef<TranslationRelatedStore> AST;
213 WriteSynchronizedRef<TranslationRelatedStore> QDeclareTrWithContext;
214 WriteSynchronizedRef<TranslationRelatedStore> QNoopTranlsationWithContext; // or with warnings that need to be
215 //displayed in the same order, always
216};
217
218namespace LupdatePrivate
219{
220 inline QString fixedLineEndings(const QString &s)
221 {
222#ifdef Q_OS_WIN
223 QString result = s;
224 result.replace(QLatin1String("\r\n"), QLatin1String("\n"));
225 return result;
226#else
227 return s;
228#endif
229 }
230
231 enum QuoteCompulsary
232 {
233 None = 0x01,
234 Left = 0x02, // Left quote is mandatory
235 Right = 0x04, // Right quote is mandatory
236 LeftAndRight = Left | Right // Both quotes are mandatory
237 };
238
239 /*
240 Removes the quotes around the lupdate extra, ID meta data, magic and
241 ID prefix comments and source string literals.
242 Depending on the given compulsory option, quotes can be unbalanced and
243 still some text is returned. This is to mimic the old lupdate behavior.
244 */
245 inline QString cleanQuote(llvm::StringRef s, QuoteCompulsary quote)
246 {
247 if (s.empty())
248 return {};
249 s = s.trim();
250 if (!s.consume_front(Prefix: "\"") && ((quote & Left) != 0))
251 return {};
252 if (!s.consume_back(Suffix: "\"") && ((quote & Right) != 0))
253 return {};
254 return fixedLineEndings(s: toQt(str: s));
255 }
256
257 /*
258 Removes the quotes and a possible existing string literal prefix
259 for a given string literal coming from the source code. Do not use
260 to clean the quotes around the lupdate translator specific comments.
261 */
262 inline QString cleanQuote(const std::string &token)
263 {
264 if (token.empty())
265 return {};
266
267 const QString string = fixedLineEndings(s: QString::fromStdString(s: token).trimmed());
268 const int index = string.indexOf(c: QLatin1Char('"'));
269 if (index <= 0)
270 return LupdatePrivate::cleanQuote(s: token, quote: QuoteCompulsary::LeftAndRight);
271
272 QRegularExpressionMatch result;
273 if (string.at(i: index - 1) == QLatin1Char('R')) {
274 static const QRegularExpression rawStringLiteral {
275 QStringLiteral(
276 "(?:\\bu8|\\b[LuU])??R\\\"([^\\(\\)\\\\ ]{0,16})\\((?<characters>.*)\\)\\1\\\""
277 ), QRegularExpression::DotMatchesEverythingOption };
278 result = rawStringLiteral.match(subject: string);
279 } else {
280 static const QRegularExpression stringLiteral {
281 QStringLiteral(
282 "(?:\\bu8|\\b[LuU])+?\\\"(?<characters>[^\\\"\\\\]*(?:\\\\.[^\\\"\\\\]*)*)\\\""
283 )
284 };
285 result = stringLiteral.match(subject: string);
286 }
287 if (result.hasMatch())
288 return result.captured(QStringLiteral("characters"));
289 return string;
290 }
291}
292
293namespace ClangCppParser
294{
295 void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd,
296 bool *fail);
297
298 using TranslatorMessageVector = std::vector<TranslatorMessage>;
299 void collectMessages(TranslatorMessageVector &result, TranslationRelatedStore &store);
300 TranslatorMessage translatorMessage(const TranslationRelatedStore &store,
301 const QString &id, bool plural, bool isID, bool isWarningOnly = false);
302
303 void correctAstTranslationContext(ReadSynchronizedRef<TranslationRelatedStore> &ast,
304 WriteSynchronizedRef<TranslationRelatedStore> &newAst, const TranslationStores &qDecl);
305 void finalize(ReadSynchronizedRef<TranslationRelatedStore> &ast,
306 WriteSynchronizedRef<TranslationRelatedStore> &newAst);
307
308 bool stringContainsTranslationInformation(llvm::StringRef ba);
309 bool hasAliases();
310 std::vector<std::string> getAliasFunctionDefinition();
311
312}
313
314QT_END_NAMESPACE
315
316#endif
317

source code of qttools/src/linguist/lupdate/cpp_clang.h