Compare commits
21 Commits
v0.16.0-be
...
master
Author | SHA1 | Date |
---|---|---|
|
e48216e4a8 | |
|
d85e752427 | |
|
27b1042959 | |
|
57794ca875 | |
|
15ea4b37b8 | |
|
584b245c8b | |
|
dcffd378e1 | |
|
fe6ce04720 | |
|
25139a8833 | |
|
2bb03265cc | |
|
b79982d509 | |
|
6d635923f7 | |
|
09f4e7e932 | |
|
5244f81367 | |
|
f15ca10acf | |
|
dc11cf704d | |
|
8a693954d3 | |
|
60ccfd520e | |
|
2e84d9d76a | |
|
0dd4c78985 | |
|
70061aee2e |
|
@ -61,7 +61,7 @@ jobs:
|
|||
echo ${{ github.event.number }} > ./pr/NR
|
||||
|
||||
- name: upload comment to high-trust action making the comment
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pr
|
||||
path: pr/
|
||||
|
|
13
README.md
13
README.md
|
@ -314,6 +314,19 @@ Otherwise the client outputs _00000_ so that the length of the answer is guarant
|
|||
45
|
||||
133
|
||||
|
||||
## Inlay Hints
|
||||
|
||||
Build a list of extra annoations for your IDE to display.
|
||||
You must submit the content of the current file displayed in your editor.
|
||||
|
||||
dcd-client --inlayHints
|
||||
|
||||
This is a W.I.P., currently it only provide annoatations about aliases for your variables,
|
||||
more is planned.
|
||||
|
||||
#### Example output
|
||||
|
||||
l ->MyAlias->MyType 42
|
||||
|
||||
# Server
|
||||
|
||||
|
|
|
@ -78,10 +78,12 @@ enum RequestKind : ushort
|
|||
localUse = 0b00000010_00000000,
|
||||
/// Remove import directory from server
|
||||
removeImport = 0b00000100_00000000,
|
||||
/// Get inlay hints
|
||||
inlayHints = 0b00001000_00000000,
|
||||
|
||||
/// These request kinds require source code and won't be executed if there
|
||||
/// is no source sent
|
||||
requiresSourceCode = autocomplete | doc | symbolLocation | search | localUse,
|
||||
requiresSourceCode = autocomplete | doc | symbolLocation | search | localUse | inlayHints,
|
||||
// dfmt on
|
||||
}
|
||||
|
||||
|
@ -260,36 +262,39 @@ AutocompleteResponse getResponse(Socket socket)
|
|||
*/
|
||||
bool serverIsRunning(bool useTCP, string socketFile, ushort port)
|
||||
{
|
||||
scope (failure)
|
||||
return false;
|
||||
AutocompleteRequest request;
|
||||
request.kind = RequestKind.query;
|
||||
Socket socket;
|
||||
scope (exit)
|
||||
{
|
||||
socket.shutdown(SocketShutdown.BOTH);
|
||||
socket.close();
|
||||
}
|
||||
version(Windows) useTCP = true;
|
||||
if (useTCP)
|
||||
{
|
||||
socket = new TcpSocket(AddressFamily.INET);
|
||||
socket.connect(new InternetAddress("localhost", port));
|
||||
}
|
||||
else
|
||||
{
|
||||
version(Windows) {} else
|
||||
try {
|
||||
AutocompleteRequest request;
|
||||
request.kind = RequestKind.query;
|
||||
Socket socket;
|
||||
scope (exit)
|
||||
{
|
||||
socket = new Socket(AddressFamily.UNIX, SocketType.STREAM);
|
||||
socket.connect(new UnixAddress(socketFile));
|
||||
socket.shutdown(SocketShutdown.BOTH);
|
||||
socket.close();
|
||||
}
|
||||
version(Windows) useTCP = true;
|
||||
if (useTCP)
|
||||
{
|
||||
socket = new TcpSocket(AddressFamily.INET);
|
||||
socket.connect(new InternetAddress("localhost", port));
|
||||
}
|
||||
else
|
||||
{
|
||||
version(Windows) {} else
|
||||
{
|
||||
socket = new Socket(AddressFamily.UNIX, SocketType.STREAM);
|
||||
socket.connect(new UnixAddress(socketFile));
|
||||
}
|
||||
}
|
||||
socket.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, dur!"seconds"(5));
|
||||
socket.blocking = true;
|
||||
if (sendRequest(socket, request))
|
||||
return getResponse(socket).completionType == "ack";
|
||||
else
|
||||
return false;
|
||||
}
|
||||
socket.setOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, dur!"seconds"(5));
|
||||
socket.blocking = true;
|
||||
if (sendRequest(socket, request))
|
||||
return getResponse(socket).completionType == "ack";
|
||||
else
|
||||
catch (Exception _) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Escapes \n, \t and \ in the string. If `single` is true \t won't be escaped.
|
||||
|
|
|
@ -27,6 +27,9 @@ version (OSX) version = haveUnixSockets;
|
|||
version (linux) version = haveUnixSockets;
|
||||
version (BSD) version = haveUnixSockets;
|
||||
version (FreeBSD) version = haveUnixSockets;
|
||||
version (OpenBSD) version = haveUnixSockets;
|
||||
version (NetBSD) version = haveUnixSockets;
|
||||
version (DragonflyBSD) version = haveUnixSockets;
|
||||
|
||||
enum DEFAULT_PORT_NUMBER = 9166;
|
||||
|
||||
|
|
|
@ -27,6 +27,15 @@ package istring[24] builtinTypeNames;
|
|||
* or the child type for single index access.
|
||||
*/
|
||||
@("*arr*") istring ARRAY_SYMBOL_NAME;
|
||||
/**
|
||||
* In breadcrumbs this is a single entry meaning that the type following this
|
||||
* started with a dot `.`, so module scope instead of local scope is to be used
|
||||
* for type resolution.
|
||||
*
|
||||
* Note that auto-completion does not rely on this symbol, only type / symbol
|
||||
* lookup relies on this.
|
||||
*/
|
||||
@("*arr*") istring MODULE_SYMBOL_NAME;
|
||||
/**
|
||||
* Type suffix, in breadcrumbs this is a single entry.
|
||||
*
|
||||
|
@ -165,6 +174,20 @@ package istring[24] builtinTypeNames;
|
|||
* class. DSymbol child of the class type, with the baseClass as its child type.
|
||||
*/
|
||||
@("super") istring SUPER_SYMBOL_NAME;
|
||||
/**
|
||||
* This symbol name may appear at the start of breadcrumbs meaning the remaining
|
||||
* breadcrumbs up until the matching $(LREF TYPEOF_END_SYMBOL_NAME) are an
|
||||
* initializer or typeof expression. Pointer/Array suffixes are parsed
|
||||
* beforehand, using popBack to remove them from the breadcrumbs.
|
||||
*
|
||||
* See_Also: $(LREF TYPEOF_END_SYMBOL_NAME)
|
||||
*/
|
||||
@("typeof(") istring TYPEOF_SYMBOL_NAME;
|
||||
/**
|
||||
* This symbol always appears in pairs with TYPEOF_SYMBOL_NAME, designates the
|
||||
* end of the typeof expression in the breadcrumbs.
|
||||
*/
|
||||
@(")/*typeof*/") istring TYPEOF_END_SYMBOL_NAME;
|
||||
|
||||
/**
|
||||
* Breadcrumb part in initializer type generation for literal values in the
|
||||
|
|
|
@ -131,6 +131,7 @@ final class FirstPass : ASTVisitor
|
|||
scope (exit) popSymbol();
|
||||
currentSymbol.acSymbol.protection = protection.current;
|
||||
currentSymbol.acSymbol.doc = makeDocumentation(dec.comment);
|
||||
currentSymbol.acSymbol.qualifier = SymbolQualifier.func;
|
||||
|
||||
istring lastComment = this.lastComment;
|
||||
this.lastComment = istring.init;
|
||||
|
@ -270,7 +271,7 @@ final class FirstPass : ASTVisitor
|
|||
part.identifier.text, CompletionKind.variableName,
|
||||
symbolFile, part.identifier.index);
|
||||
symbol.parent = currentSymbol;
|
||||
populateInitializer(symbol, part.initializer);
|
||||
populateInitializer(symbol.typeLookups, part.initializer);
|
||||
symbol.acSymbol.protection = protection.current;
|
||||
symbol.acSymbol.doc = makeDocumentation(dec.comment);
|
||||
currentSymbol.addChild(symbol, true);
|
||||
|
@ -718,7 +719,7 @@ final class FirstPass : ASTVisitor
|
|||
currentSymbol.addChild(symbol, true);
|
||||
currentScope.addSymbol(symbol.acSymbol, true);
|
||||
if (symbol.typeLookups.empty && feExpression !is null)
|
||||
populateInitializer(symbol, feExpression, true);
|
||||
populateInitializer(symbol.typeLookups, feExpression, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -737,7 +738,7 @@ final class FirstPass : ASTVisitor
|
|||
currentSymbol.addChild(symbol, true);
|
||||
currentScope.addSymbol(symbol.acSymbol, true);
|
||||
if (symbol.typeLookups.empty && ifs.condition !is null && ifs.condition.expression !is null)
|
||||
populateInitializer(symbol, ifs.condition.expression, false);
|
||||
populateInitializer(symbol.typeLookups, ifs.condition.expression, false);
|
||||
}
|
||||
ifs.accept(this);
|
||||
}
|
||||
|
@ -755,7 +756,7 @@ final class FirstPass : ASTVisitor
|
|||
currentScope.startLocation, null);
|
||||
scope(exit) popSymbol();
|
||||
|
||||
populateInitializer(currentSymbol, withStatement.expression, false);
|
||||
populateInitializer(currentSymbol.typeLookups, withStatement.expression, false);
|
||||
withStatement.accept(this);
|
||||
|
||||
}
|
||||
|
@ -908,7 +909,6 @@ private:
|
|||
CompletionKind.functionName, symbolFile, location);
|
||||
symbol.parent = currentSymbol;
|
||||
currentSymbol.addChild(symbol, true);
|
||||
processParameters(symbol, null, THIS_SYMBOL_NAME, parameters, templateParameters);
|
||||
symbol.acSymbol.protection = protection.current;
|
||||
symbol.acSymbol.doc = makeDocumentation(doc);
|
||||
|
||||
|
@ -921,9 +921,16 @@ private:
|
|||
pushFunctionScope(functionBody, location + 4); // 4 == "this".length
|
||||
scope(exit) popScope();
|
||||
currentSymbol = symbol;
|
||||
processParameters(symbol, null, THIS_SYMBOL_NAME, parameters, templateParameters);
|
||||
functionBody.accept(this);
|
||||
currentSymbol = currentSymbol.parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSymbol = symbol;
|
||||
processParameters(symbol, null, THIS_SYMBOL_NAME, parameters, templateParameters);
|
||||
currentSymbol = currentSymbol.parent;
|
||||
}
|
||||
}
|
||||
|
||||
void visitDestructor(size_t location, const FunctionBody functionBody, string doc)
|
||||
|
@ -999,7 +1006,6 @@ private:
|
|||
break;
|
||||
}
|
||||
}
|
||||
currentSymbol.acSymbol.argNames.insert(parameter.acSymbol.name);
|
||||
|
||||
currentSymbol.acSymbol.functionParameters ~= parameter.acSymbol;
|
||||
|
||||
|
@ -1121,12 +1127,20 @@ private:
|
|||
return istring(app.data);
|
||||
}
|
||||
|
||||
void populateInitializer(T)(SemanticSymbol* symbol, const T initializer,
|
||||
bool appendForeach = false)
|
||||
void populateInitializer(T)(ref TypeLookups lookups, const T initializer,
|
||||
bool appendForeach = false, TypeLookup* l = null)
|
||||
{
|
||||
auto lookup = TypeLookupsAllocator.instance.make!TypeLookup(TypeLookupKind.initializer);
|
||||
auto lookup = l ? l : TypeLookupsAllocator.instance.make!TypeLookup(TypeLookupKind.varOrFunType);
|
||||
|
||||
lookup.breadcrumbs.insert(TYPEOF_SYMBOL_NAME);
|
||||
scope visitor = new InitializerVisitor(lookup, appendForeach, this);
|
||||
symbol.typeLookups.insert(lookup);
|
||||
scope (exit)
|
||||
if (!visitor.isCast)
|
||||
lookup.breadcrumbs.insert(TYPEOF_END_SYMBOL_NAME);
|
||||
|
||||
if (l is null)
|
||||
lookups.insert(lookup);
|
||||
|
||||
static if (is(T == typeof(feExpression)))
|
||||
visitor.dynamicDispatch(initializer);
|
||||
else
|
||||
|
@ -1149,22 +1163,20 @@ private:
|
|||
auto lookup = l !is null ? l : TypeLookupsAllocator.instance.make!TypeLookup(
|
||||
TypeLookupKind.varOrFunType);
|
||||
auto t2 = type.type2;
|
||||
if (t2.type !is null)
|
||||
addTypeToLookups(lookups, t2.type, lookup);
|
||||
if (t2.typeofExpression !is null)
|
||||
populateInitializer(lookups, t2.typeofExpression, false, lookup);
|
||||
else if (t2.superOrThis is tok!"this")
|
||||
lookup.breadcrumbs.insert(internString("this"));
|
||||
else if (t2.superOrThis is tok!"super")
|
||||
lookup.breadcrumbs.insert(internString("super"));
|
||||
|
||||
if (t2.type !is null)
|
||||
addTypeToLookups(lookups, t2.type, lookup);
|
||||
else if (t2.builtinType !is tok!"")
|
||||
lookup.breadcrumbs.insert(getBuiltinTypeName(t2.builtinType));
|
||||
else if (t2.typeIdentifierPart !is null)
|
||||
writeIotcTo(t2.typeIdentifierPart, lookup.breadcrumbs);
|
||||
else
|
||||
{
|
||||
// TODO: Add support for typeof expressions
|
||||
// TODO: Add support for __vector
|
||||
// warning("typeof() and __vector are not yet supported");
|
||||
}
|
||||
// TODO: support __vector, traits and mixin
|
||||
|
||||
foreach (suffix; type.typeSuffixes)
|
||||
{
|
||||
|
@ -1346,6 +1358,10 @@ void writeIotcTo(T)(const TypeIdentifierPart tip, ref T output) nothrow
|
|||
{
|
||||
if (!tip.identifierOrTemplateInstance)
|
||||
return;
|
||||
|
||||
if (tip.dot)
|
||||
output.insert(MODULE_SYMBOL_NAME);
|
||||
|
||||
if (tip.identifierOrTemplateInstance.identifier != tok!"")
|
||||
output.insert(internString(tip.identifierOrTemplateInstance.identifier.text));
|
||||
else
|
||||
|
@ -1575,6 +1591,25 @@ class InitializerVisitor : ASTVisitor
|
|||
on = false;
|
||||
}
|
||||
|
||||
override void visit(const CastExpression expression)
|
||||
{
|
||||
if (expression.type)
|
||||
{
|
||||
if (lookup.breadcrumbs.empty || lookup.breadcrumbs.back != TYPEOF_SYMBOL_NAME)
|
||||
return;
|
||||
|
||||
isCast = true;
|
||||
lookup.breadcrumbs.popBack();
|
||||
TypeLookups none;
|
||||
fp.addTypeToLookups(none, expression.type, lookup);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we don't care about non-type casts (e.g. `cast()` or `cast(const)`) yet
|
||||
expression.accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
override void dynamicDispatch(const ExpressionNode expression)
|
||||
{
|
||||
on = true;
|
||||
|
@ -1588,6 +1623,7 @@ class InitializerVisitor : ASTVisitor
|
|||
bool on = false;
|
||||
const bool appendForeach;
|
||||
FirstPass fp;
|
||||
bool isCast;
|
||||
}
|
||||
|
||||
class ArgumentListVisitor : ASTVisitor
|
||||
|
|
|
@ -115,6 +115,7 @@ class AutocompleteParser : Parser
|
|||
{
|
||||
if (!currentIs(tok!"{"))
|
||||
return null;
|
||||
if (cursorPosition == -1) return super.parseBlockStatement();
|
||||
if (current.index > cursorPosition)
|
||||
{
|
||||
BlockStatement bs = allocator.make!(BlockStatement);
|
||||
|
|
|
@ -237,16 +237,40 @@ do
|
|||
}
|
||||
|
||||
// Follow all the names and try to resolve them
|
||||
size_t i = 0;
|
||||
foreach (part; lookup.breadcrumbs[])
|
||||
bool first = true;
|
||||
auto breadcrumbs = lookup.breadcrumbs[];
|
||||
|
||||
while (!breadcrumbs.empty)
|
||||
{
|
||||
if (i == 0)
|
||||
auto part = breadcrumbs.front;
|
||||
breadcrumbs.popFront();
|
||||
scope (exit)
|
||||
first = false;
|
||||
|
||||
if (part == TYPEOF_SYMBOL_NAME)
|
||||
{
|
||||
if (currentSymbol !is null)
|
||||
{
|
||||
warning("Invalid breadcrumbs, found `Type.typeof(...)`");
|
||||
return;
|
||||
}
|
||||
resolveTypeFromInitializer(symbol, lookup, moduleScope, cache,
|
||||
breadcrumbs, currentSymbol);
|
||||
}
|
||||
else if (first)
|
||||
{
|
||||
if (moduleScope is null)
|
||||
getSymbolFromImports(imports, part);
|
||||
else
|
||||
{
|
||||
auto symbols = moduleScope.getSymbolsByNameAndCursor(part, symbol.location);
|
||||
auto symbols = part == MODULE_SYMBOL_NAME
|
||||
? {
|
||||
assert(!breadcrumbs.empty);
|
||||
part = breadcrumbs.front;
|
||||
breadcrumbs.popFront();
|
||||
return moduleScope.getSymbolsByName(part);
|
||||
}()
|
||||
: moduleScope.getSymbolsByNameAndCursor(part, symbol.location);
|
||||
if (symbols.length > 0)
|
||||
currentSymbol = symbols[0];
|
||||
else
|
||||
|
@ -273,7 +297,6 @@ do
|
|||
return;
|
||||
currentSymbol = currentSymbol.getFirstPartNamed(part);
|
||||
}
|
||||
++i;
|
||||
if (currentSymbol is null)
|
||||
return;
|
||||
}
|
||||
|
@ -311,19 +334,32 @@ do
|
|||
}
|
||||
}
|
||||
|
||||
void resolveTypeFromInitializer(DSymbol* symbol, TypeLookup* lookup,
|
||||
Scope* moduleScope, ref ModuleCache cache)
|
||||
private void resolveTypeFromInitializer(R)(DSymbol* symbol, TypeLookup* lookup,
|
||||
Scope* moduleScope, ref ModuleCache cache,
|
||||
ref R breadcrumbs, ref DSymbol* currentSymbol)
|
||||
{
|
||||
if (lookup.breadcrumbs.length == 0)
|
||||
if (breadcrumbs.empty)
|
||||
return;
|
||||
DSymbol* currentSymbol = null;
|
||||
size_t i = 0;
|
||||
|
||||
auto crumbs = lookup.breadcrumbs[];
|
||||
foreach (crumb; crumbs)
|
||||
bool first = true;
|
||||
while (!breadcrumbs.empty)
|
||||
{
|
||||
if (i == 0)
|
||||
auto crumb = breadcrumbs.front;
|
||||
breadcrumbs.popFront();
|
||||
if (crumb == TYPEOF_SYMBOL_NAME)
|
||||
{
|
||||
resolveTypeFromInitializer(symbol, lookup, moduleScope, cache,
|
||||
breadcrumbs, currentSymbol);
|
||||
if (currentSymbol is null)
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
if (crumb == TYPEOF_END_SYMBOL_NAME)
|
||||
break;
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
currentSymbol = moduleScope.getFirstSymbolByNameAndCursor(
|
||||
symbolNameToTypeName(crumb), symbol.location);
|
||||
|
||||
|
@ -348,18 +384,21 @@ void resolveTypeFromInitializer(DSymbol* symbol, TypeLookup* lookup,
|
|||
|| currentSymbol.qualifier == SymbolQualifier.pointer
|
||||
|| currentSymbol.kind == CompletionKind.aliasName)
|
||||
{
|
||||
if (currentSymbol.type !is null)
|
||||
currentSymbol = currentSymbol.type;
|
||||
else
|
||||
return;
|
||||
// may become null, returns later
|
||||
currentSymbol = currentSymbol.type;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto opIndex = currentSymbol.getFirstPartNamed(internString("opIndex"));
|
||||
if (opIndex !is null)
|
||||
{
|
||||
currentSymbol = opIndex.type;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSymbol = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (crumb == "foreach")
|
||||
|
@ -371,19 +410,19 @@ void resolveTypeFromInitializer(DSymbol* symbol, TypeLookup* lookup,
|
|||
|| currentSymbol.qualifier == SymbolQualifier.assocArray)
|
||||
{
|
||||
currentSymbol = currentSymbol.type;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
auto front = currentSymbol.getFirstPartNamed(internString("front"));
|
||||
if (front !is null)
|
||||
{
|
||||
currentSymbol = front.type;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
auto opApply = currentSymbol.getFirstPartNamed(internString("opApply"));
|
||||
if (opApply !is null)
|
||||
{
|
||||
currentSymbol = opApply.type;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -393,13 +432,10 @@ void resolveTypeFromInitializer(DSymbol* symbol, TypeLookup* lookup,
|
|||
return;
|
||||
currentSymbol = currentSymbol.getFirstPartNamed(crumb);
|
||||
}
|
||||
++i;
|
||||
if (currentSymbol is null)
|
||||
return;
|
||||
}
|
||||
typeSwap(currentSymbol);
|
||||
symbol.type = currentSymbol;
|
||||
symbol.ownType = false;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -506,8 +542,6 @@ void resolveType(DSymbol* symbol, ref TypeLookups typeLookups,
|
|||
foreach(lookup; typeLookups) {
|
||||
if (lookup.kind == TypeLookupKind.varOrFunType)
|
||||
resolveTypeFromType(symbol, lookup, moduleScope, cache, null);
|
||||
else if (lookup.kind == TypeLookupKind.initializer)
|
||||
resolveTypeFromInitializer(symbol, lookup, moduleScope, cache);
|
||||
// issue 94
|
||||
else if (lookup.kind == TypeLookupKind.inherit)
|
||||
resolveInheritance(symbol, typeLookups, moduleScope, cache);
|
||||
|
|
|
@ -64,8 +64,6 @@ void checkMissingTypes(SemanticSymbol* currentSymbol, Scope* moduleScope, ref Mo
|
|||
auto lookup = currentSymbol.typeLookups.front;
|
||||
if (lookup.kind == TypeLookupKind.varOrFunType)
|
||||
resolveTypeFromType(currentSymbol.acSymbol, lookup, moduleScope, cache, null);
|
||||
else if (lookup.kind == TypeLookupKind.initializer)
|
||||
resolveTypeFromInitializer(currentSymbol.acSymbol, lookup, moduleScope, cache);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -234,7 +234,7 @@ struct DSymbol
|
|||
|
||||
// pointers are implicitly dereferenced on members (a single layer)
|
||||
if (qualifier == SymbolQualifier.pointer
|
||||
&& this.type.qualifier != SymbolQualifier.pointer)
|
||||
&& (this.type && this.type.qualifier != SymbolQualifier.pointer))
|
||||
return type.getParts!OR(name, app, visited, onlyOne);
|
||||
|
||||
if (name is null)
|
||||
|
@ -386,11 +386,6 @@ struct DSymbol
|
|||
|
||||
// Is alias this symbols
|
||||
DSymbol*[] aliasThisSymbols;
|
||||
/**
|
||||
* Names of function arguments
|
||||
*/
|
||||
// TODO: remove since we have function arguments
|
||||
UnrolledList!(istring) argNames;
|
||||
|
||||
/**
|
||||
* Function parameter symbols
|
||||
|
|
|
@ -10,7 +10,6 @@ enum TypeLookupKind : ubyte
|
|||
{
|
||||
inherit,
|
||||
aliasThis,
|
||||
initializer,
|
||||
mixinTemplate,
|
||||
varOrFunType,
|
||||
selectiveImport,
|
||||
|
|
2
dub.json
2
dub.json
|
@ -8,7 +8,7 @@
|
|||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
":dsymbol": "*",
|
||||
"libdparse": ">=0.23.0 <0.24.0",
|
||||
"libdparse": ">=0.23.0 <0.26.0",
|
||||
":common": "*",
|
||||
"emsi_containers": "~>0.9.0"
|
||||
},
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"versions": {
|
||||
"dsymbol": "0.14.1",
|
||||
"emsi_containers": "0.9.0",
|
||||
"libdparse": "0.23.0",
|
||||
"msgpack-d": "1.0.4",
|
||||
"libdparse": "0.25.0",
|
||||
"msgpack-d": "1.0.5",
|
||||
"stdx-allocator": "2.77.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 86c9bf44c96e1666eb175c749cc26f62c2008979
|
||||
Subproject commit f8a6c28589aae180532fb460a1b22e92a0978292
|
12
makefile
12
makefile
|
@ -13,8 +13,6 @@ LDC := ldc2
|
|||
DPARSE_DIR := libdparse
|
||||
DSYMBOL_DIR := dsymbol
|
||||
|
||||
SHELL:=/bin/bash
|
||||
|
||||
githash:
|
||||
@mkdir -p bin
|
||||
git describe --tags > bin/githash.txt
|
||||
|
@ -37,7 +35,6 @@ CLIENT_SRC := \
|
|||
DMD_CLIENT_FLAGS := -Imsgpack-d/src\
|
||||
-Imsgpack-d/src\
|
||||
-Jbin\
|
||||
-inline\
|
||||
-O\
|
||||
-wi\
|
||||
-ofbin/dcd-client
|
||||
|
@ -56,6 +53,10 @@ LDC_CLIENT_FLAGS := -Imsgpack-d/src\
|
|||
-oq\
|
||||
-of=bin/dcd-client
|
||||
|
||||
ifneq ($(shell uname), OpenBSD)
|
||||
override DMD_CLIENT_FLAGS += -inline
|
||||
endif
|
||||
|
||||
override DMD_CLIENT_FLAGS += $(DFLAGS)
|
||||
override LDC_CLIENT_FLAGS += $(DFLAGS)
|
||||
override GDC_CLIENT_FLAGS += $(DFLAGS)
|
||||
|
@ -76,7 +77,6 @@ DMD_SERVER_FLAGS := -Icontainers/src\
|
|||
-wi\
|
||||
-O\
|
||||
-release\
|
||||
-inline\
|
||||
-ofbin/dcd-server
|
||||
|
||||
DEBUG_SERVER_FLAGS := -Icontainers/src\
|
||||
|
@ -106,6 +106,10 @@ LDC_SERVER_FLAGS := -Icontainers/src\
|
|||
-O5\
|
||||
-release
|
||||
|
||||
ifneq ($(shell uname), OpenBSD)
|
||||
DMD_SERVER_FLAGS += -inline
|
||||
endif
|
||||
|
||||
override DMD_SERVER_FLAGS += $(DFLAGS)
|
||||
override LDC_SERVER_FLAGS += $(DFLAGS)
|
||||
override GDC_SERVER_FLAGS += $(DFLAGS)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 480f3bf9ee80ccf6695ed900cfcc1850ba8da991
|
||||
Subproject commit 26ef07e16023483ad93e3f86374b19d0e541c924
|
|
@ -62,6 +62,7 @@ int runClient(string[] args)
|
|||
bool clearCache;
|
||||
bool symbolLocation;
|
||||
bool doc;
|
||||
bool inlayHints;
|
||||
bool query;
|
||||
bool printVersion;
|
||||
bool listImports;
|
||||
|
@ -86,6 +87,7 @@ int runClient(string[] args)
|
|||
"R", &removedImportPaths, "port|p", &port, "help|h", &help,
|
||||
"shutdown", &shutdown, "clearCache", &clearCache,
|
||||
"symbolLocation|l", &symbolLocation, "doc|d", &doc,
|
||||
"inlayHints", &inlayHints,
|
||||
"query|status|q", &query, "search|s", &search,
|
||||
"version", &printVersion, "listImports", &listImports,
|
||||
"tcp", &useTCP, "socketFile", &socketFile,
|
||||
|
@ -181,7 +183,7 @@ int runClient(string[] args)
|
|||
printImportList(response);
|
||||
return 0;
|
||||
}
|
||||
else if (search == null && cursorPos == size_t.max)
|
||||
else if (search == null && !inlayHints && cursorPos == size_t.max)
|
||||
{
|
||||
// cursor position is a required argument
|
||||
printHelp(args[0]);
|
||||
|
@ -234,6 +236,8 @@ int runClient(string[] args)
|
|||
request.kind |= RequestKind.search;
|
||||
else if(localUse)
|
||||
request.kind |= RequestKind.localUse;
|
||||
else if (inlayHints)
|
||||
request.kind |= RequestKind.inlayHints;
|
||||
else
|
||||
request.kind |= RequestKind.autocomplete;
|
||||
|
||||
|
@ -250,11 +254,13 @@ int runClient(string[] args)
|
|||
else if (getIdentifier)
|
||||
printIdentifierResponse(response);
|
||||
else if (doc)
|
||||
printDocResponse(response);
|
||||
printDocResponse(response, fullOutput);
|
||||
else if (search !is null)
|
||||
printSearchResponse(response);
|
||||
else if (localUse)
|
||||
printLocalUse(response);
|
||||
else if (inlayHints)
|
||||
printInlayHintsResponse(response);
|
||||
else
|
||||
printCompletionResponse(response, fullOutput);
|
||||
|
||||
|
@ -295,6 +301,10 @@ Options:
|
|||
Gets documentation comments associated with the symbol at the cursor
|
||||
location.
|
||||
|
||||
--inlayHints
|
||||
For all supported variable usages, show value types. Currently shows
|
||||
alias definitions.
|
||||
|
||||
--search | -s symbolName
|
||||
Searches for symbolName in both stdin / the given file name as well as
|
||||
others files cached by the server.
|
||||
|
@ -359,10 +369,14 @@ Socket createSocket(string socketFile, ushort port)
|
|||
return socket;
|
||||
}
|
||||
|
||||
void printDocResponse(ref const AutocompleteResponse response)
|
||||
void printDocResponse(ref const AutocompleteResponse response, bool extended)
|
||||
{
|
||||
import std.algorithm : each;
|
||||
response.completions.each!(a => a.documentation.escapeConsoleOutputString(true).writeln);
|
||||
foreach (ref completion; response.completions)
|
||||
{
|
||||
if (extended)
|
||||
writeln(completion.definition);
|
||||
writeln(completion.documentation.escapeConsoleOutputString(true));
|
||||
}
|
||||
}
|
||||
|
||||
void printIdentifierResponse(ref const AutocompleteResponse response)
|
||||
|
@ -380,6 +394,21 @@ void printLocationResponse(ref const AutocompleteResponse response)
|
|||
writeln(makeTabSeparated(response.symbolFilePath, response.symbolLocation.to!string));
|
||||
}
|
||||
|
||||
void printInlayHintsResponse(ref const AutocompleteResponse response)
|
||||
{
|
||||
auto app = appender!(string[])();
|
||||
foreach (ref completion; response.completions)
|
||||
{
|
||||
app.put(makeTabSeparated(
|
||||
completion.kind == char.init ? "" : "" ~ completion.kind,
|
||||
completion.identifier,
|
||||
completion.symbolLocation.to!string
|
||||
));
|
||||
}
|
||||
foreach (line; app.data)
|
||||
writeln(line);
|
||||
}
|
||||
|
||||
void printCompletionResponse(ref const AutocompleteResponse response, bool extended)
|
||||
{
|
||||
if (response.completions.length > 0)
|
||||
|
|
|
@ -361,7 +361,12 @@ CalltipHint getCalltipHint(T)(T beforeTokens, out size_t parenIndex)
|
|||
// evaluate at comma case
|
||||
if (beforeTokens.isComma)
|
||||
{
|
||||
parenIndex = beforeTokens.goBackToOpenParen;
|
||||
size_t tmp = beforeTokens.goBackToOpenParen;
|
||||
if(tmp == size_t.max){
|
||||
return CalltipHint.regularArguments;
|
||||
}
|
||||
parenIndex = tmp;
|
||||
|
||||
// check if we are actually a "!("
|
||||
if (beforeTokens[0 .. parenIndex].isTemplateBangParen)
|
||||
{
|
||||
|
|
|
@ -62,7 +62,7 @@ public AutocompleteResponse getDoc(const AutocompleteRequest request,
|
|||
continue;
|
||||
firstSymbol = false;
|
||||
|
||||
AutocompleteResponse.Completion c;
|
||||
AutocompleteResponse.Completion c = makeSymbolCompletionInfo(symbol, symbol.kind);
|
||||
c.documentation = symbol.doc;
|
||||
response.completions ~= c;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* This file is part of DCD, a development tool for the D programming language.
|
||||
* Copyright (C) 2014 Brian Schott
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
module dcd.server.autocomplete.inlayhints;
|
||||
|
||||
import std.stdio;
|
||||
import std.algorithm;
|
||||
import std.array;
|
||||
import std.experimental.allocator;
|
||||
import std.experimental.logger;
|
||||
import std.typecons;
|
||||
|
||||
import dcd.server.autocomplete.util;
|
||||
|
||||
import dparse.lexer;
|
||||
import dparse.rollback_allocator;
|
||||
|
||||
import dsymbol.modulecache;
|
||||
import dsymbol.symbol;
|
||||
import dsymbol.scope_;
|
||||
import dsymbol.conversion;
|
||||
import dsymbol.string_interning;
|
||||
|
||||
import dcd.common.messages;
|
||||
|
||||
import containers.hashset;
|
||||
|
||||
public AutocompleteResponse getInlayHints(const AutocompleteRequest request,
|
||||
ref ModuleCache moduleCache)
|
||||
{
|
||||
// trace("Getting inlay hints comments");
|
||||
AutocompleteResponse response;
|
||||
|
||||
LexerConfig config;
|
||||
config.fileName = "";
|
||||
auto cache = StringCache(request.sourceCode.length.optimalBucketCount);
|
||||
auto tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode, config, &cache);
|
||||
RollbackAllocator rba;
|
||||
auto pair = generateAutocompleteTrees(tokenArray, &rba, -1, moduleCache);
|
||||
scope(exit) pair.destroy();
|
||||
|
||||
void check(DSymbol* it, ref HashSet!size_t visited)
|
||||
{
|
||||
if (visited.contains(cast(size_t) it))
|
||||
return;
|
||||
if (it.symbolFile != "stdin") return;
|
||||
visited.insert(cast(size_t) it);
|
||||
|
||||
//writeln("sym: ", it.name," ", it.location, " kind: ", it.kind," qualifier: ", it.qualifier);
|
||||
//if (auto type = it.type)
|
||||
//{
|
||||
// writeln(" ", type.name, " kind: ", type.kind, " qualifier", type.qualifier);
|
||||
// if (auto ttype = type.type)
|
||||
// writeln(" ", ttype.name, " kind: ", ttype.kind, " qualifier", ttype.qualifier);
|
||||
//}
|
||||
|
||||
|
||||
// aliases
|
||||
// struct Data {}
|
||||
// alias Alias1 = Data;
|
||||
// Alias1 var; becomes: Alias1 [-> Data] var;
|
||||
if (it.kind == CompletionKind.variableName && it.type && it.type.kind == CompletionKind.aliasName)
|
||||
{
|
||||
AutocompleteResponse.Completion c;
|
||||
c.symbolLocation = it.location - 1;
|
||||
c.kind = CompletionKind.aliasName;
|
||||
|
||||
DSymbol* type = it.type;
|
||||
|
||||
while (type)
|
||||
{
|
||||
if (type.kind == CompletionKind.aliasName && type.type)
|
||||
c.identifier ~= "->" ~ type.type.name;
|
||||
if (type.type && type.type.kind != CompletionKind.aliasName) break;
|
||||
type = type.type;
|
||||
}
|
||||
|
||||
response.completions ~= c;
|
||||
}
|
||||
|
||||
foreach(part; it.opSlice())
|
||||
check(part, visited);
|
||||
}
|
||||
|
||||
HashSet!size_t visited;
|
||||
foreach (symbol; pair.scope_.symbols)
|
||||
{
|
||||
check(symbol, visited);
|
||||
foreach(part; symbol.opSlice())
|
||||
check(part, visited);
|
||||
}
|
||||
|
||||
response.completions.sort!"a.symbolLocation < b.symbolLocation";
|
||||
|
||||
return response;
|
||||
}
|
|
@ -57,12 +57,14 @@ public AutocompleteResponse findLocalUse(AutocompleteRequest request,
|
|||
config.fileName = "";
|
||||
const(Token)[] tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode,
|
||||
config, &cache);
|
||||
auto sortedTokens = assumeSorted(tokenArray);
|
||||
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray,
|
||||
&rba, request.cursorPosition, moduleCache);
|
||||
scope(exit) pair.destroy();
|
||||
|
||||
SymbolStuff getSymbolsAtCursor(size_t cursorPosition)
|
||||
{
|
||||
auto sortedTokens = assumeSorted(tokenArray);
|
||||
auto beforeTokens = sortedTokens.lowerBound(cursorPosition);
|
||||
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray,
|
||||
&rba, request.cursorPosition, moduleCache);
|
||||
auto expression = getExpression(beforeTokens);
|
||||
return SymbolStuff(getSymbolsByTokenChain(pair.scope_, expression,
|
||||
cursorPosition, CompletionType.location), pair.symbol, pair.scope_);
|
||||
|
@ -70,7 +72,6 @@ public AutocompleteResponse findLocalUse(AutocompleteRequest request,
|
|||
|
||||
// gets the symbol matching to cursor pos
|
||||
SymbolStuff stuff = getSymbolsAtCursor(cast(size_t)request.cursorPosition);
|
||||
scope(exit) stuff.destroy();
|
||||
|
||||
// starts searching only if no ambiguity with the symbol
|
||||
if (stuff.symbols.length == 1)
|
||||
|
@ -100,7 +101,6 @@ public AutocompleteResponse findLocalUse(AutocompleteRequest request,
|
|||
{
|
||||
size_t pos = cast(size_t) t.index + 1; // place cursor inside the token
|
||||
SymbolStuff candidate = getSymbolsAtCursor(pos);
|
||||
scope(exit) candidate.destroy();
|
||||
if (candidate.symbols.length == 1 &&
|
||||
candidate.symbols[0].location == sourceSymbol.location &&
|
||||
candidate.symbols[0].symbolFile == sourceSymbol.symbolFile)
|
||||
|
|
|
@ -24,3 +24,4 @@ import dcd.server.autocomplete.complete;
|
|||
import dcd.server.autocomplete.doc;
|
||||
import dcd.server.autocomplete.localuse;
|
||||
import dcd.server.autocomplete.symbols;
|
||||
import dcd.server.autocomplete.inlayhints;
|
||||
|
|
|
@ -147,7 +147,8 @@ SymbolStuff getSymbolsForCompletion(const AutocompleteRequest request,
|
|||
auto expression = getExpression(beforeTokens);
|
||||
auto symbols = getSymbolsByTokenChain(pair.scope_, expression,
|
||||
request.cursorPosition, type);
|
||||
if (symbols.length == 0 && doUFCSSearch(stringToken(beforeTokens.front), stringToken(beforeTokens.back))) {
|
||||
if (symbols.length == 0 && !beforeTokens.empty &&
|
||||
doUFCSSearch(stringToken(beforeTokens.front), stringToken(beforeTokens.back))) {
|
||||
// Let search for UFCS, since we got no hit
|
||||
symbols ~= getSymbolsByTokenChain(pair.scope_, getExpression([beforeTokens.back]), request.cursorPosition, type);
|
||||
}
|
||||
|
|
|
@ -347,6 +347,11 @@ int runServer(string[] args)
|
|||
s.trySendResponse(symbolSearch(request, cache), "Could not perform symbol search");
|
||||
else if (request.kind & RequestKind.localUse)
|
||||
s.trySendResponse(findLocalUse(request, cache), "Couldnot find local usage");
|
||||
else if (request.kind & RequestKind.inlayHints)
|
||||
{
|
||||
info("Getting inlay hints");
|
||||
s.trySendResponse(getInlayHints(request, cache), "Could not get inlay hints");
|
||||
}
|
||||
else if (needResponse)
|
||||
s.trySendResponse(AutocompleteResponse.ack, "Could not send ack");
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ enum CONFIG_FILE_NAME = "dcd.conf";
|
|||
version(linux) version = useXDG;
|
||||
version(BSD) version = useXDG;
|
||||
version(FreeBSD) version = useXDG;
|
||||
version(OpenBSD) version = useXDG;
|
||||
version(NetBSD) version = useXDG;
|
||||
version(DragonflyBSD) version = useXDG;
|
||||
version(OSX) version = useXDG;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ -z "${DC:-}" ]; then
|
||||
DC=dmd
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#! /bin/bash
|
||||
#! /usr/bin/env bash
|
||||
RED="\033[31m"
|
||||
GREEN="\033[32m"
|
||||
YELLOW="\033[33m"
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
identifiers
|
||||
alignof k
|
||||
init k
|
||||
inside_c v
|
||||
mangleof k
|
||||
sizeof k
|
||||
stringof k
|
||||
tupleof k
|
|
@ -0,0 +1,34 @@
|
|||
struct A
|
||||
{
|
||||
struct B
|
||||
{
|
||||
struct C
|
||||
{
|
||||
int inside_c;
|
||||
}
|
||||
int inside_b;
|
||||
}
|
||||
int inside_a;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
auto from_cast = cast(A.B.C) nonExist;
|
||||
from_cast.
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
struct A {}
|
||||
|
||||
auto from_cast = cast(A.B.C) nonExist;
|
||||
from_cast.
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
struct A {}
|
||||
|
||||
auto from_cast = cast(.A.B.C) nonExist;
|
||||
from_cast.
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
set -e
|
||||
set -u
|
||||
|
||||
../../bin/dcd-client $1 file.d -c159 > actual1.txt
|
||||
diff actual1.txt expected1.txt --strip-trailing-cr
|
||||
|
||||
../../bin/dcd-client $1 file.d -c239 > actual2.txt
|
||||
diff actual2.txt expected2.txt --strip-trailing-cr
|
||||
|
||||
../../bin/dcd-client $1 file.d -c320 > actual3.txt
|
||||
diff actual3.txt expected1.txt --strip-trailing-cr
|
|
@ -0,0 +1,3 @@
|
|||
identifiers
|
||||
mangleof k
|
||||
member1 v
|
|
@ -0,0 +1,19 @@
|
|||
struct Foo {
|
||||
this(int mCtor) {}
|
||||
int member1;
|
||||
}
|
||||
|
||||
class Bar {
|
||||
this(int mCtor) {}
|
||||
int member1;
|
||||
}
|
||||
|
||||
unittest {
|
||||
Foo f;
|
||||
f.m
|
||||
}
|
||||
|
||||
unittest {
|
||||
Bar b = new Bar(1);
|
||||
b.m
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
set -e
|
||||
set -u
|
||||
|
||||
../../bin/dcd-client $1 file.d -c122 > actual.txt
|
||||
diff actual.txt expected.txt --strip-trailing-cr
|
||||
|
||||
../../bin/dcd-client $1 file.d -c162 > actual.txt
|
||||
diff actual.txt expected.txt --strip-trailing-cr
|
|
@ -0,0 +1,2 @@
|
|||
l ->Point 208
|
||||
l ->Point 247
|
|
@ -0,0 +1,17 @@
|
|||
// when extending the inlayHints capabilities, don't forget to update the --help
|
||||
// text inside client.d
|
||||
|
||||
import point;
|
||||
import point : P = Point;
|
||||
|
||||
void foo(int x, int y) {}
|
||||
void foo(Point point) {}
|
||||
void bar(P point, int z = 1) {}
|
||||
|
||||
void main()
|
||||
{
|
||||
P p;
|
||||
foo(1, 2);
|
||||
foo(p);
|
||||
bar(p, 3);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
set -e
|
||||
set -u
|
||||
|
||||
../../bin/dcd-client $1 --inlayHints file.d > actual.txt
|
||||
diff actual.txt expected.txt --strip-trailing-cr
|
|
@ -0,0 +1,20 @@
|
|||
identifiers
|
||||
getMember f typeof(member) getMember() stdin 78 Result
|
||||
identifiers
|
||||
staticMember f typeof(S.member) staticMember() stdin 133 Result
|
||||
identifiers
|
||||
alignof k
|
||||
expected v int expected stdin 21 int
|
||||
init k
|
||||
mangleof k
|
||||
sizeof k
|
||||
stringof k
|
||||
tupleof k
|
||||
identifiers
|
||||
alignof k
|
||||
expected v int expected stdin 21 int
|
||||
init k
|
||||
mangleof k
|
||||
sizeof k
|
||||
stringof k
|
||||
tupleof k
|
|
@ -0,0 +1,2 @@
|
|||
identifiers
|
||||
test v Enum test stdin 121 Enum
|
|
@ -0,0 +1,8 @@
|
|||
identifiers
|
||||
alignof k
|
||||
init k
|
||||
mangleof k
|
||||
ok v bool ok stdin 16 bool
|
||||
sizeof k
|
||||
stringof k
|
||||
tupleof k
|
|
@ -0,0 +1,14 @@
|
|||
set -e
|
||||
set -u
|
||||
|
||||
../../bin/dcd-client $1 test1.d -x -c213 > actual1.txt
|
||||
../../bin/dcd-client $1 test1.d -x -c239 >> actual1.txt
|
||||
../../bin/dcd-client $1 test1.d -x -c254 >> actual1.txt
|
||||
../../bin/dcd-client $1 test1.d -x -c265 >> actual1.txt
|
||||
diff actual1.txt expected1.txt --strip-trailing-cr
|
||||
|
||||
../../bin/dcd-client $1 test2.d -x -c132 > actual2.txt
|
||||
diff actual2.txt expected2.txt --strip-trailing-cr
|
||||
|
||||
../../bin/dcd-client $1 test3.d -x -c83 > actual3.txt
|
||||
diff actual3.txt expected3.txt --strip-trailing-cr
|
|
@ -0,0 +1,32 @@
|
|||
struct Result
|
||||
{
|
||||
int expected;
|
||||
}
|
||||
|
||||
struct S
|
||||
{
|
||||
Result member;
|
||||
|
||||
typeof(member) getMember()
|
||||
{
|
||||
return member;
|
||||
}
|
||||
}
|
||||
|
||||
typeof(S.member) staticMember()
|
||||
{
|
||||
return S.init.member;
|
||||
}
|
||||
|
||||
void test()
|
||||
{
|
||||
S s;
|
||||
auto a = S.getMember();
|
||||
auto b = staticMember();
|
||||
{
|
||||
a.
|
||||
}
|
||||
{
|
||||
b.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
struct MyTemplate(T)
|
||||
{
|
||||
enum Enum { a, b }
|
||||
|
||||
T member1;
|
||||
}
|
||||
|
||||
MyTemplate!long global2;
|
||||
|
||||
void main()
|
||||
{
|
||||
typeof(global2).Enum test;
|
||||
test
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
struct S { bool ok; }
|
||||
|
||||
S global3;
|
||||
|
||||
void main()
|
||||
{
|
||||
typeof(global3)[] test;
|
||||
test[0].
|
||||
}
|
||||
|
Loading…
Reference in New Issue