/// <summary> /// Logs the generated Q# code for the part of the given evaluated syntax tree that corresponds to each file /// in the given compilation as Information using the given logger. /// If the given evaluated tree is null, queries the tree contained in the given compilation instead. /// If the id of a file is consistent with the one assigned to a code snippet, /// strips the lines of code that correspond to the wrapping defined by WrapSnippet. /// Throws an ArgumentException if this is not possible because the given syntax tree is inconsistent with that wrapping. /// Throws an ArgumentNullException if the given compilation is null. /// </summary> private static void PrintGeneratedQs(IEnumerable <QsNamespace> evaluatedTree, Compilation compilation, ILogger logger) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } evaluatedTree ??= compilation.SyntaxTree.Values; foreach (var file in compilation.SourceFiles) { if (Options.IsCodeSnippet(file)) { var subtree = evaluatedTree.Select(ns => FilterBySourceFile.Apply(ns, file)).Where(ns => ns.Elements.Any()); var code = new string[] { "" }.Concat(StripSnippetWrapping(subtree).Select(SyntaxTreeToQsharp.Default.ToCode)); logger.Log(InformationCode.FormattedQsCode, Enumerable.Empty <string>(), messageParam: code.ToArray()); } else { var imports = evaluatedTree.ToImmutableDictionary(ns => ns.Name, ns => compilation.OpenDirectives(file, ns.Name).ToImmutableArray()); SyntaxTreeToQsharp.Apply(out List <ImmutableDictionary <NonNullable <string>, string> > generated, evaluatedTree, (file, imports)); var code = new string[] { "" }.Concat(generated.Single().Values.Select(nsCode => $"{nsCode}{Environment.NewLine}")); logger.Log(InformationCode.FormattedQsCode, Enumerable.Empty <string>(), file.Value, messageParam: code.ToArray()); }; } }
/// <inheritdoc/> public AssemblyInfo BuildEntryPoint(OperationInfo operation, CompilerMetadata metadatas, QSharpLogger logger, string dllName, string executionTarget = null) { var signature = operation.Header.PrintSignature(); var argumentTuple = SyntaxTreeToQsharp.ArgumentTuple(operation.Header.ArgumentTuple, type => type.ToString(), symbolsOnly: true); var entryPointUri = new Uri(Path.GetFullPath(Path.Combine("/", $"entrypoint.qs"))); var entryPointSnippet = @$ "namespace ENTRYPOINT {{ open {operation.Header.QualifiedName.Namespace.Value};
/// <summary> /// Generates formatted Q# code based on the part of the syntax tree that corresponds to each file in the given compilation. /// If the id of a file is consistent with the one assigned to a code snippet, /// strips the lines of code that correspond to the wrapping defined by WrapSnippet. /// Throws an ArgumentException if this is not possible because the given syntax tree is inconsistent with that wrapping. /// Throws an ArgumentNullException if the given compilation is null. /// </summary> private static IEnumerable <string> GenerateQsCode(Compilation compilation, NonNullable <string> file, ILogger logger) { if (compilation == null) { throw new ArgumentNullException(nameof(compilation)); } if (Options.IsCodeSnippet(file)) { var subtree = compilation.SyntaxTree.Values.Select(ns => FilterBySourceFile.Apply(ns, file)).Where(ns => ns.Elements.Any()); return(DiagnoseCompilation.StripSnippetWrapping(subtree).Select(SyntaxTreeToQsharp.Default.ToCode)); } else { var imports = compilation.SyntaxTree.Values .ToImmutableDictionary(ns => ns.Name, ns => compilation.OpenDirectives(file, ns.Name).ToImmutableArray()); var success = SyntaxTreeToQsharp.Apply(out List <ImmutableDictionary <NonNullable <string>, string> > generated, compilation.SyntaxTree.Values, (file, imports)); if (!success) { logger?.Log(WarningCode.UnresolvedItemsInGeneratedQs, Enumerable.Empty <string>(), file.Value); } return(generated.Single().Select(entry => { var nsComments = compilation.NamespaceComments(file, entry.Key); string FormatComments(IEnumerable <string> comments) => string.Join( Environment.NewLine, comments.Select(line => line.Trim()).Select(line => string.IsNullOrWhiteSpace(line) ? "" : $"// {line}")) .Trim(); var leadingComments = entry.Value.StartsWith("///") ? $"{FormatComments(nsComments.OpeningComments)}{Environment.NewLine}" : FormatComments(nsComments.OpeningComments); var trailingComments = FormatComments(nsComments.ClosingComments); var code = new string[] { leadingComments, entry.Value, trailingComments }.Where(s => !string.IsNullOrWhiteSpace(s)); return string.Join(Environment.NewLine, code); })); } }
/// <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 string CharacteristicsAnnotation(Characteristics c) { var charEx = SyntaxTreeToQsharp.CharacteristicsExpression(SymbolResolution.ResolveCharacteristics(c)); return(charEx == null ? "" : $"{Keywords.qsCharacteristics.id} {charEx}"); } 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)); }