summaryrefslogtreecommitdiff
path: root/src/plugins/clangcodemodel/semanticmarker.cpp
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@digia.com>2013-12-10 14:37:32 +0100
committerhjk <hjk121@nokiamail.com>2013-12-20 17:05:09 +0100
commit5beb74fd9d11b31b360e0a336e269b81cbca1f5a (patch)
tree6b1bad7c9798e29c694e216f1196e0f47b77ceff /src/plugins/clangcodemodel/semanticmarker.cpp
parent93b7528431857d67aa0ffdc60f835d987aa7b101 (diff)
downloadqt-creator-5beb74fd9d11b31b360e0a336e269b81cbca1f5a.tar.gz
Add experimental clang code-model plug-in.
Previously known as the wip/clang branch. Contributors (in alphabetical order): - Christian Kamm <christian.d.kamm@nokia.com> - Erik Verbruggen <erik.verbruggen@digia.com> - Leandro Melo <leandro.melo@nokia.com> - Peter Kuemmel <syntheticpp@gmx.net> - Sergey Shambir <sergey.shambir.auto@gmail.com> Change-Id: I4c3ff600a19b6732641c1d5ef28236bf2cc17737 Reviewed-by: hjk <hjk121@nokiamail.com>
Diffstat (limited to 'src/plugins/clangcodemodel/semanticmarker.cpp')
-rw-r--r--src/plugins/clangcodemodel/semanticmarker.cpp506
1 files changed, 506 insertions, 0 deletions
diff --git a/src/plugins/clangcodemodel/semanticmarker.cpp b/src/plugins/clangcodemodel/semanticmarker.cpp
new file mode 100644
index 0000000000..34c7acf934
--- /dev/null
+++ b/src/plugins/clangcodemodel/semanticmarker.cpp
@@ -0,0 +1,506 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "semanticmarker.h"
+#include "unit.h"
+#include "utils_p.h"
+#include "cxraii.h"
+
+using namespace ClangCodeModel;
+using namespace ClangCodeModel::Internal;
+
+static const unsigned ATTACHED_NOTES_LIMIT = 10;
+
+SemanticMarker::SemanticMarker()
+{
+}
+
+SemanticMarker::~SemanticMarker()
+{
+}
+
+QString SemanticMarker::fileName() const
+{
+ if (!m_unit)
+ return QString();
+
+ return m_unit->fileName();
+}
+
+void SemanticMarker::setFileName(const QString &fileName)
+{
+ if (this->fileName() == fileName)
+ return;
+
+ QStringList oldOptions;
+ if (m_unit)
+ oldOptions = m_unit->compilationOptions();
+ m_unit.reset(new Unit(fileName));
+ if (!oldOptions.isEmpty())
+ m_unit->setCompilationOptions(oldOptions);
+
+ unsigned clangOpts = clang_defaultEditingTranslationUnitOptions();
+ clangOpts |= CXTranslationUnit_Incomplete;
+ clangOpts |= CXTranslationUnit_DetailedPreprocessingRecord;
+ clangOpts &= ~CXTranslationUnit_CacheCompletionResults;
+ m_unit->setManagementOptions(clangOpts);
+}
+
+void SemanticMarker::setCompilationOptions(const QStringList &options)
+{
+ Q_ASSERT(m_unit);
+
+ if (m_unit->compilationOptions() == options)
+ return;
+
+ m_unit->setCompilationOptions(options);
+}
+
+void SemanticMarker::reparse(const UnsavedFiles &unsavedFiles)
+{
+ Q_ASSERT(m_unit);
+
+ m_unit->setUnsavedFiles(unsavedFiles);
+ if (m_unit->isLoaded())
+ m_unit->reparse();
+ else
+ m_unit->parse();
+}
+
+/**
+ * \brief Calculate one or several ranges and append diagnostic for each range
+ * Extracted from SemanticMarker::diagnostics() to reuse code
+ */
+static void appendDiagnostic(const CXDiagnostic &diag,
+ const CXSourceLocation &cxLocation,
+ Diagnostic::Severity severity,
+ const QString &spelling,
+ QList<Diagnostic> &diagnostics)
+{
+ const unsigned rangeCount = clang_getDiagnosticNumRanges(diag);
+ bool expandLocation = true;
+
+ for (unsigned i = 0; i < rangeCount; ++i) {
+ CXSourceRange r = clang_getDiagnosticRange(diag, i);
+ const SourceLocation &spellBegin = Internal::getSpellingLocation(clang_getRangeStart(r));
+ const SourceLocation &spellEnd = Internal::getSpellingLocation(clang_getRangeEnd(r));
+ unsigned length = spellEnd.offset() - spellBegin.offset();
+
+ // File name can be empty due clang bug
+ if (!spellBegin.fileName().isEmpty()) {
+ Diagnostic d(severity, spellBegin, length, spelling);
+ diagnostics.append(d);
+ expandLocation = false;
+ }
+ }
+
+ if (expandLocation) {
+ const SourceLocation &location = Internal::getExpansionLocation(cxLocation);
+ Diagnostic d(severity, location, 0, spelling);
+ diagnostics.append(d);
+ }
+}
+
+QList<Diagnostic> SemanticMarker::diagnostics() const
+{
+ QList<Diagnostic> diagnostics;
+ if (!m_unit || !m_unit->isLoaded())
+ return diagnostics;
+
+ const unsigned diagCount = m_unit->getNumDiagnostics();
+ for (unsigned i = 0; i < diagCount; ++i) {
+ ScopedCXDiagnostic diag(m_unit->getDiagnostic(i));
+
+ Diagnostic::Severity severity = static_cast<Diagnostic::Severity>(clang_getDiagnosticSeverity(diag));
+ if (severity == Diagnostic::Ignored || severity == Diagnostic::Note)
+ continue;
+
+ CXSourceLocation cxLocation = clang_getDiagnosticLocation(diag);
+ QString spelling = Internal::getQString(clang_getDiagnosticSpelling(diag));
+
+ // Attach messages with Diagnostic::Note severity
+ ScopedCXDiagnosticSet cxChildren(clang_getChildDiagnostics(diag));
+ const unsigned numChildren = clang_getNumDiagnosticsInSet(cxChildren);
+ const unsigned size = qMin(ATTACHED_NOTES_LIMIT, numChildren);
+ for (unsigned di = 0; di < size; ++di) {
+ ScopedCXDiagnostic child(clang_getDiagnosticInSet(cxChildren, di));
+ spelling.append(QLatin1String("\n "));
+ spelling.append(Internal::getQString(clang_getDiagnosticSpelling(child)));
+ }
+
+ // Fatal error may occur in another file, but it breaks whole parsing
+ // Typical fatal error is unresolved #include
+ if (severity == Diagnostic::Fatal) {
+ for (unsigned di = 0; di < numChildren; ++di) {
+ ScopedCXDiagnostic child(clang_getDiagnosticInSet(cxChildren, di));
+ appendDiagnostic(child, clang_getDiagnosticLocation(child), Diagnostic::Warning, spelling, diagnostics);
+ }
+ }
+
+ appendDiagnostic(diag, cxLocation, severity, spelling, diagnostics);
+ }
+
+ return diagnostics;
+}
+
+QList<TextEditor::BlockRange> SemanticMarker::ifdefedOutBlocks() const
+{
+ QList<TextEditor::BlockRange> blocks;
+
+ if (!m_unit || !m_unit->isLoaded())
+ return blocks;
+
+#if CINDEX_VERSION_MINOR >= 21
+ CXSourceRangeList *skippedRanges = clang_getSkippedRanges(m_unit->clangTranslationUnit(),
+ m_unit->getFile());
+ blocks.reserve(skippedRanges->count);
+ for (unsigned i = 0; i < skippedRanges->count; ++i) {
+ const CXSourceRange &r = skippedRanges->ranges[i];
+ const SourceLocation &spellBegin = Internal::getSpellingLocation(clang_getRangeStart(r));
+ if (spellBegin.fileName() != fileName())
+ continue;
+ const SourceLocation &spellEnd = Internal::getSpellingLocation(clang_getRangeEnd(r));
+ const int begin = spellBegin.offset() + 1;
+ const int end = spellEnd.offset() - spellEnd.column();
+ blocks.append(TextEditor::BlockRange(begin, end));
+ }
+ clang_disposeSourceRangeList(skippedRanges);
+#endif
+
+ return blocks;
+}
+
+namespace {
+static void add(QList<SourceMarker> &markers,
+ const CXSourceRange &extent,
+ SourceMarker::Kind kind)
+{
+ CXSourceLocation start = clang_getRangeStart(extent);
+ CXSourceLocation end = clang_getRangeEnd(extent);
+ const SourceLocation &location = Internal::getExpansionLocation(start);
+ const SourceLocation &locationEnd = Internal::getExpansionLocation(end);
+
+ if (location.offset() < locationEnd.offset()) {
+ const unsigned length = locationEnd.offset() - location.offset();
+ markers.append(SourceMarker(location, length, kind));
+ }
+}
+
+/**
+ * @brief Selects correct highlighting for cursor that is reference
+ * @return SourceMarker::Unknown if cannot select highlighting
+ */
+static SourceMarker::Kind getKindByReferencedCursor(const CXCursor &cursor)
+{
+ const CXCursor referenced = clang_getCursorReferenced(cursor);
+ switch (clang_getCursorKind(referenced)) {
+ case CXCursor_EnumConstantDecl:
+ return SourceMarker::Enumeration;
+
+ case CXCursor_FieldDecl:
+ case CXCursor_ObjCIvarDecl:
+ case CXCursor_ObjCPropertyDecl:
+ return SourceMarker::Field;
+
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ case CXCursor_Constructor:
+ return SourceMarker::Function;
+
+ case CXCursor_VarDecl:
+ case CXCursor_ParmDecl:
+ case CXCursor_NonTypeTemplateParameter:
+ return SourceMarker::Local;
+
+ case CXCursor_CXXMethod:
+ if (clang_CXXMethod_isVirtual(referenced))
+ return SourceMarker::VirtualMethod;
+ else
+ return SourceMarker::Function;
+
+ case CXCursor_ObjCClassMethodDecl:
+ case CXCursor_ObjCInstanceMethodDecl:
+ // calling method as property, e.h. "layer.shouldRasterize = YES"
+ return SourceMarker::Field;
+
+ case CXCursor_UnexposedDecl:
+ // NSObject "self" method which is a pseudo keyword
+ if (clang_getCursorLanguage(referenced) == CXLanguage_ObjC)
+ return SourceMarker::PseudoKeyword;
+ break;
+
+ default:
+ break;
+ }
+ return SourceMarker::Unknown;
+}
+
+static const QSet<QString> ObjcPseudoKeywords = QSet<QString>()
+ << QLatin1String("end")
+ << QLatin1String("try")
+ << QLatin1String("defs")
+ << QLatin1String("throw")
+ << QLatin1String("class")
+ << QLatin1String("catch")
+ << QLatin1String("encode")
+ << QLatin1String("public")
+ << QLatin1String("dynamic")
+ << QLatin1String("finally")
+ << QLatin1String("package")
+ << QLatin1String("private")
+ << QLatin1String("optional")
+ << QLatin1String("property")
+ << QLatin1String("protocol")
+ << QLatin1String("required")
+ << QLatin1String("selector")
+ << QLatin1String("interface")
+ << QLatin1String("protected")
+ << QLatin1String("synthesize")
+ << QLatin1String("not_keyword")
+ << QLatin1String("synchronized")
+ << QLatin1String("implementation")
+ << QLatin1String("compatibility_alias")
+ ;
+
+} // Anonymous namespace
+
+/**
+ * @brief SemanticMarker::sourceMarkersInRange
+ * @param firstLine - first line where to generate highlighting markers
+ * @param lastLine - last line where to generate highlighting markers
+ *
+ * There still two kinds of problems:
+ * - clang_annotateTokens() can return wrong cursor, and it's normal behavior
+ * - some cases no handled
+ *
+ * Problems caused by wrong cursors:
+ * - range-based for from C++ 2011
+ * - identifiers in some compound statements have type DeclStmt
+ * or CompoundStmt which refers to top-level construction.
+ * - CXCursor_ObjCIvarDecl mapped to field, but instance variable have
+ * incorrect cursor kind if it declared in private interface
+ * @interface MyApplication() {
+ * NSArray* _items;
+ * }
+ *
+ * Missed cases:
+ * - global variables highlighted as locals
+ * - appropriate marker had not been selected for listed cursors:
+ * CXCursor_ObjCProtocolExpr, CXCursor_ObjCEncodeExpr,
+ * CXCursor_ObjCDynamicDecl, CXCursor_ObjCBridgedCastExpr,
+ * CXCursor_ObjCSuperClassRef
+ * - template members of template classes&functions always highlighted
+ * as members, even if they are functions - no way to differ found.
+ * - @1, @{}, @[]
+ */
+QList<SourceMarker> SemanticMarker::sourceMarkersInRange(unsigned firstLine,
+ unsigned lastLine)
+{
+ Q_ASSERT(m_unit);
+
+ QList<SourceMarker> result;
+
+ if (!m_unit->isLoaded())
+ return result;
+
+ // Highlighting called asynchronously, and a few lines at the end can be deleted for this time.
+ CXSourceRange unitRange = clang_getCursorExtent(m_unit->getTranslationUnitCursor());
+ SourceLocation unitEnd = getExpansionLocation(clang_getRangeEnd(unitRange));
+ if (lastLine > unitEnd.line())
+ lastLine = unitEnd.line();
+
+ if (firstLine > lastLine)
+ return result;
+
+ IdentifierTokens idTokens(*m_unit, firstLine, lastLine);
+
+ const CXSourceRange *atTokenExtent = 0;
+ for (unsigned i = 0; i < idTokens.count(); ++i) {
+ const CXToken &tok = idTokens.token(i);
+ CXTokenKind kind = clang_getTokenKind(tok);
+ if (atTokenExtent) {
+ if (CXToken_Literal == kind) {
+ if (m_unit->getTokenSpelling(tok).startsWith(QLatin1Char('"')))
+ add(result, *atTokenExtent, SourceMarker::ObjCString);
+ atTokenExtent = 0;
+ continue;
+ } else {
+ add(result, *atTokenExtent, SourceMarker::PseudoKeyword);
+ atTokenExtent = 0;
+ }
+ }
+
+ const CXSourceRange &tokenExtent = idTokens.extent(i);
+
+ if (CXToken_Keyword == kind) {
+ QString spell = m_unit->getTokenSpelling(tok);
+ if (ObjcPseudoKeywords.contains(spell))
+ add(result, tokenExtent, SourceMarker::PseudoKeyword);
+ continue;
+ }
+
+ if (CXToken_Punctuation == kind) {
+ static const QLatin1String at("@");
+ if (m_unit->getTokenSpelling(tok) == at)
+ atTokenExtent = &tokenExtent;
+ continue;
+ }
+
+ if (CXToken_Identifier != kind)
+ continue;
+
+ const CXCursor &cursor = idTokens.cursor(i);
+ const CXCursorKind cursorKind = clang_getCursorKind(cursor);
+ if (clang_isInvalid(cursorKind))
+ continue;
+
+ switch (cursorKind) {
+ case CXCursor_EnumConstantDecl:
+ add(result, tokenExtent, SourceMarker::Enumeration);
+ break;
+
+ case CXCursor_ClassDecl:
+ case CXCursor_UnionDecl:
+ case CXCursor_ClassTemplate:
+ case CXCursor_ClassTemplatePartialSpecialization:
+ case CXCursor_EnumDecl:
+ case CXCursor_Namespace:
+ case CXCursor_NamespaceRef:
+ case CXCursor_NamespaceAlias:
+ case CXCursor_StructDecl:
+ case CXCursor_TemplateRef:
+ case CXCursor_TypeRef:
+ case CXCursor_TypedefDecl:
+ case CXCursor_Constructor:
+ case CXCursor_TemplateTypeParameter:
+ case CXCursor_TemplateTemplateParameter:
+ case CXCursor_UnexposedDecl: /* friend class MyClass; */
+ add(result, tokenExtent, SourceMarker::Type);
+ break;
+
+ case CXCursor_ParmDecl:
+ case CXCursor_VariableRef:
+ case CXCursor_VarDecl:
+ case CXCursor_NonTypeTemplateParameter:
+ add(result, tokenExtent, SourceMarker::Local);
+ break;
+
+ case CXCursor_MemberRefExpr:
+ case CXCursor_MemberRef:
+ case CXCursor_DeclRefExpr:
+ case CXCursor_CallExpr: {
+ SourceMarker::Kind kind = getKindByReferencedCursor(cursor);
+ if (kind == SourceMarker::Unknown && cursorKind == CXCursor_MemberRefExpr) {
+ /* template class member in template function */
+ kind = SourceMarker::Field;
+ }
+ if (kind != SourceMarker::Unknown)
+ add(result, tokenExtent, kind);
+ } break;
+
+ case CXCursor_FieldDecl:
+ add(result, tokenExtent, SourceMarker::Field);
+ break;
+
+ case CXCursor_Destructor:
+ case CXCursor_CXXMethod: {
+ if (clang_CXXMethod_isVirtual(cursor))
+ add(result, tokenExtent, SourceMarker::VirtualMethod);
+ else
+ add(result, tokenExtent, SourceMarker::Function);
+ } break;
+
+ case CXCursor_CXXOverrideAttr:
+ case CXCursor_CXXFinalAttr:
+ case CXCursor_AnnotateAttr: // 'annotate' in '__attribute__((annotate("AnyComment")))'
+ case CXCursor_UnexposedAttr: // 'align' in '__declspec(align(8))'
+ add(result, tokenExtent, SourceMarker::PseudoKeyword);
+ break;
+
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ case CXCursor_OverloadedDeclRef:
+ add(result, tokenExtent, SourceMarker::Function);
+ break;
+
+ case CXCursor_ObjCInstanceMethodDecl:
+ case CXCursor_ObjCClassMethodDecl:
+ case CXCursor_ObjCSelectorExpr:
+ add(result, tokenExtent, SourceMarker::ObjectiveCMessage);
+ break;
+
+ case CXCursor_ObjCMessageExpr: {
+ static const QLatin1String super("super");
+ if (m_unit->getTokenSpelling(tok) == super)
+ add(result, tokenExtent, SourceMarker::PseudoKeyword);
+ else
+ add(result, tokenExtent, SourceMarker::ObjectiveCMessage);
+ } break;
+
+ case CXCursor_ObjCCategoryDecl:
+ case CXCursor_ObjCCategoryImplDecl:
+ case CXCursor_ObjCImplementationDecl:
+ case CXCursor_ObjCInterfaceDecl:
+ case CXCursor_ObjCProtocolDecl:
+ case CXCursor_ObjCProtocolRef:
+ case CXCursor_ObjCClassRef:
+ case CXCursor_ObjCSuperClassRef:
+ case CXCursor_TypeAliasDecl: // C++11 type alias: 'using value_t = T'
+ add(result, tokenExtent, SourceMarker::Type);
+ break;
+
+ case CXCursor_ObjCSynthesizeDecl:
+ case CXCursor_ObjCDynamicDecl:
+ case CXCursor_ObjCPropertyDecl:
+ case CXCursor_ObjCIvarDecl:
+ add(result, tokenExtent, SourceMarker::Field);
+ break;
+
+ case CXCursor_MacroDefinition:
+ case CXCursor_MacroExpansion:
+ add(result, tokenExtent, SourceMarker::Macro);
+ break;
+
+ case CXCursor_LabelRef:
+ case CXCursor_LabelStmt:
+ add(result, tokenExtent, SourceMarker::Label);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return result;
+}
+
+Unit SemanticMarker::unit() const
+{
+ return *m_unit;
+}