forked from qt-creator/qt-creator
Highlight local and member symbols.
This commit is contained in:
@@ -28,6 +28,8 @@
|
||||
**************************************************************************/
|
||||
|
||||
#include "cppchecksymbols.h"
|
||||
#include "cpplocalsymbols.h"
|
||||
|
||||
#include <cplusplus/Overview.h>
|
||||
|
||||
#include <Names.h>
|
||||
@@ -45,6 +47,7 @@
|
||||
#include <qtconcurrent/runextensions.h>
|
||||
|
||||
using namespace CPlusPlus;
|
||||
using namespace CppEditor::Internal;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -53,6 +56,7 @@ class CollectTypes: protected SymbolVisitor
|
||||
Document::Ptr _doc;
|
||||
Snapshot _snapshot;
|
||||
QSet<QByteArray> _types;
|
||||
QSet<QByteArray> _members;
|
||||
QList<ScopedSymbol *> _scopes;
|
||||
QList<NameAST *> _names;
|
||||
bool _mainDocument;
|
||||
@@ -70,6 +74,11 @@ public:
|
||||
return _types;
|
||||
}
|
||||
|
||||
const QSet<QByteArray> &members() const
|
||||
{
|
||||
return _members;
|
||||
}
|
||||
|
||||
const QList<ScopedSymbol *> &scopes() const
|
||||
{
|
||||
return _scopes;
|
||||
@@ -126,6 +135,18 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void addMember(const Name *name)
|
||||
{
|
||||
if (! name) {
|
||||
return;
|
||||
|
||||
} else if (name->isNameId()) {
|
||||
const Identifier *id = name->identifier();
|
||||
_members.insert(QByteArray::fromRawData(id->chars(), id->size()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void addScope(ScopedSymbol *symbol)
|
||||
{
|
||||
if (_mainDocument)
|
||||
@@ -166,6 +187,8 @@ protected:
|
||||
{
|
||||
if (symbol->isTypedef())
|
||||
addType(symbol->name());
|
||||
else if (! symbol->type()->isFunctionType() && symbol->enclosingSymbol()->isClass())
|
||||
addMember(symbol->name());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -267,6 +290,7 @@ CheckSymbols::CheckSymbols(Document::Ptr doc, const LookupContext &context)
|
||||
_fileName = doc->fileName();
|
||||
CollectTypes collectTypes(doc, context.snapshot());
|
||||
_potentialTypes = collectTypes.types();
|
||||
_potentialMembers = collectTypes.members();
|
||||
_scopes = collectTypes.scopes();
|
||||
_flushRequested = false;
|
||||
_flushLine = 0;
|
||||
@@ -351,6 +375,12 @@ bool CheckSymbols::visit(NamedTypeSpecifierAST *)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckSymbols::visit(MemberAccessAST *ast)
|
||||
{
|
||||
accept(ast->base_expression);
|
||||
return false;
|
||||
}
|
||||
|
||||
void CheckSymbols::checkNamespace(NameAST *name)
|
||||
{
|
||||
if (! name)
|
||||
@@ -380,6 +410,28 @@ void CheckSymbols::checkName(NameAST *ast)
|
||||
Scope *scope = findScope(ast);
|
||||
const QList<Symbol *> candidates = _context.lookup(ast->name, scope);
|
||||
addTypeUsage(candidates, ast);
|
||||
} else if (_potentialMembers.contains(id)) {
|
||||
Scope *scope = findScope(ast);
|
||||
const QList<Symbol *> candidates = _context.lookup(ast->name, scope);
|
||||
addMemberUsage(candidates, ast);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckSymbols::checkMemberName(NameAST *ast)
|
||||
{
|
||||
if (ast && ast->name) {
|
||||
if (const Identifier *ident = ast->name->identifier()) {
|
||||
const QByteArray id = QByteArray::fromRawData(ident->chars(), ident->size());
|
||||
if (_potentialMembers.contains(id)) {
|
||||
Scope *scope = findScope(ast);
|
||||
const QList<Symbol *> candidates = _context.lookup(ast->name, scope);
|
||||
addMemberUsage(candidates, ast);
|
||||
} else if (_potentialMembers.contains(id)) {
|
||||
Scope *scope = findScope(ast);
|
||||
const QList<Symbol *> candidates = _context.lookup(ast->name, scope);
|
||||
addMemberUsage(candidates, ast);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -473,8 +525,28 @@ void CheckSymbols::endVisit(TemplateDeclarationAST *)
|
||||
_templateDeclarationStack.takeFirst();
|
||||
}
|
||||
|
||||
bool CheckSymbols::visit(FunctionDefinitionAST *ast)
|
||||
{
|
||||
_functionDefinitionStack.append(ast);
|
||||
const LocalSymbols locals(_doc, ast);
|
||||
QList<SemanticInfo::Use> uses;
|
||||
foreach (uses, locals.uses) {
|
||||
foreach (const SemanticInfo::Use &u, uses)
|
||||
addTypeUsage(u);
|
||||
}
|
||||
|
||||
accept(ast->decl_specifier_list);
|
||||
accept(ast->declarator);
|
||||
accept(ast->ctor_initializer);
|
||||
accept(ast->function_body);
|
||||
|
||||
_functionDefinitionStack.removeLast();
|
||||
return false;
|
||||
}
|
||||
|
||||
void CheckSymbols::addTypeUsage(const Use &use)
|
||||
{
|
||||
if (_functionDefinitionStack.isEmpty()) {
|
||||
if (_typeUsages.size() >= 50) {
|
||||
if (_flushRequested && use.line != _flushLine)
|
||||
flush();
|
||||
@@ -483,6 +555,7 @@ void CheckSymbols::addTypeUsage(const Use &use)
|
||||
_flushLine = use.line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_typeUsages.append(use);
|
||||
}
|
||||
@@ -538,6 +611,36 @@ void CheckSymbols::addTypeUsage(const QList<Symbol *> &candidates, NameAST *ast)
|
||||
}
|
||||
}
|
||||
|
||||
void CheckSymbols::addMemberUsage(const QList<Symbol *> &candidates, NameAST *ast)
|
||||
{
|
||||
unsigned startToken = ast->firstToken();
|
||||
if (DestructorNameAST *dtor = ast->asDestructorName())
|
||||
startToken = dtor->identifier_token;
|
||||
|
||||
const Token &tok = tokenAt(startToken);
|
||||
if (tok.generated())
|
||||
return;
|
||||
|
||||
unsigned line, column;
|
||||
getTokenStartPosition(startToken, &line, &column);
|
||||
const unsigned length = tok.length();
|
||||
|
||||
foreach (Symbol *c, candidates) {
|
||||
if (! c->isDeclaration())
|
||||
continue;
|
||||
else if (c->isTypedef())
|
||||
continue;
|
||||
else if (c->type()->isFunctionType())
|
||||
continue;
|
||||
else if (! c->enclosingSymbol()->isClass())
|
||||
continue;
|
||||
|
||||
const Use use(line, column, length, Use::Field);
|
||||
addTypeUsage(use);
|
||||
//qDebug() << "added use" << oo(ast->name) << line << column << length;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned CheckSymbols::startOfTemplateDeclaration(TemplateDeclarationAST *ast) const
|
||||
{
|
||||
if (ast->declaration) {
|
||||
|
||||
@@ -87,6 +87,9 @@ protected:
|
||||
void addTypeUsage(const QList<Symbol *> &candidates, NameAST *ast);
|
||||
void addTypeUsage(const Use &use);
|
||||
|
||||
void checkMemberName(NameAST *ast);
|
||||
void addMemberUsage(const QList<Symbol *> &candidates, NameAST *ast);
|
||||
|
||||
virtual bool preVisit(AST *);
|
||||
|
||||
virtual bool visit(NamespaceAST *);
|
||||
@@ -105,6 +108,9 @@ protected:
|
||||
virtual bool visit(TypenameTypeParameterAST *ast);
|
||||
virtual bool visit(TemplateTypeParameterAST *ast);
|
||||
|
||||
virtual bool visit(FunctionDefinitionAST *ast);
|
||||
virtual bool visit(MemberAccessAST *ast);
|
||||
|
||||
unsigned startOfTemplateDeclaration(TemplateDeclarationAST *ast) const;
|
||||
Scope *findScope(AST *ast) const;
|
||||
|
||||
@@ -116,8 +122,10 @@ private:
|
||||
QString _fileName;
|
||||
QList<Document::DiagnosticMessage> _diagnosticMessages;
|
||||
QSet<QByteArray> _potentialTypes;
|
||||
QSet<QByteArray> _potentialMembers;
|
||||
QList<ScopedSymbol *> _scopes;
|
||||
QList<TemplateDeclarationAST *> _templateDeclarationStack;
|
||||
QList<FunctionDefinitionAST *> _functionDefinitionStack;
|
||||
QVector<Use> _typeUsages;
|
||||
bool _flushRequested;
|
||||
unsigned _flushLine;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "cpphighlighter.h"
|
||||
#include "cppchecksymbols.h"
|
||||
#include "cppquickfix.h"
|
||||
#include "cpplocalsymbols.h"
|
||||
|
||||
#include <AST.h>
|
||||
#include <Control.h>
|
||||
@@ -172,240 +173,6 @@ private:
|
||||
CPlusPlus::OverviewModel *m_sourceModel;
|
||||
};
|
||||
|
||||
|
||||
class FindLocalUses: protected ASTVisitor
|
||||
{
|
||||
Scope *_functionScope;
|
||||
Document::Ptr _doc;
|
||||
|
||||
public:
|
||||
FindLocalUses(Document::Ptr doc)
|
||||
: ASTVisitor(doc->translationUnit()), _doc(doc), hasD(false), hasQ(false)
|
||||
{ }
|
||||
|
||||
// local and external uses.
|
||||
SemanticInfo::LocalUseMap localUses;
|
||||
bool hasD;
|
||||
bool hasQ;
|
||||
|
||||
void operator()(DeclarationAST *ast)
|
||||
{
|
||||
localUses.clear();
|
||||
|
||||
if (!ast)
|
||||
return;
|
||||
|
||||
if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
|
||||
if (def->symbol) {
|
||||
_functionScope = def->symbol->members();
|
||||
accept(ast);
|
||||
}
|
||||
} else if (ObjCMethodDeclarationAST *decl = ast->asObjCMethodDeclaration()) {
|
||||
if (decl->method_prototype->symbol) {
|
||||
_functionScope = decl->method_prototype->symbol->members();
|
||||
accept(ast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
using ASTVisitor::visit;
|
||||
|
||||
bool findMember(Scope *scope, NameAST *ast, unsigned line, unsigned column)
|
||||
{
|
||||
if (! (ast && ast->name))
|
||||
return false;
|
||||
|
||||
const Identifier *id = ast->name->identifier();
|
||||
|
||||
if (scope) {
|
||||
for (Symbol *member = scope->lookat(id); member; member = member->next()) {
|
||||
if (member->identifier() != id)
|
||||
continue;
|
||||
else if (member->line() < line || (member->line() == line && member->column() <= column)) {
|
||||
localUses[member].append(SemanticInfo::Use(line, column, id->size()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void searchUsesInTemplateArguments(NameAST *name)
|
||||
{
|
||||
if (! name)
|
||||
return;
|
||||
|
||||
else if (TemplateIdAST *template_id = name->asTemplateId()) {
|
||||
for (TemplateArgumentListAST *it = template_id->template_argument_list; it; it = it->next) {
|
||||
accept(it->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool visit(SimpleNameAST *ast)
|
||||
{ return findMemberForToken(ast->firstToken(), ast); }
|
||||
|
||||
bool findMemberForToken(unsigned tokenIdx, NameAST *ast)
|
||||
{
|
||||
const Token &tok = tokenAt(tokenIdx);
|
||||
if (tok.generated())
|
||||
return false;
|
||||
|
||||
unsigned line, column;
|
||||
getTokenStartPosition(tokenIdx, &line, &column);
|
||||
|
||||
Scope *scope = _doc->scopeAt(line, column);
|
||||
|
||||
while (scope) {
|
||||
if (scope->isFunctionScope()) {
|
||||
Function *fun = scope->owner()->asFunction();
|
||||
if (findMember(fun->members(), ast, line, column))
|
||||
return false;
|
||||
else if (findMember(fun->arguments(), ast, line, column))
|
||||
return false;
|
||||
} else if (scope->isObjCMethodScope()) {
|
||||
ObjCMethod *method = scope->owner()->asObjCMethod();
|
||||
if (findMember(method->members(), ast, line, column))
|
||||
return false;
|
||||
else if (findMember(method->arguments(), ast, line, column))
|
||||
return false;
|
||||
} else if (scope->isBlockScope()) {
|
||||
if (findMember(scope, ast, line, column))
|
||||
return false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
scope = scope->enclosingScope();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(TemplateIdAST *ast)
|
||||
{
|
||||
for (TemplateArgumentListAST *arg = ast->template_argument_list; arg; arg = arg->next)
|
||||
accept(arg->value);
|
||||
|
||||
const Token &tok = tokenAt(ast->identifier_token);
|
||||
if (tok.generated())
|
||||
return false;
|
||||
|
||||
unsigned line, column;
|
||||
getTokenStartPosition(ast->firstToken(), &line, &column);
|
||||
|
||||
Scope *scope = _doc->scopeAt(line, column);
|
||||
|
||||
while (scope) {
|
||||
if (scope->isFunctionScope()) {
|
||||
Function *fun = scope->owner()->asFunction();
|
||||
if (findMember(fun->members(), ast, line, column))
|
||||
return false;
|
||||
else if (findMember(fun->arguments(), ast, line, column))
|
||||
return false;
|
||||
} else if (scope->isBlockScope()) {
|
||||
if (findMember(scope, ast, line, column))
|
||||
return false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
scope = scope->enclosingScope();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(QualifiedNameAST *ast)
|
||||
{
|
||||
for (NestedNameSpecifierListAST *it = ast->nested_name_specifier_list; it; it = it->next)
|
||||
searchUsesInTemplateArguments(it->value->class_or_namespace_name);
|
||||
|
||||
searchUsesInTemplateArguments(ast->unqualified_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(MemberAccessAST *ast)
|
||||
{
|
||||
// accept only the base expression
|
||||
accept(ast->base_expression);
|
||||
// and ignore the member name.
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(ElaboratedTypeSpecifierAST *)
|
||||
{
|
||||
// ### template args
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(ClassSpecifierAST *)
|
||||
{
|
||||
// ### template args
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(EnumSpecifierAST *)
|
||||
{
|
||||
// ### template args
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(UsingDirectiveAST *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(UsingAST *ast)
|
||||
{
|
||||
accept(ast->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(QtMemberDeclarationAST *ast)
|
||||
{
|
||||
if (tokenKind(ast->q_token) == T_Q_D)
|
||||
hasD = true;
|
||||
else
|
||||
hasQ = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool visit(ExpressionOrDeclarationStatementAST *ast)
|
||||
{
|
||||
accept(ast->declaration);
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(FunctionDeclaratorAST *ast)
|
||||
{
|
||||
accept(ast->parameters);
|
||||
|
||||
for (SpecifierListAST *it = ast->cv_qualifier_list; it; it = it->next)
|
||||
accept(it->value);
|
||||
|
||||
accept(ast->exception_specification);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(ObjCMethodPrototypeAST *ast)
|
||||
{
|
||||
accept(ast->argument_list);
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(ObjCMessageArgumentDeclarationAST *ast)
|
||||
{
|
||||
accept(ast->param_name);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class FunctionDefinitionUnderCursor: protected ASTVisitor
|
||||
{
|
||||
unsigned _line;
|
||||
@@ -1131,6 +898,12 @@ void CPPEditor::highlightTypeUsages(int from, int to)
|
||||
Q_ASSERT(!chunks.isEmpty());
|
||||
QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
|
||||
|
||||
QTextCharFormat localUseFormat;
|
||||
localUseFormat.setForeground(Qt::darkBlue); // ### hardcoded
|
||||
|
||||
QTextCharFormat memberUseFormat;
|
||||
memberUseFormat.setForeground(Qt::darkRed); // ### hardcoded
|
||||
|
||||
QMapIterator<int, QVector<SemanticInfo::Use> > it(chunks);
|
||||
while (b.isValid() && it.hasNext()) {
|
||||
it.next();
|
||||
@@ -1146,7 +919,24 @@ void CPPEditor::highlightTypeUsages(int from, int to)
|
||||
QList<QTextLayout::FormatRange> formats;
|
||||
foreach (const SemanticInfo::Use &use, it.value()) {
|
||||
QTextLayout::FormatRange formatRange;
|
||||
|
||||
switch (use.kind) {
|
||||
case SemanticInfo::Use::Type:
|
||||
formatRange.format = m_typeFormat;
|
||||
break;
|
||||
|
||||
case SemanticInfo::Use::Field:
|
||||
formatRange.format = memberUseFormat;
|
||||
break;
|
||||
|
||||
case SemanticInfo::Use::Local:
|
||||
formatRange.format = localUseFormat;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
formatRange.start = use.column - 1;
|
||||
formatRange.length = use.length;
|
||||
formats.append(formatRange);
|
||||
@@ -2187,8 +1977,7 @@ SemanticInfo SemanticHighlighter::semanticInfo(const Source &source)
|
||||
Snapshot snapshot;
|
||||
Document::Ptr doc;
|
||||
QList<Document::DiagnosticMessage> diagnosticMessages;
|
||||
QList<SemanticInfo::Use> typeUsages, objcKeywords;
|
||||
LookupContext context;
|
||||
QList<SemanticInfo::Use> objcKeywords;
|
||||
|
||||
if (! source.force && revision == source.revision) {
|
||||
m_mutex.lock();
|
||||
@@ -2207,13 +1996,7 @@ SemanticInfo SemanticHighlighter::semanticInfo(const Source &source)
|
||||
doc->check();
|
||||
|
||||
#if 0
|
||||
context = LookupContext(doc, snapshot);
|
||||
|
||||
if (TranslationUnit *unit = doc->translationUnit()) {
|
||||
CheckUndefinedSymbols checkUndefinedSymbols(unit, context);
|
||||
diagnosticMessages = checkUndefinedSymbols(unit->ast());
|
||||
typeUsages = checkUndefinedSymbols.typeUsages();
|
||||
|
||||
FindObjCKeywords findObjCKeywords(unit); // ### remove me
|
||||
objcKeywords = findObjCKeywords();
|
||||
}
|
||||
@@ -2226,14 +2009,13 @@ SemanticInfo SemanticHighlighter::semanticInfo(const Source &source)
|
||||
FunctionDefinitionUnderCursor functionDefinitionUnderCursor(translationUnit);
|
||||
DeclarationAST *currentFunctionDefinition = functionDefinitionUnderCursor(ast, source.line, source.column);
|
||||
|
||||
FindLocalUses useTable(doc);
|
||||
useTable(currentFunctionDefinition);
|
||||
const LocalSymbols useTable(doc, currentFunctionDefinition);
|
||||
|
||||
SemanticInfo semanticInfo;
|
||||
semanticInfo.revision = source.revision;
|
||||
semanticInfo.snapshot = snapshot;
|
||||
semanticInfo.doc = doc;
|
||||
semanticInfo.localUses = useTable.localUses;
|
||||
semanticInfo.localUses = useTable.uses;
|
||||
semanticInfo.hasQ = useTable.hasQ;
|
||||
semanticInfo.hasD = useTable.hasD;
|
||||
semanticInfo.forced = source.force;
|
||||
|
||||
@@ -20,7 +20,8 @@ HEADERS += cppplugin.h \
|
||||
cppchecksymbols.h \
|
||||
cppsemanticinfo.h \
|
||||
cppoutline.h \
|
||||
cppdeclfromdef.h
|
||||
cppdeclfromdef.h \
|
||||
cpplocalsymbols.h
|
||||
|
||||
SOURCES += cppplugin.cpp \
|
||||
cppeditor.cpp \
|
||||
@@ -33,7 +34,8 @@ SOURCES += cppplugin.cpp \
|
||||
cppchecksymbols.cpp \
|
||||
cppsemanticinfo.cpp \
|
||||
cppoutline.cpp \
|
||||
cppdeclfromdef.cpp
|
||||
cppdeclfromdef.cpp \
|
||||
cpplocalsymbols.cpp
|
||||
|
||||
RESOURCES += cppeditor.qrc
|
||||
|
||||
|
||||
289
src/plugins/cppeditor/cpplocalsymbols.cpp
Normal file
289
src/plugins/cppeditor/cpplocalsymbols.cpp
Normal file
@@ -0,0 +1,289 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** Commercial Usage
|
||||
**
|
||||
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||
** accordance with the Qt Commercial License Agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Nokia.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** If you are unsure which license is appropriate for your use, please
|
||||
** contact the sales department at http://qt.nokia.com/contact.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cpplocalsymbols.h"
|
||||
#include "cppsemanticinfo.h"
|
||||
|
||||
#include <cplusplus/CppDocument.h>
|
||||
#include <ASTVisitor.h>
|
||||
#include <AST.h>
|
||||
#include <Scope.h>
|
||||
#include <Symbols.h>
|
||||
#include <CoreTypes.h>
|
||||
#include <Names.h>
|
||||
#include <Literals.h>
|
||||
|
||||
using namespace CPlusPlus;
|
||||
using namespace CppEditor::Internal;
|
||||
|
||||
namespace {
|
||||
|
||||
class FindLocalSymbols: protected ASTVisitor
|
||||
{
|
||||
Scope *_functionScope;
|
||||
Document::Ptr _doc;
|
||||
|
||||
public:
|
||||
FindLocalSymbols(Document::Ptr doc)
|
||||
: ASTVisitor(doc->translationUnit()), _doc(doc), hasD(false), hasQ(false)
|
||||
{ }
|
||||
|
||||
// local and external uses.
|
||||
SemanticInfo::LocalUseMap localUses;
|
||||
bool hasD;
|
||||
bool hasQ;
|
||||
|
||||
void operator()(DeclarationAST *ast)
|
||||
{
|
||||
localUses.clear();
|
||||
|
||||
if (!ast)
|
||||
return;
|
||||
|
||||
if (FunctionDefinitionAST *def = ast->asFunctionDefinition()) {
|
||||
if (def->symbol) {
|
||||
_functionScope = def->symbol->members();
|
||||
accept(ast);
|
||||
}
|
||||
} else if (ObjCMethodDeclarationAST *decl = ast->asObjCMethodDeclaration()) {
|
||||
if (decl->method_prototype->symbol) {
|
||||
_functionScope = decl->method_prototype->symbol->members();
|
||||
accept(ast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
using ASTVisitor::visit;
|
||||
|
||||
bool findMember(Scope *scope, NameAST *ast, unsigned line, unsigned column)
|
||||
{
|
||||
if (! (ast && ast->name))
|
||||
return false;
|
||||
|
||||
const Identifier *id = ast->name->identifier();
|
||||
|
||||
if (scope) {
|
||||
for (Symbol *member = scope->lookat(id); member; member = member->next()) {
|
||||
if (member->identifier() != id)
|
||||
continue;
|
||||
else if (member->line() < line || (member->line() == line && member->column() <= column)) {
|
||||
localUses[member].append(SemanticInfo::Use(line, column, id->size(), SemanticInfo::Use::Local));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void searchUsesInTemplateArguments(NameAST *name)
|
||||
{
|
||||
if (! name)
|
||||
return;
|
||||
|
||||
else if (TemplateIdAST *template_id = name->asTemplateId()) {
|
||||
for (TemplateArgumentListAST *it = template_id->template_argument_list; it; it = it->next) {
|
||||
accept(it->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool visit(SimpleNameAST *ast)
|
||||
{ return findMemberForToken(ast->firstToken(), ast); }
|
||||
|
||||
bool findMemberForToken(unsigned tokenIdx, NameAST *ast)
|
||||
{
|
||||
const Token &tok = tokenAt(tokenIdx);
|
||||
if (tok.generated())
|
||||
return false;
|
||||
|
||||
unsigned line, column;
|
||||
getTokenStartPosition(tokenIdx, &line, &column);
|
||||
|
||||
Scope *scope = _doc->scopeAt(line, column);
|
||||
|
||||
while (scope) {
|
||||
if (scope->isFunctionScope()) {
|
||||
Function *fun = scope->owner()->asFunction();
|
||||
if (findMember(fun->members(), ast, line, column))
|
||||
return false;
|
||||
else if (findMember(fun->arguments(), ast, line, column))
|
||||
return false;
|
||||
} else if (scope->isObjCMethodScope()) {
|
||||
ObjCMethod *method = scope->owner()->asObjCMethod();
|
||||
if (findMember(method->members(), ast, line, column))
|
||||
return false;
|
||||
else if (findMember(method->arguments(), ast, line, column))
|
||||
return false;
|
||||
} else if (scope->isBlockScope()) {
|
||||
if (findMember(scope, ast, line, column))
|
||||
return false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
scope = scope->enclosingScope();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(TemplateIdAST *ast)
|
||||
{
|
||||
for (TemplateArgumentListAST *arg = ast->template_argument_list; arg; arg = arg->next)
|
||||
accept(arg->value);
|
||||
|
||||
const Token &tok = tokenAt(ast->identifier_token);
|
||||
if (tok.generated())
|
||||
return false;
|
||||
|
||||
unsigned line, column;
|
||||
getTokenStartPosition(ast->firstToken(), &line, &column);
|
||||
|
||||
Scope *scope = _doc->scopeAt(line, column);
|
||||
|
||||
while (scope) {
|
||||
if (scope->isFunctionScope()) {
|
||||
Function *fun = scope->owner()->asFunction();
|
||||
if (findMember(fun->members(), ast, line, column))
|
||||
return false;
|
||||
else if (findMember(fun->arguments(), ast, line, column))
|
||||
return false;
|
||||
} else if (scope->isBlockScope()) {
|
||||
if (findMember(scope, ast, line, column))
|
||||
return false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
scope = scope->enclosingScope();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(QualifiedNameAST *ast)
|
||||
{
|
||||
for (NestedNameSpecifierListAST *it = ast->nested_name_specifier_list; it; it = it->next)
|
||||
searchUsesInTemplateArguments(it->value->class_or_namespace_name);
|
||||
|
||||
searchUsesInTemplateArguments(ast->unqualified_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(MemberAccessAST *ast)
|
||||
{
|
||||
// accept only the base expression
|
||||
accept(ast->base_expression);
|
||||
// and ignore the member name.
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(ElaboratedTypeSpecifierAST *)
|
||||
{
|
||||
// ### template args
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(ClassSpecifierAST *)
|
||||
{
|
||||
// ### template args
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(EnumSpecifierAST *)
|
||||
{
|
||||
// ### template args
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(UsingDirectiveAST *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(UsingAST *ast)
|
||||
{
|
||||
accept(ast->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(QtMemberDeclarationAST *ast)
|
||||
{
|
||||
if (tokenKind(ast->q_token) == T_Q_D)
|
||||
hasD = true;
|
||||
else
|
||||
hasQ = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool visit(ExpressionOrDeclarationStatementAST *ast)
|
||||
{
|
||||
accept(ast->declaration);
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(FunctionDeclaratorAST *ast)
|
||||
{
|
||||
accept(ast->parameters);
|
||||
|
||||
for (SpecifierListAST *it = ast->cv_qualifier_list; it; it = it->next)
|
||||
accept(it->value);
|
||||
|
||||
accept(ast->exception_specification);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(ObjCMethodPrototypeAST *ast)
|
||||
{
|
||||
accept(ast->argument_list);
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool visit(ObjCMessageArgumentDeclarationAST *ast)
|
||||
{
|
||||
accept(ast->param_name);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
|
||||
LocalSymbols::LocalSymbols(CPlusPlus::Document::Ptr doc, CPlusPlus::DeclarationAST *ast)
|
||||
{
|
||||
FindLocalSymbols FindLocalSymbols(doc);
|
||||
FindLocalSymbols(ast);
|
||||
hasD = FindLocalSymbols.hasD;
|
||||
hasQ = FindLocalSymbols.hasQ;
|
||||
uses = FindLocalSymbols.localUses;
|
||||
}
|
||||
56
src/plugins/cppeditor/cpplocalsymbols.h
Normal file
56
src/plugins/cppeditor/cpplocalsymbols.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** Commercial Usage
|
||||
**
|
||||
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||
** accordance with the Qt Commercial License Agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Nokia.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** If you are unsure which license is appropriate for your use, please
|
||||
** contact the sales department at http://qt.nokia.com/contact.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CPPLOCALSYMBOLS_H
|
||||
#define CPPLOCALSYMBOLS_H
|
||||
|
||||
#include "cppsemanticinfo.h"
|
||||
#include <cplusplus/CppDocument.h>
|
||||
#include <ASTfwd.h>
|
||||
|
||||
namespace CppEditor {
|
||||
namespace Internal {
|
||||
|
||||
class LocalSymbols
|
||||
{
|
||||
Q_DISABLE_COPY(LocalSymbols)
|
||||
|
||||
public:
|
||||
LocalSymbols(CPlusPlus::Document::Ptr doc, CPlusPlus::DeclarationAST *ast);
|
||||
|
||||
bool hasD;
|
||||
bool hasQ;
|
||||
SemanticInfo::LocalUseMap uses;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // CPPLOCALSYMBOLS_H
|
||||
@@ -46,9 +46,16 @@ public:
|
||||
unsigned line;
|
||||
unsigned column;
|
||||
unsigned length;
|
||||
unsigned kind;
|
||||
|
||||
Use(unsigned line = 0, unsigned column = 0, unsigned length = 0)
|
||||
: line(line), column(column), length(length) {}
|
||||
enum {
|
||||
Type = 0,
|
||||
Local,
|
||||
Field
|
||||
};
|
||||
|
||||
Use(unsigned line = 0, unsigned column = 0, unsigned length = 0, unsigned kind = Type)
|
||||
: line(line), column(column), length(length), kind(kind) {}
|
||||
};
|
||||
|
||||
typedef QHash<CPlusPlus::Symbol *, QList<Use> > LocalUseMap;
|
||||
|
||||
Reference in New Issue
Block a user