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