/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing ** ** 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 The Qt Company. For licensing terms and ** conditions see http://www.qt.io/terms-conditions. For further information ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "cppelementevaluator.h" #include "cppeditor.h" #include #include #include #include #include #include #include #include #include using namespace CPlusPlus; namespace CppEditor { namespace Internal { static QStringList stripName(const QString &name) { QStringList all; all << name; int colonColon = 0; const int size = name.size(); while ((colonColon = name.indexOf(QLatin1String("::"), colonColon)) != -1) { all << name.right(size - colonColon - 2); colonColon += 2; } return all; } CppElementEvaluator::CppElementEvaluator(TextEditor::TextEditorWidget *editor) : m_editor(editor), m_modelManager(CppTools::CppModelManager::instance()), m_tc(editor->textCursor()), m_lookupBaseClasses(false), m_lookupDerivedClasses(false) {} void CppElementEvaluator::setTextCursor(const QTextCursor &tc) { m_tc = tc; } void CppElementEvaluator::setLookupBaseClasses(const bool lookup) { m_lookupBaseClasses = lookup; } void CppElementEvaluator::setLookupDerivedClasses(const bool lookup) { m_lookupDerivedClasses = lookup; } // @todo: Consider refactoring code from CppEditor::findLinkAt into here. void CppElementEvaluator::execute() { clear(); if (!m_modelManager) return; const Snapshot &snapshot = m_modelManager->snapshot(); Document::Ptr doc = snapshot.document(m_editor->textDocument()->filePath()); if (!doc) return; int line = 0; int column = 0; const int pos = m_tc.position(); m_editor->convertPosition(pos, &line, &column); checkDiagnosticMessage(pos); if (!matchIncludeFile(doc, line) && !matchMacroInUse(doc, pos)) { CppTools::moveCursorToEndOfIdentifier(&m_tc); // Fetch the expression's code ExpressionUnderCursor expressionUnderCursor; const QString &expression = expressionUnderCursor(m_tc); Scope *scope = doc->scopeAt(line, column); TypeOfExpression typeOfExpression; typeOfExpression.init(doc, snapshot); // make possible to instantiate templates typeOfExpression.setExpandTemplates(true); const QList &lookupItems = typeOfExpression(expression.toUtf8(), scope); if (lookupItems.isEmpty()) return; const LookupItem &lookupItem = lookupItems.first(); // ### TODO: select best candidate. handleLookupItemMatch(snapshot, lookupItem, typeOfExpression.context(), scope); } } void CppElementEvaluator::checkDiagnosticMessage(int pos) { foreach (const QTextEdit::ExtraSelection &sel, m_editor->extraSelections(TextEditor::TextEditorWidget::CodeWarningsSelection)) { if (pos >= sel.cursor.selectionStart() && pos <= sel.cursor.selectionEnd()) { m_diagnosis = sel.format.toolTip(); break; } } } bool CppElementEvaluator::matchIncludeFile(const CPlusPlus::Document::Ptr &document, unsigned line) { foreach (const Document::Include &includeFile, document->resolvedIncludes()) { if (includeFile.line() == line) { m_element = QSharedPointer(new CppInclude(includeFile)); return true; } } return false; } bool CppElementEvaluator::matchMacroInUse(const CPlusPlus::Document::Ptr &document, unsigned pos) { foreach (const Document::MacroUse &use, document->macroUses()) { if (use.containsUtf16charOffset(pos)) { const unsigned begin = use.utf16charsBegin(); if (pos < begin + use.macro().nameToQString().size()) { m_element = QSharedPointer(new CppMacro(use.macro())); return true; } } } return false; } void CppElementEvaluator::handleLookupItemMatch(const Snapshot &snapshot, const LookupItem &lookupItem, const LookupContext &context, const Scope *scope) { Symbol *declaration = lookupItem.declaration(); if (!declaration) { const QString &type = Overview().prettyType(lookupItem.type(), QString()); // special case for bug QTCREATORBUG-4780 if (scope && scope->isFunction() && lookupItem.type().match(scope->asFunction()->returnType())) { return; } m_element = QSharedPointer(new Unknown(type)); } else { const FullySpecifiedType &type = declaration->type(); if (declaration->isNamespace()) { m_element = QSharedPointer(new CppNamespace(declaration)); } else if (declaration->isClass() || declaration->isForwardClassDeclaration() || (declaration->isTemplate() && declaration->asTemplate()->declaration() && (declaration->asTemplate()->declaration()->isClass() || declaration->asTemplate()->declaration()->isForwardClassDeclaration()))) { LookupContext contextToUse = context; if (declaration->isForwardClassDeclaration()) if (Symbol *classDeclaration = m_symbolFinder.findMatchingClassDeclaration(declaration, snapshot)) { declaration = classDeclaration; const QString fileName = QString::fromUtf8(declaration->fileName(), declaration->fileNameLength()); const Document::Ptr declarationDocument = snapshot.document(fileName); if (declarationDocument != context.thisDocument()) contextToUse = LookupContext(declarationDocument, snapshot); } CppClass *cppClass = new CppClass(declaration); if (m_lookupBaseClasses) cppClass->lookupBases(declaration, contextToUse); if (m_lookupDerivedClasses) cppClass->lookupDerived(declaration, snapshot); m_element = QSharedPointer(cppClass); } else if (Enum *enumDecl = declaration->asEnum()) { m_element = QSharedPointer(new CppEnum(enumDecl)); } else if (EnumeratorDeclaration *enumerator = dynamic_cast(declaration)) { m_element = QSharedPointer(new CppEnumerator(enumerator)); } else if (declaration->isTypedef()) { m_element = QSharedPointer(new CppTypedef(declaration)); } else if (declaration->isFunction() || (type.isValid() && type->isFunctionType()) || declaration->isTemplate()) { m_element = QSharedPointer(new CppFunction(declaration)); } else if (declaration->isDeclaration() && type.isValid()) { m_element = QSharedPointer( new CppVariable(declaration, context, lookupItem.scope())); } else { m_element = QSharedPointer(new CppDeclarableElement(declaration)); } } } bool CppElementEvaluator::identifiedCppElement() const { return !m_element.isNull(); } const QSharedPointer &CppElementEvaluator::cppElement() const { return m_element; } bool CppElementEvaluator::hasDiagnosis() const { return !m_diagnosis.isEmpty(); } const QString &CppElementEvaluator::diagnosis() const { return m_diagnosis; } void CppElementEvaluator::clear() { m_element.clear(); m_diagnosis.clear(); } // CppElement CppElement::CppElement() : helpCategory(TextEditor::HelpItem::Unknown) {} CppElement::~CppElement() {} // Unknown Unknown::Unknown(const QString &type) : type(type) { tooltip = type; } // CppInclude CppInclude::CppInclude(const Document::Include &includeFile) : path(QDir::toNativeSeparators(includeFile.resolvedFileName())), fileName(QFileInfo(includeFile.resolvedFileName()).fileName()) { helpCategory = TextEditor::HelpItem::Brief; helpIdCandidates = QStringList(fileName); helpMark = fileName; link = TextEditor::TextEditorWidget::Link(path); tooltip = path; } // CppMacro CppMacro::CppMacro(const Macro ¯o) { helpCategory = TextEditor::HelpItem::Macro; const QString macroName = QString::fromUtf8(macro.name(), macro.name().size()); helpIdCandidates = QStringList(macroName); helpMark = macroName; link = TextEditor::TextEditorWidget::Link(macro.fileName(), macro.line()); tooltip = macro.toStringWithLineBreaks(); } // CppDeclarableElement CppDeclarableElement::CppDeclarableElement(Symbol *declaration) : CppElement() , declaration(declaration) , icon(Icons().iconForSymbol(declaration)) { Overview overview; overview.showArgumentNames = true; overview.showReturnTypes = true; name = overview.prettyName(declaration->name()); if (declaration->enclosingScope()->isClass() || declaration->enclosingScope()->isNamespace() || declaration->enclosingScope()->isEnum()) { qualifiedName = overview.prettyName(LookupContext::fullyQualifiedName(declaration)); helpIdCandidates = stripName(qualifiedName); } else { qualifiedName = name; helpIdCandidates.append(name); } tooltip = overview.prettyType(declaration->type(), qualifiedName); link = CppEditorWidget::linkToSymbol(declaration); helpMark = name; } // CppNamespace CppNamespace::CppNamespace(Symbol *declaration) : CppDeclarableElement(declaration) { helpCategory = TextEditor::HelpItem::ClassOrNamespace; tooltip = qualifiedName; } // CppClass CppClass::CppClass(Symbol *declaration) : CppDeclarableElement(declaration) { helpCategory = TextEditor::HelpItem::ClassOrNamespace; tooltip = qualifiedName; } bool CppClass::operator==(const CppClass &other) { return this->declaration == other.declaration; } void CppClass::lookupBases(Symbol *declaration, const CPlusPlus::LookupContext &context) { typedef QPair Data; if (ClassOrNamespace *clazz = context.lookupType(declaration)) { QSet visited; QQueue q; q.enqueue(qMakePair(clazz, this)); while (!q.isEmpty()) { Data current = q.dequeue(); clazz = current.first; visited.insert(clazz); const QList &bases = clazz->usings(); foreach (ClassOrNamespace *baseClass, bases) { const QList &symbols = baseClass->symbols(); foreach (Symbol *symbol, symbols) { if (symbol->isClass() && ( clazz = context.lookupType(symbol)) && !visited.contains(clazz)) { CppClass baseCppClass(symbol); CppClass *cppClass = current.second; cppClass->bases.append(baseCppClass); q.enqueue(qMakePair(clazz, &cppClass->bases.last())); } } } } } } void CppClass::lookupDerived(CPlusPlus::Symbol *declaration, const CPlusPlus::Snapshot &snapshot) { typedef QPair Data; CppTools::TypeHierarchyBuilder builder(declaration, snapshot); const CppTools::TypeHierarchy &completeHierarchy = builder.buildDerivedTypeHierarchy(); QQueue q; q.enqueue(qMakePair(this, completeHierarchy)); while (!q.isEmpty()) { const Data ¤t = q.dequeue(); CppClass *clazz = current.first; const CppTools::TypeHierarchy &classHierarchy = current.second; foreach (const CppTools::TypeHierarchy &derivedHierarchy, classHierarchy.hierarchy()) { clazz->derived.append(CppClass(derivedHierarchy.symbol())); q.enqueue(qMakePair(&clazz->derived.last(), derivedHierarchy)); } } } // CppFunction CppFunction::CppFunction(Symbol *declaration) : CppDeclarableElement(declaration) { helpCategory = TextEditor::HelpItem::Function; const FullySpecifiedType &type = declaration->type(); // Functions marks can be found either by the main overload or signature based // (with no argument names and no return). Help ids have no signature at all. Overview overview; overview.showDefaultArguments = false; helpMark = overview.prettyType(type, name); overview.showFunctionSignatures = false; helpIdCandidates.append(overview.prettyName(declaration->name())); } // CppEnum CppEnum::CppEnum(Enum *declaration) : CppDeclarableElement(declaration) { helpCategory = TextEditor::HelpItem::Enum; tooltip = qualifiedName; } // CppTypedef CppTypedef::CppTypedef(Symbol *declaration) : CppDeclarableElement(declaration) { helpCategory = TextEditor::HelpItem::Typedef; tooltip = Overview().prettyType(declaration->type(), qualifiedName); } // CppVariable CppVariable::CppVariable(Symbol *declaration, const LookupContext &context, Scope *scope) : CppDeclarableElement(declaration) { const FullySpecifiedType &type = declaration->type(); const Name *typeName = 0; if (type->isNamedType()) { typeName = type->asNamedType()->name(); } else if (type->isPointerType() || type->isReferenceType()) { FullySpecifiedType associatedType; if (type->isPointerType()) associatedType = type->asPointerType()->elementType(); else associatedType = type->asReferenceType()->elementType(); if (associatedType->isNamedType()) typeName = associatedType->asNamedType()->name(); } if (typeName) { if (ClassOrNamespace *clazz = context.lookupType(typeName, scope)) { if (!clazz->symbols().isEmpty()) { Overview overview; Symbol *symbol = clazz->symbols().at(0); const QString &name = overview.prettyName(LookupContext::fullyQualifiedName(symbol)); if (!name.isEmpty()) { tooltip = name; helpCategory = TextEditor::HelpItem::ClassOrNamespace; const QStringList &allNames = stripName(name); if (!allNames.isEmpty()) { helpMark = allNames.last(); helpIdCandidates = allNames; } } } } } } CppEnumerator::CppEnumerator(CPlusPlus::EnumeratorDeclaration *declaration) : CppDeclarableElement(declaration) { helpCategory = TextEditor::HelpItem::Enum; Overview overview; Symbol *enumSymbol = declaration->enclosingScope()->asEnum(); const QString enumName = overview.prettyName(LookupContext::fullyQualifiedName(enumSymbol)); const QString enumeratorName = overview.prettyName(declaration->name()); QString enumeratorValue; if (const StringLiteral *value = declaration->constantValue()) enumeratorValue = QString::fromUtf8(value->chars(), value->size()); helpMark = overview.prettyName(enumSymbol->name()); tooltip = enumeratorName; if (!enumName.isEmpty()) tooltip.prepend(enumName + QLatin1Char(' ')); if (!enumeratorValue.isEmpty()) tooltip.append(QLatin1String(" = ") + enumeratorValue); } } // namespace Internal } // namespace CppEditor