Beispiel #1
0
        /// <summary>
        /// Returns a sequence of suggestions on how errors for ambiguous types and callable in the given diagnostics can be fixed,
        /// given the file for which those diagnostics were generated and the corresponding compilation.
        /// Returns an empty enumerable if any of the given arguments is null.
        /// </summary>
        internal static IEnumerable <(string, WorkspaceEdit)> SuggestionsForAmbiguousIdentifiers
            (this FileContentManager file, CompilationUnit compilation, IEnumerable <Diagnostic> diagnostics)
        {
            if (file == null || diagnostics == null)
            {
                return(Enumerable.Empty <(string, WorkspaceEdit)>());
            }
            var ambiguousCallables = diagnostics.Where(DiagnosticTools.ErrorType(ErrorCode.AmbiguousCallable));
            var ambiguousTypes     = diagnostics.Where(DiagnosticTools.ErrorType(ErrorCode.AmbiguousType));

            if (!ambiguousCallables.Any() && !ambiguousTypes.Any())
            {
                return(Enumerable.Empty <(string, WorkspaceEdit)>());
            }

            (string, WorkspaceEdit) SuggestedNameQualification(NonNullable <string> suggestedNS, string id, Position pos)
            {
                var edit = new TextEdit {
                    Range = new LSP.Range {
                        Start = pos, End = pos
                    }, NewText = $"{suggestedNS.Value}."
                };

                return($"{suggestedNS.Value}.{id}", file.GetWorkspaceEdit(edit));
            }

            var suggestedIdQualifications = ambiguousCallables.Select(d => d.Range.Start)
                                            .SelectMany(pos => file.NamespaceSuggestionsForIdAtPosition(pos, compilation, out var id)
                                                        .Select(ns => SuggestedNameQualification(ns, id, pos)));
            var suggestedTypeQualifications = ambiguousTypes.Select(d => d.Range.Start)
                                              .SelectMany(pos => file.NamespaceSuggestionsForTypeAtPosition(pos, compilation, out var id)
                                                          .Select(ns => SuggestedNameQualification(ns, id, pos)));

            return(suggestedIdQualifications.Concat(suggestedTypeQualifications));
        }
Beispiel #2
0
        /// <summary>
        /// Returns workspace edits for correcting the casing or capitalization of unknown identifiers.
        /// </summary>
        private static IEnumerable <(string, WorkspaceEdit)> UnknownIdCaseSuggestions(
            this FileContentManager file, CompilationUnit compilation, IReadOnlyCollection <Diagnostic> diagnostics)
        {
            (string, WorkspaceEdit) SuggestedIdEdit(string suggestedId, Lsp.Range range)
            {
                var edit = new TextEdit {
                    Range = range, NewText = suggestedId
                };

                return($"Replace with \"{suggestedId}\".", file.GetWorkspaceEdit(edit));
            }

            var idSuggestions =
                from diagnostic in diagnostics
                where DiagnosticTools.ErrorType(ErrorCode.UnknownIdentifier)(diagnostic)
                from id in file.IdCaseSuggestions(diagnostic.Range.Start.ToQSharp(), compilation)
                select SuggestedIdEdit(id, diagnostic.Range);

            var typeSuggestions =
                from diagnostic in diagnostics
                where DiagnosticTools.ErrorType(ErrorCode.UnknownType)(diagnostic)
                from type in file.TypeCaseSuggestions(diagnostic.Range.Start.ToQSharp(), compilation)
                select SuggestedIdEdit(type, diagnostic.Range);

            return(idSuggestions.Concat(typeSuggestions));
        }
Beispiel #3
0
        /// <summary>
        /// Returns workspace edits for opening namespaces of unknown identifiers.
        /// </summary>
        private static IEnumerable <(string, WorkspaceEdit)> UnknownIdNamespaceSuggestions(
            this FileContentManager file,
            CompilationUnit compilation,
            int lineNr,
            IReadOnlyCollection <Diagnostic> diagnostics)
        {
            var idSuggestions = diagnostics
                                .Where(DiagnosticTools.ErrorType(ErrorCode.UnknownIdentifier))
                                .SelectMany(d => file.IdNamespaceSuggestions(d.Range.Start.ToQSharp(), compilation, out _));
            var typeSuggestions = diagnostics
                                  .Where(DiagnosticTools.ErrorType(ErrorCode.UnknownType))
                                  .SelectMany(d => file.TypeNamespaceSuggestions(d.Range.Start.ToQSharp(), compilation, out _));

            return(file
                   .OpenDirectiveSuggestions(lineNr, idSuggestions.Concat(typeSuggestions))
                   .Select(edit => (edit.NewText.Trim().Trim(';'), file.GetWorkspaceEdit(edit))));
        }
Beispiel #4
0
        /// <summary>
        /// Returns a sequence of suggestions on how errors for ambiguous types and callable in the given diagnostics can be fixed,
        /// given the file for which those diagnostics were generated and the corresponding compilation.
        /// Returns an empty enumerable if any of the given arguments is null.
        /// </summary>
        internal static IEnumerable <(string, WorkspaceEdit)> AmbiguousIdSuggestions(
            this FileContentManager file, CompilationUnit compilation, IEnumerable <Diagnostic> diagnostics)
        {
            if (file == null || diagnostics == null)
            {
                return(Enumerable.Empty <(string, WorkspaceEdit)>());
            }
            var ambiguousCallables = diagnostics.Where(DiagnosticTools.ErrorType(ErrorCode.AmbiguousCallable));
            var ambiguousTypes     = diagnostics.Where(DiagnosticTools.ErrorType(ErrorCode.AmbiguousType));

            if (!ambiguousCallables.Any() && !ambiguousTypes.Any())
            {
                return(Enumerable.Empty <(string, WorkspaceEdit)>());
            }

            (string, WorkspaceEdit)? SuggestedNameQualification(string suggestedNS, string?id, Position pos)
            {
                var edit = new TextEdit
                {
                    Range = new Lsp.Range {
                        Start = pos.ToLsp(), End = pos.ToLsp()
                    },
                    NewText = $"{suggestedNS}."
                };

                return(id is null
                    ? null as (string, WorkspaceEdit)?
                    : ($"{suggestedNS}.{id}", file.GetWorkspaceEdit(edit)));
            }

            var suggestedIdQualifications = ambiguousCallables
                                            .Select(d => d.Range.Start.ToQSharp())
                                            .SelectMany(pos => file
                                                        .IdNamespaceSuggestions(pos, compilation, out var id)
                                                        .SelectNotNull(ns => SuggestedNameQualification(ns, id, pos)));
            var suggestedTypeQualifications = ambiguousTypes
                                              .Select(d => d.Range.Start.ToQSharp())
                                              .SelectMany(pos => file
                                                          .TypeNamespaceSuggestions(pos, compilation, out var id)
                                                          .SelectNotNull(ns => SuggestedNameQualification(ns, id, pos)));

            return(suggestedIdQualifications.Concat(suggestedTypeQualifications));
        }
Beispiel #5
0
        /// <summary>
        /// Returns a sequence of suggestions on how errors for unknown types and callable in the given diagnostics can be fixed,
        /// given the file for which those diagnostics were generated and the corresponding compilation.
        /// The given line number is used to determine the containing namespace.
        /// Returns an empty enumerable if any of the given arguments is null.
        /// </summary>
        internal static IEnumerable <(string, WorkspaceEdit)> SuggestionsForUnknownIdentifiers
            (this FileContentManager file, CompilationUnit compilation, int lineNr, IEnumerable <Diagnostic> diagnostics)
        {
            if (file == null || diagnostics == null)
            {
                return(Enumerable.Empty <(string, WorkspaceEdit)>());
            }
            var unknownCallables = diagnostics.Where(DiagnosticTools.ErrorType(ErrorCode.UnknownIdentifier));
            var unknownTypes     = diagnostics.Where(DiagnosticTools.ErrorType(ErrorCode.UnknownType));

            if (!unknownCallables.Any() && !unknownTypes.Any())
            {
                return(Enumerable.Empty <(string, WorkspaceEdit)>());
            }

            var suggestionsForIds = unknownCallables.Select(d => d.Range.Start)
                                    .SelectMany(pos => file.NamespaceSuggestionsForIdAtPosition(pos, compilation, out var _));
            var suggestionsForTypes = unknownTypes.Select(d => d.Range.Start)
                                      .SelectMany(pos => file.NamespaceSuggestionsForTypeAtPosition(pos, compilation, out var _));

            return(file.OpenDirectiveSuggestions(lineNr, suggestionsForIds.Concat(suggestionsForTypes).ToArray())
                   .Select(edit => (edit.NewText.Trim().Trim(';'), file.GetWorkspaceEdit(edit))));
        }
Beispiel #6
0
        /// <summary>
        /// Returns a sequence of suggestions for update-and-reassign statements based on the generated diagnostics,
        /// and given the file for which those diagnostics were generated.
        /// Returns an empty enumerable if any of the given arguments is null.
        /// </summary>
        internal static IEnumerable <(string, WorkspaceEdit)> SuggestionsForUpdateAndReassignStatements
            (this FileContentManager file, IEnumerable <Diagnostic> diagnostics)
        {
            if (file == null || diagnostics == null)
            {
                return(Enumerable.Empty <(string, WorkspaceEdit)>());
            }
            var updateOfArrayItemExprs = diagnostics.Where(DiagnosticTools.ErrorType(ErrorCode.UpdateOfArrayItemExpr));

            (string, WorkspaceEdit) SuggestedCopyAndUpdateExpr(CodeFragment fragment)
            {
                var exprInfo = Parsing.ProcessUpdateOfArrayItemExpr.Invoke(fragment.Text);

                // Skip if the statement did not match a pattern for which we can give a code action
                if (exprInfo == null || (exprInfo.Item1.Line == 1 && exprInfo.Item1.Column == 1))
                {
                    return("", null);
                }

                // Convert set <identifier>[<index>] = <rhs> to set <identifier> w/= <index> <- <rhs>
                var rhs           = $"{exprInfo.Item3} {Keywords.qsCopyAndUpdateOp.cont} {exprInfo.Item4}";
                var outputStr     = $"{Keywords.qsValueUpdate.id} {exprInfo.Item2} {Keywords.qsCopyAndUpdateOp.op}= {rhs}";
                var fragmentRange = fragment.GetRange();
                var edit          = new TextEdit {
                    Range = fragmentRange.Copy(), NewText = outputStr
                };

                return("Replace with an update-and-reassign statement.", file.GetWorkspaceEdit(edit));
            }

            return(updateOfArrayItemExprs
                   .Select(d => file?.TryGetFragmentAt(d.Range.Start, out var _, includeEnd: true))
                   .Where(frag => frag != null)
                   .Select(frag => SuggestedCopyAndUpdateExpr(frag))
                   .Where(s => s.Item2 != null));
        }
Beispiel #7
0
        /// <summary>
        /// Returns a sequence of suggestions on how deprecated syntax can be updated based on the generated diagnostics,
        /// and given the file for which those diagnostics were generated.
        /// Returns an empty enumerable if any of the given arguments is null.
        /// </summary>
        internal static IEnumerable <(string, WorkspaceEdit)> SuggestionsForDeprecatedSyntax
            (this FileContentManager file, IEnumerable <Diagnostic> diagnostics)
        {
            if (file == null || diagnostics == null)
            {
                return(Enumerable.Empty <(string, WorkspaceEdit)>());
            }
            var deprecatedUnitTypes         = diagnostics.Where(DiagnosticTools.WarningType(WarningCode.DeprecatedUnitType));
            var deprecatedNOToperators      = diagnostics.Where(DiagnosticTools.WarningType(WarningCode.DeprecatedNOToperator));
            var deprecatedANDoperators      = diagnostics.Where(DiagnosticTools.WarningType(WarningCode.DeprecatedANDoperator));
            var deprecatedORoperators       = diagnostics.Where(DiagnosticTools.WarningType(WarningCode.DeprecatedORoperator));
            var deprecatedOpCharacteristics = diagnostics.Where(DiagnosticTools.WarningType(WarningCode.DeprecatedOpCharacteristics));

            (string, WorkspaceEdit) ReplaceWith(string text, LSP.Range range)
            {
                bool NeedsWs(Char ch) => Char.IsLetterOrDigit(ch) || ch == '_';

                if (range?.Start != null && range.End != null)
                {
                    var beforeEdit = file.GetLine(range.Start.Line).Text.Substring(0, range.Start.Character);
                    var afterEdit  = file.GetLine(range.End.Line).Text.Substring(range.End.Character);
                    if (beforeEdit.Any() && NeedsWs(beforeEdit.Last()))
                    {
                        text = $" {text}";
                    }
                    if (afterEdit.Any() && NeedsWs(afterEdit.First()))
                    {
                        text = $"{text} ";
                    }
                }
                var edit = new TextEdit {
                    Range = range?.Copy(), NewText = text
                };

                return($"Replace with \"{text.Trim()}\".", file.GetWorkspaceEdit(edit));
            }

            // update deprecated keywords and operators

            var suggestionsForUnitType = deprecatedUnitTypes.Select(d => ReplaceWith(Keywords.qsUnit.id, d.Range));
            var suggestionsForNOT      = deprecatedNOToperators.Select(d => ReplaceWith(Keywords.qsNOTop.op, d.Range));
            var suggestionsForAND      = deprecatedANDoperators.Select(d => ReplaceWith(Keywords.qsANDop.op, d.Range));
            var suggestionsForOR       = deprecatedORoperators.Select(d => ReplaceWith(Keywords.qsORop.op, d.Range));

            // update deprecated operation characteristics syntax

            var typeToQs = new ExpressionTypeToQs(new ExpressionToQs());

            string CharacteristicsAnnotation(Characteristics c)
            {
                typeToQs.onCharacteristicsExpression(SymbolResolution.ResolveCharacteristics(c));
                return($"{Keywords.qsCharacteristics.id} {typeToQs.Output}");
            }

            var suggestionsForOpCharacteristics = deprecatedOpCharacteristics.SelectMany(d =>
            {
                // TODO: TryGetQsSymbolInfo currently only returns information about the inner most leafs rather than all types etc.
                // Once it returns indeed all types in the fragment, the following code block should be replaced by the commented out code below.
                var fragment = file.TryGetFragmentAt(d.Range.Start, out var _);
                IEnumerable <Characteristics> GetCharacteristics(QsTuple <Tuple <QsSymbol, QsType> > argTuple) =>
                SyntaxGenerator.ExtractItems(argTuple).SelectMany(item => item.Item2.ExtractCharacteristics()).Distinct();
                var characteristicsInFragment =
                    fragment?.Kind is QsFragmentKind.FunctionDeclaration function ? GetCharacteristics(function.Item2.Argument) :
                    fragment?.Kind is QsFragmentKind.OperationDeclaration operation ? GetCharacteristics(operation.Item2.Argument) :
                    fragment?.Kind is QsFragmentKind.TypeDefinition type ? GetCharacteristics(type.Item2) :
                    Enumerable.Empty <Characteristics>();

                //var symbolInfo = file.TryGetQsSymbolInfo(d.Range.Start, false, out var fragment);
                //var characteristicsInFragment = (symbolInfo?.UsedTypes ?? Enumerable.Empty<QsType>())
                //    .SelectMany(t => t.ExtractCharacteristics()).Distinct();
                var fragmentStart = fragment?.GetRange()?.Start;
                return(characteristicsInFragment
                       .Where(c => c.Range.IsValue && DiagnosticTools.GetAbsoluteRange(fragmentStart, c.Range.Item).Overlaps(d.Range))
                       .Select(c => ReplaceWith(CharacteristicsAnnotation(c), d.Range)));
            });

            return(suggestionsForOpCharacteristics.ToArray()
                   .Concat(suggestionsForUnitType)
                   .Concat(suggestionsForNOT)
                   .Concat(suggestionsForAND)
                   .Concat(suggestionsForOR));
        }