Skip to content

Commit

Permalink
[clang][Sema] Add CodeCompletionContext::CCC_ObjCClassForwardDecl
Browse files Browse the repository at this point in the history
- Use this new context in Sema to limit completions to seen ObjC class
  names

- Use this new context in clangd to disable include insertions when
  completing ObjC forward decls

Reviewed By: kadircet

Differential Revision: https://reviews.llvm.org/D150978
  • Loading branch information
DavidGoldman committed Jun 27, 2023
1 parent 9fdde69 commit a42ce09
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 8 deletions.
31 changes: 24 additions & 7 deletions clang-tools-extra/clangd/CodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ struct CompletionCandidate {
// Returns a token identifying the overload set this is part of.
// 0 indicates it's not part of any overload set.
size_t overloadSet(const CodeCompleteOptions &Opts, llvm::StringRef FileName,
IncludeInserter *Inserter) const {
IncludeInserter *Inserter,
CodeCompletionContext::Kind CCContextKind) const {
if (!Opts.BundleOverloads.value_or(false))
return 0;

Expand All @@ -223,7 +224,7 @@ struct CompletionCandidate {
// bundle those, so we must resolve the header to be included here.
std::string HeaderForHash;
if (Inserter) {
if (auto Header = headerToInsertIfAllowed(Opts)) {
if (auto Header = headerToInsertIfAllowed(Opts, CCContextKind)) {
if (auto HeaderFile = toHeaderFile(*Header, FileName)) {
if (auto Spelled =
Inserter->calculateIncludePath(*HeaderFile, FileName))
Expand Down Expand Up @@ -271,11 +272,21 @@ struct CompletionCandidate {
return 0;
}

bool contextAllowsHeaderInsertion(CodeCompletionContext::Kind Kind) const {
// Explicitly disable insertions for forward declarations since they don't
// reference the declaration.
if (Kind == CodeCompletionContext::CCC_ObjCClassForwardDecl)
return false;
return true;
}

// The best header to include if include insertion is allowed.
std::optional<llvm::StringRef>
headerToInsertIfAllowed(const CodeCompleteOptions &Opts) const {
headerToInsertIfAllowed(const CodeCompleteOptions &Opts,
CodeCompletionContext::Kind ContextKind) const {
if (Opts.InsertIncludes == CodeCompleteOptions::NeverInsert ||
RankedIncludeHeaders.empty())
RankedIncludeHeaders.empty() ||
!contextAllowsHeaderInsertion(ContextKind))
return std::nullopt;
if (SemaResult && SemaResult->Declaration) {
// Avoid inserting new #include if the declaration is found in the current
Expand Down Expand Up @@ -401,7 +412,8 @@ struct CodeCompletionBuilder {
std::move(*Spelled),
Includes.shouldInsertInclude(*ResolvedDeclaring, *ResolvedInserted));
};
bool ShouldInsert = C.headerToInsertIfAllowed(Opts).has_value();
bool ShouldInsert =
C.headerToInsertIfAllowed(Opts, ContextKind).has_value();
Symbol::IncludeDirective Directive = insertionDirective(Opts);
// Calculate include paths and edits for all possible headers.
for (const auto &Inc : C.RankedIncludeHeaders) {
Expand Down Expand Up @@ -780,6 +792,7 @@ bool contextAllowsIndex(enum CodeCompletionContext::Kind K) {
case CodeCompletionContext::CCC_ObjCInterfaceName:
case CodeCompletionContext::CCC_Symbol:
case CodeCompletionContext::CCC_SymbolOrNewName:
case CodeCompletionContext::CCC_ObjCClassForwardDecl:
return true;
case CodeCompletionContext::CCC_OtherWithMacros:
case CodeCompletionContext::CCC_DotMemberAccess:
Expand Down Expand Up @@ -1422,6 +1435,10 @@ bool includeSymbolFromIndex(CodeCompletionContext::Kind Kind,
else if (Kind == CodeCompletionContext::CCC_ObjCProtocolName)
// Don't show anything else in ObjC protocol completions.
return false;

if (Kind == CodeCompletionContext::CCC_ObjCClassForwardDecl)
return Sym.SymInfo.Kind == index::SymbolKind::Class &&
Sym.SymInfo.Lang == index::SymbolLanguage::ObjC;
return true;
}

Expand Down Expand Up @@ -1832,8 +1849,8 @@ class CodeCompleteFlow {
assert(IdentifierResult);
C.Name = IdentifierResult->Name;
}
if (auto OverloadSet =
C.overloadSet(Opts, FileName, Inserter ? &*Inserter : nullptr)) {
if (auto OverloadSet = C.overloadSet(
Opts, FileName, Inserter ? &*Inserter : nullptr, CCContextKind)) {
auto Ret = BundleLookup.try_emplace(OverloadSet, Bundles.size());
if (Ret.second)
Bundles.emplace_back();
Expand Down
14 changes: 14 additions & 0 deletions clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3434,6 +3434,20 @@ TEST(CompletionTest, ObjectiveCCategoryFromIndexIgnored) {
EXPECT_THAT(Results.Completions, IsEmpty());
}

TEST(CompletionTest, ObjectiveCForwardDeclFromIndex) {
Symbol FoodClass = objcClass("FoodClass");
FoodClass.IncludeHeaders.emplace_back("\"Foo.h\"", 2, Symbol::Import);
Symbol SymFood = objcProtocol("Food");
auto Results = completions("@class Foo^", {SymFood, FoodClass},
/*Opts=*/{}, "Foo.m");

// Should only give class names without any include insertion.
EXPECT_THAT(Results.Completions,
UnorderedElementsAre(AllOf(named("FoodClass"),
kind(CompletionItemKind::Class),
Not(insertInclude()))));
}

TEST(CompletionTest, CursorInSnippets) {
clangd::CodeCompleteOptions Options;
Options.EnableSnippets = true;
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/Sema/CodeCompleteConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,10 @@ class CodeCompletionContext {

/// An unknown context, in which we are recovering from a parsing
/// error and don't know which completions we should give.
CCC_Recovery
CCC_Recovery,

/// Code completion in a @class forward declaration.
CCC_ObjCClassForwardDecl
};

using VisitedContextSet = llvm::SmallPtrSet<DeclContext *, 8>;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -13429,6 +13429,7 @@ class Sema final {
ArrayRef<IdentifierLocPair> Protocols);
void CodeCompleteObjCProtocolDecl(Scope *S);
void CodeCompleteObjCInterfaceDecl(Scope *S);
void CodeCompleteObjCClassForwardDecl(Scope *S);
void CodeCompleteObjCSuperclass(Scope *S,
IdentifierInfo *ClassName,
SourceLocation ClassNameLoc);
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Frontend/ASTUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ static uint64_t getDeclShowContexts(const NamedDecl *ND,
if (ID->getDefinition())
Contexts |= (1LL << CodeCompletionContext::CCC_Expression);
Contexts |= (1LL << CodeCompletionContext::CCC_ObjCInterfaceName);
Contexts |= (1LL << CodeCompletionContext::CCC_ObjCClassForwardDecl);
}

// Deal with tag names.
Expand Down Expand Up @@ -2028,6 +2029,7 @@ static void CalculateHiddenNames(const CodeCompletionContext &Context,
case CodeCompletionContext::CCC_IncludedFile:
case CodeCompletionContext::CCC_Attribute:
case CodeCompletionContext::CCC_NewName:
case CodeCompletionContext::CCC_ObjCClassForwardDecl:
// We're looking for nothing, or we're looking for names that cannot
// be hidden.
return;
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Parse/ParseObjc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) {

while (true) {
MaybeSkipAttributes(tok::objc_class);
if (Tok.is(tok::code_completion)) {
cutOffParsing();
Actions.CodeCompleteObjCClassForwardDecl(getCurScope());
return Actions.ConvertDeclToDeclGroup(nullptr);
}
if (expectIdentifier()) {
SkipUntil(tok::semi);
return Actions.ConvertDeclToDeclGroup(nullptr);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/CodeCompleteConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ bool CodeCompletionContext::wantConstructorResults() const {
case CCC_ObjCCategoryName:
case CCC_IncludedFile:
case CCC_Attribute:
case CCC_ObjCClassForwardDecl:
return false;
}

Expand Down Expand Up @@ -166,6 +167,8 @@ StringRef clang::getCompletionKindString(CodeCompletionContext::Kind Kind) {
return "Attribute";
case CCKind::CCC_Recovery:
return "Recovery";
case CCKind::CCC_ObjCClassForwardDecl:
return "ObjCClassForwardDecl";
}
llvm_unreachable("Invalid CodeCompletionContext::Kind!");
}
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/Sema/SemaCodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8460,6 +8460,24 @@ void Sema::CodeCompleteObjCInterfaceDecl(Scope *S) {
Results.data(), Results.size());
}

void Sema::CodeCompleteObjCClassForwardDecl(Scope *S) {
ResultBuilder Results(*this, CodeCompleter->getAllocator(),
CodeCompleter->getCodeCompletionTUInfo(),
CodeCompletionContext::CCC_ObjCClassForwardDecl);
Results.EnterNewScope();

if (CodeCompleter->includeGlobals()) {
// Add all classes.
AddInterfaceResults(Context.getTranslationUnitDecl(), CurContext, false,
false, Results);
}

Results.ExitScope();

HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
Results.data(), Results.size());
}

void Sema::CodeCompleteObjCSuperclass(Scope *S, IdentifierInfo *ClassName,
SourceLocation ClassNameLoc) {
ResultBuilder Results(*this, CodeCompleter->getAllocator(),
Expand Down
1 change: 1 addition & 0 deletions clang/tools/libclang/CIndexCodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ static unsigned long long getContextsForContextKind(
case CodeCompletionContext::CCC_Other:
case CodeCompletionContext::CCC_ObjCInterface:
case CodeCompletionContext::CCC_ObjCImplementation:
case CodeCompletionContext::CCC_ObjCClassForwardDecl:
case CodeCompletionContext::CCC_NewName:
case CodeCompletionContext::CCC_MacroName:
case CodeCompletionContext::CCC_PreprocessorExpression:
Expand Down

0 comments on commit a42ce09

Please sign in to comment.