/tools/porting/src/tokenreplacements.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 371 lines · 222 code · 43 blank · 106 comment · 60 complexity · 2abb3fcac617760308f8fd03a2bc4282 MD5 · raw file

  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
  4. ** All rights reserved.
  5. ** Contact: Nokia Corporation (qt-info@nokia.com)
  6. **
  7. ** This file is part of the qt3to4 porting application of the Qt Toolkit.
  8. **
  9. ** $QT_BEGIN_LICENSE:LGPL$
  10. ** GNU Lesser General Public License Usage
  11. ** This file may be used under the terms of the GNU Lesser General Public
  12. ** License version 2.1 as published by the Free Software Foundation and
  13. ** appearing in the file LICENSE.LGPL included in the packaging of this
  14. ** file. Please review the following information to ensure the GNU Lesser
  15. ** General Public License version 2.1 requirements will be met:
  16. ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  17. **
  18. ** In addition, as a special exception, Nokia gives you certain additional
  19. ** rights. These rights are described in the Nokia Qt LGPL Exception
  20. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  21. **
  22. ** GNU General Public License Usage
  23. ** Alternatively, this file may be used under the terms of the GNU General
  24. ** Public License version 3.0 as published by the Free Software Foundation
  25. ** and appearing in the file LICENSE.GPL included in the packaging of this
  26. ** file. Please review the following information to ensure the GNU General
  27. ** Public License version 3.0 requirements will be met:
  28. ** http://www.gnu.org/copyleft/gpl.html.
  29. **
  30. ** Other Usage
  31. ** Alternatively, this file may be used in accordance with the terms and
  32. ** conditions contained in a signed written agreement between you and Nokia.
  33. **
  34. **
  35. **
  36. **
  37. **
  38. ** $QT_END_LICENSE$
  39. **
  40. ****************************************************************************/
  41. #include "tokenreplacements.h"
  42. #include "logger.h"
  43. #include "portingrules.h"
  44. QT_BEGIN_NAMESPACE
  45. using namespace TokenEngine;
  46. void addLogSourceEntry(const QString &text, const TokenContainer &tokenContainer, const int index)
  47. {
  48. Logger *logger = Logger::instance();
  49. int line = tokenContainer.line(index);
  50. int col = tokenContainer.column(index);
  51. SourcePointLogEntry *logEntry =
  52. new SourcePointLogEntry(QLatin1String("Info"), QLatin1String("Porting"),
  53. logger->globalState.value(QLatin1String("currentFileName")),
  54. line, col, text);
  55. logger->addEntry(logEntry);
  56. }
  57. void addLogWarning(const QString &text)
  58. {
  59. Logger::instance()->addEntry(new PlainLogEntry(QLatin1String("Warning"), QLatin1String("Porting"), text));
  60. }
  61. QualifiedNameParser::QualifiedNameParser(const TokenContainer &tokenContainer, const int tokenIndex)
  62. :tokenContainer(tokenContainer)
  63. ,currentIndex(tokenIndex)
  64. {
  65. Q_ASSERT(isValidIndex(currentIndex));
  66. }
  67. bool QualifiedNameParser::isPartOfQualifiedName()
  68. {
  69. return ((nextScopeToken(Left) != -1) || (nextScopeToken(Right) != -1));
  70. }
  71. bool QualifiedNameParser::isValidIndex(int index)
  72. {
  73. return (index < tokenContainer.count() && index >= 0);
  74. }
  75. /*
  76. A qualifier is a the leftmost or middle part of a qualified name
  77. */
  78. bool QualifiedNameParser::isQualifier()
  79. {
  80. return (nextScopeToken(Right) != -1);
  81. }
  82. /*
  83. A name is a the rightmost part of a qualified name.
  84. */
  85. bool QualifiedNameParser::isName()
  86. {
  87. return (nextScopeToken(Left) != -1);
  88. }
  89. /*
  90. Peek for a qualifier or name in the given direction
  91. */
  92. int QualifiedNameParser::peek(Direction direction)
  93. {
  94. return nextScopeToken(direction);
  95. }
  96. /*
  97. Look for a qualifier or name in the given direction,update
  98. current position if found.
  99. */
  100. int QualifiedNameParser::move(Direction direction)
  101. {
  102. int tokenIndex = nextScopeToken(direction);
  103. if(tokenIndex != -1)
  104. currentIndex = tokenIndex;
  105. return tokenIndex;
  106. }
  107. /*
  108. Looks for "::" starting at currentIndex, returns the token index
  109. for it if found. If the first non-whitespace token found is something else,
  110. -1 is returned.
  111. */
  112. int QualifiedNameParser::findScopeOperator(Direction direction)
  113. {
  114. int tokenIndex = currentIndex;
  115. QByteArray tokenText;
  116. //loop until we get a token containing text or we pass the beginning/end of the source
  117. tokenIndex += direction;
  118. while(tokenText.isEmpty() && isValidIndex(tokenIndex)) {
  119. tokenText = tokenContainer.text(tokenIndex).trimmed();
  120. if(tokenText==QByteArray("::"))
  121. return tokenIndex;
  122. tokenIndex += direction;
  123. }
  124. return -1;
  125. }
  126. /*
  127. Walks a qualified name. Returns the token index
  128. for the next identifer in the qualified name, or -1 if its not found.
  129. */
  130. int QualifiedNameParser::nextScopeToken(Direction direction)
  131. {
  132. int tokenIndex = findScopeOperator(direction);
  133. if (tokenIndex == -1)
  134. return -1;
  135. QByteArray tokenText;
  136. //loop until we get a token containing text or we pass the start of the source
  137. tokenIndex += direction;
  138. while(tokenText.isEmpty() && isValidIndex(tokenIndex)) {
  139. tokenText = tokenContainer.text(tokenIndex).trimmed();
  140. tokenIndex += direction;
  141. }
  142. return tokenIndex - direction;
  143. }
  144. /////////////////////
  145. GenericTokenReplacement::GenericTokenReplacement(QByteArray oldToken, QByteArray newToken)
  146. :oldToken(oldToken)
  147. ,newToken(newToken)
  148. {}
  149. QByteArray GenericTokenReplacement::getReplaceKey()
  150. {
  151. return QByteArray(oldToken);
  152. }
  153. bool GenericTokenReplacement::doReplace(const TokenContainer &tokenContainer,
  154. int index, TextReplacements &textReplacements)
  155. {
  156. QByteArray tokenText = tokenContainer.text(index);
  157. if(tokenText == oldToken){
  158. addLogSourceEntry(QString::fromLatin1(tokenText + QByteArray(" -> ") + newToken), tokenContainer, index);
  159. TokenEngine::Token token = tokenContainer.token(index);
  160. textReplacements.insert(newToken, token.start, token.length);
  161. return true;
  162. }
  163. return false;
  164. }
  165. ///////////////////
  166. ClassNameReplacement::ClassNameReplacement(QByteArray oldToken, QByteArray newToken)
  167. :oldToken(oldToken)
  168. ,newToken(newToken)
  169. {}
  170. QByteArray ClassNameReplacement::getReplaceKey()
  171. {
  172. return QByteArray(oldToken);
  173. }
  174. /*
  175. Replace a class name token. If the class name is a scope specifier (a "qualifier")
  176. in a qualified name, we check if qualified name will be replaced by a porting rule.
  177. If so, we don't do the class name replacement.
  178. */
  179. bool ClassNameReplacement::doReplace(const TokenContainer &tokenContainer, int index, TextReplacements &textReplacements)
  180. {
  181. QByteArray tokenText = tokenContainer.text(index);
  182. if(tokenText != oldToken)
  183. return false;
  184. QualifiedNameParser nameParser(tokenContainer, index);
  185. if(nameParser.isPartOfQualifiedName() &&
  186. nameParser.peek(QualifiedNameParser::Right) != -1) {
  187. int nameTokenIndex = nameParser.peek(QualifiedNameParser::Right);
  188. QByteArray name = tokenContainer.text(nameTokenIndex);
  189. TextReplacements textReplacements;
  190. QList<TokenReplacement*> tokenReplacements
  191. = PortingRules::instance()->getTokenReplacementRules();
  192. bool changed = false;
  193. foreach(TokenReplacement *tokenReplacement, tokenReplacements) {
  194. changed = tokenReplacement->doReplace(tokenContainer, nameTokenIndex, textReplacements);
  195. if(changed)
  196. break;
  197. }
  198. if(changed)
  199. return false;
  200. }
  201. addLogSourceEntry(QString::fromLatin1(tokenText + QByteArray(" -> ") + newToken), tokenContainer, index);
  202. TokenEngine::Token token = tokenContainer.token(index);
  203. textReplacements.insert(newToken, token.start, token.length);
  204. return true;
  205. }
  206. ///////////////////
  207. ScopedTokenReplacement::ScopedTokenReplacement(const QByteArray &oldToken,
  208. const QByteArray &newToken)
  209. :newScopedName(newToken)
  210. {
  211. Q_ASSERT(oldToken.contains(QByteArray("::")));
  212. // Split oldToken into scope and name parts.
  213. oldName = oldToken.mid(oldToken.lastIndexOf(':')+1);
  214. oldScope = oldToken.mid(0, oldToken.indexOf(':'));
  215. // Split newToken into scope and name parts, execept if we have a spcial
  216. // case like Qt::WType_Modal -> (Qt::WType_Dialog | Qt::WShowModal)
  217. if (newToken.count(QByteArray("::")) != 1 || newToken.contains(QByteArray("("))) {
  218. newName = newToken;
  219. } else {
  220. newName = newToken.mid(newToken.lastIndexOf(':')+1);
  221. newScope = newToken.mid(0, newToken.indexOf(':'));
  222. }
  223. strictMode = Logger::instance()->globalState.contains(QString::fromLatin1("strictMode"));
  224. }
  225. bool ScopedTokenReplacement::doReplace(const TokenContainer &tokenContainer, int sourceIndex, TextReplacements &textReplacements)
  226. {
  227. const QByteArray sourceName = tokenContainer.text(sourceIndex);
  228. // Check if the token texts matches.
  229. if (sourceName != oldName)
  230. return false;
  231. // Get token attributes. The attributes are created by the the C++ parser/analyzer.
  232. const TokenAttributes *attributes = tokenContainer.tokenAttributes();
  233. // If the declaration attribute is set we don't replace.
  234. if (!attributes->attribute(sourceIndex, "declaration").isEmpty())
  235. return false;
  236. // If the unknown (undeclared) attribute is set we don't replace.
  237. if (!attributes->attribute(sourceIndex, "unknown").isEmpty())
  238. return false;
  239. // If nameUse is set we test if the nameUse refers to the correct declaration.
  240. // This is done by checking the parentScope attribute, which returns the scope
  241. // for the declaration associated with this name use.
  242. const bool haveNameUseInfo = !attributes->attribute(sourceIndex, "nameUse").isEmpty();
  243. if (haveNameUseInfo) {
  244. if (attributes->attribute(sourceIndex, "parentScope") != oldScope)
  245. return false;
  246. // If the user has specified -strict, we don't replace tokens when we don't have name use info.
  247. } else if (strictMode) {
  248. return false;
  249. }
  250. // The token might have a qualifier, and in that case we need to check if
  251. // we should replace the qualifier as well.
  252. QualifiedNameParser nameParser(tokenContainer, sourceIndex);
  253. // This is a pretty special case, it means that in a qualified
  254. // name like aaa::bbb the replacement rule has been triggered for
  255. // the aaa part. Since this is not what we'd normally use a
  256. // ScopedReplacement for, we just return here.
  257. if (nameParser.isQualifier())
  258. return false;
  259. // If the token is unqualified, just replace it.
  260. if (!nameParser.isPartOfQualifiedName()) {
  261. // If we have no name use info we try to avoid replacements of
  262. // e.g. Vertical with QSizePolicy::Vertically. Unqualified tokens
  263. // can't happen for classes one does not usually inherit from, so
  264. // we only let them pass for stuff that people usually inherited from.
  265. if (!haveNameUseInfo && newScope != "Qt" && newScope != "QFrame" && newScope != "QValidator")
  266. return false;
  267. const Token sourceToken = tokenContainer.token(sourceIndex);
  268. addLogSourceEntry(QString::fromLatin1(sourceName + QByteArray(" -> ") + newScopedName), tokenContainer, sourceIndex);
  269. textReplacements.insert(newScopedName, sourceToken.start, sourceName.size());
  270. return true;
  271. }
  272. // Peek left for the qualifer token.
  273. const int sourceScopeIndex = nameParser.peek(QualifiedNameParser::Left);
  274. if (sourceScopeIndex == -1) {
  275. return false;
  276. }
  277. const Token sourceNameToken = tokenContainer.token(sourceIndex);
  278. const Token sourceScopeToken = tokenContainer.token(sourceScopeIndex);
  279. const QByteArray sourceScope = tokenContainer.text(sourceScopeIndex);
  280. // If we have no name use info and the source and old scopes don't match,
  281. // we generally don't do a replace, unless the old scope is Qt and
  282. // the source scope inherits Qt. For example, QWidget::ButtonState should
  283. // be renamed to Qt::ButtonState.
  284. if (!haveNameUseInfo && sourceScope != oldScope) {
  285. if (oldScope != "Qt")
  286. return false;
  287. // Check if sourceScope inherits the Qt class.
  288. if (!PortingRules::instance()->getInheritsQt().contains(QString::fromLatin1(sourceScope.constData()))) //TODO optimize: linear search
  289. return false;
  290. }
  291. // Spcecial cases, such as QIODevice::Offset -> Q_LONGLONG
  292. // or Qt::WType_Modal -> (Qt::WType_Dialog | Qt::WShowModal).
  293. if (newScope.isEmpty()) {
  294. addLogSourceEntry(QString::fromLatin1((sourceScope + QByteArray("::") + sourceName +
  295. QByteArray(" -> ") + newScopedName).constData()), tokenContainer, sourceIndex);
  296. const int qualiferLength = sourceNameToken.start - sourceScopeToken.start;
  297. const int length = qualiferLength + sourceNameToken.length;
  298. textReplacements.insert(newName, sourceScopeToken.start, length);
  299. return true;
  300. }
  301. // If the old and new scopes are equal, we replace the name part only.
  302. if (newScope == sourceScope) {
  303. // If the names are equal, there is no need to do anything.
  304. if (newName == sourceName)
  305. return true;
  306. addLogSourceEntry(QString::fromLatin1((sourceName + QByteArray(" -> ") + newName).constData()), tokenContainer, sourceIndex);
  307. textReplacements.insert(newName, sourceNameToken.start, sourceNameToken.length);
  308. return true;
  309. }
  310. // If the names are equal, replace scope only.
  311. if (newName == sourceName) {
  312. addLogSourceEntry(QString::fromLatin1((sourceScope + QByteArray(" -> ") + newScope).constData()), tokenContainer, sourceScopeIndex);
  313. textReplacements.insert(newScope, sourceScopeToken.start, sourceScopeToken.length);
  314. return true;
  315. }
  316. // Replace scope and name.
  317. addLogSourceEntry(QString::fromLatin1((sourceScope + QByteArray("::") + sourceName +
  318. QByteArray(" -> ") + newScopedName).constData()),
  319. tokenContainer, sourceScopeIndex);
  320. textReplacements.insert(newScope, sourceScopeToken.start, sourceScopeToken.length);
  321. textReplacements.insert(newName, sourceNameToken.start, sourceNameToken.length);
  322. return true;
  323. }
  324. QByteArray ScopedTokenReplacement::getReplaceKey()
  325. {
  326. return oldName;
  327. }
  328. QT_END_NAMESPACE