Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly implement pointer types & resolve pointers, arrays and AAs in output #725

Merged
merged 2 commits into from
Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,19 @@ a tab separated format:
* definition: function or variable definition string or close approximation for information display purpose
* symbol location: in which file (or `stdin`) & byte offset this symbol is defined. Separated with a space.
* documentation: escaped documentation string of this symbol
* typeOf: resolved type name of this symbol:
<!-- the items in list are copied from messages.d -->
* For variables, fields, globals, constants: resolved type or empty if unresolved.
* For functions: resolved return type or empty if unresolved.
* For constructors: may be struct/class name or empty in any case.
* Otherwise (probably) empty.

#### Example `--extended` output

identifiers
libraryFunction f Tuple!long libraryFunction(string s, string s2) stdin 190 foobar
libraryFunction f int* libraryFunction(string s) stdin 99 Hello\nWorld
libraryVariable v int libraryVariable stdin 56 My variable
libraryFunction f int* libraryFunction(string s) stdin 99 Hello\nWorld int*
libraryVariable v int libraryVariable stdin 56 My variable int
libreTypes g stdin 298

#### Note
Expand All @@ -190,6 +196,9 @@ DCD's output will start with "identifiers" when completing at a left paren
character if the keywords *pragma*, *scope*, *__traits*, *extern*, or *version*
were just before the paren.

Types in the calltips and typeOf column may not be complete, e.g. missing
template parameters or typeof expressions, etc.

### Parenthesis completion

When the first line of output is "calltips", the editor should display a function
Expand Down
8 changes: 8 additions & 0 deletions common/src/dcd/common/messages.d
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ struct AutocompleteResponse
* Documentation associated with this symbol.
*/
string documentation;
// when changing the behavior here, update README.md
/**
* For variables, fields, globals, constants: resolved type or empty if unresolved.
* For functions: resolved return type or empty if unresolved.
* For constructors: may be struct/class name or empty in any case.
* Otherwise (probably) empty.
*/
string typeOf;
}

/**
Expand Down
12 changes: 12 additions & 0 deletions dsymbol/src/dsymbol/builtin/symbols.d
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ TTree!(DSymbol*, SymbolsAllocator, true, "a < b") classSymbols;
*/
TTree!(DSymbol*, SymbolsAllocator, true, "a < b") enumSymbols;

/**
* Pointer properties (when not implicitly dereferencing)
*/
TTree!(DSymbol*, SymbolsAllocator, true, "a < b") pointerSymbols;

/**
* Variadic template parameters properties
*/
Expand Down Expand Up @@ -191,6 +196,12 @@ static this()
aggregateSymbols.insert(stringof_);
aggregateSymbols.insert(init);

pointerSymbols.insert(mangleof_);
pointerSymbols.insert(alignof_);
pointerSymbols.insert(sizeof_);
pointerSymbols.insert(stringof_);
pointerSymbols.insert(init);

classSymbols.insert(makeSymbol("classinfo", CompletionKind.variableName));
classSymbols.insert(tupleof);
classSymbols.insert(makeSymbol("__vptr", CompletionKind.variableName));
Expand Down Expand Up @@ -283,6 +294,7 @@ static ~this()
destroy(aggregateSymbols);
destroy(classSymbols);
destroy(enumSymbols);
destroy(pointerSymbols);

foreach (sym; symbolsMadeHere[])
destroy(*sym);
Expand Down
4 changes: 1 addition & 3 deletions dsymbol/src/dsymbol/conversion/first.d
Original file line number Diff line number Diff line change
Expand Up @@ -1129,9 +1129,7 @@ private:

foreach (suffix; type.typeSuffixes)
{
if (suffix.star != tok!"")
continue;
else if (suffix.type)
if (suffix.type)
lookup.breadcrumbs.insert(ASSOC_ARRAY_SYMBOL_NAME);
else if (suffix.array)
lookup.breadcrumbs.insert(ARRAY_SYMBOL_NAME);
Expand Down
36 changes: 19 additions & 17 deletions dsymbol/src/dsymbol/conversion/second.d
Original file line number Diff line number Diff line change
Expand Up @@ -180,29 +180,30 @@ do
while (!lookup.breadcrumbs.empty)
{
auto back = lookup.breadcrumbs.back;
immutable bool isArr = back == ARRAY_SYMBOL_NAME;
immutable bool isAssoc = back == ASSOC_ARRAY_SYMBOL_NAME;
immutable bool isFunction = back == FUNCTION_SYMBOL_NAME;
if (back == POINTER_SYMBOL_NAME)
{
lastSuffix.isPointer = true;
lookup.breadcrumbs.popBack();
continue;
}
if (!isArr && !isAssoc && !isFunction)
break;
immutable qualifier = isAssoc ? SymbolQualifier.assocArray
: (isFunction ? SymbolQualifier.func : SymbolQualifier.array);
SymbolQualifier qualifier;
if (back == ARRAY_SYMBOL_NAME) qualifier = SymbolQualifier.array;
else if (back == ASSOC_ARRAY_SYMBOL_NAME) qualifier = SymbolQualifier.assocArray;
else if (back == FUNCTION_SYMBOL_NAME) qualifier = SymbolQualifier.func;
else if (back == POINTER_SYMBOL_NAME) qualifier = SymbolQualifier.pointer;
else break;

WebFreak001 marked this conversation as resolved.
Show resolved Hide resolved
lastSuffix = GCAllocator.instance.make!DSymbol(back, CompletionKind.dummy, lastSuffix);
lastSuffix.qualifier = qualifier;
lastSuffix.ownType = true;
if (isFunction)

final switch (qualifier)
{
case SymbolQualifier.none:
case SymbolQualifier.selectiveImport:
assert(false, "this should never be generated");
case SymbolQualifier.func:
lookup.breadcrumbs.popBack();
lastSuffix.callTip = lookup.breadcrumbs.back();
break;
case SymbolQualifier.array: lastSuffix.addChildren(arraySymbols[], false); break;
case SymbolQualifier.assocArray: lastSuffix.addChildren(assocArraySymbols[], false); break;
case SymbolQualifier.pointer: lastSuffix.addChildren(pointerSymbols[], false); break;
}
else
lastSuffix.addChildren(isArr ? arraySymbols[] : assocArraySymbols[], false);

if (suffix is null)
suffix = lastSuffix;
Expand Down Expand Up @@ -454,9 +455,10 @@ void resolveTypeFromInitializer(DSymbol* symbol, TypeLookup* lookup,
if (currentSymbol is null)
return;

// Index expressions can be an array index or an AA index
// Index expressions can be on a pointer, an array or an AA
if (currentSymbol.qualifier == SymbolQualifier.array
|| currentSymbol.qualifier == SymbolQualifier.assocArray
|| currentSymbol.qualifier == SymbolQualifier.pointer
|| currentSymbol.kind == CompletionKind.aliasName)
{
if (currentSymbol.type !is null)
Expand Down
52 changes: 50 additions & 2 deletions dsymbol/src/dsymbol/symbol.d
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ enum SymbolQualifier : ubyte
func,
/// Selective import
selectiveImport,
/// The symbol is a pointer
pointer,
}

/**
Expand Down Expand Up @@ -228,6 +230,11 @@ struct DSymbol
return;
visited.insert(cast(size_t) &this);

// pointers are implicitly dereferenced on members (a single layer)
if (qualifier == SymbolQualifier.pointer
&& this.type.qualifier != SymbolQualifier.pointer)
return type.getParts!OR(name, app, visited, onlyOne);

if (name is null)
{
foreach (part; parts[].filter!(a => a.name != IMPORT_SYMBOL_NAME))
Expand Down Expand Up @@ -427,13 +434,54 @@ struct DSymbol
// dfmt off
mixin(bitfields!(bool, "ownType", 1,
bool, "skipOver", 1,
bool, "isPointer", 1,
ubyte, "", 5));
ubyte, "", 6));
// dfmt on

deprecated bool isPointer()
{
return qualifier == SymbolQualifier.pointer;
}

/// Protection level for this symbol
IdType protection;

string formatType(string suffix = "") const
{
if (kind == CompletionKind.functionName)
{
if (type) // try to give return type symbol
return type.formatType;
else // null if unresolved, user can manually pick .name or .callTip if needed
return null;
}
else if (name == POINTER_SYMBOL_NAME)
WebFreak001 marked this conversation as resolved.
Show resolved Hide resolved
{
if (!type)
return suffix ~ "*";
else
return type.formatType(suffix ~ "*");
}
else if (name == ARRAY_SYMBOL_NAME)
{
if (!type)
return suffix ~ "[]";
else
return type.formatType(suffix ~ "[]");
}
else if (name == ASSOC_ARRAY_SYMBOL_NAME)
{
// TODO: include AA key type
if (!type)
return suffix ~ "[...]";
else
return type.formatType(suffix ~ "[...]");
}
else
{
// TODO: include template parameters
return name ~ suffix;
}
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions dsymbol/src/dsymbol/tests.d
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,8 @@ unittest
};
ScopeSymbolPair pair = generateAutocompleteTrees(source, cache);
DSymbol* meaningOfLife = pair.symbol.getFirstPartNamed(istring("meaningOfLife"));
writeln(meaningOfLife.type.name);
assert(meaningOfLife.type.name == "int");
writeln(meaningOfLife.type.formatType);
assert(meaningOfLife.type.formatType == "int*");
}


Expand Down
3 changes: 2 additions & 1 deletion src/dcd/client/client.d
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,8 @@ void printCompletionResponse(ref const AutocompleteResponse response, bool exten
completion.kind == char.init ? "" : "" ~ completion.kind,
completion.definition,
completion.symbolFilePath.length ? completion.symbolFilePath ~ " " ~ completion.symbolLocation.to!string : "",
completion.documentation
completion.documentation,
completion.typeOf
));
else
app.put(makeTabSeparated(completion.identifier, "" ~ completion.kind));
Expand Down
1 change: 1 addition & 0 deletions src/dcd/server/autocomplete/complete.d
Original file line number Diff line number Diff line change
Expand Up @@ -701,5 +701,6 @@ do
auto completion = makeSymbolCompletionInfo(symbol, char.init);
completion.identifier = "this";
completion.definition = generatedStructConstructorCalltip;
completion.typeOf = symbol.name;
return completion;
}
27 changes: 19 additions & 8 deletions src/dcd/server/autocomplete/util.d
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,8 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
case tok!"[":
if (symbols.length == 0)
break loop;
if (symbols[0].qualifier == SymbolQualifier.array)
if (symbols[0].qualifier == SymbolQualifier.array
|| symbols[0].qualifier == SymbolQualifier.pointer)
{
skip(tok!"[", tok!"]");
if (!isSliceExpression(tokens, i))
Expand Down Expand Up @@ -613,16 +614,26 @@ bool isUdaExpression(T)(ref T tokens)

AutocompleteResponse.Completion makeSymbolCompletionInfo(const DSymbol* symbol, char kind)
{
string definition;
auto ret = AutocompleteResponse.Completion(symbol.name, kind, null,
symbol.symbolFile, symbol.location, symbol.doc);

if (symbol.type)
ret.typeOf = symbol.type.formatType;

if ((kind == CompletionKind.variableName || kind == CompletionKind.memberVariableName) && symbol.type)
definition = symbol.type.name ~ ' ' ~ symbol.name;
{
if (symbol.type.kind == CompletionKind.functionName && !ret.typeOf.length)
ret.definition = symbol.type.name ~ ' ' ~ symbol.name;
else
ret.definition = ret.typeOf ~ ' ' ~ symbol.name;
}
else if (kind == CompletionKind.enumMember)
definition = symbol.name; // TODO: add enum value to definition string
ret.definition = symbol.name; // TODO: add enum value to definition string
else
definition = symbol.callTip;
// TODO: definition strings could include more information, like on classes inheritance
return AutocompleteResponse.Completion(symbol.name, kind, definition,
symbol.symbolFile, symbol.location, symbol.doc);
ret.definition = symbol.callTip;

// TODO: extend completion with more info such as class inheritance
return ret;
}

bool doUFCSSearch(string beforeToken, string lastToken)
Expand Down
7 changes: 4 additions & 3 deletions tests/tc059/expected1.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
identifiers
libraryFunction f Tuple!long libraryFunction(string s, string s2) stdin 190 foobar
libraryFunction f int* libraryFunction(string s) stdin 99 Hello\nWorld
libraryVariable v int libraryVariable stdin 56 My variable
libraryFunction f Tuple!long libraryFunction(string s, string s2) stdin 223 foobar
libraryFunction f int* libraryFunction(string s) stdin 132 Hello\nWorld int*
libraryVariable v int libraryVariable stdin 56 My variable int
libraryVariable2 v int* libraryVariable2 stdin 88 My variable int*
2 changes: 2 additions & 0 deletions tests/tc059/file.d
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ void main(string[] args)

/// My variable
int libraryVariable;
/// ditto
int* libraryVariable2;

/// Hello
/// World
Expand Down
4 changes: 2 additions & 2 deletions tests/tc061/expected1.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
calltips
libraryFunction Tuple!long libraryFunction(string s, string s2) stdin 166 foobar
libraryFunction int* libraryFunction(string s) stdin 75 Hello\nWorld
libraryFunction Tuple!long libraryFunction(string s, string s2) stdin 166 foobar
libraryFunction int* libraryFunction(string s) stdin 75 Hello\nWorld int*
4 changes: 2 additions & 2 deletions tests/tc_extended_ditto/expected1.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
identifiers
foo f void foo() stdin 26 my documentation
foo f void foo(int i) stdin 49 my documentation
foo f void foo() stdin 26 my documentation void
foo f void foo(int i) stdin 49 my documentation void
2 changes: 2 additions & 0 deletions tests/tc_extended_types/expected1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
identifiers
bar v foo bar stdin 92
12 changes: 12 additions & 0 deletions tests/tc_extended_types/file.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// my documentation
struct S
{
T foo(T)() { return T.init; }
}

void test()
{
S s;
auto bar = s.foo!int();
bar
}
5 changes: 5 additions & 0 deletions tests/tc_extended_types/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
set -e
set -u

../../bin/dcd-client $1 file.d -x -c115 > actual1.txt
diff actual1.txt expected1.txt --strip-trailing-cr
2 changes: 1 addition & 1 deletion tests/tc_import_symbol_list/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ set -e
set -u

../../bin/dcd-client $1 file.d --extended -I"$PWD"/newpackage -c$(wc -c < file.d) > actual1.txt
echo -e "identifiers\nSomeStruct\ts\t\t$PWD/newpackage/newmodule.d 26\t" > expected1.txt
echo -e "identifiers\nSomeStruct\ts\t\t$PWD/newpackage/newmodule.d 26\t\t" > expected1.txt
diff actual1.txt expected1.txt --strip-trailing-cr
14 changes: 14 additions & 0 deletions tests/tc_pointer_type_printing/expected1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
identifiers
itemA v S itemA stdin 44 S
itemB v S* itemB stdin 55 S*
itemC v S[]* itemC stdin 68 S[]*
itemD v S[][]* itemD stdin 83 S[][]*
itemE v S[][]*[] itemE stdin 100 S[][]*[]
itemF v S[][]*[][] itemF stdin 119 S[][]*[][]
itemG v S[][...]*[][] itemG stdin 141 S[][...]*[][]
itemH v S[][]*[...][] itemH stdin 163 S[][]*[...][]
itemI v S[...][]*[...][] itemI stdin 188 S[...][]*[...][]
itemJ v S[...]*[]*[...][] itemJ stdin 214 S[...]*[]*[...][]
itemK v S[...]*[]**[...][] itemK stdin 241 S[...]*[]**[...][]
itemL v S[...]*[]*[...]*[] itemL stdin 268 S[...]*[]*[...]*[]
itemM v S[...]*[]*[...]*[]* itemM stdin 296 S[...]*[]*[...]*[]*
23 changes: 23 additions & 0 deletions tests/tc_pointer_type_printing/file.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
struct S
{
int member;
}

void test()
{
S itemA;
S* itemB;
S[]* itemC;
S[][]* itemD;
S[][]*[] itemE;
S[][]*[][] itemF;
S[][int]*[][] itemG;
S[][]*[int][] itemH;
S[int][]*[int][] itemI;
S[int]*[]*[int][] itemJ;
S[int]*[]**[int][] itemK;
S[int]*[]*[int]*[] itemL;
S[int]*[]*[int]*[]* itemM;

item
}