diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/cppeditor/cppcheckundefinedsymbols.cpp | 531 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppcheckundefinedsymbols.h | 102 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppeditor.cpp | 3 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppeditor.pro | 6 | ||||
-rw-r--r-- | src/plugins/cpptools/cppmodelmanager.cpp | 1 |
5 files changed, 639 insertions, 4 deletions
diff --git a/src/plugins/cppeditor/cppcheckundefinedsymbols.cpp b/src/plugins/cppeditor/cppcheckundefinedsymbols.cpp new file mode 100644 index 0000000000..d5f8e3337a --- /dev/null +++ b/src/plugins/cppeditor/cppcheckundefinedsymbols.cpp @@ -0,0 +1,531 @@ +/************************************************************************** +** +** 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 "cppcheckundefinedsymbols.h" +#include <cplusplus/Overview.h> + +#include <Names.h> +#include <Literals.h> +#include <Symbols.h> +#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) + { + for (TemplateParameters *p = symbol->templateParameters(); p; p = p->previous()) { + Scope *scope = p->scope(); + for (unsigned i = 0; i < scope->symbolCount(); ++i) + accept(scope->symbolAt(i)); + } + + 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) + { + for (TemplateParameters *p = symbol->templateParameters(); p; p = p->previous()) { + Scope *scope = p->scope(); + for (unsigned i = 0; i < scope->symbolCount(); ++i) + accept(scope->symbolAt(i)); + } + + addScope(symbol); + addType(symbol->name()); + return true; + } + + virtual bool visit(ForwardClassDeclaration *symbol) + { + for (TemplateParameters *p = symbol->templateParameters(); p; p = p->previous()) { + Scope *scope = p->scope(); + for (unsigned i = 0; i < scope->symbolCount(); ++i) + accept(scope->symbolAt(i)); + } + + 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() +{ } + +QList<Document::DiagnosticMessage> CheckUndefinedSymbols::operator()(AST *ast) +{ + _diagnosticMessages.clear(); + accept(ast); + return _diagnosticMessages; +} + +bool CheckUndefinedSymbols::warning(unsigned line, unsigned column, const QString &text, unsigned length) +{ + Document::DiagnosticMessage m(Document::DiagnosticMessage::Warning, _fileName, line, column, text, length); + _diagnosticMessages.append(m); + return false; +} + +bool CheckUndefinedSymbols::warning(AST *ast, const QString &text) +{ + const Token &firstToken = tokenAt(ast->firstToken()); + const Token &lastToken = tokenAt(ast->lastToken() - 1); + + const unsigned length = lastToken.end() - firstToken.begin(); + unsigned line = 1, column = 1; + getTokenStartPosition(ast->firstToken(), &line, &column); + + warning(line, column, text, length); + return false; +} + +bool CheckUndefinedSymbols::visit(NamespaceAST *ast) +{ + if (ast->identifier_token) { + const Token &tok = tokenAt(ast->identifier_token); + if (! tok.generated()) { + unsigned line, column; + getTokenStartPosition(ast->identifier_token, &line, &column); + Use use(line, column, tok.length()); + _typeUsages.append(use); + } + } + + return true; +} + +bool CheckUndefinedSymbols::visit(UsingDirectiveAST *) +{ + return true; +} + +bool CheckUndefinedSymbols::visit(SimpleDeclarationAST *) +{ + return true; +} + +bool CheckUndefinedSymbols::visit(NamedTypeSpecifierAST *) +{ + return true; +} + +void CheckUndefinedSymbols::checkNamespace(NameAST *name) +{ + if (! name) + return; + + unsigned line, column; + getTokenStartPosition(name->firstToken(), &line, &column); + + Scope *enclosingScope = _context.thisDocument()->scopeAt(line, column); + if (ClassOrNamespace *b = _context.lookupType(name->name, enclosingScope)) { + foreach (Symbol *s, b->symbols()) { + if (s->isNamespace()) + return; + } + } + + const unsigned length = tokenAt(name->lastToken() - 1).end() - tokenAt(name->firstToken()).begin(); + warning(line, column, QCoreApplication::translate("CheckUndefinedSymbols", "Expected a namespace-name"), length); +} + +void CheckUndefinedSymbols::checkName(NameAST *ast) +{ + if (ast->name) { + if (const Identifier *ident = ast->name->identifier()) { + const QByteArray id = QByteArray::fromRawData(ident->chars(), ident->size()); + if (_potentialTypes.contains(id)) { + Scope *scope = findScope(ast); + const QList<Symbol *> candidates = _context.lookup(ast->name, scope); + addTypeUsage(candidates, ast); + } + } + } +} + +bool CheckUndefinedSymbols::visit(SimpleNameAST *ast) +{ + checkName(ast); + return true; +} + +bool CheckUndefinedSymbols::visit(TemplateIdAST *ast) +{ + checkName(ast); + return true; +} + +bool CheckUndefinedSymbols::visit(DestructorNameAST *ast) +{ + checkName(ast); + return true; +} + +bool CheckUndefinedSymbols::visit(QualifiedNameAST *ast) +{ + if (ast->name) { + Scope *scope = findScope(ast); + + ClassOrNamespace *b = 0; + if (NestedNameSpecifierListAST *it = ast->nested_name_specifier_list) { + NestedNameSpecifierAST *nested_name_specifier = it->value; + if (NameAST *class_or_namespace_name = nested_name_specifier->class_or_namespace_name) { // ### remove shadowing + + if (TemplateIdAST *template_id = class_or_namespace_name->asTemplateId()) + accept(template_id->template_argument_list); + + 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) { + if (TemplateIdAST *template_id = class_or_namespace_name->asTemplateId()) + accept(template_id->template_argument_list); + + 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; +} + +bool CheckUndefinedSymbols::visit(TypenameTypeParameterAST *ast) +{ + if (ast->name && ast->name->name) { + if (const Identifier *templId = ast->name->name->identifier()) { + const QByteArray id = QByteArray::fromRawData(templId->chars(), templId->size()); + if (_potentialTypes.contains(id)) { + Scope *scope = findScope(_templateDeclarationStack.back()); + const QList<Symbol *> candidates = _context.lookup(ast->name->name, scope); + addTypeUsage(candidates, ast->name); + } + } + } + return true; +} + +bool CheckUndefinedSymbols::visit(TemplateTypeParameterAST *ast) +{ + checkName(ast->name); + return true; +} + +bool CheckUndefinedSymbols::visit(TemplateDeclarationAST *ast) +{ + _templateDeclarationStack.append(ast); + return true; +} + +void CheckUndefinedSymbols::endVisit(TemplateDeclarationAST *) +{ + _templateDeclarationStack.takeFirst(); +} + +void CheckUndefinedSymbols::addTypeUsage(ClassOrNamespace *b, NameAST *ast) +{ + if (! b) + return; + + 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(); + 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) +{ + 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->isUsingDeclaration()) // skip using declarations... + continue; + else if (c->isUsingNamespaceDirective()) // ... and using namespace directives. + continue; + else if (c->isTypedef() || c->isNamespace() || + 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; +} + +unsigned CheckUndefinedSymbols::startOfTemplateDeclaration(TemplateDeclarationAST *ast) const +{ + if (ast->declaration) { + if (TemplateDeclarationAST *templ = ast->declaration->asTemplateDeclaration()) + return startOfTemplateDeclaration(templ); + + return ast->declaration->firstToken(); + } + + return ast->firstToken(); +} + +Scope *CheckUndefinedSymbols::findScope(AST *ast) const +{ + Scope *scope = 0; + + if (ast) { + unsigned startToken = ast->firstToken(); + if (TemplateDeclarationAST *templ = ast->asTemplateDeclaration()) + startToken = startOfTemplateDeclaration(templ); + + const unsigned tokenOffset = tokenAt(startToken).offset; + scope = CollectTypes::findScope(tokenOffset, _scopes); + } + + if (! scope) + scope = _context.thisDocument()->globalSymbols(); + + return scope; +} diff --git a/src/plugins/cppeditor/cppcheckundefinedsymbols.h b/src/plugins/cppeditor/cppcheckundefinedsymbols.h new file mode 100644 index 0000000000..e4dd960f50 --- /dev/null +++ b/src/plugins/cppeditor/cppcheckundefinedsymbols.h @@ -0,0 +1,102 @@ +/************************************************************************** +** +** 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 CPLUSPLUS_CHECKUNDEFINEDSYMBOLS_H +#define CPLUSPLUS_CHECKUNDEFINEDSYMBOLS_H + +#include <cplusplus/CppDocument.h> +#include <cplusplus/LookupContext.h> +#include <ASTVisitor.h> +#include <QtCore/QSet> + +namespace CPlusPlus { + +class CheckUndefinedSymbols: protected ASTVisitor +{ +public: + CheckUndefinedSymbols(TranslationUnit *unit, const LookupContext &context); + virtual ~CheckUndefinedSymbols(); + + QList<Document::DiagnosticMessage> operator()(AST *ast); + + struct Use { // ### remove me + unsigned line; + unsigned column; + unsigned length; + + Use(unsigned line = 0, unsigned column = 0, unsigned length = 0) + : line(line), column(column), length(length) {} + }; + + QList<Use> typeUsages() const; + +protected: + using ASTVisitor::visit; + using ASTVisitor::endVisit; + + bool warning(unsigned line, unsigned column, const QString &text, unsigned length = 0); + bool warning(AST *ast, const QString &text); + + void checkName(NameAST *ast); + void checkNamespace(NameAST *name); + void addTypeUsage(ClassOrNamespace *b, NameAST *ast); + void addTypeUsage(const QList<Symbol *> &candidates, NameAST *ast); + + virtual bool visit(NamespaceAST *); + virtual bool visit(UsingDirectiveAST *); + virtual bool visit(SimpleDeclarationAST *); + virtual bool visit(NamedTypeSpecifierAST *); + + virtual bool visit(SimpleNameAST *ast); + virtual bool visit(DestructorNameAST *ast); + virtual bool visit(QualifiedNameAST *ast); + virtual bool visit(TemplateIdAST *ast); + + virtual bool visit(TemplateDeclarationAST *ast); + virtual void endVisit(TemplateDeclarationAST *ast); + + virtual bool visit(TypenameTypeParameterAST *ast); + virtual bool visit(TemplateTypeParameterAST *ast); + + unsigned startOfTemplateDeclaration(TemplateDeclarationAST *ast) const; + Scope *findScope(AST *ast) const; + +private: + LookupContext _context; + QString _fileName; + QList<Document::DiagnosticMessage> _diagnosticMessages; + QSet<QByteArray> _potentialTypes; + QList<ScopedSymbol *> _scopes; + QList<Use> _typeUsages; + QList<TemplateDeclarationAST *> _templateDeclarationStack; +}; + +} // end of namespace CPlusPlus + +#endif // CPLUSPLUS_CHECKUNDEFINEDSYMBOLS_H diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index fc7b9f3a66..bb89ca7187 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -31,6 +31,8 @@ #include "cppeditorconstants.h" #include "cppplugin.h" #include "cpphighlighter.h" +#include "cppcheckundefinedsymbols.h" + #include "cppquickfix.h" #include <cpptools/cpptoolsplugin.h> @@ -54,7 +56,6 @@ #include <cplusplus/MatchingText.h> #include <cplusplus/BackwardsScanner.h> #include <cplusplus/FastPreprocessor.h> -#include <cplusplus/CheckUndefinedSymbols.h> #include <cpptools/cppmodelmanagerinterface.h> #include <cpptools/cpptoolsconstants.h> diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 8e3b022e4b..0fbfba0781 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -15,7 +15,8 @@ HEADERS += cppplugin.h \ cppeditor_global.h \ cppclasswizard.h \ cppquickfix.h \ - cpprefactoringchanges.h + cpprefactoringchanges.h \ + cppcheckundefinedsymbols.h SOURCES += cppplugin.cpp \ cppeditor.cpp \ @@ -24,7 +25,8 @@ SOURCES += cppplugin.cpp \ cppfilewizard.cpp \ cppclasswizard.cpp \ cppquickfix.cpp \ - cpprefactoringchanges.cpp + cpprefactoringchanges.cpp \ + cppcheckundefinedsymbols.cpp RESOURCES += cppeditor.qrc diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 317fba77d8..486eabb51f 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -29,7 +29,6 @@ #include <cplusplus/pp.h> #include <cplusplus/Overview.h> -#include <cplusplus/CheckUndefinedSymbols.h> #include "cppmodelmanager.h" #ifndef ICHECK_BUILD |