2019-06-14 12:21:25 +00:00
/****************************************************************************
* *
* * Copyright ( C ) 2019 The Qt Company Ltd .
* * Contact : https : //www.qt.io/licensing/
* *
* * This file is part of the tools applications of the Qt Toolkit .
* *
* * $ QT_BEGIN_LICENSE : GPL - EXCEPT $
* * Commercial License Usage
* * Licensees holding valid commercial Qt licenses may use this file in
* * accordance with the commercial license agreement provided with the
* * Software or , alternatively , in accordance with the terms contained in
* * a written agreement between you and The Qt Company . For licensing terms
* * and conditions see https : //www.qt.io/terms-conditions. For further
* * information use the contact form at https : //www.qt.io/contact-us.
* *
* * GNU General Public License Usage
* * Alternatively , this file may be used under the terms of the GNU
* * General Public License version 3 as published by the Free Software
* * Foundation with exceptions as appearing in the file LICENSE . GPL3 - EXCEPT
* * included in the packaging of this file . Please review the following
* * information to ensure the GNU General Public License requirements will
* * be met : https : //www.gnu.org/licenses/gpl-3.0.html.
* *
* * $ QT_END_LICENSE $
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "findunqualified.h"
# include "scopetree.h"
# include "qmljstypedescriptionreader.h"
# include <QFile>
# include <QDirIterator>
# include <QScopedValueRollback>
# include <private/qqmljsast_p.h>
# include <private/qqmljslexer_p.h>
# include <private/qqmljsparser_p.h>
2019-07-24 06:42:17 +00:00
# include <private/qv4codegen_p.h>
2019-06-14 12:21:25 +00:00
QDebug & operator < < ( QDebug dbg , const QQmlJS : : AST : : SourceLocation & loc ) ;
static QQmlJS : : TypeDescriptionReader createReaderForFile ( QString const & filename )
{
QFile f ( filename ) ;
f . open ( QFile : : ReadOnly ) ;
QQmlJS : : TypeDescriptionReader reader { filename , f . readAll ( ) } ;
return reader ;
}
void FindUnqualifiedIDVisitor : : enterEnvironment ( ScopeType type , QString name )
{
m_currentScope = m_currentScope - > createNewChildScope ( type , name ) ;
}
void FindUnqualifiedIDVisitor : : leaveEnvironment ( )
{
m_currentScope = m_currentScope - > parentScope ( ) ;
}
enum ImportVersion { FullyVersioned , PartiallyVersioned , Unversioned } ;
QStringList completeQmltypesPaths ( const QString & uri , const QStringList & basePaths , int vmaj , int vmin )
{
static const QLatin1Char Slash ( ' / ' ) ;
static const QLatin1Char Backslash ( ' \\ ' ) ;
static const QLatin1String SlashPluginsDotQmltypes ( " /plugins.qmltypes " ) ;
const QVector < QStringRef > parts = uri . splitRef ( QLatin1Char ( ' . ' ) , QString : : SkipEmptyParts ) ;
QStringList qmlDirPathsPaths ;
// fully & partially versioned parts + 1 unversioned for each base path
qmlDirPathsPaths . reserve ( basePaths . count ( ) * ( 2 * parts . count ( ) + 1 ) ) ;
auto versionString = [ ] ( int vmaj , int vmin , ImportVersion version )
{
if ( version = = FullyVersioned ) {
// extension with fully encoded version number (eg. MyModule.3.2)
return QString : : asprintf ( " .%d.%d " , vmaj , vmin ) ;
} else if ( version = = PartiallyVersioned ) {
// extension with encoded version major (eg. MyModule.3)
return QString : : asprintf ( " .%d " , vmaj ) ;
} // else extension without version number (eg. MyModule)
return QString ( ) ;
} ;
auto joinStringRefs = [ ] ( const QVector < QStringRef > & refs , const QChar & sep )
{
QString str ;
for ( auto it = refs . cbegin ( ) ; it ! = refs . cend ( ) ; + + it ) {
if ( it ! = refs . cbegin ( ) )
str + = sep ;
str + = * it ;
}
return str ;
} ;
for ( int version = FullyVersioned ; version < = Unversioned ; + + version ) {
const QString ver = versionString ( vmaj , vmin , static_cast < ImportVersion > ( version ) ) ;
for ( const QString & path : basePaths ) {
QString dir = path ;
if ( ! dir . endsWith ( Slash ) & & ! dir . endsWith ( Backslash ) )
dir + = Slash ;
// append to the end
qmlDirPathsPaths + = dir + joinStringRefs ( parts , Slash ) + ver + SlashPluginsDotQmltypes ;
if ( version ! = Unversioned ) {
// insert in the middle
for ( int index = parts . count ( ) - 2 ; index > = 0 ; - - index ) {
qmlDirPathsPaths + = dir + joinStringRefs ( parts . mid ( 0 , index + 1 ) , Slash )
+ ver + Slash
+ joinStringRefs ( parts . mid ( index + 1 ) , Slash ) + SlashPluginsDotQmltypes ;
}
}
}
}
return qmlDirPathsPaths ;
}
void FindUnqualifiedIDVisitor : : importHelper ( QString id , QString prefix , int major , int minor )
{
QPair < QString , QString > importId { id , prefix } ;
if ( m_alreadySeenImports . contains ( importId ) ) {
return ;
} else {
m_alreadySeenImports . insert ( importId ) ;
}
id = id . replace ( QLatin1String ( " / " ) , QLatin1String ( " . " ) ) ;
auto qmltypesPaths = completeQmltypesPaths ( id , m_qmltypeDirs , major , minor ) ;
QHash < QString , LanguageUtils : : FakeMetaObject : : ConstPtr > objects ;
QList < QQmlJS : : ModuleApiInfo > moduleApis ;
QStringList dependencies ;
for ( auto const & qmltypesPath : qmltypesPaths ) {
if ( QFile : : exists ( qmltypesPath ) ) {
auto reader = createReaderForFile ( qmltypesPath ) ;
auto succ = reader ( & objects , & moduleApis , & dependencies ) ;
if ( ! succ ) {
qDebug ( ) < < reader . errorMessage ( ) ;
}
break ;
}
}
for ( auto const & dependency : qAsConst ( dependencies ) ) {
auto const split = dependency . split ( " " ) ;
auto const id = split . at ( 0 ) ;
auto const major = split . at ( 1 ) . split ( ' . ' ) . at ( 0 ) . toInt ( ) ;
auto const minor = split . at ( 1 ) . split ( ' . ' ) . at ( 1 ) . toInt ( ) ;
importHelper ( id , QString ( ) , major , minor ) ;
}
// add objects
for ( auto ob_it = objects . begin ( ) ; ob_it ! = objects . end ( ) ; + + ob_it ) {
auto val = ob_it . value ( ) ;
m_exportedName2MetaObject [ prefix + val - > className ( ) ] = val ;
for ( auto export_ : val - > exports ( ) ) {
m_exportedName2MetaObject [ prefix + export_ . type ] = val ;
}
for ( auto enumCount = 0 ; enumCount < val - > enumeratorCount ( ) ; + + enumCount ) {
m_currentScope - > insertQMLIdentifier ( val - > enumerator ( enumCount ) . name ( ) ) ;
}
}
}
LanguageUtils : : FakeMetaObject *
FindUnqualifiedIDVisitor : : localQmlFile2FakeMetaObject ( QString filePath )
{
using namespace QQmlJS : : AST ;
auto fake = new LanguageUtils : : FakeMetaObject ;
fake - > setClassName ( QFileInfo { filePath } . baseName ( ) ) ;
QFile file ( filePath ) ;
if ( ! file . open ( QFile : : ReadOnly ) ) {
return fake ;
}
QString code = file . readAll ( ) ;
file . close ( ) ;
QQmlJS : : Engine engine ;
QQmlJS : : Lexer lexer ( & engine ) ;
lexer . setCode ( code , 1 , true ) ;
QQmlJS : : Parser parser ( & engine ) ;
if ( ! parser . parse ( ) ) {
return fake ;
}
QQmlJS : : AST : : UiProgram * program = parser . ast ( ) ;
auto header = program - > headers ;
while ( header ) {
if ( auto import = cast < UiImport * > ( header - > headerItem ) ) {
if ( import - > version ) {
QString path ;
auto uri = import - > importUri ;
while ( uri ) {
path . append ( uri - > name ) ;
path . append ( " / " ) ;
uri = uri - > next ;
}
path . chop ( 1 ) ;
QString prefix = QLatin1String ( " " ) ;
if ( import - > asToken . isValid ( ) ) {
prefix + = import - > importId + QLatin1Char ( ' . ' ) ;
}
importHelper ( path , prefix , import - > version - > majorVersion , import - > version - > minorVersion ) ;
}
}
header = header - > next ;
}
auto member = program - > members ;
// member should be the sole element
Q_ASSERT ( ! member - > next ) ;
Q_ASSERT ( member & & member - > member - > kind = = UiObjectMember : : Kind_UiObjectDefinition ) ;
auto definition = static_cast < UiObjectDefinition * > ( member - > member ) ;
auto qualifiedId = definition - > qualifiedTypeNameId ;
while ( qualifiedId & & qualifiedId - > next ) {
qualifiedId = qualifiedId - > next ;
}
fake - > setSuperclassName ( qualifiedId - > name . toString ( ) ) ;
UiObjectMemberList * initMembers = definition - > initializer - > members ;
while ( initMembers ) {
switch ( initMembers - > member - > kind ) {
case UiObjectMember : : Kind_UiArrayBinding : {
// nothing to do
break ;
}
case UiObjectMember : : Kind_UiEnumDeclaration : {
2019-07-24 06:42:17 +00:00
// nothing to do
2019-06-14 12:21:25 +00:00
break ;
}
case UiObjectMember : : Kind_UiObjectBinding : {
// nothing to do
break ;
}
case UiObjectMember : : Kind_UiObjectDefinition : {
// creates nothing accessible
break ;
}
case UiObjectMember : : Kind_UiPublicMember : {
auto publicMember = static_cast < UiPublicMember * > ( initMembers - > member ) ;
switch ( publicMember - > type ) {
case UiPublicMember : : Signal : {
UiParameterList * param = publicMember - > parameters ;
LanguageUtils : : FakeMetaMethod method ;
method . setMethodType ( LanguageUtils : : FakeMetaMethod : : Signal ) ;
method . setMethodName ( publicMember - > name . toString ( ) ) ;
while ( param ) {
method . addParameter ( param - > name . toString ( ) , param - > type - > name . toString ( ) ) ;
param = param - > next ;
}
fake - > addMethod ( method ) ;
break ;
}
case UiPublicMember : : Property : {
LanguageUtils : : FakeMetaProperty fakeprop { publicMember - > name . toString ( ) ,
publicMember - > typeModifier . toString ( ) ,
false ,
false ,
false ,
0 } ;
fake - > addProperty ( fakeprop ) ;
break ;
}
}
break ;
}
case UiObjectMember : : Kind_UiScriptBinding : {
// does not create anything new, ignore
break ;
}
case UiObjectMember : : Kind_UiSourceElement : {
auto sourceElement = static_cast < UiSourceElement * > ( initMembers - > member ) ;
if ( FunctionExpression * fexpr = sourceElement - > sourceElement - > asFunctionDefinition ( ) ) {
LanguageUtils : : FakeMetaMethod method ;
method . setMethodType ( LanguageUtils : : FakeMetaMethod : : Method ) ;
FormalParameterList * parameters = fexpr - > formals ;
while ( parameters ) {
method . addParameter ( parameters - > element - > bindingIdentifier . toString ( ) ,
" " ) ;
parameters = parameters - > next ;
}
fake - > addMethod ( method ) ;
} else if ( ClassExpression * clexpr =
sourceElement - > sourceElement - > asClassDefinition ( ) ) {
LanguageUtils : : FakeMetaProperty prop {
clexpr - > name . toString ( ) , " " , false , false , false , 1
} ;
fake - > addProperty ( prop ) ;
} else if ( cast < VariableStatement * > ( sourceElement - > sourceElement ) ) {
// nothing to do
} else {
qDebug ( ) < < " unsupportedd sourceElement at " < < sourceElement - > firstSourceLocation ( )
< < sourceElement - > sourceElement - > kind ;
}
break ;
}
default : {
qDebug ( ) < < " unsupported element of kind " < < initMembers - > member - > kind ;
}
}
initMembers = initMembers - > next ;
}
return fake ;
}
void FindUnqualifiedIDVisitor : : importExportedNames ( QStringRef prefix , QString name )
{
for ( ; ; ) {
auto metaObject = m_exportedName2MetaObject [ m_exportedName2MetaObject . contains ( name )
? name
: prefix + QLatin1Char ( ' . ' ) + name ] ;
if ( metaObject ) {
auto propertyCount = metaObject - > propertyCount ( ) ;
for ( auto i = 0 ; i < propertyCount ; + + i ) {
m_currentScope - > insertPropertyIdentifier ( metaObject - > property ( i ) . name ( ) ) ;
}
m_currentScope - > addMethodsFromMetaObject ( metaObject ) ;
name = metaObject - > superclassName ( ) ;
if ( name . isEmpty ( ) | | name = = QLatin1String ( " QObject " ) ) {
break ;
}
} else {
2019-07-23 14:10:24 +00:00
m_colorOut . write ( QLatin1String ( " warning: " ) , Warning ) ;
m_colorOut . write ( name + QLatin1String ( " was not found. Did you add all import paths? \n " ) ) ;
m_unknownImports . insert ( name ) ;
2019-06-14 12:21:25 +00:00
break ;
}
}
}
void FindUnqualifiedIDVisitor : : throwRecursionDepthError ( )
{
2019-08-16 08:14:49 +00:00
m_colorOut . write ( QStringLiteral ( " Error " ) , Error ) ;
m_colorOut . write ( QStringLiteral ( " Maximum statement or expression depth exceeded " ) , Error ) ;
m_visitFailed = true ;
2019-06-14 12:21:25 +00:00
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : UiProgram * )
{
enterEnvironment ( ScopeType : : QMLScope , " program " ) ;
QHash < QString , LanguageUtils : : FakeMetaObject : : ConstPtr > objects ;
QList < QQmlJS : : ModuleApiInfo > moduleApis ;
QStringList dependencies ;
for ( auto const & dir : m_qmltypeDirs ) {
QDirIterator it { dir , QStringList ( ) < < QLatin1String ( " builtins.qmltypes " ) , QDir : : NoFilter ,
QDirIterator : : Subdirectories } ;
while ( it . hasNext ( ) ) {
auto reader = createReaderForFile ( it . next ( ) ) ;
auto succ = reader ( & objects , & moduleApis , & dependencies ) ;
if ( ! succ ) {
qDebug ( ) < < reader . errorMessage ( ) ;
}
}
}
// add builtins
for ( auto ob_it = objects . begin ( ) ; ob_it ! = objects . end ( ) ; + + ob_it ) {
auto val = ob_it . value ( ) ;
for ( auto export_ : val - > exports ( ) ) {
m_exportedName2MetaObject [ export_ . type ] = val ;
}
for ( auto enumCount = 0 ; enumCount < val - > enumeratorCount ( ) ; + + enumCount ) {
m_currentScope - > insertQMLIdentifier ( val - > enumerator ( enumCount ) . name ( ) ) ;
}
}
// add "self" (as we only ever check the first part of a qualified identifier, we get away with
// using an empty FakeMetaObject
m_exportedName2MetaObject [ QFileInfo { m_filePath } . baseName ( ) ] = { } ;
// add QML builtins
m_exportedName2MetaObject [ " QtObject " ] = { } ; // QtObject contains nothing of interest
LanguageUtils : : FakeMetaObject * meta = new LanguageUtils : : FakeMetaObject { } ;
meta - > addProperty ( LanguageUtils : : FakeMetaProperty { " enabled " , " bool " , false , false , false , 0 } ) ;
meta - > addProperty ( LanguageUtils : : FakeMetaProperty { " ignoreUnknownSignals " , " bool " , false , false , false , 0 } ) ;
meta - > addProperty ( LanguageUtils : : FakeMetaProperty { " target " , " QObject " , false , false , false , 0 } ) ;
m_exportedName2MetaObject [ " Connections " ] = LanguageUtils : : FakeMetaObject : : ConstPtr { meta } ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : UiProgram * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : ClassExpression * ast )
{
enterEnvironment ( ScopeType : : JSFunctionScope , ast - > name . toString ( ) ) ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : ClassExpression * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : ClassDeclaration * ast )
{
enterEnvironment ( ScopeType : : JSFunctionScope , ast - > name . toString ( ) ) ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : ClassDeclaration * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : ForStatement * )
{
enterEnvironment ( ScopeType : : JSLexicalScope , " forloop " ) ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : ForStatement * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : ForEachStatement * )
{
enterEnvironment ( ScopeType : : JSLexicalScope , " foreachloop " ) ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : ForEachStatement * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : Block * )
{
enterEnvironment ( ScopeType : : JSLexicalScope , " block " ) ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : Block * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : CaseBlock * )
{
enterEnvironment ( ScopeType : : JSLexicalScope , " case " ) ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : CaseBlock * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : Catch * )
{
enterEnvironment ( ScopeType : : JSLexicalScope , " catch " ) ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : Catch * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : WithStatement * withStatement )
{
m_colorOut . write ( QString : : asprintf ( " Warning: " ) , Warning ) ;
m_colorOut . write ( QString : : asprintf ( " %d:%d: with statements are strongly discouraged in QML and might cause false positives when analying unqalified identifiers \n " , withStatement - > firstSourceLocation ( ) . startLine , withStatement - > firstSourceLocation ( ) . startColumn ) , Normal ) ;
enterEnvironment ( ScopeType : : JSLexicalScope , " with " ) ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : WithStatement * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : UiScriptBinding * uisb )
{
using namespace QQmlJS : : AST ;
auto name = uisb - > qualifiedId - > name ;
if ( name = = QLatin1String ( " id " ) ) {
// found id
auto expstat = static_cast < ExpressionStatement * > ( uisb - > statement ) ;
auto identexp = static_cast < IdentifierExpression * > ( expstat - > expression ) ;
QString elementName = m_currentScope - > name ( ) ;
m_qmlid2meta . insert ( identexp - > name . toString ( ) , m_exportedName2MetaObject [ elementName ] ) ;
if ( m_currentScope - > isVisualRootScope ( ) ) {
m_rootId = identexp - > name . toString ( ) ;
}
} else if ( name . startsWith ( " on " ) & & name . size ( ) > 2 & & name . at ( 2 ) . isUpper ( ) ) {
auto statement = uisb - > statement ;
if ( statement - > kind = = Node : : Kind : : Kind_ExpressionStatement ) {
if ( static_cast < ExpressionStatement * > ( statement ) - > expression - > asFunctionDefinition ( ) ) {
// functions are already handled
// they do not get names inserted according to the signal, but access their formal
// parameters
return true ;
}
}
QString signal = name . mid ( 2 ) . toString ( ) ;
signal [ 0 ] = signal [ 0 ] . toLower ( ) ;
if ( ! m_currentScope - > methods ( ) . contains ( signal ) ) {
qDebug ( ) < < " Info file does not contain signal " < < signal ;
} else {
auto method = m_currentScope - > methods ( ) [ signal ] ;
for ( auto const & param : method . parameterNames ( ) ) {
2019-07-24 13:43:49 +00:00
auto firstSourceLocation = uisb - > statement - > firstSourceLocation ( ) ;
bool hasMultilineStatementBody = uisb - > statement - > lastSourceLocation ( ) . startLine > firstSourceLocation . startLine ;
m_currentScope - > insertSignalIdentifier ( param , method , firstSourceLocation , hasMultilineStatementBody ) ;
2019-06-14 12:21:25 +00:00
}
}
return true ;
}
return true ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : UiPublicMember * uipm )
{
// property bool inactive: !active
// extract name inactive
m_currentScope - > insertPropertyIdentifier ( uipm - > name . toString ( ) ) ;
return true ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : IdentifierExpression * idexp )
{
auto name = idexp - > name ;
if ( ! m_exportedName2MetaObject . contains ( name . toString ( ) ) ) {
m_currentScope - > addIdToAccssedIfNotInParentScopes (
2019-07-23 14:10:24 +00:00
{ name . toString ( ) , idexp - > firstSourceLocation ( ) } , m_unknownImports ) ;
2019-06-14 12:21:25 +00:00
}
return true ;
}
FindUnqualifiedIDVisitor : : FindUnqualifiedIDVisitor ( QStringList const & qmltypeDirs ,
const QString & code , const QString & fileName )
: m_rootScope ( new ScopeTree { ScopeType : : JSFunctionScope , " global " } ) ,
m_currentScope ( m_rootScope . get ( ) ) ,
m_qmltypeDirs ( qmltypeDirs ) ,
m_code ( code ) ,
m_rootId ( QLatin1String ( " <id> " ) ) ,
m_filePath ( fileName )
{
// setup color output
m_colorOut . insertColorMapping ( Error , ColorOutput : : RedForeground ) ;
m_colorOut . insertColorMapping ( Warning , ColorOutput : : PurpleForeground ) ;
m_colorOut . insertColorMapping ( Info , ColorOutput : : BlueForeground ) ;
m_colorOut . insertColorMapping ( Normal , ColorOutput : : DefaultColor ) ;
m_colorOut . insertColorMapping ( Hint , ColorOutput : : GreenForeground ) ;
QLatin1String jsGlobVars [ ] = {
2019-07-24 06:42:17 +00:00
/* Not listed on the MDN page; browser and QML extensions: */
// console/debug api
QLatin1String ( " console " ) , QLatin1String ( " print " ) ,
// garbage collector
QLatin1String ( " gc " ) ,
// i18n
2019-06-14 12:21:25 +00:00
QLatin1String ( " qsTr " ) , QLatin1String ( " qsTrId " ) , QLatin1String ( " QT_TR_NOOP " ) , QLatin1String ( " QT_TRANSLATE_NOOP " ) , QLatin1String ( " QT_TRID_NOOP " ) ,
2019-07-24 06:42:17 +00:00
// XMLHttpRequest
QLatin1String ( " XMLHttpRequest " )
2019-06-14 12:21:25 +00:00
} ;
2019-07-24 06:42:17 +00:00
for ( const char * * globalName = QV4 : : Compiler : : Codegen : : s_globalNames ; * globalName ! = nullptr ; + + globalName ) {
m_currentScope - > insertJSIdentifier ( QString : : fromLatin1 ( * globalName ) , QQmlJS : : AST : : VariableScope : : Const ) ;
}
2019-06-14 12:21:25 +00:00
for ( const auto & jsGlobVar : jsGlobVars )
m_currentScope - > insertJSIdentifier ( jsGlobVar , QQmlJS : : AST : : VariableScope : : Const ) ;
}
FindUnqualifiedIDVisitor : : ~ FindUnqualifiedIDVisitor ( ) = default ;
bool FindUnqualifiedIDVisitor : : check ( )
{
2019-08-16 08:14:49 +00:00
if ( m_visitFailed )
return false ;
2019-06-14 12:21:25 +00:00
// now that all ids are known, revisit any Connections whose target were perviously unknown
for ( auto const & outstandingConnection : m_outstandingConnections ) {
auto metaObject = m_qmlid2meta [ outstandingConnection . targetName ] ;
outstandingConnection . scope - > addMethodsFromMetaObject ( metaObject ) ;
QScopedValueRollback < ScopeTree * > rollback ( m_currentScope , outstandingConnection . scope ) ;
outstandingConnection . uiod - > initializer - > accept ( this ) ;
}
return m_rootScope - > recheckIdentifiers ( m_code , m_qmlid2meta , m_rootScope . get ( ) , m_rootId , m_colorOut ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : VariableDeclarationList * vdl )
{
while ( vdl ) {
m_currentScope - > insertJSIdentifier ( vdl - > declaration - > bindingIdentifier . toString ( ) ,
vdl - > declaration - > scope ) ;
vdl = vdl - > next ;
}
return true ;
}
void FindUnqualifiedIDVisitor : : visitFunctionExpressionHelper ( QQmlJS : : AST : : FunctionExpression * fexpr )
{
using namespace QQmlJS : : AST ;
if ( ! fexpr - > name . isEmpty ( ) ) {
auto name = fexpr - > name . toString ( ) ;
if ( m_currentScope - > scopeType ( ) = = ScopeType : : QMLScope ) {
m_currentScope - > insertQMLIdentifier ( name ) ;
} else {
m_currentScope - > insertJSIdentifier ( name , VariableScope : : Const ) ;
}
}
QString name = fexpr - > name . toString ( ) ;
if ( name . isEmpty ( ) )
name = " <anon> " ;
enterEnvironment ( ScopeType : : JSFunctionScope , name ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : FunctionExpression * fexpr )
{
visitFunctionExpressionHelper ( fexpr ) ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : FunctionExpression * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : FunctionDeclaration * fdecl )
{
visitFunctionExpressionHelper ( fdecl ) ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : FunctionDeclaration * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : FormalParameterList * fpl )
{
for ( auto const & boundName : fpl - > boundNames ( ) ) {
m_currentScope - > insertJSIdentifier ( boundName . id , QQmlJS : : AST : : VariableScope : : Const ) ;
}
return true ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : UiImport * import )
{
// construct path
QString prefix = QLatin1String ( " " ) ;
if ( import - > asToken . isValid ( ) ) {
prefix + = import - > importId + QLatin1Char ( ' . ' ) ;
}
auto dirname = import - > fileName . toString ( ) ;
if ( ! dirname . isEmpty ( ) ) {
QFileInfo info { dirname } ;
if ( info . isRelative ( ) ) {
dirname = QDir ( QFileInfo { m_filePath } . path ( ) ) . filePath ( dirname ) ;
}
QDirIterator it { dirname , QStringList ( ) < < QLatin1String ( " *.qml " ) , QDir : : NoFilter } ;
while ( it . hasNext ( ) ) {
LanguageUtils : : FakeMetaObject * fake = localQmlFile2FakeMetaObject ( it . next ( ) ) ;
m_exportedName2MetaObject . insert (
fake - > className ( ) , QSharedPointer < const LanguageUtils : : FakeMetaObject > ( fake ) ) ;
}
}
QString path { } ;
if ( ! import - > importId . isEmpty ( ) ) {
m_qmlid2meta . insert ( import - > importId . toString ( ) , { } ) ; // TODO: do not put imported ids into the same space as qml IDs
}
if ( import - > version ) {
auto uri = import - > importUri ;
while ( uri ) {
path . append ( uri - > name ) ;
path . append ( " / " ) ;
uri = uri - > next ;
}
path . chop ( 1 ) ;
importHelper ( path , prefix , import - > version - > majorVersion , import - > version - > minorVersion ) ;
}
return true ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : UiEnumDeclaration * uied )
{
m_currentScope - > insertQMLIdentifier ( uied - > name . toString ( ) ) ;
return true ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : UiObjectBinding * uiob )
{
// property QtObject __styleData: QtObject {...}
m_currentScope - > insertPropertyIdentifier ( uiob - > qualifiedId - > name . toString ( ) ) ;
QString name { } ;
auto id = uiob - > qualifiedTypeNameId ;
QStringRef prefix = uiob - > qualifiedTypeNameId - > name ;
while ( id ) {
name + = id - > name . toString ( ) + QLatin1Char ( ' . ' ) ;
id = id - > next ;
}
name . chop ( 1 ) ;
enterEnvironment ( ScopeType : : QMLScope , name ) ;
if ( name = = QLatin1String ( " Component " ) | | name = = QLatin1String ( " QtObject " ) ) // there is no typeinfo for Component and QtObject, but they also have no interesting properties
return true ;
importExportedNames ( prefix , name ) ;
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : UiObjectBinding * )
{
leaveEnvironment ( ) ;
}
bool FindUnqualifiedIDVisitor : : visit ( QQmlJS : : AST : : UiObjectDefinition * uiod )
{
QString name { } ;
auto id = uiod - > qualifiedTypeNameId ;
QStringRef prefix = uiod - > qualifiedTypeNameId - > name ;
while ( id ) {
name + = id - > name . toString ( ) + QLatin1Char ( ' . ' ) ;
id = id - > next ;
}
name . chop ( 1 ) ;
enterEnvironment ( ScopeType : : QMLScope , name ) ;
if ( name . isLower ( ) )
return false ; // Ignore grouped properties for now
if ( name = = QLatin1String ( " Component " ) | | name = = QLatin1String ( " QtObject " ) ) // there is no typeinfo for Component
return true ;
importExportedNames ( prefix , name ) ;
if ( name . endsWith ( " Connections " ) ) {
QString target ;
auto member = uiod - > initializer - > members ;
while ( member ) {
if ( member - > member - > kind = = QQmlJS : : AST : : Node : : Kind_UiScriptBinding ) {
auto asBinding = static_cast < QQmlJS : : AST : : UiScriptBinding * > ( member - > member ) ;
if ( asBinding - > qualifiedId - > name = = QLatin1String ( " target " ) ) {
if ( asBinding - > statement - > kind = = QQmlJS : : AST : : Node : : Kind_ExpressionStatement ) {
auto expr = static_cast < QQmlJS : : AST : : ExpressionStatement * > ( asBinding - > statement ) - > expression ;
if ( auto idexpr = QQmlJS : : AST : : cast < QQmlJS : : AST : : IdentifierExpression * > ( expr ) ) {
target = idexpr - > name . toString ( ) ;
} else {
// more complex expressions are not supported
}
}
break ;
}
}
member = member - > next ;
}
LanguageUtils : : FakeMetaObject : : ConstPtr metaObject { } ;
if ( target . isEmpty ( ) ) {
// no target set, connection comes from parentF
ScopeTree * scope = m_currentScope ;
do {
scope = scope - > parentScope ( ) ; // TODO: rename method
} while ( scope - > scopeType ( ) ! = ScopeType : : QMLScope ) ;
auto metaObject = m_exportedName2MetaObject [ scope - > name ( ) ] ;
} else {
// there was a target, check if we already can find it
auto metaObjectIt = m_qmlid2meta . find ( target ) ;
if ( metaObjectIt ! = m_qmlid2meta . end ( ) ) {
metaObject = * metaObjectIt ;
} else {
m_outstandingConnections . push_back ( { target , m_currentScope , uiod } ) ;
return false ; // visit children later once target is known
}
}
m_currentScope - > addMethodsFromMetaObject ( metaObject ) ;
}
return true ;
}
void FindUnqualifiedIDVisitor : : endVisit ( QQmlJS : : AST : : UiObjectDefinition * )
{
leaveEnvironment ( ) ;
}
QDebug & operator < < ( QDebug dbg , const QQmlJS : : AST : : SourceLocation & loc )
{
QDebugStateSaver saver ( dbg ) ;
dbg . nospace ( ) < < loc . startLine ;
dbg . nospace ( ) < < " : " ;
dbg . nospace ( ) < < loc . startColumn ;
return dbg . maybeSpace ( ) ;
}