diff --git a/dsymbol/src/dsymbol/ufcs.d b/dsymbol/src/dsymbol/ufcs.d index 96d4377..73b29c8 100644 --- a/dsymbol/src/dsymbol/ufcs.d +++ b/dsymbol/src/dsymbol/ufcs.d @@ -41,7 +41,7 @@ enum string[string] INTEGER_PROMOTIONS = [ "dchar": "uint", ]; -enum MAX_RECURSION_DEPTH = 50; +enum MAX_NUMBER_OF_MATCHING_RUNS = 50; private const(DSymbol)* deduceSymbolType(const(DSymbol)* symbol) { @@ -257,60 +257,74 @@ private DSymbol*[] getUFCSSymbolsForParenCompletion(const(DSymbol)* symbolType, } -private bool willImplicitBeUpcasted(const(DSymbol)* from, const(DSymbol)* to) +private bool willImplicitBeUpcasted(ref const(DSymbol) incomingSymbolType, ref const(DSymbol) significantSymbolType) { - if (from is null || to is null || to.functionParameters.empty || to.functionParameters.front.type is null) { - return false; - } - - string fromTypeName = from.name.data; - string toTypeName = to.functionParameters.front.type.name.data; + string fromTypeName = significantSymbolType.name.data; + string toTypeName = incomingSymbolType.name.data; return typeWillBeUpcastedTo(fromTypeName, toTypeName); } -private bool typeWillBeUpcastedTo(string from, string to) { +private bool typeWillBeUpcastedTo(string from, string to) +{ if (auto promotionType = from in INTEGER_PROMOTIONS) return *promotionType == to; return false; } -private bool matchAliasThis(const(DSymbol)* beforeDotType, const(DSymbol)* incomingSymbol, int recursionDepth) +bool isNonConstrainedTemplate(ref const(DSymbol) symbolType) { - // For now we are only resolving the first alias this symbol - // when multiple alias this are supported, we can rethink another solution - if (beforeDotType.aliasThisSymbols.empty || beforeDotType.aliasThisSymbols.front == beforeDotType) - { - return false; - } - - //Incrementing depth count to ensure we don't run into an infinite loop - recursionDepth++; - - return isCallableWithArg(incomingSymbol, beforeDotType.aliasThisSymbols.front.type, false, recursionDepth); + return symbolType.kind is CompletionKind.typeTmpParam; } -bool isNonConstrainedTemplate(const(DSymbol)* incomingSymbol){ - return incomingSymbol.functionParameters.front.type !is null - && incomingSymbol.functionParameters.front.type.kind is CompletionKind.typeTmpParam; +private bool matchesWithTypeOfPointer(ref const(DSymbol) incomingSymbolType, ref const(DSymbol) significantSymbolType) +{ + return incomingSymbolType.qualifier == SymbolQualifier.pointer + && significantSymbolType.qualifier == SymbolQualifier.pointer + && incomingSymbolType.type is significantSymbolType.type; } -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; - -} - -private bool matchesWithTypeOfArray(const(DSymbol)* incomingSymbol, const(DSymbol)* cursorSymbolType) { - return incomingSymbol.functionParameters.front.type.qualifier == SymbolQualifier.array +private bool matchesWithTypeOfArray(ref const(DSymbol) incomingSymbolType, ref const(DSymbol) cursorSymbolType) +{ + return incomingSymbolType.qualifier == SymbolQualifier.array && cursorSymbolType.qualifier == SymbolQualifier.array - && incomingSymbol.functionParameters.front.type.type is cursorSymbolType.type; + && incomingSymbolType.type is cursorSymbolType.type; } +private bool typeMatchesWith(ref const(DSymbol) incomingSymbolType, ref const(DSymbol) significantSymbolType) { + return incomingSymbolType is significantSymbolType + || isNonConstrainedTemplate(incomingSymbolType) + || matchesWithTypeOfArray(incomingSymbolType, significantSymbolType) + || matchesWithTypeOfPointer(incomingSymbolType, significantSymbolType) + || willImplicitBeUpcasted(incomingSymbolType, significantSymbolType); +} + +private bool matchSymbolType(const(DSymbol)* incomingSymbolType, const(DSymbol)* significantSymbolType) { + + auto currentSignificantSymbolType = significantSymbolType; + uint numberOfRetries = 0; + + do + { + if (typeMatchesWith(*incomingSymbolType, *currentSignificantSymbolType)){ + return true; + } + + if (currentSignificantSymbolType.aliasThisSymbols.empty || currentSignificantSymbolType is currentSignificantSymbolType.aliasThisSymbols.front){ + return false; + } + + numberOfRetries++; + // For now we are only resolving the first alias this symbol + // when multiple alias this are supported, we can rethink another solution + currentSignificantSymbolType = currentSignificantSymbolType.aliasThisSymbols.front.type; + } + while(numberOfRetries <= MAX_NUMBER_OF_MATCHING_RUNS); + return false; +} + /** * Params: * incomingSymbol = the function symbol to check if it is valid for UFCS with `beforeDotType`. @@ -320,25 +334,18 @@ private bool matchesWithTypeOfArray(const(DSymbol)* incomingSymbol, const(DSymbo * `true` if `incomingSymbols`' first parameter matches `beforeDotType` * `false` otherwise */ -bool isCallableWithArg(const(DSymbol)* incomingSymbol, const(DSymbol)* beforeDotType, bool isGlobalScope = false, int recursionDepth = 0) +bool isCallableWithArg(const(DSymbol)* incomingSymbol, const(DSymbol)* beforeDotType, bool isGlobalScope = false) { 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) + || isGlobalScope && incomingSymbol.protection is tok!"private") // don't show private functions if we are in global scope { return false; } if (incomingSymbol.kind is CompletionKind.functionName && !incomingSymbol.functionParameters.empty && incomingSymbol.functionParameters.front.type) { - return beforeDotType is incomingSymbol.functionParameters.front.type - || isNonConstrainedTemplate(incomingSymbol) - || matchesWithTypeOfArray(incomingSymbol, beforeDotType) - || matchesWithTypeOfPointer(incomingSymbol, beforeDotType) - || willImplicitBeUpcasted(beforeDotType, incomingSymbol) - || matchAliasThis(beforeDotType, incomingSymbol, recursionDepth); - + return matchSymbolType(incomingSymbol.functionParameters.front.type, beforeDotType); } return false; }