OSDN Git Service

QmlJS checks: Limit warning about unintentional empty blocks.
[qt-creator-jp/qt-creator-jp.git] / src / libs / qmljs / qmljscheck.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
30 **
31 **************************************************************************/
32
33 #include "qmljscheck.h"
34 #include "qmljsbind.h"
35 #include "qmljscontext.h"
36 #include "qmljsevaluate.h"
37 #include "qmljsutils.h"
38 #include "parser/qmljsast_p.h"
39
40 #include <utils/qtcassert.h>
41
42 #include <QtCore/QDebug>
43 #include <QtCore/QDir>
44 #include <QtGui/QColor>
45 #include <QtGui/QApplication>
46
47 using namespace QmlJS;
48 using namespace QmlJS::AST;
49 using namespace QmlJS::StaticAnalysis;
50
51 namespace {
52
53 class AssignmentCheck : public ValueVisitor
54 {
55 public:
56     Message operator()(
57             const Document::Ptr &document,
58             const SourceLocation &location,
59             const Value *lhsValue,
60             const Value *rhsValue,
61             Node *ast)
62     {
63         _doc = document;
64         _rhsValue = rhsValue;
65         _location = location;
66         if (ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast))
67             _ast = expStmt->expression;
68         else
69             _ast = ast->expressionCast();
70
71         if (lhsValue)
72             lhsValue->accept(this);
73
74         return _message;
75     }
76
77     void setMessage(Type type)
78     {
79         _message = Message(type, _location);
80     }
81
82     virtual void visit(const NumberValue *value)
83     {
84         if (const QmlEnumValue *enumValue = value_cast<QmlEnumValue>(value)) {
85             if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
86                 const QString valueName = stringLiteral->value.toString();
87
88                 if (!enumValue->keys().contains(valueName)) {
89                     setMessage(ErrInvalidEnumValue);
90                 }
91             } else if (! _rhsValue->asStringValue() && ! _rhsValue->asNumberValue()
92                        && ! _rhsValue->asUndefinedValue()) {
93                 setMessage(ErrEnumValueMustBeStringOrNumber);
94             }
95         } else {
96             if (cast<TrueLiteral *>(_ast)
97                     || cast<FalseLiteral *>(_ast)) {
98                 setMessage(ErrNumberValueExpected);
99             }
100         }
101     }
102
103     virtual void visit(const BooleanValue *)
104     {
105         UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);
106
107         if (cast<StringLiteral *>(_ast)
108                 || cast<NumericLiteral *>(_ast)
109                 || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))) {
110             setMessage(ErrBooleanValueExpected);
111         }
112     }
113
114     virtual void visit(const StringValue *value)
115     {
116         UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);
117
118         if (cast<NumericLiteral *>(_ast)
119                 || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))
120                 || cast<TrueLiteral *>(_ast)
121                 || cast<FalseLiteral *>(_ast)) {
122             setMessage(ErrStringValueExpected);
123         }
124
125         if (value && value->asUrlValue()) {
126             if (StringLiteral *literal = cast<StringLiteral *>(_ast)) {
127                 QUrl url(literal->value.toString());
128                 if (!url.isValid() && !url.isEmpty()) {
129                     setMessage(ErrInvalidUrl);
130                 } else {
131                     QString fileName = url.toLocalFile();
132                     if (!fileName.isEmpty()) {
133                         if (QFileInfo(fileName).isRelative()) {
134                             fileName.prepend(QDir::separator());
135                             fileName.prepend(_doc->path());
136                         }
137                         if (!QFileInfo(fileName).exists()) {
138                             setMessage(WarnFileOrDirectoryDoesNotExist);
139                         }
140                     }
141                 }
142             }
143         }
144     }
145
146     virtual void visit(const ColorValue *)
147     {
148         if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
149             if (!toQColor(stringLiteral->value.toString()).isValid())
150                 setMessage(ErrInvalidColor);
151         } else {
152             visit((StringValue *)0);
153         }
154     }
155
156     virtual void visit(const AnchorLineValue *)
157     {
158         if (! (_rhsValue->asAnchorLineValue() || _rhsValue->asUndefinedValue()))
159             setMessage(ErrAnchorLineExpected);
160     }
161
162     Document::Ptr _doc;
163     Message _message;
164     SourceLocation _location;
165     const Value *_rhsValue;
166     ExpressionNode *_ast;
167 };
168
169 class ReachesEndCheck : protected Visitor
170 {
171 public:
172     bool operator()(Node *node)
173     {
174         _labels.clear();
175         _labelledBreaks.clear();
176         return check(node) == ReachesEnd;
177     }
178
179 protected:
180     // Sorted by how much code will be reachable from that state, i.e.
181     // ReachesEnd is guaranteed to reach more code than Break and so on.
182     enum State
183     {
184         ReachesEnd = 0,
185         Break = 1,
186         Continue = 2,
187         ReturnOrThrow = 3
188     };
189     State _state;
190     QHash<QString, Node *> _labels;
191     QSet<Node *> _labelledBreaks;
192
193     virtual void onUnreachable(Node *)
194     {}
195
196     virtual State check(Node *node)
197     {
198         _state = ReachesEnd;
199         Node::accept(node, this);
200         return _state;
201     }
202
203     virtual bool preVisit(Node *ast)
204     {
205         if (ast->expressionCast())
206             return false;
207         if (_state == ReachesEnd)
208             return true;
209         if (Statement *stmt = ast->statementCast())
210             onUnreachable(stmt);
211         if (FunctionSourceElement *fun = cast<FunctionSourceElement *>(ast))
212             onUnreachable(fun->declaration);
213         if (StatementSourceElement *stmt = cast<StatementSourceElement *>(ast))
214             onUnreachable(stmt->statement);
215         return false;
216     }
217
218     virtual bool visit(LabelledStatement *ast)
219     {
220         // get the target statement
221         Statement *end = ast->statement;
222         forever {
223             if (LabelledStatement *label = cast<LabelledStatement *>(end))
224                 end = label->statement;
225             else
226                 break;
227         }
228         if (!ast->label.isEmpty())
229             _labels[ast->label.toString()] = end;
230         return true;
231     }
232
233     virtual bool visit(BreakStatement *ast)
234     {
235         _state = Break;
236         if (!ast->label.isEmpty()) {
237             if (Node *target = _labels.value(ast->label.toString())) {
238                 _labelledBreaks.insert(target);
239                 _state = ReturnOrThrow; // unwind until label is hit
240             }
241         }
242         return false;
243     }
244
245     // labelled continues don't change control flow...
246     virtual bool visit(ContinueStatement *) { _state = Continue; return false; }
247
248     virtual bool visit(ReturnStatement *) { _state = ReturnOrThrow; return false; }
249     virtual bool visit(ThrowStatement *) { _state = ReturnOrThrow; return false; }
250
251     virtual bool visit(IfStatement *ast)
252     {
253         State ok = check(ast->ok);
254         State ko = check(ast->ko);
255         _state = qMin(ok, ko);
256         return false;
257     }
258
259     void handleClause(StatementList *statements, State *result, bool *fallthrough)
260     {
261         State clauseResult = check(statements);
262         if (clauseResult == ReachesEnd) {
263             *fallthrough = true;
264         } else {
265             *fallthrough = false;
266             *result = qMin(*result, clauseResult);
267         }
268     }
269
270     virtual bool visit(SwitchStatement *ast)
271     {
272         if (!ast->block)
273             return false;
274         State result = ReturnOrThrow;
275         bool lastWasFallthrough = false;
276
277         for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
278             if (it->clause)
279                 handleClause(it->clause->statements, &result, &lastWasFallthrough);
280         }
281         if (ast->block->defaultClause)
282             handleClause(ast->block->defaultClause->statements, &result, &lastWasFallthrough);
283         for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) {
284             if (it->clause)
285                 handleClause(it->clause->statements, &result, &lastWasFallthrough);
286         }
287
288         if (lastWasFallthrough || !ast->block->defaultClause)
289             result = ReachesEnd;
290         if (result == Break || _labelledBreaks.contains(ast))
291             result = ReachesEnd;
292         _state = result;
293         return false;
294     }
295
296     virtual bool visit(TryStatement *ast)
297     {
298         State tryBody = check(ast->statement);
299         State catchBody = ReturnOrThrow;
300         if (ast->catchExpression)
301             catchBody = check(ast->catchExpression->statement);
302         State finallyBody = ReachesEnd;
303         if (ast->finallyExpression)
304             finallyBody = check(ast->finallyExpression->statement);
305
306         _state = qMax(qMin(tryBody, catchBody), finallyBody);
307         return false;
308     }
309
310     bool preconditionLoopStatement(Node *, Statement *body)
311     {
312         check(body);
313         _state = ReachesEnd; // condition could be false...
314         return false;
315     }
316
317     virtual bool visit(WhileStatement *ast) { return preconditionLoopStatement(ast, ast->statement); }
318     virtual bool visit(ForStatement *ast) { return preconditionLoopStatement(ast, ast->statement); }
319     virtual bool visit(ForEachStatement *ast) { return preconditionLoopStatement(ast, ast->statement); }
320     virtual bool visit(LocalForStatement *ast) { return preconditionLoopStatement(ast, ast->statement); }
321     virtual bool visit(LocalForEachStatement *ast) { return preconditionLoopStatement(ast, ast->statement); }
322
323     virtual bool visit(DoWhileStatement *ast)
324     {
325         check(ast->statement);
326         // not necessarily an infinite loop due to labelled breaks
327         if (_state == Continue)
328             _state = ReturnOrThrow;
329         if (_state == Break || _labelledBreaks.contains(ast))
330             _state = ReachesEnd;
331         return false;
332     }
333 };
334
335 class MarkUnreachableCode : protected ReachesEndCheck
336 {
337     QList<Message> _messages;
338     bool _emittedWarning;
339
340 public:
341     QList<Message> operator()(Node *ast)
342     {
343         _messages.clear();
344         check(ast);
345         return _messages;
346     }
347
348 protected:
349     virtual State check(Node *node)
350     {
351         bool oldwarning = _emittedWarning;
352         _emittedWarning = false;
353         State s = ReachesEndCheck::check(node);
354         _emittedWarning = oldwarning;
355         return s;
356     }
357
358     virtual void onUnreachable(Node *node)
359     {
360         if (_emittedWarning)
361             return;
362         _emittedWarning = true;
363
364         Message message(WarnUnreachable, SourceLocation());
365         if (Statement *statement = node->statementCast())
366             message.location = locationFromRange(statement->firstSourceLocation(), statement->lastSourceLocation());
367         else if (ExpressionNode *expr = node->expressionCast())
368             message.location = locationFromRange(expr->firstSourceLocation(), expr->lastSourceLocation());
369         if (message.isValid())
370             _messages += message;
371     }
372 };
373
374 class DeclarationsCheck : protected Visitor
375 {
376 public:
377     QList<Message> operator()(FunctionExpression *function)
378     {
379         clear();
380         for (FormalParameterList *plist = function->formals; plist; plist = plist->next) {
381             if (!plist->name.isEmpty())
382                 _formalParameterNames += plist->name.toString();
383         }
384
385         Node::accept(function->body, this);
386         return _messages;
387     }
388
389     QList<Message> operator()(Node *node)
390     {
391         clear();
392         Node::accept(node, this);
393         return _messages;
394     }
395
396 protected:
397     void clear()
398     {
399         _messages.clear();
400         _declaredFunctions.clear();
401         _declaredVariables.clear();
402         _possiblyUndeclaredUses.clear();
403         _seenNonDeclarationStatement = false;
404         _formalParameterNames.clear();
405     }
406
407     void postVisit(Node *ast)
408     {
409         if (!_seenNonDeclarationStatement && ast->statementCast()
410                 && !cast<VariableStatement *>(ast)) {
411             _seenNonDeclarationStatement = true;
412         }
413     }
414
415     bool visit(IdentifierExpression *ast)
416     {
417         if (ast->name.isEmpty())
418             return false;
419         const QString &name = ast->name.toString();
420         if (!_declaredFunctions.contains(name) && !_declaredVariables.contains(name))
421             _possiblyUndeclaredUses[name].append(ast->identifierToken);
422         return false;
423     }
424
425     bool visit(VariableStatement *ast)
426     {
427         if (_seenNonDeclarationStatement) {
428             addMessage(HintDeclarationsShouldBeAtStartOfFunction, ast->declarationKindToken);
429         }
430         return true;
431     }
432
433     bool visit(VariableDeclaration *ast)
434     {
435         if (ast->name.isEmpty())
436             return true;
437         const QString &name = ast->name.toString();
438
439         if (_formalParameterNames.contains(name)) {
440             addMessage(WarnAlreadyFormalParameter, ast->identifierToken, name);
441         } else if (_declaredFunctions.contains(name)) {
442             addMessage(WarnAlreadyFunction, ast->identifierToken, name);
443         } else if (_declaredVariables.contains(name)) {
444             addMessage(WarnDuplicateDeclaration, ast->identifierToken, name);
445         }
446
447         if (_possiblyUndeclaredUses.contains(name)) {
448             foreach (const SourceLocation &loc, _possiblyUndeclaredUses.value(name)) {
449                 addMessage(WarnVarUsedBeforeDeclaration, loc, name);
450             }
451             _possiblyUndeclaredUses.remove(name);
452         }
453         _declaredVariables[name] = ast;
454
455         return true;
456     }
457
458     bool visit(FunctionDeclaration *ast)
459     {
460         if (_seenNonDeclarationStatement) {
461             addMessage(HintDeclarationsShouldBeAtStartOfFunction, ast->functionToken);
462         }
463
464         return visit(static_cast<FunctionExpression *>(ast));
465     }
466
467     bool visit(FunctionExpression *ast)
468     {
469         if (ast->name.isEmpty())
470             return false;
471         const QString &name = ast->name.toString();
472
473         if (_formalParameterNames.contains(name)) {
474             addMessage(WarnAlreadyFormalParameter, ast->identifierToken, name);
475         } else if (_declaredVariables.contains(name)) {
476             addMessage(WarnAlreadyVar, ast->identifierToken, name);
477         } else if (_declaredFunctions.contains(name)) {
478             addMessage(WarnDuplicateDeclaration, ast->identifierToken, name);
479         }
480
481         if (FunctionDeclaration *decl = cast<FunctionDeclaration *>(ast)) {
482             if (_possiblyUndeclaredUses.contains(name)) {
483                 foreach (const SourceLocation &loc, _possiblyUndeclaredUses.value(name)) {
484                     addMessage(WarnFunctionUsedBeforeDeclaration, loc, name);
485                 }
486                 _possiblyUndeclaredUses.remove(name);
487             }
488             _declaredFunctions[name] = decl;
489         }
490
491         return false;
492     }
493
494 private:
495     void addMessage(Type type, const SourceLocation &loc, const QString &arg1 = QString())
496     {
497         _messages.append(Message(type, loc, arg1));
498     }
499
500     QList<Message> _messages;
501     QStringList _formalParameterNames;
502     QHash<QString, VariableDeclaration *> _declaredVariables;
503     QHash<QString, FunctionDeclaration *> _declaredFunctions;
504     QHash<QString, QList<SourceLocation> > _possiblyUndeclaredUses;
505     bool _seenNonDeclarationStatement;
506 };
507
508 } // end of anonymous namespace
509
510 Check::Check(Document::Ptr doc, const ContextPtr &context)
511     : _doc(doc)
512     , _context(context)
513     , _scopeChain(doc, _context)
514     , _scopeBuilder(&_scopeChain)
515     , _lastValue(0)
516     , _importsOk(false)
517 {
518     const Imports *imports = context->imports(doc.data());
519     if (imports && !imports->importFailed())
520         _importsOk = true;
521
522     _enabledMessages = Message::allMessageTypes().toSet();
523     disableMessage(HintAnonymousFunctionSpacing);
524     disableMessage(HintDeclareVarsInOneLine);
525     disableMessage(HintDeclarationsShouldBeAtStartOfFunction);
526     disableMessage(HintBinaryOperatorSpacing);
527     disableMessage(HintOneStatementPerLine);
528     disableMessage(HintExtraParentheses);
529 }
530
531 Check::~Check()
532 {
533 }
534
535 QList<Message> Check::operator()()
536 {
537     _messages.clear();
538     Node::accept(_doc->ast(), this);
539     return _messages;
540 }
541
542 void Check::enableMessage(Type type)
543 {
544     _enabledMessages.insert(type);
545 }
546
547 void Check::disableMessage(Type type)
548 {
549     _enabledMessages.remove(type);
550 }
551
552 bool Check::preVisit(Node *ast)
553 {
554     _chain.append(ast);
555     return true;
556 }
557
558 void Check::postVisit(Node *)
559 {
560     _chain.removeLast();
561 }
562
563 bool Check::visit(UiProgram *)
564 {
565     return true;
566 }
567
568 bool Check::visit(UiObjectInitializer *)
569 {
570     m_propertyStack.push(StringSet());
571     UiObjectDefinition *objectDefinition = cast<UiObjectDefinition *>(parent());
572     if (objectDefinition && objectDefinition->qualifiedTypeNameId->name == "Component")
573         m_idStack.push(StringSet());
574     UiObjectBinding *objectBinding = cast<UiObjectBinding *>(parent());
575     if (objectBinding && objectBinding->qualifiedTypeNameId->name == "Component")
576         m_idStack.push(StringSet());
577     if (m_idStack.isEmpty())
578         m_idStack.push(StringSet());
579     return true;
580 }
581
582 void Check::endVisit(UiObjectInitializer *)
583 {
584     m_propertyStack.pop();
585     UiObjectDefinition *objectDenition = cast<UiObjectDefinition *>(parent());
586     if (objectDenition && objectDenition->qualifiedTypeNameId->name == "Component")
587         m_idStack.pop();
588     UiObjectBinding *objectBinding = cast<UiObjectBinding *>(parent());
589     if (objectBinding && objectBinding->qualifiedTypeNameId->name == "Component")
590         m_idStack.pop();
591 }
592
593 void Check::checkProperty(UiQualifiedId *qualifiedId)
594 {
595     const QString id = toString(qualifiedId);
596     if (id.at(0).isLower()) {
597         if (m_propertyStack.top().contains(id)) {
598             addMessage(ErrPropertiesCanOnlyHaveOneBinding, fullLocationForQualifiedId(qualifiedId));
599         }
600         m_propertyStack.top().insert(id);
601     }
602 }
603
604 bool Check::visit(UiObjectDefinition *ast)
605 {
606     visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
607     return false;
608 }
609
610 bool Check::visit(UiObjectBinding *ast)
611 {
612     checkScopeObjectMember(ast->qualifiedId);
613     if (!ast->hasOnToken)
614         checkProperty(ast->qualifiedId);
615
616     visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
617     return false;
618 }
619
620 void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
621                            UiObjectInitializer *initializer)
622 {
623     // Don't do type checks if it's a grouped property binding.
624     // For instance: anchors { ... }
625     if (_doc->bind()->isGroupedPropertyBinding(ast)) {
626         checkScopeObjectMember(typeId);
627         // ### don't give up!
628         return;
629     }
630
631     bool typeError = false;
632     if (_importsOk) {
633         const SourceLocation typeErrorLocation = fullLocationForQualifiedId(typeId);
634         const ObjectValue *prototype = _context->lookupType(_doc.data(), typeId);
635         if (!prototype) {
636             typeError = true;
637             addMessage(ErrUnknownComponent, typeErrorLocation);
638         } else {
639             PrototypeIterator iter(prototype, _context);
640             QList<const ObjectValue *> prototypes = iter.all();
641             if (iter.error() != PrototypeIterator::NoError)
642                 typeError = true;
643             const ObjectValue *lastPrototype = prototypes.last();
644             if (iter.error() == PrototypeIterator::ReferenceResolutionError) {
645                 if (const QmlPrototypeReference *ref =
646                         value_cast<QmlPrototypeReference>(lastPrototype->prototype())) {
647                     addMessage(ErrCouldNotResolvePrototypeOf, typeErrorLocation,
648                                toString(ref->qmlTypeName()), lastPrototype->className());
649                 } else {
650                     addMessage(ErrCouldNotResolvePrototype, typeErrorLocation,
651                                lastPrototype->className());
652                 }
653             } else if (iter.error() == PrototypeIterator::CycleError) {
654                 addMessage(ErrPrototypeCycle, typeErrorLocation,
655                            lastPrototype->className());
656             }
657         }
658     }
659
660     _scopeBuilder.push(ast);
661
662     if (typeError) {
663         // suppress subsequent errors about scope object lookup by clearing
664         // the scope object list
665         // ### todo: better way?
666         _scopeChain.setQmlScopeObjects(QList<const ObjectValue *>());
667     }
668
669     Node::accept(initializer, this);
670
671     _scopeBuilder.pop();
672 }
673
674 bool Check::visit(UiScriptBinding *ast)
675 {
676     // special case for id property
677     if (ast->qualifiedId->name == QLatin1String("id") && ! ast->qualifiedId->next) {
678         if (! ast->statement)
679             return false;
680
681         const SourceLocation loc = locationFromRange(ast->statement->firstSourceLocation(),
682                                                      ast->statement->lastSourceLocation());
683
684         ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement);
685         if (!expStmt) {
686             addMessage(ErrIdExpected, loc);
687             return false;
688         }
689
690         QString id;
691         if (IdentifierExpression *idExp = cast<IdentifierExpression *>(expStmt->expression)) {
692             id = idExp->name.toString();
693         } else if (StringLiteral *strExp = cast<StringLiteral *>(expStmt->expression)) {
694             id = strExp->value.toString();
695             addMessage(ErrInvalidId, loc);
696         } else {
697             addMessage(ErrIdExpected, loc);
698             return false;
699         }
700
701         if (id.isEmpty() || (!id.at(0).isLower() && id.at(0) != '_')) {
702             addMessage(ErrInvalidId, loc);
703             return false;
704         }
705
706         if (m_idStack.top().contains(id)) {
707             addMessage(ErrDuplicateId, loc);
708             return false;
709         }
710         m_idStack.top().insert(id);
711     }
712
713     checkProperty(ast->qualifiedId);
714
715     if (!ast->statement)
716         return false;
717
718     const Value *lhsValue = checkScopeObjectMember(ast->qualifiedId);
719     if (lhsValue) {
720         Evaluate evaluator(&_scopeChain);
721         const Value *rhsValue = evaluator(ast->statement);
722
723         const SourceLocation loc = locationFromRange(ast->statement->firstSourceLocation(),
724                                                      ast->statement->lastSourceLocation());
725         AssignmentCheck assignmentCheck;
726         Message message = assignmentCheck(_doc, loc, lhsValue, rhsValue, ast->statement);
727         if (message.isValid())
728             addMessage(message);
729     }
730
731     checkBindingRhs(ast->statement);
732
733     Node::accept(ast->qualifiedId, this);
734     _scopeBuilder.push(ast);
735     Node::accept(ast->statement, this);
736     _scopeBuilder.pop();
737
738     return false;
739 }
740
741 bool Check::visit(UiArrayBinding *ast)
742 {
743     checkScopeObjectMember(ast->qualifiedId);
744     checkProperty(ast->qualifiedId);
745
746     return true;
747 }
748
749 bool Check::visit(UiPublicMember *ast)
750 {
751     // check if the member type is valid
752     if (!ast->memberType.isEmpty()) {
753         const QString &name = ast->memberType.toString();
754         if (!name.isEmpty() && name.at(0).isLower()) {
755             if (!isValidBuiltinPropertyType(name))
756                 addMessage(ErrInvalidPropertyType, ast->typeToken, name);
757         }
758     }
759
760     checkBindingRhs(ast->statement);
761
762     _scopeBuilder.push(ast);
763     Node::accept(ast->statement, this);
764     Node::accept(ast->binding, this);
765     _scopeBuilder.pop();
766
767     return false;
768 }
769
770 bool Check::visit(IdentifierExpression *)
771 {
772     // currently disabled: too many false negatives
773     return true;
774
775 //    _lastValue = 0;
776 //    if (!ast->name.isEmpty()) {
777 //        Evaluate evaluator(&_scopeChain);
778 //        _lastValue = evaluator.reference(ast);
779 //        if (!_lastValue)
780 //            addMessage(ErrUnknownIdentifier, ast->identifierToken);
781 //        if (const Reference *ref = value_cast<Reference>(_lastValue)) {
782 //            _lastValue = _context->lookupReference(ref);
783 //            if (!_lastValue)
784 //                error(ast->identifierToken, tr("could not resolve"));
785 //        }
786 //    }
787 //    return false;
788 }
789
790 bool Check::visit(FieldMemberExpression *)
791 {
792     // currently disabled: too many false negatives
793     return true;
794
795 //    Node::accept(ast->base, this);
796 //    if (!_lastValue)
797 //        return false;
798 //    const ObjectValue *obj = _lastValue->asObjectValue();
799 //    if (!obj) {
800 //        error(locationFromRange(ast->base->firstSourceLocation(), ast->base->lastSourceLocation()),
801 //              tr("does not have members"));
802 //    }
803 //    if (!obj || ast->name.isEmpty()) {
804 //        _lastValue = 0;
805 //        return false;
806 //    }
807 //    _lastValue = obj->lookupMember(ast->name.toString(), _context);
808 //    if (!_lastValue)
809 //        error(ast->identifierToken, tr("unknown member"));
810 //    return false;
811 }
812
813 bool Check::visit(FunctionDeclaration *ast)
814 {
815     return visit(static_cast<FunctionExpression *>(ast));
816 }
817
818 bool Check::visit(FunctionExpression *ast)
819 {
820     if (ast->name.isEmpty()) {
821         SourceLocation locfunc = ast->functionToken;
822         SourceLocation loclparen = ast->lparenToken;
823         if (locfunc.isValid() && loclparen.isValid()
824                 && (locfunc.startLine != loclparen.startLine
825                     || locfunc.end() + 1 != loclparen.begin())) {
826             addMessage(HintAnonymousFunctionSpacing, locationFromRange(locfunc, loclparen));
827         }
828     }
829
830     DeclarationsCheck bodyCheck;
831     addMessages(bodyCheck(ast));
832
833     MarkUnreachableCode unreachableCheck;
834     addMessages(unreachableCheck(ast->body));
835
836     Node::accept(ast->formals, this);
837     _scopeBuilder.push(ast);
838     Node::accept(ast->body, this);
839     _scopeBuilder.pop();
840     return false;
841 }
842
843 static bool shouldAvoidNonStrictEqualityCheck(const Value *lhs, const Value *rhs)
844 {
845     // we currently use undefined as a "we don't know" value
846     if (lhs->asUndefinedValue() || rhs->asUndefinedValue())
847         return true;
848
849     if (lhs->asStringValue() && rhs->asNumberValue())
850         return true; // coerces string to number
851
852     if (lhs->asObjectValue() && rhs->asNumberValue())
853         return true; // coerces object to primitive
854
855     if (lhs->asObjectValue() && rhs->asStringValue())
856         return true; // coerces object to primitive
857
858     if (lhs->asBooleanValue() && !rhs->asBooleanValue())
859         return true; // coerces bool to number
860
861     return false;
862 }
863
864 bool Check::visit(BinaryExpression *ast)
865 {
866     const QString source = _doc->source();
867
868     // check spacing
869     SourceLocation op = ast->operatorToken;
870     if ((op.begin() > 0 && !source.at(op.begin() - 1).isSpace())
871             || (int(op.end()) < source.size() && !source.at(op.end()).isSpace())) {
872         addMessage(HintBinaryOperatorSpacing, op);
873     }
874
875     // check ==, !=
876     if (ast->op == QSOperator::Equal || ast->op == QSOperator::NotEqual) {
877         Evaluate eval(&_scopeChain);
878         const Value *lhsValue = eval(ast->left);
879         const Value *rhsValue = eval(ast->right);
880         if (shouldAvoidNonStrictEqualityCheck(lhsValue, rhsValue)
881                 || shouldAvoidNonStrictEqualityCheck(rhsValue, lhsValue)) {
882             addMessage(MaybeWarnEqualityTypeCoercion, ast->operatorToken);
883         }
884     }
885
886     // check odd + ++ combinations
887     const QLatin1Char newline('\n');
888     if (ast->op == QSOperator::Add || ast->op == QSOperator::Sub) {
889         QChar match;
890         Type msg;
891         if (ast->op == QSOperator::Add) {
892             match = QLatin1Char('+');
893             msg = WarnConfusingPluses;
894         } else {
895             QTC_CHECK(ast->op == QSOperator::Sub);
896             match = QLatin1Char('-');
897             msg = WarnConfusingMinuses;
898         }
899
900         if (int(op.end()) + 1 < source.size()) {
901             const QChar next = source.at(op.end());
902             if (next.isSpace() && next != newline
903                     && source.at(op.end() + 1) == match)
904                 addMessage(msg, SourceLocation(op.begin(), 3, op.startLine, op.startColumn));
905         }
906         if (op.begin() >= 2) {
907             const QChar prev = source.at(op.begin() - 1);
908             if (prev.isSpace() && prev != newline
909                     && source.at(op.begin() - 2) == match)
910                 addMessage(msg, SourceLocation(op.begin() - 2, 3, op.startLine, op.startColumn - 2));
911         }
912     }
913
914     return true;
915 }
916
917 bool Check::visit(Block *ast)
918 {
919     if (Node *p = parent()) {
920         if (!cast<UiScriptBinding *>(p)
921                 && !cast<UiPublicMember *>(p)
922                 && !cast<TryStatement *>(p)
923                 && !cast<Catch *>(p)
924                 && !cast<Finally *>(p)
925                 && !cast<ForStatement *>(p)
926                 && !cast<ForEachStatement *>(p)
927                 && !cast<LocalForStatement *>(p)
928                 && !cast<LocalForEachStatement *>(p)
929                 && !cast<DoWhileStatement *>(p)
930                 && !cast<WhileStatement *>(p)
931                 && !cast<IfStatement *>(p)
932                 && !cast<SwitchStatement *>(p)
933                 && !cast<WithStatement *>(p)) {
934             addMessage(WarnBlock, ast->lbraceToken);
935         }
936         if (!ast->statements
937                 && (cast<UiPublicMember *>(p)
938                     || cast<UiScriptBinding *>(p))
939                 && ast->lbraceToken.startLine == ast->rbraceToken.startLine) {
940             addMessage(WarnUnintentinalEmptyBlock, locationFromRange(ast->firstSourceLocation(), ast->lastSourceLocation()));
941         }
942     }
943     return true;
944 }
945
946 bool Check::visit(WithStatement *ast)
947 {
948     addMessage(WarnWith, ast->withToken);
949     return true;
950 }
951
952 bool Check::visit(VoidExpression *ast)
953 {
954     addMessage(WarnVoid, ast->voidToken);
955     return true;
956 }
957
958 bool Check::visit(Expression *ast)
959 {
960     if (ast->left && ast->right) {
961         Node *p = parent();
962         if (!cast<ForStatement *>(p)
963                 && !cast<LocalForStatement *>(p)) {
964             addMessage(WarnComma, ast->commaToken);
965         }
966     }
967     return true;
968 }
969
970 bool Check::visit(ExpressionStatement *ast)
971 {
972     if (ast->expression) {
973         bool ok = cast<CallExpression *>(ast->expression)
974                 || cast<DeleteExpression *>(ast->expression)
975                 || cast<PreDecrementExpression *>(ast->expression)
976                 || cast<PreIncrementExpression *>(ast->expression)
977                 || cast<PostIncrementExpression *>(ast->expression)
978                 || cast<PostDecrementExpression *>(ast->expression)
979                 || cast<FunctionExpression *>(ast->expression);
980         if (BinaryExpression *binary = cast<BinaryExpression *>(ast->expression)) {
981             switch (binary->op) {
982             case QSOperator::Assign:
983             case QSOperator::InplaceAdd:
984             case QSOperator::InplaceAnd:
985             case QSOperator::InplaceDiv:
986             case QSOperator::InplaceLeftShift:
987             case QSOperator::InplaceRightShift:
988             case QSOperator::InplaceMod:
989             case QSOperator::InplaceMul:
990             case QSOperator::InplaceOr:
991             case QSOperator::InplaceSub:
992             case QSOperator::InplaceURightShift:
993             case QSOperator::InplaceXor:
994                 ok = true;
995             default: break;
996             }
997         }
998         if (!ok) {
999             for (int i = 0; Node *p = parent(i); ++i) {
1000                 if (UiScriptBinding *binding = cast<UiScriptBinding *>(p)) {
1001                     if (!cast<Block *>(binding->statement)) {
1002                         ok = true;
1003                         break;
1004                     }
1005                 }
1006                 if (UiPublicMember *member = cast<UiPublicMember *>(p)) {
1007                     if (!cast<Block *>(member->statement)) {
1008                         ok = true;
1009                         break;
1010                     }
1011                 }
1012             }
1013         }
1014
1015         if (!ok) {
1016             addMessage(WarnConfusingExpressionStatement,
1017                        locationFromRange(ast->firstSourceLocation(), ast->lastSourceLocation()));
1018         }
1019     }
1020     return true;
1021 }
1022
1023 bool Check::visit(IfStatement *ast)
1024 {
1025     if (ast->expression)
1026         checkAssignInCondition(ast->expression);
1027     return true;
1028 }
1029
1030 bool Check::visit(ForStatement *ast)
1031 {
1032     if (ast->condition)
1033         checkAssignInCondition(ast->condition);
1034     return true;
1035 }
1036
1037 bool Check::visit(LocalForStatement *ast)
1038 {
1039     if (ast->condition)
1040         checkAssignInCondition(ast->condition);
1041     return true;
1042 }
1043
1044 bool Check::visit(WhileStatement *ast)
1045 {
1046     if (ast->expression)
1047         checkAssignInCondition(ast->expression);
1048     return true;
1049 }
1050
1051 bool Check::visit(DoWhileStatement *ast)
1052 {
1053     if (ast->expression)
1054         checkAssignInCondition(ast->expression);
1055     return true;
1056 }
1057
1058 bool Check::visit(CaseClause *ast)
1059 {
1060     checkEndsWithControlFlow(ast->statements, ast->caseToken);
1061     return true;
1062 }
1063
1064 bool Check::visit(DefaultClause *ast)
1065 {
1066     checkEndsWithControlFlow(ast->statements, ast->defaultToken);
1067     return true;
1068 }
1069
1070 static QString functionName(ExpressionNode *ast, SourceLocation *location)
1071 {
1072     if (IdentifierExpression *id = cast<IdentifierExpression *>(ast)) {
1073         if (!id->name.isEmpty()) {
1074             *location = id->identifierToken;
1075             return id->name.toString();
1076         }
1077     } else if (FieldMemberExpression *fme = cast<FieldMemberExpression *>(ast)) {
1078         if (!fme->name.isEmpty()) {
1079             *location = fme->identifierToken;
1080             return fme->name.toString();
1081         }
1082     }
1083     return QString();
1084 }
1085
1086 void Check::checkNewExpression(ExpressionNode *ast)
1087 {
1088     SourceLocation location;
1089     const QString name = functionName(ast, &location);
1090     if (name.isEmpty())
1091         return;
1092     if (!name.at(0).isUpper()) {
1093         addMessage(WarnNewWithLowercaseFunction, location);
1094     }
1095 }
1096
1097 void Check::checkBindingRhs(Statement *statement)
1098 {
1099     if (!statement)
1100         return;
1101
1102     DeclarationsCheck bodyCheck;
1103     addMessages(bodyCheck(statement));
1104
1105     MarkUnreachableCode unreachableCheck;
1106     addMessages(unreachableCheck(statement));
1107 }
1108
1109 void Check::checkExtraParentheses(ExpressionNode *expression)
1110 {
1111     if (NestedExpression *nested = cast<NestedExpression *>(expression)) {
1112         addMessage(HintExtraParentheses, nested->lparenToken);
1113     }
1114 }
1115
1116 void Check::addMessages(const QList<Message> &messages)
1117 {
1118     foreach (const Message &msg, messages)
1119         addMessage(msg);
1120 }
1121
1122 void Check::addMessage(const Message &message)
1123 {
1124     if (message.isValid() && _enabledMessages.contains(message.type))
1125         _messages += message;
1126 }
1127
1128 void Check::addMessage(Type type, const SourceLocation &location, const QString &arg1, const QString &arg2)
1129 {
1130     addMessage(Message(type, location, arg1, arg2));
1131 }
1132
1133 bool Check::visit(NewExpression *ast)
1134 {
1135     checkNewExpression(ast->expression);
1136     return true;
1137 }
1138
1139 bool Check::visit(NewMemberExpression *ast)
1140 {
1141     checkNewExpression(ast->base);
1142
1143     // check for Number, Boolean, etc constructor usage
1144     if (IdentifierExpression *idExp = cast<IdentifierExpression *>(ast->base)) {
1145         const QStringRef name = idExp->name;
1146         if (name == QLatin1String("Number")) {
1147             addMessage(WarnNumberConstructor, idExp->identifierToken);
1148         } else if (name == QLatin1String("Boolean")) {
1149             addMessage(WarnBooleanConstructor, idExp->identifierToken);
1150         } else if (name == QLatin1String("String")) {
1151             addMessage(WarnStringConstructor, idExp->identifierToken);
1152         } else if (name == QLatin1String("Object")) {
1153             addMessage(WarnObjectConstructor, idExp->identifierToken);
1154         } else if (name == QLatin1String("Array")) {
1155             bool ok = false;
1156             if (ast->arguments && ast->arguments->expression && !ast->arguments->next) {
1157                 Evaluate evaluate(&_scopeChain);
1158                 const Value *arg = evaluate(ast->arguments->expression);
1159                 if (arg->asNumberValue() || arg->asUndefinedValue())
1160                     ok = true;
1161             }
1162             if (!ok)
1163                 addMessage(WarnArrayConstructor, idExp->identifierToken);
1164         } else if (name == QLatin1String("Function")) {
1165             addMessage(WarnFunctionConstructor, idExp->identifierToken);
1166         }
1167     }
1168
1169     return true;
1170 }
1171
1172 bool Check::visit(CallExpression *ast)
1173 {
1174     // check for capitalized function name being called
1175     SourceLocation location;
1176     const QString name = functionName(ast->base, &location);
1177     if (!name.isEmpty() && name.at(0).isUpper()
1178             && name != QLatin1String("String")
1179             && name != QLatin1String("Boolean")
1180             && name != QLatin1String("Date")
1181             && name != QLatin1String("Number")
1182             && name != QLatin1String("Object")) {
1183         addMessage(WarnExpectedNewWithUppercaseFunction, location);
1184     }
1185     if (cast<IdentifierExpression *>(ast->base) && name == QLatin1String("eval"))
1186         addMessage(WarnEval, location);
1187     return true;
1188 }
1189
1190 bool Check::visit(StatementList *ast)
1191 {
1192     SourceLocation warnStart;
1193     SourceLocation warnEnd;
1194     unsigned currentLine = 0;
1195     for (StatementList *it = ast; it; it = it->next) {
1196         if (!it->statement)
1197             continue;
1198         const SourceLocation itLoc = it->statement->firstSourceLocation();
1199         if (itLoc.startLine != currentLine) { // first statement on a line
1200             if (warnStart.isValid())
1201                 addMessage(HintOneStatementPerLine, locationFromRange(warnStart, warnEnd));
1202             warnStart = SourceLocation();
1203             currentLine = itLoc.startLine;
1204         } else { // other statements on the same line
1205             if (!warnStart.isValid())
1206                 warnStart = itLoc;
1207             warnEnd = it->statement->lastSourceLocation();
1208         }
1209     }
1210     if (warnStart.isValid())
1211         addMessage(HintOneStatementPerLine, locationFromRange(warnStart, warnEnd));
1212
1213     return true;
1214 }
1215
1216 bool Check::visit(ReturnStatement *ast)
1217 {
1218     checkExtraParentheses(ast->expression);
1219     return true;
1220 }
1221
1222 bool Check::visit(ThrowStatement *ast)
1223 {
1224     checkExtraParentheses(ast->expression);
1225     return true;
1226 }
1227
1228 bool Check::visit(DeleteExpression *ast)
1229 {
1230     checkExtraParentheses(ast->expression);
1231     return true;
1232 }
1233
1234 bool Check::visit(TypeOfExpression *ast)
1235 {
1236     checkExtraParentheses(ast->expression);
1237     return true;
1238 }
1239
1240 /// When something is changed here, also change ReadingContext::lookupProperty in
1241 /// texttomodelmerger.cpp
1242 /// ### Maybe put this into the context as a helper method.
1243 const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
1244 {
1245     if (!_importsOk)
1246         return 0;
1247
1248     QList<const ObjectValue *> scopeObjects = _scopeChain.qmlScopeObjects();
1249     if (scopeObjects.isEmpty())
1250         return 0;
1251
1252     if (! id)
1253         return 0; // ### error?
1254
1255     if (id->name.isEmpty()) // possible after error recovery
1256         return 0;
1257
1258     QString propertyName = id->name.toString();
1259
1260     if (propertyName == QLatin1String("id") && ! id->next)
1261         return 0; // ### should probably be a special value
1262
1263     // attached properties
1264     bool isAttachedProperty = false;
1265     if (! propertyName.isEmpty() && propertyName[0].isUpper()) {
1266         isAttachedProperty = true;
1267         if (const ObjectValue *qmlTypes = _scopeChain.qmlTypes())
1268             scopeObjects += qmlTypes;
1269     }
1270
1271     if (scopeObjects.isEmpty())
1272         return 0;
1273
1274     // global lookup for first part of id
1275     const Value *value = 0;
1276     for (int i = scopeObjects.size() - 1; i >= 0; --i) {
1277         value = scopeObjects[i]->lookupMember(propertyName, _context);
1278         if (value)
1279             break;
1280     }
1281     if (!value) {
1282         addMessage(ErrInvalidPropertyName, id->identifierToken, propertyName);
1283         return 0;
1284     }
1285
1286     // can't look up members for attached properties
1287     if (isAttachedProperty)
1288         return 0;
1289
1290     // resolve references
1291     if (const Reference *ref = value->asReference())
1292         value = _context->lookupReference(ref);
1293
1294     // member lookup
1295     const UiQualifiedId *idPart = id;
1296     while (idPart->next) {
1297         const ObjectValue *objectValue = value_cast<ObjectValue>(value);
1298         if (! objectValue) {
1299             addMessage(ErrDoesNotHaveMembers, idPart->identifierToken, propertyName);
1300             return 0;
1301         }
1302
1303         if (idPart->next->name.isEmpty()) {
1304             // somebody typed "id." and error recovery still gave us a valid tree,
1305             // so just bail out here.
1306             return 0;
1307         }
1308
1309         idPart = idPart->next;
1310         propertyName = idPart->name.toString();
1311
1312         value = objectValue->lookupMember(propertyName, _context);
1313         if (! value) {
1314             addMessage(ErrInvalidMember, idPart->identifierToken, propertyName, objectValue->className());
1315             return 0;
1316         }
1317     }
1318
1319     return value;
1320 }
1321
1322 void Check::checkAssignInCondition(AST::ExpressionNode *condition)
1323 {
1324     if (BinaryExpression *binary = cast<BinaryExpression *>(condition)) {
1325         if (binary->op == QSOperator::Assign)
1326             addMessage(WarnAssignmentInCondition, binary->operatorToken);
1327     }
1328 }
1329
1330 void Check::checkEndsWithControlFlow(StatementList *statements, SourceLocation errorLoc)
1331 {
1332     if (!statements)
1333         return;
1334
1335     ReachesEndCheck check;
1336     if (check(statements)) {
1337         addMessage(WarnCaseWithoutFlowControl, errorLoc);
1338     }
1339 }
1340
1341 Node *Check::parent(int distance)
1342 {
1343     const int index = _chain.size() - 2 - distance;
1344     if (index < 0)
1345         return 0;
1346     return _chain.at(index);
1347 }