1 | // Copyright (C) 2020 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #include "lupdatepreprocessoraction.h" |
5 | #include "filesignificancecheck.h" |
6 | |
7 | #include <clang/Lex/MacroArgs.h> |
8 | #include <clang/Basic/TokenKinds.h> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | void LupdatePPCallbacks::MacroExpands(const clang::Token &token, |
13 | const clang::MacroDefinition ¯oDefinition, clang::SourceRange sourceRange, |
14 | const clang::MacroArgs *macroArgs) |
15 | { |
16 | Q_UNUSED(macroDefinition); |
17 | |
18 | const auto &sm = m_preprocessor.getSourceManager(); |
19 | llvm::StringRef fileName = sm.getFilename(SpellingLoc: sourceRange.getBegin()); |
20 | if (!LupdatePrivate::isFileSignificant(filePath: fileName.str())) |
21 | return; |
22 | |
23 | const QString funcName = QString::fromStdString(s: m_preprocessor.getSpelling(Tok: token)); |
24 | switch (trFunctionAliasManager.trFunctionByName(trFunctionName: funcName)) { |
25 | default: |
26 | return; |
27 | case TrFunctionAliasManager::Function_Q_DECLARE_TR_FUNCTIONS: |
28 | case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP: |
29 | case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3: |
30 | case TrFunctionAliasManager::Function_QT_TRID_NOOP: |
31 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP: |
32 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3: |
33 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP_UTF8: |
34 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3_UTF8: |
35 | case TrFunctionAliasManager::Function_QT_TR_NOOP: |
36 | case TrFunctionAliasManager::Function_QT_TR_NOOP_UTF8: |
37 | case TrFunctionAliasManager::Function_QT_TR_N_NOOP: |
38 | qCDebug(lcClang) << "MacroExpands: Function name:" << funcName; |
39 | break; |
40 | } |
41 | |
42 | TranslationRelatedStore store; |
43 | store.callType = QStringLiteral("MacroExpands" ); |
44 | store.funcName = funcName; |
45 | store.lupdateLocationFile = toQt(str: fileName); |
46 | store.lupdateInputFile = toQt(str: m_inputFile); |
47 | store.lupdateLocationLine = sm.getExpansionLineNumber(Loc: sourceRange.getBegin()); |
48 | store.locationCol = sm.getExpansionColumnNumber(Loc: sourceRange.getBegin()); |
49 | |
50 | if (macroArgs) { |
51 | std::vector<QString> arguments(macroArgs->getNumMacroArguments()); |
52 | for (unsigned i = 0; i < macroArgs->getNumMacroArguments(); i++) { |
53 | auto preExpArguments = const_cast<clang::MacroArgs*>(macroArgs)->getPreExpArgument(Arg: i, |
54 | PP&: m_preprocessor); |
55 | QString temp; |
56 | bool errorArgument = false; |
57 | for (const auto &preExpArgument : preExpArguments) { |
58 | const auto kind = preExpArgument.getKind(); |
59 | switch (trFunctionAliasManager.trFunctionByName(trFunctionName: funcName)) { |
60 | default: |
61 | break; |
62 | case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP: |
63 | case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3: |
64 | case TrFunctionAliasManager::Function_QT_TRID_NOOP: |
65 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP: |
66 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3: |
67 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP_UTF8: |
68 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3_UTF8: |
69 | case TrFunctionAliasManager::Function_QT_TR_NOOP_UTF8: |
70 | case TrFunctionAliasManager::Function_QT_TR_NOOP: |
71 | case TrFunctionAliasManager::Function_QT_TR_N_NOOP: |
72 | if (!clang::tok::isStringLiteral(K: kind)) |
73 | errorArgument = true; |
74 | break; |
75 | } |
76 | if (errorArgument) |
77 | break; |
78 | if (clang::tok::isStringLiteral(K: kind)) |
79 | temp += LupdatePrivate::cleanQuote(token: m_preprocessor.getSpelling(Tok: preExpArgument)); |
80 | else |
81 | temp += QString::fromStdString(s: m_preprocessor.getSpelling(Tok: preExpArgument)); |
82 | } |
83 | arguments[i] = temp; |
84 | qCDebug(lcClang) << "*********** macro argument : " << temp; |
85 | } |
86 | storeMacroArguments(args: arguments, store: &store); |
87 | } |
88 | if (store.isValid()) |
89 | m_ppStores.emplace_back(args: std::move(store)); |
90 | } |
91 | |
92 | void LupdatePPCallbacks::storeMacroArguments(const std::vector<QString> &args, |
93 | TranslationRelatedStore *store) |
94 | { |
95 | switch (trFunctionAliasManager.trFunctionByName(trFunctionName: store->funcName)) { |
96 | // only one argument: the context with no " |
97 | case TrFunctionAliasManager::Function_Q_DECLARE_TR_FUNCTIONS: |
98 | if (args.size() == 1) |
99 | store->contextArg = args[0]; |
100 | break; |
101 | case TrFunctionAliasManager::Function_QT_TR_NOOP_UTF8: |
102 | case TrFunctionAliasManager::Function_QT_TR_NOOP: |
103 | case TrFunctionAliasManager::Function_QT_TR_N_NOOP: |
104 | if (args.size() >= 1) |
105 | store->lupdateSource = args[0]; |
106 | break; |
107 | case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP: |
108 | case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3: |
109 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP: |
110 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP_UTF8: |
111 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3: |
112 | case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3_UTF8: |
113 | if (args.size() >= 2) { |
114 | store->contextArg = args[0]; |
115 | store->lupdateSource = args[1]; |
116 | } |
117 | if (args.size() == 3) |
118 | store->lupdateComment = args[2]; |
119 | break; |
120 | // only one argument (?) the message Id |
121 | case TrFunctionAliasManager::Function_QT_TRID_N_NOOP: |
122 | case TrFunctionAliasManager::Function_qtTrId: |
123 | case TrFunctionAliasManager::Function_QT_TRID_NOOP: |
124 | if (args.size() == 1) |
125 | store->lupdateId = args[0]; |
126 | break; |
127 | } |
128 | } |
129 | |
130 | // Hook called when a source range is skipped. |
131 | // Emit a warning if translation information is found within this range. |
132 | void LupdatePPCallbacks::SourceRangeSkipped(clang::SourceRange sourceRange, |
133 | clang::SourceLocation endifLoc) |
134 | { |
135 | Q_UNUSED(endifLoc); |
136 | |
137 | const auto &sm = m_preprocessor.getSourceManager(); |
138 | llvm::StringRef fileName = sm.getFilename(SpellingLoc: sourceRange.getBegin()); |
139 | |
140 | if (!LupdatePrivate::isFileSignificant(filePath: fileName.str())) |
141 | return; |
142 | |
143 | const char *begin = sm.getCharacterData(SL: sourceRange.getBegin()); |
144 | const char *end = sm.getCharacterData(SL: sourceRange.getEnd()); |
145 | llvm::StringRef skippedText = llvm::StringRef(begin, end - begin); |
146 | if (ClangCppParser::stringContainsTranslationInformation(ba: skippedText)) { |
147 | qCDebug(lcClang) << "SourceRangeSkipped: skipped text:" << QString::fromStdString(s: skippedText.str()); |
148 | unsigned int beginLine = sm.getExpansionLineNumber(Loc: sourceRange.getBegin()); |
149 | unsigned int endLine = sm.getExpansionLineNumber(Loc: sourceRange.getEnd()); |
150 | qWarning(msg: "%s Code with translation information has been skipped " |
151 | "between lines %d and %d" , |
152 | fileName.str().c_str(), beginLine, endLine); |
153 | } |
154 | } |
155 | |
156 | // To list the included files |
157 | void LupdatePPCallbacks::InclusionDirective(clang::SourceLocation /*hashLoc*/, |
158 | const clang::Token & /*includeTok*/, clang::StringRef /*fileName*/, bool /*isAngled*/, |
159 | clang::CharSourceRange /*filenameRange*/, |
160 | #if (LUPDATE_CLANG_VERSION >= LUPDATE_CLANG_VERSION_CHECK(16,0,0)) |
161 | const clang::OptionalFileEntryRef file, |
162 | #elif (LUPDATE_CLANG_VERSION >= LUPDATE_CLANG_VERSION_CHECK(15,0,0)) |
163 | const clang::Optional<clang::FileEntryRef> file, |
164 | #else |
165 | const clang::FileEntry *file, |
166 | #endif |
167 | clang::StringRef /*searchPath*/, clang::StringRef /*relativePath*/, |
168 | const clang::Module */*imported*/, clang::SrcMgr::CharacteristicKind /*fileType*/) |
169 | { |
170 | if (!file) |
171 | return; |
172 | |
173 | clang::StringRef fileNameRealPath = file-> |
174 | #if (LUPDATE_CLANG_VERSION >= LUPDATE_CLANG_VERSION_CHECK(15,0,0)) |
175 | getFileEntry(). |
176 | #endif |
177 | tryGetRealPathName(); |
178 | if (!LupdatePrivate::isFileSignificant(filePath: fileNameRealPath.str())) |
179 | return; |
180 | |
181 | TranslationRelatedStore store; |
182 | store.callType = QStringLiteral("InclusionDirective" ); |
183 | store.lupdateLocationFile = toQt(str: fileNameRealPath); |
184 | store.lupdateLocationLine = 1; |
185 | store.locationCol = 1; |
186 | store.lupdateInputFile = toQt(str: m_inputFile); |
187 | // do not fill the store.funcName. There is no function at this point |
188 | // the information is retrieved here to look for TRANSLATOR comments in header files |
189 | // when traversing the AST |
190 | |
191 | if (store.isValid()) |
192 | m_ppStores.emplace_back(args: std::move(store)); |
193 | } |
194 | |
195 | QT_END_NAMESPACE |
196 | |