diff --git a/dsymbol/src/dsymbol/tests.d b/dsymbol/src/dsymbol/tests.d index 0ff9d46..41e47a5 100644 --- a/dsymbol/src/dsymbol/tests.d +++ b/dsymbol/src/dsymbol/tests.d @@ -658,7 +658,11 @@ ScopeSymbolPair generateAutocompleteTreesProd(string source, string filename, si version (linux) { - enum string ufcsExampleCode = + + unittest + { + +enum string ufcsExampleCode = q{class Incrementer { int run(int x) @@ -676,9 +680,6 @@ void doIncrement() life. }}; - unittest - { - import dsymbol.ufcs; writeln("Getting UFCS Symbols For life"); ModuleCache cache; @@ -701,9 +702,19 @@ void doIncrement() life. }}; -unittest + unittest { - import dsymbol.ufcs; + +enum string ufcsTemplateExampleCode = +q{int increment(T)(T x) +{ + return x++; +} +void doIncrement() +{ + int life = 42; + life. +}}; writeln("Getting Templated UFCS Symbols For life"); ModuleCache cache; @@ -715,4 +726,23 @@ unittest } + unittest + { + +enum string ufcsPointerExampleCode = +q{void increment(int* x) { } +void doIncrement(int* x, int* y) +{ + y. +}}; + + writeln("Getting Ptr UFCS completion"); + ModuleCache cache; + // position of variable life + size_t cursorPos = 65; + auto pair = generateAutocompleteTreesProd(ufcsPointerExampleCode, randomDFilename, cursorPos, cache); + assert(pair.ufcsSymbols[0].name == "increment"); + assert(pair.ufcsSymbols[1].name == "doIncrement"); + } + } diff --git a/dsymbol/src/dsymbol/ufcs.d b/dsymbol/src/dsymbol/ufcs.d index 12a49c1..2349e02 100644 --- a/dsymbol/src/dsymbol/ufcs.d +++ b/dsymbol/src/dsymbol/ufcs.d @@ -42,9 +42,9 @@ enum string[string] INTEGER_PROMOTIONS = [ enum MAX_RECURSION_DEPTH = 50; -private DSymbol* deduceSymbolType(DSymbol* symbol) +private const(DSymbol)* deduceSymbolType(const(DSymbol)* symbol) { - DSymbol* symbolType = symbol.type; + const(DSymbol)* symbolType = symbol.type; while (symbolType !is null && (symbolType.qualifier == SymbolQualifier.func || symbolType.kind == CompletionKind.functionName || symbolType.kind == CompletionKind.importSymbol @@ -57,6 +57,7 @@ private DSymbol* deduceSymbolType(DSymbol* symbol) //look at next type to deduce symbolType = symbolType.type; } + return symbolType; } @@ -170,9 +171,6 @@ private void getUFCSSymbols(T, Y)(ref T localAppender, ref Y globalAppender, Sco DSymbol*[] getUFCSSymbolsForCursor(Scope* completionScope, ref const(Token)[] tokens, size_t cursorPosition) { - DSymbol* cursorSymbol; - DSymbol* cursorSymbolType; - TokenCursorResult tokenCursorResult = getCursorToken(tokens, cursorPosition); if (tokenCursorResult.completionContext is CompletionContext.UnknownCompletion) @@ -181,7 +179,7 @@ DSymbol*[] getUFCSSymbolsForCursor(Scope* completionScope, ref const(Token)[] to return []; } - cursorSymbol = completionScope.getFirstSymbolByNameAndCursor( + const(DSymbol)* cursorSymbol = completionScope.getFirstSymbolByNameAndCursor( tokenCursorResult.symbolIdentifierName, cursorPosition); if (cursorSymbol is null) @@ -196,7 +194,7 @@ DSymbol*[] getUFCSSymbolsForCursor(Scope* completionScope, ref const(Token)[] to return []; } - cursorSymbolType = deduceSymbolType(cursorSymbol); + const(DSymbol)* cursorSymbolType = deduceSymbolType(cursorSymbol); if (cursorSymbolType is null) { @@ -220,7 +218,7 @@ DSymbol*[] getUFCSSymbolsForCursor(Scope* completionScope, ref const(Token)[] to } -private DSymbol*[] getUFCSSymbolsForDotCompletion(DSymbol* symbolType, Scope* completionScope, size_t cursorPosition) +private DSymbol*[] getUFCSSymbolsForDotCompletion(const(DSymbol)* symbolType, Scope* completionScope, size_t cursorPosition) { // local appender FilteredAppender!(a => a.isCallableWithArg(symbolType), DSymbol*[]) localAppender; @@ -232,7 +230,7 @@ private DSymbol*[] getUFCSSymbolsForDotCompletion(DSymbol* symbolType, Scope* co return localAppender.data ~ globalAppender.data; } -private DSymbol*[] getUFCSSymbolsForParenCompletion(DSymbol* symbolType, Scope* completionScope, istring searchWord, size_t cursorPosition) +private DSymbol*[] getUFCSSymbolsForParenCompletion(const(DSymbol)* symbolType, Scope* completionScope, istring searchWord, size_t cursorPosition) { // local appender FilteredAppender!(a => a.isCallableWithArg(symbolType) && a.name.among(searchWord), DSymbol*[]) localAppender; @@ -280,7 +278,16 @@ private bool matchAliasThis(const(DSymbol)* beforeDotType, const(DSymbol)* incom } bool isNonConstrainedTemplate(const(DSymbol)* incomingSymbol){ - return incomingSymbol.functionParameters.front.type !is null && incomingSymbol.functionParameters.front.type.kind is CompletionKind.typeTmpParam; + return incomingSymbol.functionParameters.front.type !is null + && incomingSymbol.functionParameters.front.type.kind is CompletionKind.typeTmpParam; +} + +private bool matchesWithTypeOfPointer(const(DSymbol)* incomingSymbol, const(DSymbol)* cursorSymbolType) { + + return incomingSymbol.functionParameters.front.type.qualifier == SymbolQualifier.pointer + && cursorSymbolType.qualifier == SymbolQualifier.pointer + && incomingSymbol.functionParameters.front.type.type is cursorSymbolType.type; + } /** @@ -294,7 +301,7 @@ bool isNonConstrainedTemplate(const(DSymbol)* incomingSymbol){ */ bool isCallableWithArg(const(DSymbol)* incomingSymbol, const(DSymbol)* beforeDotType, bool isGlobalScope = false, int recursionDepth = 0) { - if (incomingSymbol is null + if (incomingSymbol is null || beforeDotType is null || isGlobalScope && incomingSymbol.protection is tok!"private" // don't show private functions if we are in global scope || recursionDepth > MAX_RECURSION_DEPTH) @@ -306,6 +313,7 @@ bool isCallableWithArg(const(DSymbol)* incomingSymbol, const(DSymbol)* beforeDot { return beforeDotType is incomingSymbol.functionParameters.front.type || isNonConstrainedTemplate(incomingSymbol) + || matchesWithTypeOfPointer(incomingSymbol, beforeDotType) || willImplicitBeUpcasted(beforeDotType, incomingSymbol) || matchAliasThis(beforeDotType, incomingSymbol, recursionDepth); diff --git a/tests/tc_ufcs_pointer_type_completion/expected_pointer_test.txt b/tests/tc_ufcs_pointer_type_completion/expected_pointer_test.txt new file mode 100644 index 0000000..1a42406 --- /dev/null +++ b/tests/tc_ufcs_pointer_type_completion/expected_pointer_test.txt @@ -0,0 +1,10 @@ +identifiers +alignof k +doIncrement F +increment F +init k +mangleof k +max k +min k +sizeof k +stringof k diff --git a/tests/tc_ufcs_pointer_type_completion/file.d b/tests/tc_ufcs_pointer_type_completion/file.d new file mode 100644 index 0000000..704846f --- /dev/null +++ b/tests/tc_ufcs_pointer_type_completion/file.d @@ -0,0 +1,5 @@ +void increment(int* x) { } +void doIncrement(int* x, int* y) +{ + y. +} \ No newline at end of file diff --git a/tests/tc_ufcs_pointer_type_completion/run.sh b/tests/tc_ufcs_pointer_type_completion/run.sh new file mode 100755 index 0000000..f0bd959 --- /dev/null +++ b/tests/tc_ufcs_pointer_type_completion/run.sh @@ -0,0 +1,5 @@ +set -e +set -u + +../../bin/dcd-client $1 -c65 file.d > actual_pointer_test.txt +diff actual_pointer_test.txt expected_pointer_test.txt \ No newline at end of file diff --git a/tests/tc_ufcs_struct_completion/fooutils/fooutils.d b/tests/tc_ufcs_struct_completion/fooutils/fooutils.d index cbd0299..65d2b65 100644 --- a/tests/tc_ufcs_struct_completion/fooutils/fooutils.d +++ b/tests/tc_ufcs_struct_completion/fooutils/fooutils.d @@ -10,6 +10,7 @@ struct IntAliased { } void u(Foo foo) {} +void ufcsHelloPtr(Foo* foo) {} void ufcsHello(ref Foo foo) {} void ufcsBar(Foo foo, string mama) {} void ufcsBarRef(ref Foo foo, string mama) {}