1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 19:48:20 +02:00

Added fixit for missing interface implementation methods

This commit is contained in:
Brian Fiete 2020-02-05 17:07:23 -08:00
parent a296eb0f49
commit 45638a6e7e
7 changed files with 431 additions and 174 deletions

View file

@ -1906,6 +1906,176 @@ void BfAutoComplete::CheckNode(BfAstNode* node)
}
}
bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* showString, StringImpl* insertString, bool isExplicitInterface)
{
auto methodDef = methodInst->mMethodDef;
bool isInterface = methodInst->GetOwner()->IsInterface();
if (methodDef->mMethodType == BfMethodType_Normal)
{
StringT<128> methodPrefix;
StringT<128> methodName;
StringT<256> impString;
bool isAbstract = (methodDef->mIsAbstract) && (!isInterface);
if (!isAbstract)
{
if (isInterface)
{
if (!methodInst->mReturnType->IsVoid())
impString += "return default;";
}
else
{
if (!methodInst->mReturnType->IsVoid())
impString = "return ";
impString += "base.";
impString += methodDef->mName;
impString += "(";
}
}
auto methodDeclaration = methodDef->GetMethodDeclaration();
if (isInterface)
{
if (!isExplicitInterface)
methodPrefix += "public ";
}
else if (methodDeclaration->mProtectionSpecifier != NULL)
methodPrefix += methodDeclaration->mProtectionSpecifier->ToString() + " ";
if (!isInterface)
methodPrefix += "override ";
methodPrefix += mModule->TypeToString(methodInst->mReturnType, BfTypeNameFlag_ReduceName);
methodPrefix += " ";
if (isExplicitInterface)
{
methodName += mModule->TypeToString(methodInst->GetOwner(), BfTypeNameFlag_ReduceName);
methodName += ".";
}
methodName += methodDef->mName;
methodName += "(";
for (int paramIdx = 0; paramIdx < (int)methodInst->GetParamCount(); paramIdx++)
{
if (paramIdx > 0)
{
methodName += ", ";
if ((!isAbstract) && (!isInterface))
impString += ", ";
}
methodName += mModule->TypeToString(methodInst->GetParamType(paramIdx), BfTypeNameFlag_ReduceName);
methodName += " ";
methodName += methodDef->mParams[paramIdx]->mName;
if ((!isAbstract) && (!isInterface))
impString += methodDef->mParams[paramIdx]->mName;
}
methodName += ")";
if ((!isAbstract) && (!isInterface))
impString += ");";
if (showString != NULL)
*showString += methodName;
if (insertString != NULL)
{
if (showString == insertString)
*insertString += "\t";
*insertString += methodPrefix + methodName + "\t" + impString;
}
return true;
}
else if ((methodDef->mMethodType == BfMethodType_PropertyGetter) || (methodDef->mMethodType == BfMethodType_PropertySetter))
{
auto propDeclaration = methodDef->GetPropertyDeclaration();
bool hasGet = propDeclaration->GetMethod("get") != NULL;
bool hasSet = propDeclaration->GetMethod("set") != NULL;
if ((methodDef->mMethodType == BfMethodType_PropertyGetter) || (!hasGet))
{
StringT<128> propName;
StringT<256> impl;
propDeclaration->mNameNode->ToString(propName);
bool isAbstract = methodDef->mIsAbstract;
if (propDeclaration->mProtectionSpecifier != NULL)
impl += propDeclaration->mProtectionSpecifier->ToString() + " ";
if (!isInterface)
impl += "override ";
BfType* propType = methodInst->mReturnType;
if (methodDef->mMethodType == BfMethodType_PropertySetter)
propType = methodInst->GetParamType(0);
impl += mModule->TypeToString(propType, BfTypeNameFlag_ReduceName);
impl += " ";
if (isExplicitInterface)
{
impl += mModule->TypeToString(methodInst->GetOwner(), BfTypeNameFlag_ReduceName);
impl += ".";
}
impl += propName;
impl += "\t";
if (hasGet)
{
impl += "get\t";
if (!isAbstract)
{
if (isInterface)
{
impl += "return default;";
}
else
{
impl += "return base.";
impl += propName;
impl += ";";
}
}
impl += "\b";
}
if (hasSet)
{
if (hasGet)
impl += "\r\r";
impl += "set\t";
if (!isAbstract)
{
if (!isInterface)
{
impl += "base.";
impl += propName;
impl += " = value;";
}
}
impl += "\b";
}
if (showString != NULL)
*showString += propName;
if (insertString != NULL)
{
if (showString == insertString)
*insertString += "\t";
*insertString += impl;
}
return true;
}
}
return false;
}
void BfAutoComplete::AddOverrides(const StringImpl& filter)
{
if (!mIsAutoComplete)
@ -1941,106 +2111,14 @@ void BfAutoComplete::AddOverrides(const StringImpl& filter)
}
auto methodInst = methodGroup.mDefault;
if ((methodDef->mIsVirtual) && (!methodDef->mIsOverride))
{
if (methodDef->mMethodType == BfMethodType_Normal)
{
String methodPrefix;
String methodName;
String impString;
bool isAbstract = methodDef->mIsAbstract;
if (!isAbstract)
{
if (!methodInst->mReturnType->IsVoid())
impString = "return ";
impString += "base.";
impString += methodDef->mName;
impString += "(";
}
auto methodDeclaration = methodDef->GetMethodDeclaration();
if (methodDeclaration->mProtectionSpecifier != NULL)
methodPrefix += methodDeclaration->mProtectionSpecifier->ToString() + " ";
methodPrefix += "override ";
methodPrefix += mModule->TypeToString(methodInst->mReturnType, BfTypeNameFlag_ReduceName);
methodPrefix += " ";
methodName += methodDef->mName;
methodName += "(";
for (int paramIdx = 0; paramIdx < (int)methodInst->GetParamCount(); paramIdx++)
{
if (paramIdx > 0)
{
methodName += ", ";
if (!isAbstract)
impString += ", ";
}
methodName += mModule->TypeToString(methodInst->GetParamType(paramIdx), BfTypeNameFlag_ReduceName);
methodName += " ";
methodName += methodDef->mParams[paramIdx]->mName;
if (!isAbstract)
impString += methodDef->mParams[paramIdx]->mName;
}
methodName += ")";
if (!isAbstract)
impString += ");";
AddEntry(AutoCompleteEntry("override", methodName + "\t" + methodPrefix + methodName + "\t" + impString, NULL), filter);
}
else if ((methodDef->mMethodType == BfMethodType_PropertyGetter) || (methodDef->mMethodType == BfMethodType_PropertySetter))
{
auto propDeclaration = methodDef->GetPropertyDeclaration();
bool hasGet = propDeclaration->GetMethod("get") != NULL;
bool hasSet = propDeclaration->GetMethod("set") != NULL;
if ((methodDef->mMethodType == BfMethodType_PropertyGetter) || (!hasGet))
{
String propName;
String impl;
propDeclaration->mNameNode->ToString(propName);
bool isAbstract = methodDef->mIsAbstract;
if (propDeclaration->mProtectionSpecifier != NULL)
impl += propDeclaration->mProtectionSpecifier->ToString() + " ";
impl += "override ";
impl += mModule->TypeToString(methodInst->mReturnType, BfTypeNameFlag_ReduceName);
impl += " ";
impl += propName;
impl += "\t";
if (hasGet)
{
impl += "get\t";
if (!isAbstract)
{
impl += "return base.";
impl += propName;
impl += ";";
}
}
if (hasSet)
{
if (hasGet)
impl += "\b\r";
impl += "set\t";
if (!isAbstract)
{
impl += "base.";
impl += propName;
impl += " = value;";
}
}
AddEntry(AutoCompleteEntry("override", propName + "\t" + impl, NULL), filter);
}
}
}
if ((!methodDef->mIsVirtual) || (methodDef->mIsOverride) || (methodDef->mMethodType != BfMethodType_Normal))
continue;
StringT<512> insertString;
GetMethodInfo(methodInst, &insertString, &insertString, false);
if (insertString.IsEmpty())
continue;
AddEntry(AutoCompleteEntry("override", insertString, NULL), filter);
}
if (curType->IsStruct())
@ -2397,6 +2475,127 @@ bool BfAutoComplete::CheckFixit(BfAstNode* node)
return true;
}
void BfAutoComplete::ChcekInterfaceFixit(BfTypeInstance* typeInstance, BfAstNode* node)
{
if (!CheckFixit(node))
return;
if (typeInstance == NULL)
return;
for (auto& ifaceTypeInst : typeInstance->mInterfaces)
{
Array<BfMethodInstance*> missingMethods;
auto ifaceInst = ifaceTypeInst.mInterfaceType;
int startIdx = ifaceTypeInst.mStartInterfaceTableIdx;
int iMethodCount = (int)ifaceInst->mMethodInstanceGroups.size();
auto declTypeDef = ifaceTypeInst.mDeclaringType;
for (int iMethodIdx = 0; iMethodIdx < iMethodCount; iMethodIdx++)
{
auto matchedMethodRef = &typeInstance->mInterfaceMethodTable[iMethodIdx + startIdx].mMethodRef;
BfMethodInstance* matchedMethod = *matchedMethodRef;
auto ifaceMethodInst = ifaceInst->mMethodInstanceGroups[iMethodIdx].mDefault;
if (ifaceMethodInst == NULL)
continue;
// Don't even try to match generics
if (!ifaceMethodInst->mMethodDef->mGenericParams.IsEmpty())
continue;
auto iReturnType = ifaceMethodInst->mReturnType;
if (iReturnType->IsSelf())
iReturnType = typeInstance;
if (ifaceMethodInst->mMethodDef->mIsOverride)
continue; // Don't consider overrides here
// If we have "ProjA depends on LibBase", "ProjB depends on LibBase", then a type ClassC in LibBase implementing IFaceD,
// where IFaceD gets extended with MethodE in ProjA, an implementing MethodE is still required to exist on ClassC --
// the visibility is bidirectional. A type ClassF implementing IFaceD inside ProjB will not be required to implement
// MethodE, however
if ((!ifaceInst->IsTypeMemberAccessible(ifaceMethodInst->mMethodDef->mDeclaringType, ifaceTypeInst.mDeclaringType)) &&
(!ifaceInst->IsTypeMemberAccessible(ifaceTypeInst.mDeclaringType, ifaceMethodInst->mMethodDef->mDeclaringType)))
continue;
if (!ifaceInst->IsTypeMemberIncluded(ifaceMethodInst->mMethodDef->mDeclaringType, ifaceTypeInst.mDeclaringType))
continue;
bool hadMatch = matchedMethod != NULL;
bool hadPubFailure = false;
bool hadMutFailure = false;
if (!hadMatch)
missingMethods.Add(ifaceMethodInst);
}
if (!missingMethods.IsEmpty())
{
BfTypeDeclaration* typeDecl = declTypeDef->mTypeDeclaration;
BfTokenNode* openNode = NULL;
BfTokenNode* closeNode = NULL;
if (auto blockNode = BfNodeDynCast<BfBlock>(typeDecl->mDefineNode))
{
openNode = blockNode->mOpenBrace;
closeNode = blockNode->mCloseBrace;
}
BfParserData* parser = declTypeDef->mTypeDeclaration->GetSourceData()->ToParserData();
if ((parser != NULL) && (closeNode != NULL))
{
int startPos = openNode->mSrcStart + 1;
int insertPos = closeNode->mSrcStart;
while (insertPos > startPos)
{
char prevC = parser->mSrc[insertPos - 1];
if (prevC == '\n')
break;
insertPos--;
}
if (insertPos > startPos)
insertPos--;
bool wantsBreak = false;
String insertStr = "\f";
for (auto methodInst : missingMethods)
{
if (wantsBreak)
{
insertStr += "\r\r";
wantsBreak = false;
}
if (GetMethodInfo(methodInst, NULL, &insertStr, false))
{
insertStr += "\b";
wantsBreak = true;
}
}
wantsBreak = false;
String explicitInsertStr = "\f";
for (auto methodInst : missingMethods)
{
if (wantsBreak)
{
explicitInsertStr += "\r\r";
wantsBreak = false;
}
if (GetMethodInfo(methodInst, NULL, &explicitInsertStr, true))
{
explicitInsertStr += "\b";
wantsBreak = true;
}
}
mCompiler->mResolvePassData->mAutoComplete->AddEntry(AutoCompleteEntry("fixit", StrFormat("Implement interface '%s'\tusing|%s|%d|%s",
mModule->TypeToString(ifaceInst).c_str(), parser->mFileName.c_str(), insertPos, insertStr.c_str()).c_str()));
mCompiler->mResolvePassData->mAutoComplete->AddEntry(AutoCompleteEntry("fixit", StrFormat("Implement interface '%s' explicitly\tusing|%s|%d|%s",
mModule->TypeToString(ifaceInst).c_str(), parser->mFileName.c_str(), insertPos, explicitInsertStr.c_str()).c_str()));
}
}
}
}
void BfAutoComplete::FixitAddMember(BfTypeInstance* typeInst, BfType* fieldType, const StringImpl& fieldName, bool isStatic, BfTypeInstance* referencedFrom)
{
if (typeInst == mModule->mContext->mBfObjectType)