internal EnvDTE.CodeParameter AddParameter(EnvDTE.CodeElement parent, SyntaxNode containerNode, string name, object type, object position)
        {
            var typeSymbol = CodeModelService.GetTypeSymbol(type, this.GetSemanticModel(), containerNode.SpanStart);
            var typeName   = typeSymbol.GetEscapedFullName();

            var parameterNode  = CodeModelService.CreateParameterNode(CodeModelService.GetUnescapedName(name), typeName);
            var insertionIndex = CodeModelService.PositionVariantToParameterInsertionIndex(position, containerNode, fileCodeModel: this);

            var newNode = InsertParameter(containerNode, parameterNode, insertionIndex);

            // Since parameters form part of the NodeKey for functions, delegates, and indexers,
            // creating a CodeParameter hooked up to the correct parent is a little tricky. After
            // the call to InsertParameter, the syntax tree has been updated, but not the NodeKey
            // map or the NodeKey in the parent CodeParameter. If we delegate the creation of the
            // CodeParameter to CodeModelService.CreateInternalCodeElement, it will attempt to
            // look up an element in the NodeKey map based on the new syntax tree. This will fail,
            // causing it to create a new, duplicate element for the parent. Later, when we
            // reacquire the NodeKeys, the original element will get the proper NodeKey, while the
            // duplicate will be updated to a meaningless NodeKey. Since the duplicate is the one
            // being used by the CodeParameter, most operations on it will then fail.
            // Instead, we need to have the parent passed in to us.
            var parentObj = ComAggregate.GetManagedObject <AbstractCodeMember>(parent);

            return(CodeParameter.Create(this.State, parentObj, CodeModelService.GetParameterName(newNode)));
        }
Beispiel #2
0
        internal T GetOrCreateCodeElement <T>(SyntaxNode node)
        {
            var nodeKey = CodeModelService.TryGetNodeKey(node);

            if (!nodeKey.IsEmpty)
            {
                // Since the node already has a key, check to see if a code element already
                // exists for it. If so, return that element it it's still valid; otherwise,
                // remove it from the table.
                if (_codeElementTable.TryGetValue(nodeKey, out var codeElement))
                {
                    if (codeElement != null)
                    {
                        var element = ComAggregate.GetManagedObject <AbstractCodeElement>(codeElement);
                        if (element.IsValidNode())
                        {
                            if (codeElement is T tcodeElement)
                            {
                                return(tcodeElement);
                            }

                            throw new InvalidOperationException($"Found a valid code element for {nodeKey}, but it is not of type, {typeof(T).ToString()}");
                        }
                    }
                }

                // Go ahead and remove the nodeKey from the table. At this point, we'll be creating a new one.
                _codeElementTable.Remove(nodeKey);
            }

            return((T)CodeModelService.CreateInternalCodeElement(this.State, this, node));
        }
Beispiel #3
0
        internal void UpdateCodeElementNodeKey(AbstractKeyedCodeElement keyedElement, SyntaxNodeKey oldNodeKey, SyntaxNodeKey newNodeKey)
        {
            if (!_codeElementTable.TryGetValue(oldNodeKey, out var codeElement))
            {
                throw new InvalidOperationException($"Could not find {oldNodeKey} in Code Model element table.");
            }

            _codeElementTable.Remove(oldNodeKey);

            var managedElement = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(codeElement);

            if (!object.Equals(managedElement, keyedElement))
            {
                throw new InvalidOperationException($"Unexpected failure in Code Model while updating node keys {oldNodeKey} -> {newNodeKey}");
            }

            // If we're updating this element with the same node key as an element that's already in the table,
            // just remove the old element. The old element will continue to function (through its node key), but
            // the new element will replace it in the cache.
            if (_codeElementTable.ContainsKey(newNodeKey))
            {
                _codeElementTable.Remove(newNodeKey);
            }

            _codeElementTable.Add(newNodeKey, codeElement);
        }
        private ImmutableArray <EnvDTE.CodeElement> GetOverloads()
        {
            // Retrieving the overloads is potentially very expensive because it can force multiple FileCodeModels to be instantiated.
            // Here, we cache the result to avoid having to perform these calculations each time GetOverloads() is called.
            // This *could* be an issue because it means that an OverloadsCollection will not necessarily reflect the
            // current state of the user's code. However, because a new OverloadsCollection is created every time the Overloads
            // property is accessed on CodeFunction, consumers would hit this behavior rarely.
            if (_overloads == null)
            {
                var symbol = (IMethodSymbol)ParentElement.LookupSymbol();

                // Only methods and constructors can be overloaded.  However, all functions
                // can successfully return a collection of overloaded functions; if not
                // really overloaded, the collection contains just the original function.
                if (symbol.MethodKind != MethodKind.Ordinary &&
                    symbol.MethodKind != MethodKind.Constructor)
                {
                    return(ImmutableArray.Create((EnvDTE.CodeElement)Parent));
                }

                var solution = this.Workspace.CurrentSolution;

                var overloadsBuilder = ArrayBuilder <EnvDTE.CodeElement> .GetInstance();

                foreach (var method in symbol.ContainingType.GetMembers(symbol.Name))
                {
                    if (method.Kind != SymbolKind.Method)
                    {
                        continue;
                    }

                    var location = method.Locations.FirstOrDefault(l => l.IsInSource);
                    if (location != null)
                    {
                        var document = solution.GetDocument(location.SourceTree);
                        if (document != null)
                        {
                            var fileCodeModelObject = this.Workspace.GetFileCodeModel(document.Id);
                            if (fileCodeModelObject != null)
                            {
                                var fileCodeModel = ComAggregate.GetManagedObject <FileCodeModel>(fileCodeModelObject);

                                var element = fileCodeModel.CodeElementFromPosition(location.SourceSpan.Start, EnvDTE.vsCMElement.vsCMElementFunction);
                                if (element != null)
                                {
                                    overloadsBuilder.Add(element);
                                }
                            }
                        }
                    }
                }

                _overloads = overloadsBuilder.ToImmutableAndFree();
            }

            return(_overloads);
        }
Beispiel #5
0
 private void ResetElementKey(GlobalNodeKey globalNodeKey)
 {
     // Failure to find the element is not an error -- it just means the code
     // element didn't exist...
     if (_codeElementTable.TryGetValue(globalNodeKey.NodeKey, out var element))
     {
         var keyedElement = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(element);
         keyedElement?.ReacquireNodeKey(globalNodeKey.Path, default);
     }
 }
Beispiel #6
0
        private void ResetElementKey(GlobalNodeKey globalNodeKey)
        {
            var element = _elementTable.TryGetValue(globalNodeKey.NodeKey);

            // Failure to find the element is not an error -- it just means the code
            // element didn't exist...
            if (element != null)
            {
                ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(element).ReaquireNodeKey(globalNodeKey.Path, default(CancellationToken));
            }
        }
Beispiel #7
0
        protected EnvDTE.CodeElements GetCollection <T>(object parentObject)
        {
            object parentInstance = ComAggregate.GetManagedObject <object>(parentObject);

            Debug.Assert(!Marshal.IsComObject(parentInstance), "We should have a pure managed object!");

            if (parentInstance is ICodeElementContainer <T> container)
            {
                return(container.GetCollection());
            }

            throw Exceptions.ThrowEFail();
        }
Beispiel #8
0
        internal void UpdateCodeElementNodeKey(AbstractKeyedCodeElement keyedElement, SyntaxNodeKey oldNodeKey, SyntaxNodeKey newNodeKey)
        {
            var codeElement = _codeElementTable.Remove(oldNodeKey);

            var managedElement = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(codeElement);

            if (!object.Equals(managedElement, keyedElement))
            {
                throw new InvalidOperationException($"Unexpected failure in Code Model while updating node keys {oldNodeKey} -> {newNodeKey}");
            }

            _codeElementTable.Add(newNodeKey, codeElement);
        }
Beispiel #9
0
        private void ResetElementKey(GlobalNodeKey globalNodeKey)
        {
            // Failure to find the element is not an error -- it just means the code
            // element didn't exist...

            EnvDTE.CodeElement element;
            if (_codeElementTable.TryGetValue(globalNodeKey.NodeKey, out element))
            {
                var keyedElement = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(element);
                if (keyedElement != null)
                {
                    keyedElement.ReacquireNodeKey(globalNodeKey.Path, default(CancellationToken));
                }
            }
        }
Beispiel #10
0
        /// <summary>
        /// This function re-adds a code element to the table, taking care not to duplicate
        /// an element (i.e., making sure that no two elements with the same key-ordinal
        /// appear in the same element chain in the table).  To resolve any conflict, each
        /// node with the given key is examined positionally, and the existing order is
        /// maintained as closely as possible -- but it is still possible the code element
        /// references to duplicate elements can get "bumped" by odd edits -- nothing we
        /// can do about this.
        /// </summary>
        internal void ResetElementNodeKey(AbstractKeyedCodeElement element, SyntaxNodeKey nodeKey)
        {
            EnvDTE.CodeElement elementInTable;
            _elementTable.Remove(element.NodeKey, out elementInTable);

            var abstractElementInTable = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(elementInTable);

            if (!object.Equals(abstractElementInTable, element))
            {
                Debug.Fail("Found a different element with the same key!");
                throw new InvalidOperationException();
            }

            abstractElementInTable.NodeKey = nodeKey;

            _elementTable.Add(nodeKey, elementInTable);
        }
Beispiel #11
0
        private ImmutableArray <EnvDTE.CodeElement> GetParts()
        {
            // Retrieving the parts is potentially very expensive because it can force multiple FileCodeModels to be instantiated.
            // Here, we cache the result to avoid having to perform these calculations each time GetParts() is called.
            // This *could* be an issue because it means that a PartialTypeCollection will not necessarily reflect the
            // current state of the user's code. However, because a new PartialTypeCollection is created every time the Parts
            // property is accessed on CodeClass, CodeStruct or CodeInterface, consumers would hit this behavior rarely.
            if (_parts == null)
            {
                var partsBuilder = ArrayBuilder <EnvDTE.CodeElement> .GetInstance();

                var solution = this.Workspace.CurrentSolution;
                var symbol   = ParentType.LookupSymbol();

                foreach (var location in symbol.Locations.Where(l => l.IsInSource))
                {
                    var document = solution.GetDocument(location.SourceTree);
                    if (document != null)
                    {
                        var fileCodeModelObject = this.Workspace.GetFileCodeModel(document.Id);
                        if (fileCodeModelObject != null)
                        {
                            var fileCodeModel = ComAggregate.GetManagedObject <FileCodeModel>(
                                fileCodeModelObject
                                );

                            var element = fileCodeModel.CodeElementFromPosition(
                                location.SourceSpan.Start,
                                ParentType.Kind
                                );
                            if (element != null)
                            {
                                partsBuilder.Add(element);
                            }
                        }
                    }
                }

                _parts = partsBuilder.ToImmutableAndFree();
            }

            return(_parts);
        }
Beispiel #12
0
        internal void OnBeforeCodeElementCreated(SyntaxNode node)
        {
            // It's conceivable that a consumer is creating a code element with the same node key as a "dead" element
            // that hasn't been removed from the cache yet. For example, the element could have been "deleted" by
            // simply replacing its text in the underlying buffer. To handle this situation, we test to see if the
            // element is "dead" by checking whether it's underlying node is invalid (that is, it can't be found by
            // its node key). If the element is "dead", we'll go ahead and remove it from the cache here to avoid a
            // collision with the new element.

            var nodeKey = CodeModelService.TryGetNodeKey(node);

            EnvDTE.CodeElement codeElement;
            if (!nodeKey.IsEmpty && _codeElementTable.TryGetValue(nodeKey, out codeElement))
            {
                var managedElement = ComAggregate.GetManagedObject <AbstractKeyedCodeElement>(codeElement);
                if (managedElement?.IsValidNode() != true)
                {
                    _codeElementTable.Remove(nodeKey);
                }
            }
        }
Beispiel #13
0
        private ImmutableArray<EnvDTE.CodeElement> EnumerateOverloads()
        {
            var symbol = (IMethodSymbol)ParentElement.LookupSymbol();

            // Only methods and constructors can be overloaded.  However, all functions
            // can successfully return a collection of overloaded functions; if not
            // really overloaded, the collection contains just the original function.
            if (symbol.MethodKind != MethodKind.Ordinary &&
                symbol.MethodKind != MethodKind.Constructor)
            {
                return ImmutableArray.Create((EnvDTE.CodeElement)Parent);
            }

            var overloadsBuilder = ImmutableArray.CreateBuilder<EnvDTE.CodeElement>();
            foreach (var method in symbol.ContainingType.GetMembers(symbol.Name))
            {
                if (method.Kind != SymbolKind.Method)
                {
                    continue;
                }

                var location = method.Locations.FirstOrDefault(l => l.IsInSource);
                if (location != null)
                {
                    var tree = location.SourceTree;
                    var document = this.Workspace.CurrentSolution.GetDocument(tree);

                    var fileCodeModelObject = this.Workspace.GetFileCodeModel(document.Id);
                    var fileCodeModel = ComAggregate.GetManagedObject<FileCodeModel>(fileCodeModelObject);

                    var element = fileCodeModel.CodeElementFromPosition(location.SourceSpan.Start, EnvDTE.vsCMElement.vsCMElementFunction);
                    if (element != null)
                    {
                        overloadsBuilder.Add(element);
                    }
                }
            }

            return overloadsBuilder.ToImmutable();
        }
Beispiel #14
0
        private ImmutableArray <EnvDTE.CodeElement> GetParts()
        {
            var partsBuilder = ImmutableArray.CreateBuilder <EnvDTE.CodeElement>();
            var symbol       = ParentType.LookupSymbol();

            foreach (var location in symbol.Locations.Where(l => l.IsInSource))
            {
                var tree     = location.SourceTree;
                var document = this.Workspace.CurrentSolution.GetDocument(tree);

                var fileCodeModelObject = this.Workspace.GetFileCodeModel(document.Id);
                var fileCodeModel       = ComAggregate.GetManagedObject <FileCodeModel>(fileCodeModelObject);

                var element = fileCodeModel.CodeElementFromPosition(location.SourceSpan.Start, ParentType.Kind);
                if (element != null)
                {
                    partsBuilder.Add(element);
                }
            }

            return(partsBuilder.ToImmutable());
        }
Beispiel #15
0
        internal override object GetBrowseObject(SymbolListItem symbolListItem)
        {
            var compilation = symbolListItem.GetCompilation(this);

            if (compilation == null)
            {
                return(null);
            }

            var symbol         = symbolListItem.ResolveSymbol(compilation);
            var sourceLocation = symbol.Locations.Where(l => l.IsInSource).FirstOrDefault();

            if (sourceLocation == null)
            {
                return(null);
            }

            var projectId = symbolListItem.ProjectId;

            if (projectId == null)
            {
                return(null);
            }

            var project = this.CurrentSolution.GetProject(projectId);

            if (project == null)
            {
                return(null);
            }

            var codeModelService = project.LanguageServices.GetService <ICodeModelService>();

            if (codeModelService == null)
            {
                return(null);
            }

            var tree     = sourceLocation.SourceTree;
            var document = project.GetDocument(tree);

            var vsFileCodeModel = this.GetFileCodeModel(document.Id);

            var fileCodeModel = ComAggregate.GetManagedObject <FileCodeModel>(vsFileCodeModel);

            if (fileCodeModel != null)
            {
                var syntaxNode = tree.GetRoot().FindNode(sourceLocation.SourceSpan);
                while (syntaxNode != null)
                {
                    if (!codeModelService.TryGetNodeKey(syntaxNode).IsEmpty)
                    {
                        break;
                    }

                    syntaxNode = syntaxNode.Parent;
                }

                if (syntaxNode != null)
                {
                    var codeElement = fileCodeModel.GetOrCreateCodeElement <EnvDTE.CodeElement>(syntaxNode);
                    if (codeElement != null)
                    {
                        return(codeElement);
                    }
                }
            }

            return(null);
        }
Beispiel #16
0
        internal override object?GetBrowseObject(SymbolListItem symbolListItem)
        {
            var compilation = symbolListItem.GetCompilation(this);

            if (compilation == null)
            {
                return(null);
            }

            var symbol         = symbolListItem.ResolveSymbol(compilation);
            var sourceLocation = symbol.Locations.Where(l => l.IsInSource).FirstOrDefault();

            if (sourceLocation == null)
            {
                return(null);
            }

            var projectId = symbolListItem.ProjectId;

            if (projectId == null)
            {
                return(null);
            }

            var project = this.CurrentSolution.GetProject(projectId);

            if (project == null)
            {
                return(null);
            }

            var codeModelService = project.LanguageServices.GetService <ICodeModelService>();

            if (codeModelService == null)
            {
                return(null);
            }

            var tree = sourceLocation.SourceTree;

            Contract.ThrowIfNull(tree, "We have a location that was in source, but doesn't have a SourceTree.");

            var document = project.GetDocument(tree);

            Contract.ThrowIfNull(document, "We have a symbol coming from a tree, and that tree isn't in the Project it supposedly came from.");

            var vsFileCodeModel = this.GetFileCodeModel(document.Id);

            var fileCodeModel = ComAggregate.GetManagedObject <FileCodeModel>(vsFileCodeModel);

            if (fileCodeModel != null)
            {
                SyntaxNode?syntaxNode = tree.GetRoot().FindNode(sourceLocation.SourceSpan);
                while (syntaxNode != null)
                {
                    if (!codeModelService.TryGetNodeKey(syntaxNode).IsEmpty)
                    {
                        break;
                    }

                    syntaxNode = syntaxNode.Parent;
                }

                if (syntaxNode != null)
                {
                    var codeElement = fileCodeModel.GetOrCreateCodeElement <EnvDTE.CodeElement>(syntaxNode);
                    if (codeElement != null)
                    {
                        return(codeElement);
                    }
                }
            }

            return(null);
        }