Ejemplo n.º 1
0
        /// <nodoc />
        public static IReadOnlyList <ISymbol> GetPropertySymbolsFromContextualType(INode node, ITypeChecker checker)
        {
            if (IsNameOfPropertyAssignment(node))
            {
                var objectLiteral  = node.Parent?.Parent.As <IObjectLiteralExpression>();
                var contextualType = objectLiteral != null?checker.GetContextualType(objectLiteral) : null;

                var name = node.GetText(); // TODO: saqadri - verify correctness (const name = (<Identifier>node).text;

                if (contextualType != null)
                {
                    if ((contextualType.Flags & TypeFlags.Union) != TypeFlags.None)
                    {
                        // TODO: saqadri - port
                        // This is a union type, first see if the property we are looking for is a union property (i.e. exists in all types)
                        // if not, search the constituent types for the property
                        var unionProperty = checker.GetPropertyOfType(contextualType, name);
                        if (unionProperty != null)
                        {
                            return(new List <ISymbol> {
                                unionProperty
                            });
                        }

                        var result = new List <ISymbol>();
                        foreach (var t in contextualType.Cast <IUnionType>().Types ?? Enumerable.Empty <IType>())
                        {
                            var symbol = checker.GetPropertyOfType(t, name);
                            if (symbol != null)
                            {
                                result.Add(symbol);
                            }
                        }

                        return(result);
                    }

                    var contextualSymbol = checker.GetPropertyOfType(contextualType, name);
                    if (contextualSymbol != null)
                    {
                        return(new List <ISymbol> {
                            contextualSymbol
                        });
                    }
                }
            }

            return(BuildXL.Utilities.Collections.CollectionUtilities.EmptyArray <ISymbol>());
        }
        /// <summary>
        /// Attempts to add a file to a source file list. The conditions that must be met are specified in the
        /// array of <paramref name="configurations"/>
        /// </summary>
        public static bool TryAddSourceFileToSourceFile(ITypeChecker checker, ISourceFile sourceFile, string sourceFileName, Workspace workspace, PathTable pathTable, AddSourceFileConfiguration[] configurations)
        {
            INode sourcesNode = null;

            try
            {
                // Use single or default to ensure that we only match a single sources property.
                // If we find more than one, we don't know which source file list to augment.
                // SingleOrDefault throws an InvalidOperationException if it finds more than one element
                // and returns default<T> if there are 0.
                sourcesNode = NodeWalker.TraverseBreadthFirstAndSelf(sourceFile).SingleOrDefault(node =>
                {
                    // We expect that the property for the source file list to be in an object literal
                    // and hence be a property assignment inside that object literal.
                    // The statement will look something like:
                    // const result = TargetType.build( { sources: [f`foo.cpp`] } );
                    if (node.Kind == SyntaxKind.PropertyAssignment &&
                        node.Cast <IPropertyAssignment>().Name.Kind == SyntaxKind.Identifier &&
                        node.Parent?.Kind == SyntaxKind.ObjectLiteralExpression)
                    {
                        var propertyName = node.Cast <IPropertyAssignment>().Name.Text;

                        // Now check the configurations to see if the any match as there
                        // can be different names (such as "references", "sources", etc.) as
                        // well as different functions, etc.

                        AddSourceFileConfiguration singleConfiguration = null;

                        try
                        {
                            // We use single or default to ensure that only one matching configuration is found.
                            // SingleOrDefault throws an InvalidOperationException if it finds more than one element
                            // and returns default<T> if there are 0.
                            singleConfiguration = configurations.SingleOrDefault(configuration =>
                            {
                                // Check to see if this is the correct property name.
                                if (propertyName != configuration.PropertyName)
                                {
                                    return(false);
                                }

                                // Now we will try to find the matching call expression (function name)
                                // The reason we are going to walk parent nodes is that we allow
                                // a "merge" or "override" to be nested inside the function call
                                // as long as the argument type and the expected module the type exists
                                // in match the configuration parameter.
                                var nodeParent = node.Parent.Parent;
                                while (nodeParent != null)
                                {
                                    if (nodeParent.Kind != SyntaxKind.CallExpression)
                                    {
                                        return(false);
                                    }

                                    var callExpression    = nodeParent.Cast <ICallExpression>();
                                    string calledFunction = string.Empty;

                                    // Depending on the module the function is being called from it may be a straight
                                    // call (such as "build()") or it could be an accessor if it was imported
                                    // from another module (such as "StaticLibrary.build()").
                                    if (callExpression.Expression?.Kind == SyntaxKind.PropertyAccessExpression)
                                    {
                                        var propertyAccessExpression = callExpression.Expression.Cast <IPropertyAccessExpression>();
                                        calledFunction = propertyAccessExpression.Name?.Text;
                                    }
                                    else if (callExpression.Expression?.Kind == SyntaxKind.Identifier)
                                    {
                                        calledFunction = callExpression.Expression.Cast <Identifier>().Text;
                                    }
                                    else
                                    {
                                        return(false);
                                    }

                                    // If the called function matches, and has the minimum number of parameters to contain our argument type
                                    // then verify it matches the type name given in the configuration.
                                    if (calledFunction == configuration.FunctionName && callExpression.Arguments?.Length > configuration.ArgumentPosition)
                                    {
                                        var type = checker.GetContextualType(callExpression.Arguments[configuration.ArgumentPosition]);
                                        if (type != null && IsTypeCorrectForAddSourceFileConfiguration(type, workspace, pathTable, configuration))
                                        {
                                            return(true);
                                        }
                                    }
                                    else if (DScriptUtilities.IsMergeOrOverrideCallExpression(callExpression))
                                    {
                                        // In the case of a merge or override function, we make sure it is the proper type and keep moving
                                        // up the parent chain to find the function call.
                                        var type = checker.GetTypeAtLocation(callExpression.TypeArguments[0]);
                                        if (type != null && IsTypeCorrectForAddSourceFileConfiguration(type, workspace, pathTable, configuration))
                                        {
                                            nodeParent = nodeParent.Parent;
                                            continue;
                                        }
                                    }

                                    return(false);
                                }

                                return(false);
                            });
                        }
                        catch (InvalidOperationException)
                        {
                            return(false);
                        }

                        return(singleConfiguration != null);
                    }

                    return(false);
                });
            }
            catch (InvalidOperationException)
            {
            }

            if (sourcesNode != null)
            {
                var propertyAssignment = sourcesNode.Cast <IPropertyAssignment>();
                // Will support array literals for now.
                var initializer = propertyAssignment.Initializer.As <IArrayLiteralExpression>();
                if (initializer == null)
                {
                    // TODO: potentially we could have a glob call here, and what we can do this:
                    // [...(oldExpression), newFile]
                    return(false);
                }

                var alreadyPresent = initializer.Elements.Any(element =>
                {
                    return(element.Kind == SyntaxKind.TaggedTemplateExpression &&
                           element.Cast <ITaggedTemplateExpression>().Template?.Text.Equals(sourceFileName, StringComparison.OrdinalIgnoreCase) == true);
                });

                if (!alreadyPresent)
                {
                    initializer.Elements.Add(new TaggedTemplateExpression("f", sourceFileName));
                    return(true);
                }
            }
            return(false);
        }