Commit 55713a15 authored by Christian Kamm's avatar Christian Kamm
Browse files

QmlJS checks: Add severity and unique id to messages.

Change-Id: I2cded26524c3f64152107e65d740658e3003ffac
Reviewed-on: http://codereview.qt-project.org/5790


Sanity-Review: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: default avatarFawzi Mohamed <fawzi.mohamed@nokia.com>
parent f25f1858
......@@ -31,7 +31,8 @@ HEADERS += \
$$PWD/qmljsvalueowner.h \
$$PWD/qmljscontext.h \
$$PWD/qmljsscopechain.h \
$$PWD/qmljsutils.h
$$PWD/qmljsutils.h \
$$PWD/qmljsstaticanalysismessage.h
SOURCES += \
$$PWD/qmljsbind.cpp \
......@@ -54,7 +55,8 @@ SOURCES += \
$$PWD/qmljsvalueowner.cpp \
$$PWD/qmljscontext.cpp \
$$PWD/qmljsscopechain.cpp \
$$PWD/qmljsutils.cpp
$$PWD/qmljsutils.cpp \
$$PWD/qmljsstaticanalysismessage.cpp
RESOURCES += \
$$PWD/qmljs.qrc
......
This diff is collapsed.
......@@ -37,6 +37,7 @@
#include <qmljs/qmljscontext.h>
#include <qmljs/qmljsscopebuilder.h>
#include <qmljs/qmljsscopechain.h>
#include <qmljs/qmljsstaticanalysismessage.h>
#include <qmljs/parser/qmljsastvisitor_p.h>
#include <QtCore/QCoreApplication>
......@@ -57,33 +58,10 @@ public:
Check(Document::Ptr doc, const ContextPtr &context);
virtual ~Check();
QList<DiagnosticMessage> operator()();
enum Option {
WarnDangerousNonStrictEqualityChecks = 1 << 0,
WarnAllNonStrictEqualityChecks = 1 << 1,
WarnBlocks = 1 << 2,
WarnWith = 1 << 3,
WarnVoid = 1 << 4,
WarnCommaExpression = 1 << 5,
WarnExpressionStatement = 1 << 6,
WarnAssignInCondition = 1 << 7,
WarnUseBeforeDeclaration = 1 << 8,
WarnDuplicateDeclaration = 1 << 9,
WarnDeclarationsNotStartOfFunction = 1 << 10,
WarnCaseWithoutFlowControlEnd = 1 << 11,
WarnNonCapitalizedNew = 1 << 12,
WarnCallsOfCapitalizedFunctions = 1 << 13,
WarnUnreachablecode = 1 << 14,
ErrCheckTypeErrors = 1 << 15
};
Q_DECLARE_FLAGS(Options, Option)
const Options options() const
{ return _options; }
void setOptions(Options options)
{ _options = options; }
QList<StaticAnalysis::Message> operator()();
void enableMessage(StaticAnalysis::Type type);
void disableMessage(StaticAnalysis::Type type);
protected:
virtual bool preVisit(AST::Node *ast);
......@@ -130,8 +108,10 @@ private:
void checkNewExpression(AST::ExpressionNode *node);
void checkBindingRhs(AST::Statement *statement);
void warning(const AST::SourceLocation &loc, const QString &message);
void error(const AST::SourceLocation &loc, const QString &message);
void addMessages(const QList<StaticAnalysis::Message> &messages);
void addMessage(const StaticAnalysis::Message &message);
void addMessage(StaticAnalysis::Type type, const AST::SourceLocation &location,
const QString &arg1 = QString(), const QString &arg2 = QString());
AST::Node *parent(int distance = 0);
......@@ -141,9 +121,8 @@ private:
ScopeChain _scopeChain;
ScopeBuilder _scopeBuilder;
QList<DiagnosticMessage> _messages;
Options _options;
QList<StaticAnalysis::Message> _messages;
QSet<StaticAnalysis::Type> _enabledMessages;
const Value *_lastValue;
QList<AST::Node *> _chain;
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#include "qmljsstaticanalysismessage.h"
#include <utils/qtcassert.h>
#include <QtCore/QCoreApplication>
using namespace QmlJS;
using namespace QmlJS::StaticAnalysis;
namespace {
class StaticAnalysisMessages
{
Q_DECLARE_TR_FUNCTIONS(StaticAnalysisMessages)
public:
class PrototypeMessageData {
public:
Type type;
Severity severity;
QString message;
int placeholders;
};
void newMsg(Type type, Severity severity, const QString &message, int placeholders = 0)
{
PrototypeMessageData prototype;
prototype.type = type;
prototype.severity = severity;
prototype.message = message;
prototype.placeholders = placeholders;
QTC_CHECK(placeholders <= 2);
QTC_ASSERT(!messages.contains(type), return);
messages[type] = prototype;
}
StaticAnalysisMessages();
QHash<Type, PrototypeMessageData> messages;
};
StaticAnalysisMessages::StaticAnalysisMessages()
{
newMsg(ErrInvalidEnumValue, Error,
tr("invalid value for enum"));
newMsg(ErrEnumValueMustBeStringOrNumber, Error,
tr("enum value must be a string or a number"));
newMsg(ErrNumberValueExpected, Error,
tr("number value expected"));
newMsg(ErrBooleanValueExpected, Error,
tr("boolean value expected"));
newMsg(ErrStringValueExpected, Error,
tr("string value expected"));
newMsg(ErrInvalidUrl, Error,
tr("invalid URL"));
newMsg(WarnFileOrDirectoryDoesNotExist, Warning,
tr("file or directory does not exist"));
newMsg(ErrInvalidColor, Error,
tr("invalid color"));
newMsg(ErrAnchorLineExpected, Error,
tr("anchor line expected"));
newMsg(ErrPropertiesCanOnlyHaveOneBinding, Error,
tr("duplicate property binding"));
newMsg(ErrIdExpected, Error,
tr("id expected"));
newMsg(ErrInvalidId, Error,
tr("invalid id"));
newMsg(ErrDuplicateId, Error,
tr("duplicate id"));
newMsg(ErrInvalidPropertyName, Error,
tr("invalid property name '%1'"), 1);
newMsg(ErrDoesNotHaveMembers, Error,
tr("'%1' does not have members"), 1);
newMsg(ErrInvalidMember, Error,
tr("'%1' is not a member of '%2'"), 2);
newMsg(WarnAssignmentInCondition, Warning,
tr("assignment in condition"));
newMsg(WarnCaseWithoutFlowControl, Warning,
tr("unterminated non-empty case block"));
newMsg(WarnEval, Warning,
tr("do not use 'eval'"));
newMsg(WarnUnreachable, Warning,
tr("unreachable"));
newMsg(WarnWith, Warning,
tr("do not use 'with'"));
newMsg(WarnComma, Warning,
tr("do not use comma expressions"));
newMsg(WarnAlreadyFormalParameter, Warning,
tr("'%1' is already a formal parameter"), 1);
newMsg(WarnAlreadyFunction, Warning,
tr("'%1' is already a function"), 1);
newMsg(WarnVarUsedBeforeDeclaration, Warning,
tr("var '%1' is used before its declaration"), 1);
newMsg(WarnAlreadyVar, Warning,
tr("'%1' is already a var"), 1);
newMsg(WarnDuplicateDeclaration, Warning,
tr("'%1' is declared more than once"), 1);
newMsg(WarnFunctionUsedBeforeDeclaration, Warning,
tr("function '%1' is used before its declaration"), 1);
newMsg(WarnBooleanConstructor, Warning,
tr("do not use 'Boolean' as a constructor"));
newMsg(WarnStringConstructor, Warning,
tr("do not use 'String' as a constructor"));
newMsg(WarnObjectConstructor, Warning,
tr("do not use 'Object' as a constructor"));
newMsg(WarnArrayConstructor, Warning,
tr("do not use 'Array' as a constructor"));
newMsg(WarnFunctionConstructor, Warning,
tr("do not use 'Function' as a constructor"));
newMsg(HintAnonymousFunctionSpacing, Hint,
tr("the 'function' keyword and the opening parenthesis should be separated by a single space"));
newMsg(WarnBlock, Warning,
tr("do not use stand-alone blocks"));
newMsg(WarnVoid, Warning,
tr("do not use void expressions"));
newMsg(WarnConfusingPluses, Warning,
tr("confusing pluses"));
newMsg(WarnConfusingPreincrement, Warning,
tr("confusing preincrement"));
newMsg(WarnConfusingMinuses, Warning,
tr("confusing minuses"));
newMsg(WarnConfusingPredecrement, Warning,
tr("confusing predecrement"));
newMsg(HintDeclareVarsInOneLine, Hint,
tr("declare all function vars on a single line"));
// unused
// newMsg(HintExtraParentheses, Hint,
// tr(""));
newMsg(MaybeWarnEqualityTypeCoercion, MaybeWarning,
tr("== and != may perform type coercion, use === or !== to avoid"));
newMsg(WarnConfusingExpressionStatement, Warning,
tr("expression statements should be assignments, calls or delete expressions only"));
newMsg(HintDeclarationsShouldBeAtStartOfFunction, Error,
tr("var declarations should be at the start of a function"));
newMsg(HintOneStatementPerLine, Error,
tr("only use one statement per line"));
newMsg(ErrUnknownComponent, Error,
tr("unknown component"));
newMsg(ErrCouldNotResolvePrototypeOf, Error,
tr("could not resolve the prototype '%1'' of '%2'"), 2);
newMsg(ErrCouldNotResolvePrototype, Error,
tr("could not resolve the prototype '%1'"), 1);
newMsg(ErrPrototypeCycle, Error,
tr("prototype cycle, the last non-repeated component is '%1'"), 1);
newMsg(ErrInvalidPropertyType, Error,
tr("invalid property type '%1'"), 1);
newMsg(WarnEqualityTypeCoercion, Warning,
tr("== and != perform type coercion, use === or !== to avoid"));
newMsg(WarnExpectedNewWithUppercaseFunction, Warning,
tr("calls of functions that start with an uppercase letter should use 'new'"));
newMsg(WarnNewWithLowercaseFunction, Warning,
tr("'new' should only be used with functions that start with an uppercase letter"));
newMsg(WarnNumberConstructor, Warning,
tr("do not use 'Number' as a constructor"));
newMsg(HintBinaryOperatorSpacing, Hint,
tr("use spaces around binary operators"));
newMsg(WarnUnintentinalEmptyBlock, Warning,
tr("unintentional empty block, use ({}) for empty object literal"));
}
} // anonymous namespace
Q_GLOBAL_STATIC(StaticAnalysisMessages, messages)
QList<Type> Message::allMessageTypes()
{
return messages()->messages.keys();
}
Message::Message()
: type(UnknownType), severity(Hint)
{}
Message::Message(Type type, AST::SourceLocation location, const QString &arg1, const QString &arg2)
: location(location), type(type)
{
QTC_ASSERT(messages()->messages.contains(type), return);
const StaticAnalysisMessages::PrototypeMessageData &prototype = messages()->messages.value(type);
severity = prototype.severity;
message = prototype.message;
if (prototype.placeholders == 0) {
if (!arg1.isEmpty() || !arg2.isEmpty())
qWarning() << "StaticAnalysis message" << type << "expects no arguments";
} else if (prototype.placeholders == 1) {
if (arg1.isEmpty() || !arg2.isEmpty())
qWarning() << "StaticAnalysis message" << type << "expects exactly one argument";
message = message.arg(arg1);
} else if (prototype.placeholders == 2) {
if (arg1.isEmpty() || arg2.isEmpty())
qWarning() << "StaticAnalysis message" << type << "expects exactly two arguments";
message = message.arg(arg1, arg2);
}
}
bool Message::isValid() const
{
return type != UnknownType && location.isValid() && !message.isEmpty();
}
DiagnosticMessage Message::toDiagnosticMessage() const
{
DiagnosticMessage diagnostic;
switch (severity) {
case Hint:
case MaybeWarning:
case Warning:
diagnostic.kind = DiagnosticMessage::Warning;
break;
default:
diagnostic.kind = DiagnosticMessage::Error;
break;
}
diagnostic.loc = location;
diagnostic.message = message;
return diagnostic;
}
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** 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.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#ifndef QMLJS_STATICANALYSIS_QMLJSSTATICANALYSISMESSAGE_H
#define QMLJS_STATICANALYSIS_QMLJSSTATICANALYSISMESSAGE_H
#include "qmljs_global.h"
#include "parser/qmljsengine_p.h"
namespace QmlJS {
namespace StaticAnalysis {
enum Severity
{
Hint, // cosmetic or convention
MaybeWarning, // possibly a warning, insufficient information
Warning, // could cause unintended behavior
MaybeError, // possibly an error, insufficient information
Error // definitely an error
};
enum Type
{
UnknownType = 0,
ErrInvalidEnumValue = 1,
ErrEnumValueMustBeStringOrNumber = 2,
ErrNumberValueExpected = 3,
ErrBooleanValueExpected = 4,
ErrStringValueExpected = 5,
ErrInvalidUrl = 6,
WarnFileOrDirectoryDoesNotExist = 7,
ErrInvalidColor = 8,
ErrAnchorLineExpected = 9,
ErrPropertiesCanOnlyHaveOneBinding = 10,
ErrIdExpected = 11,
ErrInvalidId = 14,
ErrDuplicateId = 15,
ErrInvalidPropertyName = 16,
ErrDoesNotHaveMembers = 17,
ErrInvalidMember = 18,
WarnAssignmentInCondition = 19,
WarnCaseWithoutFlowControl = 20,
WarnEval = 23,
WarnUnreachable = 28,
WarnWith = 29,
WarnComma = 30,
WarnAlreadyFormalParameter = 103,
WarnAlreadyFunction = 104,
WarnVarUsedBeforeDeclaration = 105,
WarnAlreadyVar = 106,
WarnDuplicateDeclaration = 107,
WarnFunctionUsedBeforeDeclaration = 108,
WarnBooleanConstructor = 109,
WarnStringConstructor = 110,
WarnObjectConstructor = 111,
WarnArrayConstructor = 112,
WarnFunctionConstructor = 113,
HintAnonymousFunctionSpacing = 114,
WarnBlock = 115,
WarnVoid = 116,
WarnConfusingPluses = 117,
WarnConfusingPreincrement = 118,
WarnConfusingMinuses = 119,
WarnConfusingPredecrement = 120,
HintDeclareVarsInOneLine = 121,
HintExtraParentheses = 123,
MaybeWarnEqualityTypeCoercion = 126,
WarnConfusingExpressionStatement = 127,
HintDeclarationsShouldBeAtStartOfFunction = 201,
HintOneStatementPerLine = 202,
ErrUnknownComponent = 300,
ErrCouldNotResolvePrototypeOf = 301,
ErrCouldNotResolvePrototype = 302,
ErrPrototypeCycle = 303,
ErrInvalidPropertyType = 304,
WarnEqualityTypeCoercion = 305,
WarnExpectedNewWithUppercaseFunction = 306,
WarnNewWithLowercaseFunction = 307,
WarnNumberConstructor = 308,
HintBinaryOperatorSpacing = 309,
WarnUnintentinalEmptyBlock = 310
};
class QMLJS_EXPORT Message
{
public:
Message();
Message(Type type, AST::SourceLocation location,
const QString &arg1 = QString(),
const QString &arg2 = QString());
static QList<Type> allMessageTypes();
bool isValid() const;
DiagnosticMessage toDiagnosticMessage() const;
AST::SourceLocation location;
QString message;
Type type;
Severity severity;
};
} // namespace StaticAnalysis
} // namespace QmlJS
#endif // QMLJS_STATICANALYSIS_QMLJSSTATICANALYSISMESSAGE_H
......@@ -765,10 +765,13 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH
if (view()->checkSemanticErrors()) {
Check check(doc, m_scopeChain->context());
check.setOptions(check.options() & ~Check::ErrCheckTypeErrors);
foreach (const QmlJS::DiagnosticMessage &diagnosticMessage, check()) {
if (diagnosticMessage.isError())
errors.append(RewriterView::Error(diagnosticMessage, QUrl::fromLocalFile(doc->fileName())));
check.disableMessage(StaticAnalysis::ErrUnknownComponent);
check.disableMessage(StaticAnalysis::ErrPrototypeCycle);
check.disableMessage(StaticAnalysis::ErrCouldNotResolvePrototype);
check.disableMessage(StaticAnalysis::ErrCouldNotResolvePrototypeOf);
foreach (const StaticAnalysis::Message &message, check()) {
if (message.severity == StaticAnalysis::Error)
errors.append(RewriterView::Error(message.toDiagnosticMessage(), QUrl::fromLocalFile(doc->fileName())));
}
if (!errors.isEmpty()) {
......
......@@ -147,7 +147,9 @@ SemanticInfo SemanticInfoUpdater::semanticInfo(const SemanticInfoUpdaterSource &
semanticInfo.m_rootScopeChain = QSharedPointer<const QmlJS::ScopeChain>(scopeChain);
QmlJS::Check checker(doc, semanticInfo.context);
semanticInfo.semanticMessages.append(checker());
foreach (const QmlJS::StaticAnalysis::Message &msg, checker()) {
semanticInfo.semanticMessages += msg.toDiagnosticMessage();
}
return semanticInfo;
}
......
......@@ -87,6 +87,14 @@ static QList<ProjectExplorer::Task> convertToTasks(const QList<DiagnosticMessage
return result;
}
static QList<ProjectExplorer::Task> convertToTasks(const QList<StaticAnalysis::Message> &messages, const QString &fileName, const QString &category)
{
QList<DiagnosticMessage> diagnostics;
foreach (const StaticAnalysis::Message &msg, messages)
diagnostics += msg.toDiagnosticMessage();
return convertToTasks(diagnostics, fileName, category);
}
void QmlTaskManager::collectMessages(
QFutureInterface<FileErrorMessages> &future,
Snapshot snapshot, QList<ModelManagerInterface::ProjectInfo> projectInfos,
......
import Qt 4.7
// DEFAULTMSG == and != perform type coercion, use === or !== instead to avoid
Rectangle {
onXChanged: {
if (0 == undefined) {} // W 15 16
if (0 == undefined) {} // 126 15 16
}
function foo() {
......@@ -14,47 +14,47 @@ Rectangle {
var o = {}
if (s == s) {}
if (s == n) {} // W 15 16
if (s == n) {} // 126 15 16
if (s == N) {} // ### should warn: always false
if (s == u) {} // W 15 16
if (s == b) {} // W 15 16
if (s == o) {} // W 15 16
if (s == u) {} // 126 15 16
if (s == b) {} // 126 15 16
if (s == o) {} // 126 15 16
if (n == s) {} // W 15 16
if (n == s) {} // 126 15 16
if (n == n) {}
if (n == N) {} // ### should warn: always false
if (n == u) {} // W 15 16
if (n == b) {} // W 15 16
if (n == o) {} // W 15 16
if (n == u) {} // 126 15 16
if (n == b) {} // 126 15 16
if (n == o) {} // 126 15 16
if (N == s) {} // ### should warn: always false
if (N == n) {} // ### should warn: always false
if (N == N) {}
if (N == u) {} // W 15 16
if (N == u) {} // 126 15 16
// ### should warn: always false
if (N == b) {} // W 15 16
if (N == b) {} // 126 15 16
if (N == o) {} // ### should warn: always false
if (u == s) {} // W 15 16
if (u == n) {} // W 15 16
if (u == N) {} // W 15 16
if (u == u) {} // W 15 16
if (u == b) {} // W 15 16
if (u == o) {} // W 15 16
if (u == s) {} // 126 15 16
if (u == n) {} // 126 15 16
if (u == N) {} // 126 15 16
if (u == u) {} // 126 15 16
if (u == b) {} // 126 15 16
if (u == o) {} // 126 15 16
if (b == s) {} // W 15 16
if (b == n) {} // W 15 16
if (b == s) {} // 126 15 16
if (b == n) {} // 126 15 16
// ### should warn: always false
if (b == N) {} // W 15 16
if (b == u) {} // W 15 16
if (b == N) {} // 126 15 16
if (b == u) {} // 126 15 16
if (b == b) {}
if (b == o) {} // W 15 16
if (b == o) {} // 126 15 16
if (o == s) {} // W 15 16
if (o == n) {} // W 15 16