aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSergio Martins <smartins@kde.org>2017-09-24 16:40:52 +0100
committerSergio Martins <iamsergio@gmail.com>2017-09-24 16:40:52 +0100
commitfd083b0811f87def47a57a86a72faf93ad2eb7b1 (patch)
treeae4f0df2e9ba8c2aa3877159966bef334d9b10aa /src
parentd357c41e5123cba648bd353d99bdf3d4f5ae6640 (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.cpp61
-rw-r--r--src/PreProcessorVisitor.h9
-rw-r--r--src/checks/level1/qhash-namespace.cpp11
-rw-r--r--src/checks/level1/qhash-namespace.h1
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