From 27f92695cff70da2f6cb3effb5a8e12682bf3a37 Mon Sep 17 00:00:00 2001 From: Roberto Raggi <roberto.raggi@nokia.com> Date: Thu, 25 Jun 2009 11:02:02 +0200 Subject: [PATCH] Initial work on smart highlight of local symbols. For-statements and symbols genarated from a macro expansion are not yet highlighted. --- src/plugins/cppeditor/cppeditor.cpp | 275 ++++++++++++++++++++-- src/shared/cplusplus/CheckDeclaration.cpp | 35 ++- src/shared/cplusplus/CheckDeclaration.h | 2 + src/shared/cplusplus/CheckStatement.cpp | 10 + src/shared/cplusplus/Symbols.cpp | 8 +- 5 files changed, 301 insertions(+), 29 deletions(-) diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index f3448474e3b..b2878529337 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -71,6 +71,7 @@ #include <QtCore/QDebug> #include <QtCore/QTime> #include <QtCore/QTimer> +#include <QtCore/QStack> #include <QtGui/QAction> #include <QtGui/QHeaderView> #include <QtGui/QLayout> @@ -109,6 +110,217 @@ public: } }; +class FindLocals: protected ASTVisitor +{ + Scope *_functionScope; + + class FindScope: protected SymbolVisitor + { + TranslationUnit *_unit; + Scope *_scope; + unsigned _line; + unsigned _column; + + public: + Scope *operator()(unsigned line, unsigned column, + Symbol *root, TranslationUnit *unit) + { + _unit = unit; + _scope = 0; + _line = line; + _column = column; + accept(root); + return _scope; + } + + private: + using SymbolVisitor::visit; + + virtual bool preVisit(Symbol *) + { return ! _scope; } + + virtual bool visit(Block *block) + { return processScope(block->members()); } + + virtual bool visit(Function *function) + { return processScope(function->members()); } + + bool processScope(Scope *scope) + { + if (_scope || ! scope) + return false; + + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + accept(scope->symbolAt(i)); + + if (_scope) + return false; + } + + unsigned startOffset = scope->owner()->startOffset(); + unsigned endOffset = scope->owner()->endOffset(); + + unsigned startLine, startColumn; + unsigned endLine, endColumn; + + _unit->getPosition(startOffset, &startLine, &startColumn); + _unit->getPosition(endOffset, &endLine, &endColumn); + + if (_line > startLine || (_line == startLine && _column >= startColumn)) { + if (_line < endLine || (_line == endLine && _column < endColumn)) { + _scope = scope; + } + } + + return false; + } + }; + +public: + FindLocals(Control *control) + : ASTVisitor(control) + { } + + struct Use { + SimpleNameAST *name; + unsigned line; + unsigned column; + unsigned length; + + Use(){} + + Use(SimpleNameAST *name, unsigned line, unsigned column, unsigned length) + : name(name), line(line), column(column), length(length) {} + }; + + typedef QHash<Symbol *, QList<Use> > UseMap; + typedef QHashIterator<Symbol *, QList<Use> > UseIterator; + + UseMap uses; // ### private + + UseMap operator()(FunctionDefinitionAST *ast) + { + uses.clear(); + if (ast && ast->symbol) { + _functionScope = ast->symbol->members(); + accept(ast); + } + return uses; + } + +protected: + using ASTVisitor::visit; + + bool findMember(Scope *scope, SimpleNameAST *ast, unsigned line, unsigned column) + { + Identifier *id = identifier(ast->identifier_token); + + 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->isGenerated() || member->column() >= column))) { + //qDebug() << "*** found member:" << member->line() << member->column() << member->name()->identifier()->chars(); + uses[member].append(Use(ast, line, column, id->size())); + return true; + } + } + } + + return false; + } + + virtual bool visit(SimpleNameAST *ast) + { + unsigned line, column; + getTokenStartPosition(ast->firstToken(), &line, &column); + + FindScope findScope; + + Scope *scope = findScope(line, column, + _functionScope->owner(), + translationUnit()); + + 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(); + } + +#if 0 + qDebug() << "symbol:" << id->chars() << "at pos:" << line << column + << "is not defined"; +#endif + + return false; + } + + virtual bool visit(QualifiedNameAST *) + { + // ### visit the template arguments. + return false; + } + + virtual bool visit(PostfixExpressionAST *ast) + { + accept(ast->base_expression); + for (PostfixAST *it = ast->postfix_expressions; it; it = it->next) { + if (it->asMemberAccess() != 0) + continue; // skip members + accept(it); + } + return false; + } + + virtual bool visit(NewExpressionAST *ast) + { + accept(ast->new_placement); + accept(ast->new_initializer); + 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; + } +}; + + class FunctionDefinitionUnderCursor: protected ASTVisitor { QTextCursor _textCursor; @@ -620,6 +832,7 @@ void CPPEditor::simplifyDeclarations() const QString fileName = file()->fileName(); const QByteArray preprocessedCode = snapshot.preprocessedCode(plainText, fileName); Document::Ptr doc = snapshot.documentFromSource(preprocessedCode, fileName); + doc->check(); SimplifyDeclarations simplify(this, doc); simplify(textCursor()); @@ -692,6 +905,7 @@ void CPPEditor::updateMethodBoxIndexNow() const Snapshot snapshot = m_modelManager->snapshot(); const QByteArray preprocessedCode = snapshot.preprocessedCode(toPlainText(), file()->fileName()); Document::Ptr doc = snapshot.documentFromSource(preprocessedCode, file()->fileName()); + doc->check(); Control *control = doc->control(); TranslationUnit *translationUnit = doc->translationUnit(); AST *ast = translationUnit->ast(); @@ -699,33 +913,56 @@ void CPPEditor::updateMethodBoxIndexNow() FunctionDefinitionUnderCursor functionDefinitionUnderCursor(control); FunctionDefinitionAST *currentFunctionDefinition = functionDefinitionUnderCursor(ast, textCursor()); + QTextCharFormat format; format.setUnderlineColor(Qt::darkGray); format.setUnderlineStyle(QTextCharFormat::DashUnderline); - ProcessDeclarators processDeclarators(control); - const QList<DeclaratorIdAST *> declarators = processDeclarators(currentFunctionDefinition); - foreach (DeclaratorIdAST *declarator, declarators) { - bool generated = false; - for (unsigned tk = declarator->firstToken(), end = declarator->lastToken(); tk != end; ++tk) { - if (translationUnit->tokenAt(tk).generated) { - generated = true; + FindLocals findLocals(control); + const FindLocals::UseMap useMap = findLocals(currentFunctionDefinition); + FindLocals::UseIterator it(useMap); + while (it.hasNext()) { + it.next(); + const QList<FindLocals::Use> &uses = it.value(); + + bool good = false; + foreach (const FindLocals::Use &use, uses) { + unsigned l = line; + unsigned c = column + 1; // convertCursorPosition() returns a 0-based column number. + if (l == use.line && c >= use.column && c <= (use.column + use.length)) { + good = true; break; } } - if (generated) + + if (! good) continue; - unsigned startLine, startColumn; - unsigned endLine, endColumn; - translationUnit->getTokenStartPosition(declarator->firstToken(), &startLine, &startColumn); - translationUnit->getTokenEndPosition(declarator->lastToken() - 1, &endLine, &endColumn); - QTextEdit::ExtraSelection sel; - sel.cursor = textCursor(); - sel.cursor.setPosition(document()->findBlockByNumber(startLine - 1).position() + startColumn - 1); - sel.cursor.setPosition(document()->findBlockByLineNumber(endLine - 1).position() + endColumn - 1, - QTextCursor::KeepAnchor); - sel.format = format; - selections.append(sel); + + foreach (const FindLocals::Use &use, uses) { + SimpleNameAST *name = use.name; + bool generated = false; + for (unsigned tk = name->firstToken(), end = name->lastToken(); tk != end; ++tk) { + if (translationUnit->tokenAt(tk).generated) { + generated = true; + break; + } + } + if (generated) + continue; + unsigned startLine, startColumn; + unsigned endLine, endColumn; + translationUnit->getTokenStartPosition(name->firstToken(), &startLine, &startColumn); + translationUnit->getTokenEndPosition(name->lastToken() - 1, &endLine, &endColumn); + QTextEdit::ExtraSelection sel; + sel.cursor = textCursor(); + sel.cursor.setPosition(document()->findBlockByNumber(startLine - 1).position() + startColumn - 1); + sel.cursor.setPosition(document()->findBlockByLineNumber(endLine - 1).position() + endColumn - 1, + QTextCursor::KeepAnchor); + sel.format = format; + selections.append(sel); + } + break; // done. } + setExtraSelections(CodeSemanticsSelection, selections); #ifdef QTCREATOR_WITH_ADVANCED_HIGHLIGHTER diff --git a/src/shared/cplusplus/CheckDeclaration.cpp b/src/shared/cplusplus/CheckDeclaration.cpp index 79392bf4e76..ef71c798094 100644 --- a/src/shared/cplusplus/CheckDeclaration.cpp +++ b/src/shared/cplusplus/CheckDeclaration.cpp @@ -122,6 +122,18 @@ void CheckDeclaration::checkFunctionArguments(Function *fun) } } +unsigned CheckDeclaration::locationOfDeclaratorId(DeclaratorAST *declarator) const +{ + if (declarator && declarator->core_declarator) { + if (DeclaratorIdAST *declaratorId = declarator->core_declarator->asDeclaratorId()) + return declaratorId->firstToken(); + else if (NestedDeclaratorAST *nested = declarator->core_declarator->asNestedDeclarator()) + return locationOfDeclaratorId(nested->declarator); + } + + return 0; +} + bool CheckDeclaration::visit(SimpleDeclarationAST *ast) { FullySpecifiedType ty = semantic()->check(ast->decl_specifier_seq, _scope); @@ -164,11 +176,13 @@ bool CheckDeclaration::visit(SimpleDeclarationAST *ast) FullySpecifiedType declTy = semantic()->check(it->declarator, qualTy, _scope, &name); - unsigned location = 0; - if (it->declarator) - location = it->declarator->firstToken(); - else - location = ast->firstToken(); + unsigned location = locationOfDeclaratorId(it->declarator); + if (! location) { + if (it->declarator) + location = it->declarator->firstToken(); + else + location = ast->firstToken(); + } Function *fun = 0; if (declTy && 0 != (fun = declTy->asFunctionType())) { @@ -355,10 +369,13 @@ bool CheckDeclaration::visit(NamespaceAliasDefinitionAST *) bool CheckDeclaration::visit(ParameterDeclarationAST *ast) { - unsigned sourceLocation = 0; - - if (ast->declarator) - sourceLocation = ast->declarator->firstToken(); + unsigned sourceLocation = locationOfDeclaratorId(ast->declarator); + if (! sourceLocation) { + if (ast->declarator) + sourceLocation = ast->declarator->firstToken(); + else + sourceLocation = ast->firstToken(); + } Name *argName = 0; FullySpecifiedType ty = semantic()->check(ast->type_specifier, _scope); diff --git a/src/shared/cplusplus/CheckDeclaration.h b/src/shared/cplusplus/CheckDeclaration.h index 0ba2568830f..4abd8598479 100644 --- a/src/shared/cplusplus/CheckDeclaration.h +++ b/src/shared/cplusplus/CheckDeclaration.h @@ -72,6 +72,8 @@ protected: using ASTVisitor::visit; + unsigned locationOfDeclaratorId(DeclaratorAST *declarator) const; + virtual bool visit(SimpleDeclarationAST *ast); virtual bool visit(EmptyDeclarationAST *ast); virtual bool visit(AccessDeclarationAST *ast); diff --git a/src/shared/cplusplus/CheckStatement.cpp b/src/shared/cplusplus/CheckStatement.cpp index 433dc5c8730..0d7bced86ec 100644 --- a/src/shared/cplusplus/CheckStatement.cpp +++ b/src/shared/cplusplus/CheckStatement.cpp @@ -144,6 +144,8 @@ bool CheckStatement::visit(ExpressionStatementAST *ast) bool CheckStatement::visit(ForStatementAST *ast) { Block *block = control()->newBlock(ast->for_token); + block->setStartOffset(tokenAt(ast->firstToken()).offset); + block->setEndOffset(tokenAt(ast->lastToken()).offset); ast->symbol = block; _scope->enterSymbol(block); Scope *previousScope = switchScope(block->members()); @@ -158,6 +160,8 @@ bool CheckStatement::visit(ForStatementAST *ast) bool CheckStatement::visit(IfStatementAST *ast) { Block *block = control()->newBlock(ast->if_token); + block->setStartOffset(tokenAt(ast->firstToken()).offset); + block->setEndOffset(tokenAt(ast->lastToken()).offset); ast->symbol = block; _scope->enterSymbol(block); Scope *previousScope = switchScope(block->members()); @@ -198,6 +202,8 @@ bool CheckStatement::visit(ReturnStatementAST *ast) bool CheckStatement::visit(SwitchStatementAST *ast) { Block *block = control()->newBlock(ast->switch_token); + block->setStartOffset(tokenAt(ast->firstToken()).offset); + block->setEndOffset(tokenAt(ast->lastToken()).offset); ast->symbol = block; _scope->enterSymbol(block); Scope *previousScope = switchScope(block->members()); @@ -219,6 +225,8 @@ bool CheckStatement::visit(TryBlockStatementAST *ast) bool CheckStatement::visit(CatchClauseAST *ast) { Block *block = control()->newBlock(ast->catch_token); + block->setStartOffset(tokenAt(ast->firstToken()).offset); + block->setEndOffset(tokenAt(ast->lastToken()).offset); ast->symbol = block; _scope->enterSymbol(block); Scope *previousScope = switchScope(block->members()); @@ -231,6 +239,8 @@ bool CheckStatement::visit(CatchClauseAST *ast) bool CheckStatement::visit(WhileStatementAST *ast) { Block *block = control()->newBlock(ast->while_token); + block->setStartOffset(tokenAt(ast->firstToken()).offset); + block->setEndOffset(tokenAt(ast->lastToken()).offset); ast->symbol = block; _scope->enterSymbol(block); Scope *previousScope = switchScope(block->members()); diff --git a/src/shared/cplusplus/Symbols.cpp b/src/shared/cplusplus/Symbols.cpp index dc1a6957c6b..dddc1539288 100644 --- a/src/shared/cplusplus/Symbols.cpp +++ b/src/shared/cplusplus/Symbols.cpp @@ -328,7 +328,13 @@ FullySpecifiedType Block::type() const { return FullySpecifiedType(); } void Block::visitSymbol0(SymbolVisitor *visitor) -{ visitor->visit(this); } +{ + if (visitor->visit(this)) { + for (unsigned i = 0; i < memberCount(); ++i) { + visitSymbol(memberAt(i), visitor); + } + } +} Enum::Enum(TranslationUnit *translationUnit, unsigned sourceLocation, Name *name) : ScopedSymbol(translationUnit, sourceLocation, name) -- GitLab