diff options
author | Sergio Martins <smartins@kde.org> | 2017-09-24 16:40:52 +0100 |
---|---|---|
committer | Sergio Martins <iamsergio@gmail.com> | 2017-09-24 16:40:52 +0100 |
commit | fd083b0811f87def47a57a86a72faf93ad2eb7b1 (patch) | |
tree | ae4f0df2e9ba8c2aa3877159966bef334d9b10aa /src | |
parent | d357c41e5123cba648bd353d99bdf3d4f5ae6640 (diff) |
qhash-namespace: Very qhash() is inside QT_*_NAMESPACE macros
This will only be done if -qt-developer is passed, as it's not
useful for user code
Diffstat (limited to 'src')
-rw-r--r-- | src/PreProcessorVisitor.cpp | 61 | ||||
-rw-r--r-- | src/PreProcessorVisitor.h | 9 | ||||
-rw-r--r-- | src/checks/level1/qhash-namespace.cpp | 11 | ||||
-rw-r--r-- | src/checks/level1/qhash-namespace.h | 1 |
4 files changed, 77 insertions, 5 deletions
diff --git a/src/PreProcessorVisitor.cpp b/src/PreProcessorVisitor.cpp index cc4bf6c3..2ddf9ffd 100644 --- a/src/PreProcessorVisitor.cpp +++ b/src/PreProcessorVisitor.cpp @@ -31,11 +31,37 @@ using namespace std; PreProcessorVisitor::PreProcessorVisitor(const clang::CompilerInstance &ci) : clang::PPCallbacks() , m_ci(ci) + , m_sm(ci.getSourceManager()) { Preprocessor &pi = m_ci.getPreprocessor(); pi.addPPCallbacks(std::unique_ptr<PPCallbacks>(this)); } +bool PreProcessorVisitor::isBetweenQtNamespaceMacros(SourceLocation loc) +{ + if (loc.isInvalid()) + return false; + + if (loc.isMacroID()) + loc = m_sm.getExpansionLoc(loc); + + uint fileId = m_sm.getFileID(loc).getHashValue(); + + vector<SourceRange> &pairs = m_q_namespace_macro_locations[fileId]; + for (SourceRange &pair : pairs) { + if (pair.getBegin().isInvalid() || pair.getEnd().isInvalid()) { + //llvm::errs() << "PreProcessorVisitor::isBetweenQtNamespaceMacros Found invalid location\n"; + continue; // shouldn't happen + } + + if (m_sm.isBeforeInSLocAddrSpace(pair.getBegin(), loc) && + m_sm.isBeforeInSLocAddrSpace(loc, pair.getEnd())) + return true; + } + + return false; +} + std::string PreProcessorVisitor::getTokenSpelling(const MacroDefinition &def) const { if (!def) @@ -62,6 +88,28 @@ void PreProcessorVisitor::updateQtVersion() } } +void PreProcessorVisitor::handleQtNamespaceMacro(SourceLocation loc, StringRef name) +{ + const bool isBegin = name == "QT_BEGIN_NAMESPACE"; + uint fileId = m_sm.getFileID(loc).getHashValue(); + vector<SourceRange> &pairs = m_q_namespace_macro_locations[fileId]; + + if (isBegin) { + pairs.push_back(SourceRange(loc, {})); + } else { + if (pairs.empty()) { + // llvm::errs() << "FOO Received end!!"; + } else { + SourceRange &range = pairs[pairs.size() - 1]; + if (range.getBegin().isInvalid()) { + // llvm::errs() << "FOO Error received end before a begin\n"; + } else { + range.setEnd(loc); + } + } + } +} + static int stringToNumber(const string &str) { if (str.empty()) @@ -71,15 +119,20 @@ static int stringToNumber(const string &str) } void PreProcessorVisitor::MacroExpands(const Token &MacroNameTok, const MacroDefinition &def, - SourceRange, const MacroArgs *) + SourceRange range, const MacroArgs *) { - if (m_qtVersion != -1) - return; - IdentifierInfo *ii = MacroNameTok.getIdentifierInfo(); if (!ii) return; + if (ii->getName() == "QT_BEGIN_NAMESPACE" || ii->getName() == "QT_END_NAMESPACE") { + handleQtNamespaceMacro(range.getBegin(), ii->getName()); + return; + } + + if (m_qtVersion != -1) + return; + auto name = ii->getName(); if (name == "QT_VERSION_MAJOR") { m_qtMajorVersion = stringToNumber(getTokenSpelling(def)); diff --git a/src/PreProcessorVisitor.h b/src/PreProcessorVisitor.h index ef2e8da1..7d4c85ce 100644 --- a/src/PreProcessorVisitor.h +++ b/src/PreProcessorVisitor.h @@ -31,9 +31,11 @@ #include <string> #include <clang/Lex/PPCallbacks.h> +#include <unordered_map> namespace clang { class CompilerInstance; + class SourceManager; class SourceRange; class Token; class MacroDefinition; @@ -49,18 +51,25 @@ public: // Returns for example 050601 (Qt 5.6.1), or -1 if we don't know the version int qtVersion() const { return m_qtVersion; } + bool isBetweenQtNamespaceMacros(clang::SourceLocation loc); + protected: void MacroExpands(const clang::Token &MacroNameTok, const clang::MacroDefinition &, clang::SourceRange range, const clang::MacroArgs *) override; private: std::string getTokenSpelling(const clang::MacroDefinition &) const; void updateQtVersion(); + void handleQtNamespaceMacro(clang::SourceLocation loc, clang::StringRef name); const clang::CompilerInstance &m_ci; int m_qtMajorVersion = -1; int m_qtMinorVersion = -1; int m_qtPatchVersion = -1; int m_qtVersion = -1; + + // Indexed by FileId, has a list of QT_BEGIN_NAMESPACE/QT_END_NAMESPACE location + std::unordered_map<uint, std::vector<clang::SourceRange>> m_q_namespace_macro_locations; + const clang::SourceManager &m_sm; }; #endif diff --git a/src/checks/level1/qhash-namespace.cpp b/src/checks/level1/qhash-namespace.cpp index ad8562c2..c78be0c0 100644 --- a/src/checks/level1/qhash-namespace.cpp +++ b/src/checks/level1/qhash-namespace.cpp @@ -26,7 +26,9 @@ #include "TypeUtils.h" #include "ContextUtils.h" #include "StringUtils.h" +#include "ClazyContext.h" #include "checkmanager.h" +#include "PreProcessorVisitor.h" #include <clang/AST/AST.h> @@ -37,6 +39,8 @@ using namespace std; qhash_namespace::qhash_namespace(const std::string &name, ClazyContext *context) : CheckBase(name, context) { + if (context->isQtDeveloper()) + context->enablePreprocessorVisitor(); } void qhash_namespace::VisitDecl(clang::Decl *decl) @@ -63,6 +67,13 @@ void qhash_namespace::VisitDecl(clang::Decl *decl) if (!msg.empty()) emitWarning(decl, msg); + + if (m_context->isQtDeveloper()) { + PreProcessorVisitor *preProcessorVisitor = m_context->preprocessorVisitor; + if (preProcessorVisitor && !preProcessorVisitor->isBetweenQtNamespaceMacros(func->getLocStart())) { + emitWarning(decl, "qHash(" + StringUtils::simpleTypeName(firstArg->getType(), lo()) + ") must be declared before QT_END_NAMESPACE"); + } + } } REGISTER_CHECK("qhash-namespace", qhash_namespace, CheckLevel1) diff --git a/src/checks/level1/qhash-namespace.h b/src/checks/level1/qhash-namespace.h index 820ca541..06e51e18 100644 --- a/src/checks/level1/qhash-namespace.h +++ b/src/checks/level1/qhash-namespace.h @@ -33,7 +33,6 @@ class qhash_namespace : public CheckBase public: explicit qhash_namespace(const std::string &name, ClazyContext *context); void VisitDecl(clang::Decl *decl) override; -private: }; #endif |