Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.

Commit 3ffd626

Browse files
Chris Granadericardo-espinoza
andauthored
Fix #885 (#886)
* [WIP] Started updating VS LSP client. * A few more nullability fixes. * Use explicit 0:0 range instead of relying on constructor. * Propagate nullability metadata to F# callers. * Avoid calling unsafe constructors. * Avoid deprecated Assert.Equals. * Set DocumentRangeFormattingProvider to false. * Fix to AssertCapability. * Assertions should work more reliabily when capabilities are null. * Disable nullable reference checking when copying unsound types. * Check if Range is null, since nullability metadata can be violated. Co-authored-by: Ricardo Espinoza <[email protected]>
1 parent 3d44bca commit 3ffd626

File tree

21 files changed

+214
-81
lines changed

21 files changed

+214
-81
lines changed

src/QsCompiler/CommandLineTool/Logging.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,14 @@ public ConsoleLogger(
4141
/// Prints the given message to the Console.
4242
/// Errors and Warnings are printed to the error stream.
4343
/// </summary>
44-
private static void PrintToConsole(DiagnosticSeverity severity, string message)
44+
private static void PrintToConsole(DiagnosticSeverity? severity, string message)
4545
{
46-
var (stream, color) =
47-
severity == DiagnosticSeverity.Error ? (Console.Error, ConsoleColor.Red) :
48-
severity == DiagnosticSeverity.Warning ? (Console.Error, ConsoleColor.Yellow) :
49-
(Console.Out, Console.ForegroundColor);
46+
var (stream, color) = severity switch
47+
{
48+
DiagnosticSeverity.Error => (Console.Error, ConsoleColor.Red),
49+
DiagnosticSeverity.Warning => (Console.Error, ConsoleColor.Yellow),
50+
_ => (Console.Out, Console.ForegroundColor)
51+
};
5052

5153
var consoleColor = Console.ForegroundColor;
5254
Console.ForegroundColor = color;

src/QsCompiler/CompilationManager/CompilationManager.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="Microsoft.VisualStudio.LanguageServer.Protocol" Version="16.3.57" />
13+
<PackageReference Include="Microsoft.VisualStudio.LanguageServer.Protocol" Version="16.9.1180" />
1414
<PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.2" />
1515
</ItemGroup>
1616

src/QsCompiler/CompilationManager/DiagnosticTools.cs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,35 @@ public static Position SymbolPosition(QsLocation rootLocation, QsNullable<Positi
3737
[return: NotNullIfNotNull("message")]
3838
public static Diagnostic? Copy(this Diagnostic message)
3939
{
40-
Lsp.Position? CopyPosition(Lsp.Position? position) =>
41-
position is null ? null : new Lsp.Position(position.Line, position.Character);
42-
43-
Lsp.Range? CopyRange(Lsp.Range? range) =>
44-
range is null
45-
? null
46-
: new Lsp.Range
47-
{
48-
Start = CopyPosition(range.Start),
49-
End = CopyPosition(range.End)
50-
};
40+
Lsp.Position CopyPosition(Lsp.Position position) =>
41+
new Lsp.Position(position.Line, position.Character);
5142

43+
Lsp.Range CopyRange(Lsp.Range range) =>
44+
new Lsp.Range
45+
{
46+
Start = CopyPosition(range.Start),
47+
End = CopyPosition(range.End)
48+
};
49+
50+
// NB: The nullability metadata on Diagnostic.Range is incorrect,
51+
// such that some Diagnostic values may have nullable ranges.
52+
// We cannot assign that to a new Diagnostic without
53+
// contradicting nullability metadata, however, so we need to
54+
// explicitly disable nullable references for the following
55+
// statement. Once the upstream bug in the LSP client package
56+
// is fixed, we can remove the nullable disable here.
57+
#nullable disable
5258
return message is null
5359
? null
5460
: new Diagnostic
5561
{
56-
Range = CopyRange(message.Range),
62+
Range = message.Range == null ? null : CopyRange(message.Range),
5763
Severity = message.Severity,
5864
Code = message.Code,
5965
Source = message.Source,
6066
Message = message.Message
6167
};
68+
#nullable restore
6269
}
6370

6471
/// <summary>
@@ -70,12 +77,17 @@ range is null
7077
public static Diagnostic WithLineNumOffset(this Diagnostic diagnostic, int offset)
7178
{
7279
var copy = diagnostic.Copy();
73-
copy.Range.Start.Line += offset;
74-
copy.Range.End.Line += offset;
75-
if (copy.Range.Start.Line < 0 || copy.Range.End.Line < 0)
80+
// NB: Despite the nullability metadata, Range may be null here.
81+
// We thus need to guard accordingly.
82+
if (copy.Range != null)
7683
{
77-
throw new ArgumentOutOfRangeException(
78-
nameof(offset), "Translated diagnostic has negative line numbers.");
84+
copy.Range.Start.Line += offset;
85+
copy.Range.End.Line += offset;
86+
if (copy.Range.Start.Line < 0 || copy.Range.End.Line < 0)
87+
{
88+
throw new ArgumentOutOfRangeException(
89+
nameof(offset), "Translated diagnostic has negative line numbers.");
90+
}
7991
}
8092
return copy;
8193
}

src/QsCompiler/CompilationManager/Diagnostics.cs

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public static Diagnostic LoadWarning(WarningCode code, IEnumerable<string> args,
121121
Code = Code(code),
122122
Source = source,
123123
Message = DiagnosticItem.Message(code, args ?? Enumerable.Empty<string>()),
124-
Range = null
124+
Range = new Lsp.Range { Start = new Lsp.Position(0, 0), End = new Lsp.Position(0, 0) }
125125
};
126126

127127
// warnings 20**
@@ -134,7 +134,7 @@ internal static Diagnostic EmptyStatementWarning(string filename, Position pos)
134134
Code = WarningCode.ExcessSemicolon.Code(),
135135
Source = filename,
136136
Message = DiagnosticItem.Message(WarningCode.ExcessSemicolon, Enumerable.Empty<string>()),
137-
Range = pos == null ? null : new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
137+
Range = new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
138138
};
139139
}
140140
}
@@ -156,82 +156,112 @@ public static Diagnostic LoadError(ErrorCode code, IEnumerable<string> args, str
156156
Code = Code(code),
157157
Source = source,
158158
Message = DiagnosticItem.Message(code, args ?? Enumerable.Empty<string>()),
159-
Range = null
159+
Range = new Lsp.Range { Start = new Lsp.Position(0, 0), End = new Lsp.Position(0, 0) }
160160
};
161161

162162
// errors 20**
163163

164164
internal static Diagnostic InvalidFragmentEnding(string filename, ErrorCode code, Position pos)
165165
{
166+
if (pos == null)
167+
{
168+
throw new ArgumentNullException(nameof(pos));
169+
}
170+
166171
return new Diagnostic
167172
{
168173
Severity = DiagnosticSeverity.Error,
169174
Code = Code(code),
170175
Source = filename,
171176
Message = DiagnosticItem.Message(code, Enumerable.Empty<string>()),
172-
Range = pos == null ? null : new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
177+
Range = new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
173178
};
174179
}
175180

176181
internal static Diagnostic MisplacedOpeningBracketError(string filename, Position pos)
177182
{
183+
if (pos == null)
184+
{
185+
throw new ArgumentNullException(nameof(pos));
186+
}
187+
178188
return new Diagnostic
179189
{
180190
Severity = DiagnosticSeverity.Error,
181191
Code = ErrorCode.MisplacedOpeningBracket.Code(),
182192
Source = filename,
183193
Message = DiagnosticItem.Message(ErrorCode.MisplacedOpeningBracket, Enumerable.Empty<string>()),
184-
Range = pos == null ? null : new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
194+
Range = new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
185195
};
186196
}
187197

188198
// errors 10**
189199

190200
internal static Diagnostic ExcessBracketError(string filename, Position pos)
191201
{
202+
if (pos == null)
203+
{
204+
throw new ArgumentNullException(nameof(pos));
205+
}
206+
192207
return new Diagnostic
193208
{
194209
Severity = DiagnosticSeverity.Error,
195210
Code = ErrorCode.ExcessBracketError.Code(),
196211
Source = filename,
197212
Message = DiagnosticItem.Message(ErrorCode.ExcessBracketError, Enumerable.Empty<string>()),
198-
Range = pos == null ? null : new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
213+
Range = new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
199214
};
200215
}
201216

202217
internal static Diagnostic MissingClosingBracketError(string filename, Position pos)
203218
{
219+
if (pos == null)
220+
{
221+
throw new ArgumentNullException(nameof(pos));
222+
}
223+
204224
return new Diagnostic
205225
{
206226
Severity = DiagnosticSeverity.Error,
207227
Code = ErrorCode.MissingBracketError.Code(),
208228
Source = filename,
209229
Message = DiagnosticItem.Message(ErrorCode.MissingBracketError, Enumerable.Empty<string>()),
210-
Range = pos == null ? null : new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
230+
Range = new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
211231
};
212232
}
213233

214234
internal static Diagnostic MissingStringDelimiterError(string filename, Position pos)
215235
{
236+
if (pos == null)
237+
{
238+
throw new ArgumentNullException(nameof(pos));
239+
}
240+
216241
return new Diagnostic
217242
{
218243
Severity = DiagnosticSeverity.Error,
219244
Code = ErrorCode.MissingStringDelimiterError.Code(),
220245
Source = filename,
221246
Message = DiagnosticItem.Message(ErrorCode.MissingStringDelimiterError, Enumerable.Empty<string>()),
222-
Range = pos == null ? null : new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
247+
Range = new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
223248
};
224249
}
225250

226251
internal static Diagnostic InvalidCharacterInInterpolatedArgument(string filename, Position pos, char invalidCharacter)
227252
{
253+
if (pos == null)
254+
{
255+
throw new ArgumentNullException(nameof(pos));
256+
}
257+
228258
return new Diagnostic
229259
{
230260
Severity = DiagnosticSeverity.Error,
231261
Code = ErrorCode.InvalidCharacterInInterpolatedArgument.Code(),
232262
Source = filename,
233263
Message = DiagnosticItem.Message(ErrorCode.InvalidCharacterInInterpolatedArgument, new[] { invalidCharacter.ToString() }),
234-
Range = pos == null ? null : new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
264+
Range = new Lsp.Range { Start = pos.ToLsp(), End = pos.ToLsp() }
235265
};
236266
}
237267
}

src/QsCompiler/CompilationManager/EditorSupport/CodeCompletion.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ private static IEnumerable<CompletionItem> GetNamespaceAliasCompletions(
430430
/// completion item data is missing properties.
431431
/// </summary>
432432
private static string? TryGetDocumentation(
433-
CompilationUnit compilation, CompletionItemData data, CompletionItemKind kind, bool useMarkdown)
433+
CompilationUnit compilation, CompletionItemData data, CompletionItemKind? kind, bool useMarkdown)
434434
{
435435
if (data.QualifiedName == null
436436
|| data.SourceFile == null
@@ -619,7 +619,7 @@ private static CompletionList ToCompletionList(this IEnumerable<CompletionItem>
619619
new CompletionList
620620
{
621621
IsIncomplete = isIncomplete,
622-
Items = items?.ToArray()
622+
Items = items?.ToArray() ?? new CompletionItem[] { }
623623
};
624624

625625
/// <summary>

src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ DocumentHighlight AsHighlight(Lsp.Range range) =>
203203
Hover? GetHover(string? info) => info == null ? null : new Hover
204204
{
205205
Contents = new MarkupContent { Kind = format, Value = info },
206-
Range = new Lsp.Range { Start = position?.ToLsp(), End = position?.ToLsp() }
206+
Range = new Lsp.Range { Start = position.ToLsp(), End = position.ToLsp() }
207207
};
208208

209209
var markdown = format == MarkupKind.Markdown;
@@ -393,7 +393,7 @@ List<QsFunctor> FunctorApplications(ref QsExpression ex)
393393
MarkupContent AsMarkupContent(string str) => new MarkupContent { Kind = format, Value = str };
394394
ParameterInformation AsParameterInfo(string? paramName) => new ParameterInformation
395395
{
396-
Label = paramName,
396+
Label = paramName ?? "<unknown parameter>",
397397
Documentation = AsMarkupContent(documentation.ParameterDescription(paramName))
398398
};
399399

src/QsCompiler/CompilationManager/EditorSupport/SymbolInformation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ internal static class SymbolInfo
2828
internal static Location AsLocation(string source, Position offset, Range relRange) =>
2929
new Location
3030
{
31-
Uri = CompilationUnitManager.TryGetUri(source, out var uri) ? uri : null,
31+
Uri = CompilationUnitManager.TryGetUri(source, out var uri) ? uri : throw new Exception($"Source location {source} could not be converted to a valid URI."),
3232
Range = (offset + relRange).ToLsp()
3333
};
3434

src/QsCompiler/CompilationManager/ProjectManager.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -929,10 +929,30 @@ public Task ManagerTaskAsync(Uri file, Action<CompilationUnitManager, bool> exec
929929

930930
try
931931
{
932+
// NB: As of version 16.9.1180 of the LSP client, document
933+
// changes are presented as the sum type
934+
// TextDocumentEdit[] | (TextDocumentEdit | CreateFile | RenameFile | DeleteFile)[].
935+
// Thus, to collect them with a SelectMany call, we need
936+
// to ensure that the first case (TextDocumentEdit[]) is
937+
// first wrapped in a cast to the sum type
938+
// TextDocumentEdit | CreateFile | RenameFile | DeleteFile.
939+
// Note that the SumType struct is defined in the LSP client,
940+
// and works by defining explicit cast operators for each case.
941+
IEnumerable<SumType<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>> CastToSumType(SumType<TextDocumentEdit[], SumType<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>[]>? editCollection) =>
942+
editCollection switch
943+
{
944+
{ } edits => edits.Match(
945+
simpleEdits => simpleEdits.Cast<SumType<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>(),
946+
complexEdits => complexEdits),
947+
null => ImmutableList<SumType<TextDocumentEdit, CreateFile, RenameFile, DeleteFile>>.Empty
948+
};
949+
932950
// if a file belongs to several compilation units, then this will fail
933951
var changes = edits.SelectMany(edit => edit.Changes)
934952
.ToDictionary(pair => pair.Key, pair => pair.Value);
935-
var documentChanges = edits.SelectMany(edit => edit.DocumentChanges).ToArray();
953+
var documentChanges = edits
954+
.SelectMany(edits => CastToSumType(edits.DocumentChanges).ToArray())
955+
.ToArray();
936956
return new WorkspaceEdit { Changes = changes, DocumentChanges = documentChanges };
937957
}
938958
catch

src/QsCompiler/CompilationManager/Utils.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,10 @@ public static Position ToQSharp(this Lsp.Position position) =>
168168
/// <summary>
169169
/// Converts the Q# compiler position into a language server protocol position.
170170
/// </summary>
171-
public static Lsp.Position ToLsp(this Position position) =>
172-
new Lsp.Position(position.Line, position.Column);
171+
public static Lsp.Position ToLsp(this Position? position) =>
172+
position == null
173+
? new Lsp.Position()
174+
: new Lsp.Position(position.Line, position.Column);
173175

174176
/// <summary>
175177
/// Converts the language server protocol range into a Q# compiler range.

src/QsCompiler/Compiler/LoadedStep.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ internal static Diagnostic ConvertDiagnostic(IRewriteStep.Diagnostic diagnostic,
123123
Severity = severity,
124124
Message = $"{stageAnnotation}{diagnostic.Message}",
125125
Source = diagnostic.Source,
126-
Range = diagnostic.Source is null ? null : diagnostic.Range?.ToLsp()
126+
Range = diagnostic.Source is null || diagnostic.Range is null
127+
? new VisualStudio.LanguageServer.Protocol.Range()
128+
: diagnostic.Range.ToLsp()
127129
};
128130
}
129131

0 commit comments

Comments
 (0)