[code-completion] Suggest module names in type and expression position

They can be used to qualify both.
This commit is contained in:
Nathan Hawes
2019-10-30 11:17:33 -07:00
parent 7350917c22
commit b871454b8f
6 changed files with 148 additions and 55 deletions

View File

@@ -1317,8 +1317,21 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
/// \returns true on success, false on failure.
bool typecheckParsedType() {
assert(ParsedTypeLoc.getTypeRepr() && "should have a TypeRepr");
return !performTypeLocChecking(P.Context, ParsedTypeLoc,
CurDeclContext, false);
if (!performTypeLocChecking(P.Context, ParsedTypeLoc,
CurDeclContext, false))
return true;
// It doesn't type check as a type, so see if it's a qualifying module name.
if (auto *ITR = dyn_cast<IdentTypeRepr>(ParsedTypeLoc.getTypeRepr())) {
SmallVector<ImportDecl::AccessPathElement, 4> AccessPath;
for (auto Component : ITR->getComponentRange())
AccessPath.push_back({ Component->getIdentifier(),
Component->getIdLoc() });
if (auto Module = Context.getLoadedModule(AccessPath))
ParsedTypeLoc.setType(ModuleType::get(Module));
return true;
}
return false;
}
public:
@@ -1583,26 +1596,37 @@ public:
bool OnlyTypes;
bool OnlyPrecedenceGroups;
bool NeedLeadingDot;
bool IncludeModuleQualifier;
static RequestedResultsTy fromModule(const ModuleDecl *TheModule) {
return { TheModule, false, false, false };
return { TheModule, false, false, false, true };
}
RequestedResultsTy onlyTypes() const {
return { TheModule, true, false, NeedLeadingDot };
return { TheModule, true, false, NeedLeadingDot, IncludeModuleQualifier };
}
RequestedResultsTy onlyPrecedenceGroups() const {
assert(!OnlyTypes && "onlyTypes() already includes precedence groups");
return { TheModule, false, true, false };
return { TheModule, false, true, false, true };
}
RequestedResultsTy needLeadingDot(bool NeedDot) const {
return { TheModule, OnlyTypes, OnlyPrecedenceGroups, NeedDot };
return {
TheModule, OnlyTypes, OnlyPrecedenceGroups, NeedDot,
IncludeModuleQualifier
};
}
RequestedResultsTy withModuleQualifier(bool IncludeModule) const {
return {
TheModule, OnlyTypes, OnlyPrecedenceGroups, NeedLeadingDot,
IncludeModule
};
}
static RequestedResultsTy toplevelResults() {
return { nullptr, false, false, false };
return { nullptr, false, false, false, true };
}
};
@@ -1741,6 +1765,21 @@ public:
}
}
void addModuleName(
const ModuleDecl *MD,
Optional<CodeCompletionResult::NotRecommendedReason> R = None) {
CodeCompletionResultBuilder Builder(
Sink,
CodeCompletionResult::ResultKind::Declaration,
SemanticContextKind::OtherModule,
expectedTypeContext);
Builder.setAssociatedDecl(MD);
Builder.addTextChunk(MD->getNameStr());
Builder.addTypeAnnotation("Module");
if (R)
Builder.setNotRecommended(*R);
}
void addImportModuleNames() {
SmallVector<Identifier, 0> ModuleNames;
Ctx.getVisibleTopLevelModuleNames(ModuleNames);
@@ -1757,19 +1796,13 @@ public:
continue;
auto MD = ModuleDecl::create(ModuleName, Ctx);
CodeCompletionResultBuilder Builder(
Sink,
CodeCompletionResult::ResultKind::Declaration,
SemanticContextKind::OtherModule,
expectedTypeContext);
Builder.setAssociatedDecl(MD);
Builder.addTextChunk(MD->getNameStr());
Builder.addTypeAnnotation("Module");
Optional<CodeCompletionResult::NotRecommendedReason> Reason = None;
// Imported modules are not recommended.
if (ImportedModules.count(MD->getNameStr()) != 0)
Builder.setNotRecommended(
CodeCompletionResult::NotRecommendedReason::Redundant);
Reason = CodeCompletionResult::NotRecommendedReason::Redundant;
addModuleName(MD, Reason);
}
}
@@ -3199,13 +3232,17 @@ public:
return false;
}
bool tryModuleCompletions(Type ExprType) {
bool tryModuleCompletions(Type ExprType, bool TypesOnly = false) {
if (auto MT = ExprType->getAs<ModuleType>()) {
ModuleDecl *M = MT->getModule();
if (CurrModule != M) {
// Only use the cache if it is not the current module.
RequestedCachedResults.push_back(
RequestedResultsTy::fromModule(M).needLeadingDot(needDot()));
RequestedResultsTy Request = RequestedResultsTy::fromModule(M)
.needLeadingDot(needDot())
.withModuleQualifier(false);
if (TypesOnly)
Request = Request.onlyTypes();
RequestedCachedResults.push_back(Request);
return true;
}
}
@@ -3763,17 +3800,16 @@ public:
void getValueCompletionsInDeclContext(SourceLoc Loc,
DeclFilter Filter = DefaultFilter,
bool IncludeTopLevel = false,
bool RequestCache = true,
bool LiteralCompletions = true) {
bool LiteralCompletions = true,
bool ModuleQualifier = true) {
ExprType = Type();
Kind = LookupKind::ValueInDeclContext;
NeedLeadingDot = false;
FilteredDeclConsumer Consumer(*this, Filter);
lookupVisibleDecls(Consumer, CurrDeclContext,
/*IncludeTopLevel=*/IncludeTopLevel, Loc);
if (RequestCache)
RequestedCachedResults.push_back(RequestedResultsTy::toplevelResults());
/*IncludeTopLevel=*/false, Loc);
RequestedCachedResults.push_back(RequestedResultsTy::toplevelResults()
.withModuleQualifier(ModuleQualifier));
// Manually add any expected nominal types from imported modules so that
// they get their expected type relation. Don't include protocols, since
@@ -3871,6 +3907,8 @@ public:
}
void getTypeCompletions(Type BaseType) {
if (tryModuleCompletions(BaseType, /*OnlyTypes=*/true))
return;
Kind = LookupKind::Type;
this->BaseType = BaseType;
NeedLeadingDot = !HaveDot;
@@ -3880,7 +3918,7 @@ public:
if (BaseType->isAnyExistentialType()) {
addKeyword("Protocol", MetatypeType::get(BaseType));
addKeyword("Type", ExistentialMetatypeType::get(BaseType));
} else {
} else if (!BaseType->is<ModuleType>()) {
addKeyword("Type", MetatypeType::get(BaseType));
}
}
@@ -3958,8 +3996,9 @@ public:
if (Module == CurrModule)
continue;
RequestedCachedResults.push_back(
RequestedResultsTy::fromModule(Module).onlyPrecedenceGroups());
RequestedCachedResults.push_back(RequestedResultsTy::fromModule(Module)
.onlyPrecedenceGroups()
.withModuleQualifier(false));
}
}
@@ -3996,13 +4035,16 @@ public:
getAttributeDeclParamCompletions(DAK_Available, 0);
}
void getTypeCompletionsInDeclContext(SourceLoc Loc) {
void getTypeCompletionsInDeclContext(SourceLoc Loc,
bool ModuleQualifier = true) {
Kind = LookupKind::TypeInDeclContext;
lookupVisibleDecls(*this, CurrDeclContext,
/*IncludeTopLevel=*/false, Loc);
RequestedCachedResults.push_back(
RequestedResultsTy::toplevelResults().onlyTypes());
RequestedResultsTy::toplevelResults()
.onlyTypes()
.withModuleQualifier(ModuleQualifier));
}
void getToplevelCompletions(bool OnlyTypes) {
@@ -5116,20 +5158,8 @@ void CodeCompletionCallbacksImpl::doneParsing() {
return;
}
if (!ParsedTypeLoc.isNull() && !typecheckParsedType()) {
// If we think we parsed a type but it doesn't type check as a type, see if
// it's a qualifying module name and recover if so.
if (auto *ITR = dyn_cast<IdentTypeRepr>(ParsedTypeLoc.getTypeRepr())) {
SmallVector<ImportDecl::AccessPathElement, 4> AccessPath;
for (auto Component : ITR->getComponentRange())
AccessPath.push_back({ Component->getIdentifier(),
Component->getIdLoc() });
if (auto Module = Context.getLoadedModule(AccessPath))
ParsedTypeLoc.setType(ModuleType::get(Module));
}
if (ParsedTypeLoc.isNull())
return;
}
if (!ParsedTypeLoc.isNull() && !typecheckParsedType())
return;
CompletionLookup Lookup(CompletionContext.getResultSink(), P.Context,
CurDeclContext, &CompletionContext);
@@ -5281,7 +5311,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
} else {
SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc();
Lookup.getValueCompletionsInDeclContext(Loc, KeyPathFilter,
false, true, false);
/*LiteralCompletions=*/false);
}
break;
}
@@ -5516,6 +5546,8 @@ void CodeCompletionCallbacksImpl::doneParsing() {
return; // already handled.
RequestedModules.push_back({std::move(K), TheModule,
Request.OnlyTypes, Request.OnlyPrecedenceGroups});
if (Request.IncludeModuleQualifier)
Lookup.addModuleName(TheModule);
}
};
@@ -5528,6 +5560,10 @@ void CodeCompletionCallbacksImpl::doneParsing() {
// Add results from current module.
Lookup.getToplevelCompletions(Request.OnlyTypes);
// Add the qualifying module name
if (Request.IncludeModuleQualifier)
Lookup.addModuleName(CurDeclContext->getParentModule());
// Add results for all imported modules.
ModuleDecl::ImportFilter ImportFilter;
ImportFilter |= ModuleDecl::ImportFilterKind::Public;