cppelementevaluator.cpp 16.8 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11 12
** 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
Eike Ziller's avatar
Eike Ziller committed
13 14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** 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.
24
**
hjk's avatar
hjk committed
25 26
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
30 31 32

#include "cppelementevaluator.h"

33 34
#include "cppeditor.h"

35
#include <cpptools/cpptoolsreuse.h>
36
#include <cpptools/typehierarchybuilder.h>
37

38
#include <cplusplus/ExpressionUnderCursor.h>
39 40
#include <cplusplus/Icons.h>
#include <cplusplus/TypeOfExpression.h>
41

42 43 44 45
#include <QDir>
#include <QFileInfo>
#include <QSet>
#include <QQueue>
46 47 48

using namespace CPlusPlus;

49 50 51 52 53 54 55 56 57 58 59 60
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;
61
    }
62
    return all;
63 64
}

65
CppElementEvaluator::CppElementEvaluator(TextEditor::TextEditorWidget *editor) :
66
    m_editor(editor),
67
    m_modelManager(CppTools::CppModelManager::instance()),
68
    m_tc(editor->textCursor()),
69 70
    m_lookupBaseClasses(false),
    m_lookupDerivedClasses(false)
71 72 73 74 75 76 77 78
{}

void CppElementEvaluator::setTextCursor(const QTextCursor &tc)
{ m_tc = tc; }

void CppElementEvaluator::setLookupBaseClasses(const bool lookup)
{ m_lookupBaseClasses = lookup; }

79 80 81
void CppElementEvaluator::setLookupDerivedClasses(const bool lookup)
{ m_lookupDerivedClasses = lookup; }

82
// @todo: Consider refactoring code from CppEditor::findLinkAt into here.
83
void CppElementEvaluator::execute()
84
{
85 86
    clear();

87 88 89 90
    if (!m_modelManager)
        return;

    const Snapshot &snapshot = m_modelManager->snapshot();
91
    Document::Ptr doc = snapshot.document(m_editor->textDocument()->filePath().toString());
92 93 94 95 96 97 98 99
    if (!doc)
        return;

    int line = 0;
    int column = 0;
    const int pos = m_tc.position();
    m_editor->convertPosition(pos, &line, &column);

100
    checkDiagnosticMessage(pos);
101

102
    if (!matchIncludeFile(doc, line) && !matchMacroInUse(doc, pos)) {
103
        CppTools::moveCursorToEndOfIdentifier(&m_tc);
104

105 106 107 108
        // Fetch the expression's code
        ExpressionUnderCursor expressionUnderCursor;
        const QString &expression = expressionUnderCursor(m_tc);
        Scope *scope = doc->scopeAt(line, column);
109

110 111
        TypeOfExpression typeOfExpression;
        typeOfExpression.init(doc, snapshot);
112 113
        // make possible to instantiate templates
        typeOfExpression.setExpandTemplates(true);
114
        const QList<LookupItem> &lookupItems = typeOfExpression(expression.toUtf8(), scope);
115 116 117 118
        if (lookupItems.isEmpty())
            return;

        const LookupItem &lookupItem = lookupItems.first(); // ### TODO: select best candidate.
119
        handleLookupItemMatch(snapshot, lookupItem, typeOfExpression.context(), scope);
120 121 122
    }
}

123
void CppElementEvaluator::checkDiagnosticMessage(int pos)
124
{
125
    foreach (const QTextEdit::ExtraSelection &sel,
126
             m_editor->extraSelections(TextEditor::TextEditorWidget::CodeWarningsSelection)) {
127 128
        if (pos >= sel.cursor.selectionStart() && pos <= sel.cursor.selectionEnd()) {
            m_diagnosis = sel.format.toolTip();
129
            break;
130 131 132 133 134 135
        }
    }
}

bool CppElementEvaluator::matchIncludeFile(const CPlusPlus::Document::Ptr &document, unsigned line)
{
136
    foreach (const Document::Include &includeFile, document->resolvedIncludes()) {
137 138 139 140 141 142 143 144 145 146 147
        if (includeFile.line() == line) {
            m_element = QSharedPointer<CppElement>(new CppInclude(includeFile));
            return true;
        }
    }
    return false;
}

bool CppElementEvaluator::matchMacroInUse(const CPlusPlus::Document::Ptr &document, unsigned pos)
{
    foreach (const Document::MacroUse &use, document->macroUses()) {
148 149 150
        if (use.containsUtf16charOffset(pos)) {
            const unsigned begin = use.utf16charsBegin();
            if (pos < begin + use.macro().nameToQString().size()) {
151 152 153 154 155 156 157 158 159 160
                m_element = QSharedPointer<CppElement>(new CppMacro(use.macro()));
                return true;
            }
        }
    }
    return false;
}

void CppElementEvaluator::handleLookupItemMatch(const Snapshot &snapshot,
                                                const LookupItem &lookupItem,
161 162
                                                const LookupContext &context,
                                                const Scope *scope)
163 164 165 166
{
    Symbol *declaration = lookupItem.declaration();
    if (!declaration) {
        const QString &type = Overview().prettyType(lookupItem.type(), QString());
167 168
        //  special case for bug QTCREATORBUG-4780
        if (scope && scope->isFunction()
169
                && lookupItem.type().match(scope->asFunction()->returnType())) {
170 171
            return;
        }
172 173 174 175 176
        m_element = QSharedPointer<CppElement>(new Unknown(type));
    } else {
        const FullySpecifiedType &type = declaration->type();
        if (declaration->isNamespace()) {
            m_element = QSharedPointer<CppElement>(new CppNamespace(declaration));
177 178 179 180 181
        } else if (declaration->isClass()
                   || declaration->isForwardClassDeclaration()
                   || (declaration->isTemplate() && declaration->asTemplate()->declaration()
                       && (declaration->asTemplate()->declaration()->isClass()
                           || declaration->asTemplate()->declaration()->isForwardClassDeclaration()))) {
182
            LookupContext contextToUse = context;
183
            if (declaration->isForwardClassDeclaration())
184
                if (Symbol *classDeclaration =
185
                        m_symbolFinder.findMatchingClassDeclaration(declaration, snapshot)) {
186
                    declaration = classDeclaration;
187 188 189 190 191
                    const QString fileName = QString::fromUtf8(declaration->fileName(),
                                                               declaration->fileNameLength());
                    const Document::Ptr declarationDocument = snapshot.document(fileName);
                    if (declarationDocument != context.thisDocument())
                        contextToUse = LookupContext(declarationDocument, snapshot);
192
                }
193

194 195
            CppClass *cppClass = new CppClass(declaration);
            if (m_lookupBaseClasses)
196
                cppClass->lookupBases(declaration, contextToUse);
197 198
            if (m_lookupDerivedClasses)
                cppClass->lookupDerived(declaration, snapshot);
199
            m_element = QSharedPointer<CppElement>(cppClass);
200 201 202 203
        } else if (Enum *enumDecl = declaration->asEnum()) {
            m_element = QSharedPointer<CppElement>(new CppEnum(enumDecl));
        } else if (EnumeratorDeclaration *enumerator = dynamic_cast<EnumeratorDeclaration *>(declaration)) {
            m_element = QSharedPointer<CppElement>(new CppEnumerator(enumerator));
204 205
        } else if (declaration->isTypedef()) {
            m_element = QSharedPointer<CppElement>(new CppTypedef(declaration));
206 207 208
        } else if (declaration->isFunction()
                   || (type.isValid() && type->isFunctionType())
                   || declaration->isTemplate()) {
209 210 211 212 213 214 215 216 217 218
            m_element = QSharedPointer<CppElement>(new CppFunction(declaration));
        } else if (declaration->isDeclaration() && type.isValid()) {
            m_element = QSharedPointer<CppElement>(
                new CppVariable(declaration, context, lookupItem.scope()));
        } else {
            m_element = QSharedPointer<CppElement>(new CppDeclarableElement(declaration));
        }
    }
}

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
bool CppElementEvaluator::identifiedCppElement() const
{
    return !m_element.isNull();
}

const QSharedPointer<CppElement> &CppElementEvaluator::cppElement() const
{
    return m_element;
}

bool CppElementEvaluator::hasDiagnosis() const
{
    return !m_diagnosis.isEmpty();
}

const QString &CppElementEvaluator::diagnosis() const
{
    return m_diagnosis;
}

239
void CppElementEvaluator::clear()
240 241 242 243 244
{
    m_element.clear();
    m_diagnosis.clear();
}

245
// CppElement
246
CppElement::CppElement() : helpCategory(TextEditor::HelpItem::Unknown)
247 248 249 250 251 252
{}

CppElement::~CppElement()
{}

// Unknown
253
Unknown::Unknown(const QString &type) : type(type)
254
{
255
    tooltip = type;
256 257 258 259 260 261
}


// CppInclude

CppInclude::CppInclude(const Document::Include &includeFile) :
262 263
    path(QDir::toNativeSeparators(includeFile.resolvedFileName())),
    fileName(QFileInfo(includeFile.resolvedFileName()).fileName())
264
{
265 266 267
    helpCategory = TextEditor::HelpItem::Brief;
    helpIdCandidates = QStringList(fileName);
    helpMark = fileName;
268
    link = TextEditor::TextEditorWidget::Link(path);
269
    tooltip = path;
270 271 272
}

// CppMacro
273
CppMacro::CppMacro(const Macro &macro)
274
{
275
    helpCategory = TextEditor::HelpItem::Macro;
276
    const QString macroName = QString::fromUtf8(macro.name(), macro.name().size());
277 278
    helpIdCandidates = QStringList(macroName);
    helpMark = macroName;
279
    link = TextEditor::TextEditorWidget::Link(macro.fileName(), macro.line());
280
    tooltip = macro.toStringWithLineBreaks();
281 282 283
}

// CppDeclarableElement
284

285 286 287 288
CppDeclarableElement::CppDeclarableElement(Symbol *declaration)
    : CppElement()
    , declaration(declaration)
    , icon(Icons().iconForSymbol(declaration))
289 290
{
    Overview overview;
291 292
    overview.showArgumentNames = true;
    overview.showReturnTypes = true;
293
    name = overview.prettyName(declaration->name());
294 295 296
    if (declaration->enclosingScope()->isClass() ||
        declaration->enclosingScope()->isNamespace() ||
        declaration->enclosingScope()->isEnum()) {
297 298
        qualifiedName = overview.prettyName(LookupContext::fullyQualifiedName(declaration));
        helpIdCandidates = stripName(qualifiedName);
299
    } else {
300 301
        qualifiedName = name;
        helpIdCandidates.append(name);
302 303
    }

304
    tooltip = overview.prettyType(declaration->type(), qualifiedName);
305
    link = CppEditorWidget::linkToSymbol(declaration);
306
    helpMark = name;
307 308 309 310 311
}

// CppNamespace
CppNamespace::CppNamespace(Symbol *declaration) : CppDeclarableElement(declaration)
{
312 313
    helpCategory = TextEditor::HelpItem::ClassOrNamespace;
    tooltip = qualifiedName;
314 315 316 317 318
}

// CppClass
CppClass::CppClass(Symbol *declaration) : CppDeclarableElement(declaration)
{
319 320
    helpCategory = TextEditor::HelpItem::ClassOrNamespace;
    tooltip = qualifiedName;
321 322
}

323 324 325 326 327
bool CppClass::operator==(const CppClass &other)
{
    return this->declaration == other.declaration;
}

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
void CppClass::lookupBases(Symbol *declaration, const CPlusPlus::LookupContext &context)
{
    typedef QPair<ClassOrNamespace *, CppClass *> Data;

    if (ClassOrNamespace *clazz = context.lookupType(declaration)) {
        QSet<ClassOrNamespace *> visited;

        QQueue<Data> q;
        q.enqueue(qMakePair(clazz, this));
        while (!q.isEmpty()) {
            Data current = q.dequeue();
            clazz = current.first;
            visited.insert(clazz);
            const QList<ClassOrNamespace *> &bases = clazz->usings();
            foreach (ClassOrNamespace *baseClass, bases) {
                const QList<Symbol *> &symbols = baseClass->symbols();
                foreach (Symbol *symbol, symbols) {
                    if (symbol->isClass() && (
                        clazz = context.lookupType(symbol)) &&
                        !visited.contains(clazz)) {
                        CppClass baseCppClass(symbol);
                        CppClass *cppClass = current.second;
350 351
                        cppClass->bases.append(baseCppClass);
                        q.enqueue(qMakePair(clazz, &cppClass->bases.last()));
352 353 354 355 356 357 358
                    }
                }
            }
        }
    }
}

359 360
void CppClass::lookupDerived(CPlusPlus::Symbol *declaration, const CPlusPlus::Snapshot &snapshot)
{
361
    typedef QPair<CppClass *, CppTools::TypeHierarchy> Data;
362

363 364
    CppTools::TypeHierarchyBuilder builder(declaration, snapshot);
    const CppTools::TypeHierarchy &completeHierarchy = builder.buildDerivedTypeHierarchy();
365 366 367 368 369 370

    QQueue<Data> q;
    q.enqueue(qMakePair(this, completeHierarchy));
    while (!q.isEmpty()) {
        const Data &current = q.dequeue();
        CppClass *clazz = current.first;
371 372
        const CppTools::TypeHierarchy &classHierarchy = current.second;
        foreach (const CppTools::TypeHierarchy &derivedHierarchy, classHierarchy.hierarchy()) {
373 374
            clazz->derived.append(CppClass(derivedHierarchy.symbol()));
            q.enqueue(qMakePair(&clazz->derived.last(), derivedHierarchy));
375 376 377 378
        }
    }
}

379
// CppFunction
380 381
CppFunction::CppFunction(Symbol *declaration)
    : CppDeclarableElement(declaration)
382
{
383
    helpCategory = TextEditor::HelpItem::Function;
384 385 386 387 388 389

    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;
390
    overview.showDefaultArguments = false;
391
    helpMark = overview.prettyType(type, name);
392

393
    overview.showFunctionSignatures = false;
394
    helpIdCandidates.append(overview.prettyName(declaration->name()));
395 396 397
}

// CppEnum
398 399
CppEnum::CppEnum(Enum *declaration)
    : CppDeclarableElement(declaration)
400
{
401 402
    helpCategory = TextEditor::HelpItem::Enum;
    tooltip = qualifiedName;
403 404 405
}

// CppTypedef
406
CppTypedef::CppTypedef(Symbol *declaration) : CppDeclarableElement(declaration)
407
{
408 409
    helpCategory = TextEditor::HelpItem::Typedef;
    tooltip = Overview().prettyType(declaration->type(), qualifiedName);
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
}

// 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));
438
                if (!name.isEmpty()) {
439 440
                    tooltip = name;
                    helpCategory = TextEditor::HelpItem::ClassOrNamespace;
441 442
                    const QStringList &allNames = stripName(name);
                    if (!allNames.isEmpty()) {
443 444
                        helpMark = allNames.last();
                        helpIdCandidates = allNames;
445 446
                    }
                }
447 448 449 450 451
            }
        }
    }
}

452 453 454
CppEnumerator::CppEnumerator(CPlusPlus::EnumeratorDeclaration *declaration)
    : CppDeclarableElement(declaration)
{
455
    helpCategory = TextEditor::HelpItem::Enum;
456 457 458 459 460 461 462

    Overview overview;

    Symbol *enumSymbol = declaration->enclosingScope()->asEnum();
    const QString enumName = overview.prettyName(LookupContext::fullyQualifiedName(enumSymbol));
    const QString enumeratorName = overview.prettyName(declaration->name());
    QString enumeratorValue;
463
    if (const StringLiteral *value = declaration->constantValue())
464 465
        enumeratorValue = QString::fromUtf8(value->chars(), value->size());

466
    helpMark = overview.prettyName(enumSymbol->name());
467

468
    tooltip = enumeratorName;
469 470 471 472 473
    if (!enumName.isEmpty())
        tooltip.prepend(enumName + QLatin1Char(' '));
    if (!enumeratorValue.isEmpty())
        tooltip.append(QLatin1String(" = ") + enumeratorValue);
}
474 475 476

} // namespace Internal
} // namespace CppEditor