summaryrefslogtreecommitdiff
path: root/src/libs/cplusplus/CheckUndefinedSymbols.cpp
diff options
context:
space:
mode:
authorRoberto Raggi <roberto.raggi@nokia.com>2010-05-25 14:53:21 +0200
committerRoberto Raggi <roberto.raggi@nokia.com>2010-05-25 17:15:22 +0200
commitb68a16f1b5d69d63e7adaa67810b99c73bcfb979 (patch)
treef697265b0b7e07ed0cfb3b45aeda0fb17f6c499a /src/libs/cplusplus/CheckUndefinedSymbols.cpp
parent2a0f901e16faa4838a0bd09f7d41dc46256589ff (diff)
downloadqt-creator-b68a16f1b5d69d63e7adaa67810b99c73bcfb979.tar.gz
Highlight user defined types.
Diffstat (limited to 'src/libs/cplusplus/CheckUndefinedSymbols.cpp')
-rw-r--r--src/libs/cplusplus/CheckUndefinedSymbols.cpp334
1 files changed, 331 insertions, 3 deletions
diff --git a/src/libs/cplusplus/CheckUndefinedSymbols.cpp b/src/libs/cplusplus/CheckUndefinedSymbols.cpp
index eba07d4b88..7de0f7c77d 100644
--- a/src/libs/cplusplus/CheckUndefinedSymbols.cpp
+++ b/src/libs/cplusplus/CheckUndefinedSymbols.cpp
@@ -36,16 +36,211 @@
#include <TranslationUnit.h>
#include <Scope.h>
#include <AST.h>
+#include <SymbolVisitor.h>
#include <QCoreApplication>
#include <QDebug>
using namespace CPlusPlus;
+namespace {
+
+class CollectTypes: protected SymbolVisitor
+{
+ Document::Ptr _doc;
+ Snapshot _snapshot;
+ QSet<QByteArray> _types;
+ QList<ScopedSymbol *> _scopes;
+ QList<NameAST *> _names;
+ bool _mainDocument;
+
+public:
+ CollectTypes(Document::Ptr doc, const Snapshot &snapshot)
+ : _doc(doc), _snapshot(snapshot), _mainDocument(false)
+ {
+ QSet<Namespace *> processed;
+ process(doc, &processed);
+ }
+
+ const QSet<QByteArray> &types() const
+ {
+ return _types;
+ }
+
+ const QList<ScopedSymbol *> &scopes() const
+ {
+ return _scopes;
+ }
+
+ static Scope *findScope(unsigned tokenOffset, const QList<ScopedSymbol *> &scopes)
+ {
+ for (int i = scopes.size() - 1; i != -1; --i) {
+ ScopedSymbol *symbol = scopes.at(i);
+ const unsigned start = symbol->startOffset();
+ const unsigned end = symbol->endOffset();
+
+ if (tokenOffset >= start && tokenOffset < end)
+ return symbol->members();
+ }
+
+ return 0;
+ }
+
+protected:
+ void process(Document::Ptr doc, QSet<Namespace *> *processed)
+ {
+ if (! doc)
+ return;
+ else if (! processed->contains(doc->globalNamespace())) {
+ processed->insert(doc->globalNamespace());
+
+ foreach (const Document::Include &i, doc->includes())
+ process(_snapshot.document(i.fileName()), processed);
+
+ _mainDocument = (doc == _doc); // ### improve
+ accept(doc->globalNamespace());
+ }
+ }
+
+ void addType(const Identifier *id)
+ {
+ if (id)
+ _types.insert(QByteArray::fromRawData(id->chars(), id->size()));
+ }
+
+ void addType(const Name *name)
+ {
+ if (! name) {
+ return;
+
+ } else if (const QualifiedNameId *q = name->asQualifiedNameId()) {
+ for (unsigned i = 0; i < q->nameCount(); ++i)
+ addType(q->nameAt(i));
+
+ } else if (name->isNameId() || name->isTemplateNameId()) {
+ addType(name->identifier());
+
+ }
+ }
+
+ void addScope(ScopedSymbol *symbol)
+ {
+ if (_mainDocument)
+ _scopes.append(symbol);
+ }
+
+ // nothing to do
+ virtual bool visit(UsingNamespaceDirective *) { return true; }
+ virtual bool visit(UsingDeclaration *) { return true; }
+ virtual bool visit(Argument *) { return true; }
+ virtual bool visit(BaseClass *) { return true; }
+
+ virtual bool visit(Function *symbol)
+ {
+ addScope(symbol);
+ return true;
+ }
+
+ virtual bool visit(Block *symbol)
+ {
+ addScope(symbol);
+ return true;
+ }
+
+ virtual bool visit(NamespaceAlias *symbol)
+ {
+ addType(symbol->name());
+ return true;
+ }
+
+ virtual bool visit(Declaration *symbol)
+ {
+ if (symbol->isTypedef())
+ addType(symbol->name());
+
+ return true;
+ }
+
+ virtual bool visit(TypenameArgument *symbol)
+ {
+ addType(symbol->name());
+ return true;
+ }
+
+ virtual bool visit(Enum *symbol)
+ {
+ addScope(symbol);
+ addType(symbol->name());
+ return true;
+ }
+
+ virtual bool visit(Namespace *symbol)
+ {
+ addScope(symbol);
+ addType(symbol->name());
+ return true;
+ }
+
+ virtual bool visit(Class *symbol)
+ {
+ addScope(symbol);
+ addType(symbol->name());
+ return true;
+ }
+
+ virtual bool visit(ForwardClassDeclaration *symbol)
+ {
+ addType(symbol->name());
+ return true;
+ }
+
+ // Objective-C
+ virtual bool visit(ObjCBaseClass *) { return true; }
+ virtual bool visit(ObjCBaseProtocol *) { return true; }
+ virtual bool visit(ObjCPropertyDeclaration *) { return true; }
+
+ virtual bool visit(ObjCMethod *symbol)
+ {
+ addScope(symbol);
+ return true;
+ }
+
+ virtual bool visit(ObjCClass *symbol)
+ {
+ addScope(symbol);
+ addType(symbol->name());
+ return true;
+ }
+
+ virtual bool visit(ObjCForwardClassDeclaration *symbol)
+ {
+ addType(symbol->name());
+ return true;
+ }
+
+ virtual bool visit(ObjCProtocol *symbol)
+ {
+ addScope(symbol);
+ addType(symbol->name());
+ return true;
+ }
+
+ virtual bool visit(ObjCForwardProtocolDeclaration *symbol)
+ {
+ addType(symbol->name());
+ return true;
+ }
+};
+
+} // end of anonymous namespace
+
CheckUndefinedSymbols::CheckUndefinedSymbols(TranslationUnit *unit, const LookupContext &context)
: ASTVisitor(unit), _context(context)
{
_fileName = context.thisDocument()->fileName();
+ CollectTypes collectTypes(context.thisDocument(), context.snapshot());
+ _potentialTypes = collectTypes.types();
+ _scopes = collectTypes.scopes();
}
CheckUndefinedSymbols::~CheckUndefinedSymbols()
@@ -84,17 +279,19 @@ bool CheckUndefinedSymbols::visit(UsingDirectiveAST *ast)
return false;
}
-bool CheckUndefinedSymbols::visit(SimpleDeclarationAST *ast)
+bool CheckUndefinedSymbols::visit(SimpleDeclarationAST *)
{
return true;
}
bool CheckUndefinedSymbols::visit(NamedTypeSpecifierAST *ast)
{
+#if 0
if (ast->name) {
unsigned line, column;
- getTokenStartPosition(ast->firstToken(), &line, &column);
+ getTokenStartPosition(ast->name->firstToken(), &line, &column);
+ // ### use the potential types.
Scope *enclosingScope = _context.thisDocument()->scopeAt(line, column);
const QList<Symbol *> candidates = _context.lookup(ast->name->name, enclosingScope);
@@ -108,8 +305,9 @@ bool CheckUndefinedSymbols::visit(NamedTypeSpecifierAST *ast)
if (! ty)
warning(ast->name, QCoreApplication::translate("CheckUndefinedSymbols", "Expected a type-name"));
}
+#endif
- return false;
+ return true;
}
void CheckUndefinedSymbols::checkNamespace(NameAST *name)
@@ -131,3 +329,133 @@ void CheckUndefinedSymbols::checkNamespace(NameAST *name)
const unsigned length = tokenAt(name->lastToken() - 1).end() - tokenAt(name->firstToken()).begin();
warning(line, column, QCoreApplication::translate("CheckUndefinedSymbols", "Expected a namespace-name"), length);
}
+
+bool CheckUndefinedSymbols::visit(SimpleNameAST *ast)
+{
+ if (ast->name) {
+ const QByteArray id = QByteArray::fromRawData(ast->name->identifier()->chars(), // ### move
+ ast->name->identifier()->size());
+ if (_potentialTypes.contains(id)) {
+ const unsigned tokenOffset = tokenAt(ast->firstToken()).offset;
+ Scope *scope = CollectTypes::findScope(tokenOffset, _scopes); // ### move
+ if (! scope)
+ scope = _context.thisDocument()->globalSymbols();
+
+ const QList<Symbol *> candidates = _context.lookup(ast->name, scope);
+ addTypeUsage(candidates, ast);
+ }
+ }
+
+ return true;
+}
+
+bool CheckUndefinedSymbols::visit(TemplateIdAST *ast)
+{
+ if (ast->name) {
+ const QByteArray id = QByteArray::fromRawData(ast->name->identifier()->chars(), // ### move
+ ast->name->identifier()->size());
+ if (_potentialTypes.contains(id)) {
+ Scope *scope = CollectTypes::findScope(tokenAt(ast->firstToken()).offset, _scopes); // ### move
+ if (! scope)
+ scope = _context.thisDocument()->globalSymbols();
+
+ ClassOrNamespace *b = _context.lookupType(ast->name, scope);
+ addTypeUsage(b, ast);
+ }
+ }
+
+ return true;
+}
+
+bool CheckUndefinedSymbols::visit(DestructorNameAST *ast)
+{
+ if (ast->name) {
+ const QByteArray id = QByteArray::fromRawData(ast->name->identifier()->chars(), // ### move
+ ast->name->identifier()->size());
+ if (_potentialTypes.contains(id)) {
+ Scope *scope = CollectTypes::findScope(tokenAt(ast->firstToken()).offset, _scopes); // ### move
+ if (! scope)
+ scope = _context.thisDocument()->globalSymbols();
+
+ ClassOrNamespace *b = _context.lookupType(ast->name, scope);
+ addTypeUsage(b, ast);
+ }
+ }
+
+ return true;
+}
+
+bool CheckUndefinedSymbols::visit(QualifiedNameAST *ast)
+{
+ if (ast->name) {
+ Scope *scope = CollectTypes::findScope(tokenAt(ast->firstToken()).offset, _scopes); // ### move
+ if (! scope)
+ scope = _context.thisDocument()->globalSymbols();
+
+ ClassOrNamespace *b = 0;
+ if (NestedNameSpecifierListAST *it = ast->nested_name_specifier_list) {
+ NestedNameSpecifierAST *nested_name_specifier = it->value;
+ NameAST *class_or_namespace_name = nested_name_specifier->class_or_namespace_name; // ### remove shadowing
+
+ const Name *name = class_or_namespace_name->name;
+ b = _context.lookupType(name, scope);
+ addTypeUsage(b, class_or_namespace_name);
+
+ for (it = it->next; b && it; it = it->next) {
+ NestedNameSpecifierAST *nested_name_specifier = it->value;
+
+ if (NameAST *class_or_namespace_name = nested_name_specifier->class_or_namespace_name) {
+ b = b->findType(class_or_namespace_name->name);
+ addTypeUsage(b, class_or_namespace_name);
+ }
+ }
+ }
+
+ if (b && ast->unqualified_name)
+ addTypeUsage(b->find(ast->unqualified_name->name), ast->unqualified_name);
+ }
+
+ return false;
+}
+
+void CheckUndefinedSymbols::addTypeUsage(ClassOrNamespace *b, NameAST *ast)
+{
+ if (! b)
+ return;
+
+ const Token &tok = tokenAt(ast->firstToken());
+ if (tok.generated())
+ return;
+
+ unsigned line, column;
+ getTokenStartPosition(ast->firstToken(), &line, &column);
+ const unsigned length = tok.length();
+ Use use(line, column, length);
+ _typeUsages.append(use);
+ //qDebug() << "added use" << oo(ast->name) << line << column << length;
+}
+
+void CheckUndefinedSymbols::addTypeUsage(const QList<Symbol *> &candidates, NameAST *ast)
+{
+ const Token &tok = tokenAt(ast->firstToken());
+ if (tok.generated())
+ return;
+
+ unsigned line, column;
+ getTokenStartPosition(ast->firstToken(), &line, &column);
+ const unsigned length = tok.length();
+
+ foreach (Symbol *c, candidates) {
+ if (c->isTypedef() || c->isClass() || c->isEnum() || c->isForwardClassDeclaration() || c->isTypenameArgument()) {
+ Use use(line, column, length);
+ _typeUsages.append(use);
+ //qDebug() << "added use" << oo(ast->name) << line << column << length;
+ break;
+ }
+ }
+}
+
+QList<CheckUndefinedSymbols::Use> CheckUndefinedSymbols::typeUsages() const
+{
+ return _typeUsages;
+}