| 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 | #if (LUPDATE_CLANG_VERSION < LUPDATE_CLANG_VERSION_CHECK(14,0,0)) |
| 158 | void LupdatePPCallbacks::InclusionDirective(clang::SourceLocation /*hashLoc*/, |
| 159 | const clang::Token & /*includeTok*/, clang::StringRef /*fileName*/, bool /*isAngled*/, |
| 160 | clang::CharSourceRange /*filenameRange*/, |
| 161 | #if (LUPDATE_CLANG_VERSION >= LUPDATE_CLANG_VERSION_CHECK(16,0,0)) |
| 162 | const clang::OptionalFileEntryRef file, |
| 163 | #elif (LUPDATE_CLANG_VERSION >= LUPDATE_CLANG_VERSION_CHECK(15,0,0)) |
| 164 | const clang::Optional<clang::FileEntryRef> file, |
| 165 | #else |
| 166 | const clang::FileEntry *file, |
| 167 | #endif |
| 168 | clang::StringRef /*searchPath*/, clang::StringRef /*relativePath*/, |
| 169 | const clang::Module */*imported*/, clang::SrcMgr::CharacteristicKind /*fileType*/) |
| 170 | { |
| 171 | if (!file) |
| 172 | return; |
| 173 | |
| 174 | clang::StringRef fileNameRealPath = file-> |
| 175 | #if (LUPDATE_CLANG_VERSION >= LUPDATE_CLANG_VERSION_CHECK(15,0,0)) |
| 176 | getFileEntry(). |
| 177 | #endif |
| 178 | tryGetRealPathName(); |
| 179 | if (!LupdatePrivate::isFileSignificant(fileNameRealPath.str())) |
| 180 | return; |
| 181 | |
| 182 | TranslationRelatedStore store; |
| 183 | store.callType = QStringLiteral("InclusionDirective" ); |
| 184 | store.lupdateLocationFile = toQt(fileNameRealPath); |
| 185 | store.lupdateLocationLine = 1; |
| 186 | store.locationCol = 1; |
| 187 | store.lupdateInputFile = toQt(m_inputFile); |
| 188 | // do not fill the store.funcName. There is no function at this point |
| 189 | // the information is retrieved here to look for TRANSLATOR comments in header files |
| 190 | // when traversing the AST |
| 191 | |
| 192 | if (store.isValid()) |
| 193 | m_ppStores.emplace_back(std::move(store)); |
| 194 | } |
| 195 | #endif |
| 196 | |
| 197 | QT_END_NAMESPACE |
| 198 | |