Skip to content

Commit f702155

Browse files
Add defensive code in LSP completion
To handle the case when we are trying to complete the first token of the file. This should not trigger any exception. Add an automatic test for it. For eng/ide/ada_language_server#1194
1 parent dc4577b commit f702155

File tree

9 files changed

+259
-20
lines changed

9 files changed

+259
-20
lines changed

source/ada/lsp-ada_completions-filters.adb

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,11 @@ package body LSP.Ada_Completions.Filters is
4242
------------------
4343

4444
function Is_End_Label (Self : in out Filter'Class) return Boolean is
45+
use Libadalang.Common;
4546
begin
4647
if not Self.Is_End_Label.Is_Set then
4748
declare
48-
use all type Libadalang.Common.Ada_Node_Kind_Type;
49-
50-
Parent : constant Libadalang.Analysis.Ada_Node :=
49+
Parent : constant Libadalang.Analysis.Ada_Node :=
5150
(if Self.Node.Is_Null then Self.Node
5251
else Skip_Dotted_Names (Self.Node.Parent));
5352
-- Skip the outermost dotted name enclosing Node.Parent, so
@@ -69,9 +68,11 @@ package body LSP.Ada_Completions.Filters is
6968
elsif Is_End_Token (Self.Token) then
7069
Self.Is_End_Label := LSP.Constants.True;
7170

72-
elsif Is_End_Token
73-
(Libadalang.Common.Previous
74-
(Self.Token, Exclude_Trivia => True))
71+
elsif Libadalang.Common.Previous
72+
(Self.Token, Exclude_Trivia => True) /= No_Token
73+
and then Is_End_Token
74+
(Libadalang.Common.Previous
75+
(Self.Token, Exclude_Trivia => True))
7576
then
7677
Self.Is_End_Label := LSP.Constants.True;
7778

@@ -217,11 +218,18 @@ package body LSP.Ada_Completions.Filters is
217218
---------------
218219

219220
function Is_Aspect (Self : in out Filter'Class) return Boolean is
221+
use Libadalang.Common;
220222
begin
221223
if not Self.Is_Aspect.Is_Set then
222-
declare
223-
use Libadalang.Common;
224224

225+
if Libadalang.Common.Previous
226+
(Self.Token, Exclude_Trivia => True) = No_Token
227+
then
228+
Self.Is_Aspect := (True, False);
229+
return Self.Is_Aspect.Value;
230+
end if;
231+
232+
declare
225233
Token_Kind : constant Libadalang.Common.Token_Kind :=
226234
Kind (Libadalang.Common.Previous
227235
(Self.Token, Exclude_Trivia => True));

source/ada/lsp-ada_completions-parameters.adb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,10 @@ package body LSP.Ada_Completions.Parameters is
260260
(N : Libadalang.Analysis.Ada_Node'Class)
261261
return Libadalang.Analysis.Aggregate is
262262
begin
263+
if N.Is_Null then
264+
return No_Aggregate;
265+
end if;
266+
263267
if N.Kind in Libadalang.Common.Ada_Aggregate_Range then
264268
return N.As_Aggregate;
265269
end if;
@@ -598,6 +602,10 @@ package body LSP.Ada_Completions.Parameters is
598602
return Libadalang.Analysis.Generic_Package_Instantiation
599603
is
600604
begin
605+
if N.Is_Null then
606+
return No_Generic_Package_Instantiation;
607+
end if;
608+
601609
if N.Kind in Ada_Generic_Package_Instantiation_Range then
602610
return N.As_Generic_Package_Instantiation;
603611
end if;

source/ada/lsp-ada_completions-use_clauses.adb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ package body LSP.Ada_Completions.Use_Clauses is
5555
and then N.Kind = Ada_With_Clause);
5656

5757
With_Node : constant Libadalang.Analysis.Ada_Node :=
58-
Libadalang.Iterators.Find_First
59-
(Node.Unit.Root, Has_With_Clause_Node'Unrestricted_Access);
58+
(if Node.Is_Null then Node
59+
else Libadalang.Iterators.Find_First
60+
(Node.Unit.Root, Has_With_Clause_Node'Unrestricted_Access));
6061
begin
6162
-- Return immediately if we don't have any with-clause node on the same
6263
-- line or if we are still within the with-clause node.

source/ada/lsp-ada_completions.adb

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,23 @@ package body LSP.Ada_Completions is
5353
------------------
5454

5555
function Is_End_Token
56-
(Token : Libadalang.Common.Token_Reference)
57-
return Boolean
56+
(Token : Libadalang.Common.Token_Reference) return Boolean
5857
is
5958
use Libadalang.Common;
60-
End_Token : constant Libadalang.Common.Token_Data_Type :=
61-
Libadalang.Common.Data (Token);
62-
63-
Token_Kind : constant Libadalang.Common.Token_Kind :=
64-
Libadalang.Common.Kind (End_Token);
6559
begin
66-
return Token_Kind = Libadalang.Common.Ada_End;
60+
if Token = No_Token then
61+
return False;
62+
end if;
63+
64+
declare
65+
End_Token : constant Libadalang.Common.Token_Data_Type :=
66+
Libadalang.Common.Data (Token);
67+
68+
Token_Kind : constant Libadalang.Common.Token_Kind :=
69+
Libadalang.Common.Kind (End_Token);
70+
begin
71+
return Token_Kind = Libadalang.Common.Ada_End;
72+
end;
6773
end Is_End_Token;
6874

6975
------------------------

source/ada/lsp-ada_handlers-invisibles.adb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ package body LSP.Ada_Handlers.Invisibles is
4040
is
4141
pragma Unreferenced (Result);
4242
use all type Libadalang.Common.Token_Kind;
43+
use all type Libadalang.Common.Token_Reference;
4344
use type Ada.Containers.Count_Type;
4445

4546
procedure On_Inaccessible_Name
@@ -78,10 +79,13 @@ package body LSP.Ada_Handlers.Invisibles is
7879
end if;
7980
end On_Inaccessible_Name;
8081

81-
Dot_Token : constant Libadalang.Common.Token_Data_Type :=
82+
Previous_Tok : constant Libadalang.Common.Token_Reference :=
83+
Libadalang.Common.Previous (Token, Exclude_Trivia => True);
84+
Dot_Token : constant Libadalang.Common.Token_Data_Type :=
8285
Libadalang.Common.Data
8386
(if Libadalang.Common.Is_Trivia (Token)
84-
then Libadalang.Common.Previous (Token, True)
87+
and then Previous_Tok /= Libadalang.Common.No_Token
88+
then Previous_Tok
8589
else Token);
8690

8791
function Dummy_Canceled return Boolean is (False);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
project Default is
2+
end Default;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
with Ada.Text_IO;
3+
4+
procedure Main is
5+
begin
6+
Ada.Text_IO.Put_Line ("Hello");
7+
end Main;
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
[
2+
{
3+
"comment": ["Check that completion does not crash on file's first token."]
4+
},
5+
{
6+
"start": {
7+
"cmd": ["${ALS}"]
8+
}
9+
},
10+
{
11+
"send": {
12+
"request": {
13+
"jsonrpc": "2.0",
14+
"id": 1,
15+
"method": "initialize",
16+
"params": {
17+
"processId": 257038,
18+
"rootUri": "$URI{.}",
19+
"capabilities": {
20+
"workspace": {
21+
"applyEdit": true,
22+
"workspaceEdit": {
23+
"documentChanges": true,
24+
"resourceOperations": ["rename"]
25+
}
26+
},
27+
"textDocument": {
28+
"synchronization": {},
29+
"completion": {
30+
"dynamicRegistration": true,
31+
"completionItem": {
32+
"snippetSupport": false,
33+
"documentationFormat": ["plaintext", "markdown"],
34+
"resolveSupport": {
35+
"properties": ["detail", "documentation"]
36+
}
37+
}
38+
},
39+
"hover": {},
40+
"signatureHelp": {},
41+
"declaration": {},
42+
"definition": {},
43+
"typeDefinition": {},
44+
"implementation": {},
45+
"documentSymbol": {
46+
"hierarchicalDocumentSymbolSupport": true
47+
},
48+
"formatting": {
49+
"dynamicRegistration": false
50+
},
51+
"rangeFormatting": {
52+
"dynamicRegistration": false
53+
},
54+
"onTypeFormatting": {
55+
"dynamicRegistration": false
56+
},
57+
"foldingRange": {
58+
"lineFoldingOnly": true
59+
}
60+
},
61+
"experimental": {
62+
"advanced_refactorings": ["add_parameter"]
63+
}
64+
}
65+
}
66+
},
67+
"wait": [
68+
{
69+
"id": 1,
70+
"result": {
71+
"capabilities": {
72+
"textDocumentSync": 2,
73+
"completionProvider": {
74+
"triggerCharacters": [".", ",", "'", "("],
75+
"resolveProvider": true
76+
}
77+
}
78+
}
79+
}
80+
]
81+
}
82+
},
83+
{
84+
"send": {
85+
"request": {
86+
"jsonrpc": "2.0",
87+
"method": "initialized"
88+
},
89+
"wait": []
90+
}
91+
},
92+
{
93+
"send": {
94+
"request": {
95+
"jsonrpc": "2.0",
96+
"method": "workspace/didChangeConfiguration",
97+
"params": {
98+
"settings": {
99+
"ada": {
100+
"scenarioVariables": {},
101+
"defaultCharset": "ISO-8859-1",
102+
"enableDiagnostics": true,
103+
"followSymlinks": false,
104+
"documentationStyle": "leading",
105+
"foldComments": false
106+
}
107+
}
108+
}
109+
},
110+
"wait": []
111+
}
112+
},
113+
{
114+
"send": {
115+
"request": {
116+
"jsonrpc": "2.0",
117+
"method": "textDocument/didOpen",
118+
"params": {
119+
"textDocument": {
120+
"uri": "$URI{main.adb}",
121+
"languageId": "Ada",
122+
"version": 0,
123+
"text": "\nwith Ada.Text_IO;\n\nprocedure Main is\nbegin\n Ada.Text_IO.Put_Line (\"Hello\");\nend Main;\n"
124+
}
125+
}
126+
},
127+
"wait": []
128+
}
129+
},
130+
{
131+
"send": {
132+
"request": {
133+
"jsonrpc": "2.0",
134+
"id": 6,
135+
"method": "textDocument/completion",
136+
"params": {
137+
"textDocument": {
138+
"uri": "$URI{main.adb}"
139+
},
140+
"position": {
141+
"line": 0,
142+
"character": 0
143+
},
144+
"context": {
145+
"triggerKind": 1
146+
}
147+
}
148+
},
149+
"wait": [
150+
{
151+
"id": 6,
152+
"result": {
153+
"isIncomplete": false,
154+
"items": []
155+
}
156+
}
157+
]
158+
}
159+
},
160+
{
161+
"send": {
162+
"request": {
163+
"jsonrpc": "2.0",
164+
"method": "textDocument/didClose",
165+
"params": {
166+
"textDocument": {
167+
"uri": "$URI{main.adb}"
168+
}
169+
}
170+
},
171+
"wait": [
172+
{
173+
"method": "textDocument/publishDiagnostics",
174+
"params": {
175+
"uri": "$URI{main.adb}",
176+
"diagnostics": []
177+
}
178+
}
179+
]
180+
}
181+
},
182+
{
183+
"send": {
184+
"request": {
185+
"jsonrpc": "2.0",
186+
"id": 8,
187+
"method": "shutdown"
188+
},
189+
"wait": [
190+
{
191+
"id": 8,
192+
"result": null
193+
}
194+
]
195+
}
196+
},
197+
{
198+
"stop": {
199+
"exit_code": 0
200+
}
201+
}
202+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
title: 'completion.first_token'

0 commit comments

Comments
 (0)