From 49f40bd0c9c9de5e74727774fec429b47d36303a Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 18 Sep 2009 19:03:04 +0000 Subject: [PATCH] Introduce four new code-completion hooks for C++: - after "using", show anything that can be a nested-name-specifier. - after "using namespace", show any visible namespaces or namespace aliases - after "namespace", show any namespace definitions in the current scope - after "namespace identifier = ", show any visible namespaces or namespace aliases git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82251 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Action.h | 35 +++++++++++- include/clang/Sema/CodeCompleteConsumer.h | 35 ++++++++++++ lib/Parse/ParseDeclCXX.cpp | 20 +++++++ lib/Sema/CodeCompleteConsumer.cpp | 68 +++++++++++++++++++++++ lib/Sema/Sema.h | 6 +- lib/Sema/SemaCodeComplete.cpp | 30 ++++++++++ test/CodeCompletion/namespace-alias.cpp | 22 ++++++++ test/CodeCompletion/namespace.cpp | 15 +++++ test/CodeCompletion/using-namespace.cpp | 24 ++++++++ test/CodeCompletion/using.cpp | 27 +++++++++ 10 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 test/CodeCompletion/namespace-alias.cpp create mode 100644 test/CodeCompletion/namespace.cpp create mode 100644 test/CodeCompletion/using-namespace.cpp create mode 100644 test/CodeCompletion/using.cpp diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 16bd8d15ce..5df0b1f596 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -2233,10 +2233,43 @@ public: /// /// \param SS the scope specifier ending with "::". /// - /// \parameter EnteringContext whether we're entering the context of this + /// \parame EnteringContext whether we're entering the context of this /// scope specifier. virtual void CodeCompleteQualifiedId(Scope *S, const CXXScopeSpec &SS, bool EnteringContext) { } + + /// \brief Code completion for a C++ "using" declaration or directive. + /// + /// This code completion action is invoked when the code-completion token is + /// found after the "using" keyword. + /// + /// \param S the scope in which the "using" occurs. + virtual void CodeCompleteUsing(Scope *S) { } + + /// \brief Code completion for a C++ using directive. + /// + /// This code completion action is invoked when the code-completion token is + /// found after "using namespace". + /// + /// \param S the scope in which the "using namespace" occurs. + virtual void CodeCompleteUsingDirective(Scope *S) { } + + /// \brief Code completion for a C++ namespace declaration or namespace + /// alias declaration. + /// + /// This code completion action is invoked when the code-completion token is + /// found after "namespace". + /// + /// \param S the scope in which the "namespace" token occurs. + virtual void CodeCompleteNamespaceDecl(Scope *S) { } + + /// \brief Code completion for a C++ namespace alias declaration. + /// + /// This code completion action is invoked when the code-completion token is + /// found after "namespace identifier = ". + /// + /// \param S the scope in which the namespace alias declaration occurs. + virtual void CodeCompleteNamespaceAliasDecl(Scope *S) { } //@} }; diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index a1d07a0fb3..d8d2bcc311 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -187,6 +187,39 @@ public: /// the qualified-id. virtual void CodeCompleteQualifiedId(Scope *S, NestedNameSpecifier *NNS, bool EnteringContext); + + /// \brief Code completion for a C++ "using" declaration or directive. + /// + /// This code completion action is invoked when the code-completion token is + /// found after the "using" keyword. + /// + /// \param S the scope in which the "using" occurs. + virtual void CodeCompleteUsing(Scope *S); + + /// \brief Code completion for a C++ using directive. + /// + /// This code completion action is invoked when the code-completion token is + /// found after "using namespace". + /// + /// \param S the scope in which the "using namespace" occurs. + virtual void CodeCompleteUsingDirective(Scope *S); + + /// \brief Code completion for a C++ namespace declaration or namespace + /// alias declaration. + /// + /// This code completion action is invoked when the code-completion token is + /// found after "namespace". + /// + /// \param S the scope in which the "namespace" token occurs. + virtual void CodeCompleteNamespaceDecl(Scope *S); + + /// \brief Code completion for a C++ namespace alias declaration. + /// + /// This code completion action is invoked when the code-completion token is + /// found after "namespace identifier = ". + /// + /// \param S the scope in which the namespace alias declaration occurs. + virtual void CodeCompleteNamespaceAliasDecl(Scope *S); //@} /// \name Name lookup functions @@ -213,6 +246,8 @@ public: bool IsEnum(NamedDecl *ND) const; bool IsClassOrStruct(NamedDecl *ND) const; bool IsUnion(NamedDecl *ND) const; + bool IsNamespace(NamedDecl *ND) const; + bool IsNamespaceOrAlias(NamedDecl *ND) const; //@} /// \name Utility functions diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 1b82c06bf8..59a6e6281c 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -47,6 +47,11 @@ Parser::DeclPtrTy Parser::ParseNamespace(unsigned Context, assert(Tok.is(tok::kw_namespace) && "Not a namespace!"); SourceLocation NamespaceLoc = ConsumeToken(); // eat the 'namespace'. + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteNamespaceDecl(CurScope); + ConsumeToken(); + } + SourceLocation IdentLoc; IdentifierInfo *Ident = 0; @@ -115,6 +120,11 @@ Parser::DeclPtrTy Parser::ParseNamespaceAlias(SourceLocation NamespaceLoc, ConsumeToken(); // eat the '='. + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteNamespaceAliasDecl(CurScope); + ConsumeToken(); + } + CXXScopeSpec SS; // Parse (optional) nested-name-specifier. ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false); @@ -188,6 +198,11 @@ Parser::DeclPtrTy Parser::ParseUsingDirectiveOrDeclaration(unsigned Context, // Eat 'using'. SourceLocation UsingLoc = ConsumeToken(); + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteUsing(CurScope); + ConsumeToken(); + } + if (Tok.is(tok::kw_namespace)) // Next token after 'using' is 'namespace' so it must be using-directive return ParseUsingDirective(Context, UsingLoc, DeclEnd); @@ -214,6 +229,11 @@ Parser::DeclPtrTy Parser::ParseUsingDirective(unsigned Context, // Eat 'namespace'. SourceLocation NamespcLoc = ConsumeToken(); + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteUsingDirective(CurScope); + ConsumeToken(); + } + CXXScopeSpec SS; // Parse (optional) nested-name-specifier. ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false); diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp index 2deaedcf09..fd187c5ef9 100644 --- a/lib/Sema/CodeCompleteConsumer.cpp +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -118,6 +118,63 @@ CodeCompleteConsumer::CodeCompleteQualifiedId(Scope *S, ProcessCodeCompleteResults(Results.data(), Results.size()); } +void CodeCompleteConsumer::CodeCompleteUsing(Scope *S) { + ResultSet Results(*this, &CodeCompleteConsumer::IsNestedNameSpecifier); + + // If we aren't in class scope, we could see the "namespace" keyword. + if (!S->isClassScope()) + Results.MaybeAddResult(Result("namespace", 0)); + + // After "using", we can see anything that would start a + // nested-name-specifier. + CollectLookupResults(S, 0, Results); + + ProcessCodeCompleteResults(Results.data(), Results.size()); +} + +void CodeCompleteConsumer::CodeCompleteUsingDirective(Scope *S) { + // After "using namespace", we expect to see a namespace name or namespace + // alias. + ResultSet Results(*this, &CodeCompleteConsumer::IsNamespaceOrAlias); + CollectLookupResults(S, 0, Results); + ProcessCodeCompleteResults(Results.data(), Results.size()); +} + +void CodeCompleteConsumer::CodeCompleteNamespaceDecl(Scope *S) { + ResultSet Results(*this, &CodeCompleteConsumer::IsNamespace); + DeclContext *Ctx = (DeclContext *)S->getEntity(); + if (!S->getParent()) + Ctx = getSema().Context.getTranslationUnitDecl(); + + if (Ctx && Ctx->isFileContext()) { + // We only want to see those namespaces that have already been defined + // within this scope, because its likely that the user is creating an + // extended namespace declaration. Keep track of the most recent + // definition of each namespace. + std::map OrigToLatest; + for (DeclContext::specific_decl_iterator + NS(Ctx->decls_begin()), NSEnd(Ctx->decls_end()); + NS != NSEnd; ++NS) + OrigToLatest[NS->getOriginalNamespace()] = *NS; + + // Add the most recent definition (or extended definition) of each + // namespace to the list of results. + for (std::map::iterator + NS = OrigToLatest.begin(), NSEnd = OrigToLatest.end(); + NS != NSEnd; ++NS) + Results.MaybeAddResult(Result(NS->second, 0)); + } + + ProcessCodeCompleteResults(Results.data(), Results.size()); +} + +void CodeCompleteConsumer::CodeCompleteNamespaceAliasDecl(Scope *S) { + // After "namespace", we expect to see a namespace or alias. + ResultSet Results(*this, &CodeCompleteConsumer::IsNamespaceOrAlias); + CollectLookupResults(S, 0, Results); + ProcessCodeCompleteResults(Results.data(), Results.size()); +} + void CodeCompleteConsumer::ResultSet::MaybeAddResult(Result R) { if (R.Kind != Result::RK_Declaration) { // For non-declaration results, just add the result. @@ -454,6 +511,17 @@ bool CodeCompleteConsumer::IsUnion(NamedDecl *ND) const { return false; } +/// \brief Determines whether the given declaration is a namespace. +bool CodeCompleteConsumer::IsNamespace(NamedDecl *ND) const { + return isa(ND); +} + +/// \brief Determines whether the given declaration is a namespace or +/// namespace alias. +bool CodeCompleteConsumer::IsNamespaceOrAlias(NamedDecl *ND) const { + return isa(ND) || isa(ND); +} + namespace { struct VISIBILITY_HIDDEN SortCodeCompleteResult { typedef CodeCompleteConsumer::Result Result; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 2952c843f4..8bec9d54f5 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -3635,11 +3635,13 @@ public: virtual void CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *Base, SourceLocation OpLoc, bool IsArrow); - virtual void CodeCompleteTag(Scope *S, unsigned TagSpec); - virtual void CodeCompleteQualifiedId(Scope *S, const CXXScopeSpec &SS, bool EnteringContext); + virtual void CodeCompleteUsing(Scope *S); + virtual void CodeCompleteUsingDirective(Scope *S); + virtual void CodeCompleteNamespaceDecl(Scope *S); + virtual void CodeCompleteNamespaceAliasDecl(Scope *S); //@} //===--------------------------------------------------------------------===// diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index bfee4d8b76..50b8ffd1f4 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -72,3 +72,33 @@ void Sema::CodeCompleteQualifiedId(Scope *S, const CXXScopeSpec &SS, (NestedNameSpecifier *)SS.getScopeRep(), EnteringContext); } + +void Sema::CodeCompleteUsing(Scope *S) { + if (!CodeCompleter) + return; + + CodeCompleter->CodeCompleteUsing(S); +} + +void Sema::CodeCompleteUsingDirective(Scope *S) { + if (!CodeCompleter) + return; + + CodeCompleter->CodeCompleteUsingDirective(S); +} + +void Sema::CodeCompleteNamespaceDecl(Scope *S) { + if (!CodeCompleter) + return; + + CodeCompleter->CodeCompleteNamespaceDecl(S); +} + +void Sema::CodeCompleteNamespaceAliasDecl(Scope *S) { + if (!CodeCompleter) + return; + + CodeCompleter->CodeCompleteNamespaceAliasDecl(S); +} + + diff --git a/test/CodeCompletion/namespace-alias.cpp b/test/CodeCompletion/namespace-alias.cpp new file mode 100644 index 0000000000..8d70c4517d --- /dev/null +++ b/test/CodeCompletion/namespace-alias.cpp @@ -0,0 +1,22 @@ +// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s && +// RUN: true + +namespace N4 { + namespace N3 { } +} + +class N3; + +namespace N2 { + namespace I1 { } + namespace I4 = I1; + namespace I5 { } + namespace I1 { } + + // CHECK-CC1: I1 : 1 + // CHECK-CC1: I4 : 1 + // CHECK-CC1: I5 : 1 + // CHECK-CC1: N2 : 2 + // CHECK-NEXT-CC1: N4 : 2 + namespace New = + diff --git a/test/CodeCompletion/namespace.cpp b/test/CodeCompletion/namespace.cpp new file mode 100644 index 0000000000..db841248ab --- /dev/null +++ b/test/CodeCompletion/namespace.cpp @@ -0,0 +1,15 @@ +// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s && +// RUN: true + +namespace N3 { +} + +namespace N2 { + namespace I1 { } + namespace I4 = I1; + namespace I5 { } + namespace I1 { } + + // CHECK-CC1: I1 : 0 + // CHECK-NEXT-CC1: I5 : 0 + namespace diff --git a/test/CodeCompletion/using-namespace.cpp b/test/CodeCompletion/using-namespace.cpp new file mode 100644 index 0000000000..b30b0bcfac --- /dev/null +++ b/test/CodeCompletion/using-namespace.cpp @@ -0,0 +1,24 @@ +// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s && +// RUN: true + +namespace N4 { + namespace N3 { } +} + +class N3; + +namespace N2 { + namespace I1 { } + namespace I4 = I1; + namespace I5 { } + namespace I1 { } + + void foo() { + // CHECK-CC1: I1 : 2 + // CHECK-CC1: I4 : 2 + // CHECK-CC1: I5 : 2 + // CHECK-CC1: N2 : 3 + // CHECK-NEXT-CC1: N4 : 3 + using namespace + + diff --git a/test/CodeCompletion/using.cpp b/test/CodeCompletion/using.cpp new file mode 100644 index 0000000000..7bef353459 --- /dev/null +++ b/test/CodeCompletion/using.cpp @@ -0,0 +1,27 @@ +// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s && +// RUN: true + +namespace N4 { + namespace N3 { } +} + +class N3; + +namespace N2 { + namespace I1 { } + namespace I4 = I1; + namespace I5 { } + namespace I1 { } + + void foo() { + int N3; + + // CHECK-CC1: I1 : 2 + // CHECK-CC1: I4 : 2 + // CHECK-CC1: I5 : 2 + // CHECK-CC1: N2 : 3 + // CHECK-CC1: N3 : 3 + // CHECK-NEXT-CC1: N4 : 3 + using + + -- 2.40.0