]> granicus.if.org Git - clang/commitdiff
Introduce four new code-completion hooks for C++:
authorDouglas Gregor <dgregor@apple.com>
Fri, 18 Sep 2009 19:03:04 +0000 (19:03 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 18 Sep 2009 19:03:04 +0000 (19:03 +0000)
  - 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
include/clang/Sema/CodeCompleteConsumer.h
lib/Parse/ParseDeclCXX.cpp
lib/Sema/CodeCompleteConsumer.cpp
lib/Sema/Sema.h
lib/Sema/SemaCodeComplete.cpp
test/CodeCompletion/namespace-alias.cpp [new file with mode: 0644]
test/CodeCompletion/namespace.cpp [new file with mode: 0644]
test/CodeCompletion/using-namespace.cpp [new file with mode: 0644]
test/CodeCompletion/using.cpp [new file with mode: 0644]

index 16bd8d15cee4bd2c398890fa39dc87df460e1076..5df0b1f596f4b204d149b00cb2bf0d34cc3c461f 100644 (file)
@@ -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) { }
   //@}
 };
 
index a1d07a0fb39e07b8f780ee303bac3575d657d91a..d8d2bcc311aca4cc80960f8b4806eed855fd35ef 100644 (file)
@@ -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
index 1b82c06bf8f86f04a299e26809134d21c4d936cc..59a6e6281c2e6615524806b9e76bee0c61504df8 100644 (file)
@@ -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);
index 2deaedcf098bb26717396f7bd610da9ee7748996..fd187c5ef9fbda31c6820874850202b37355ffeb 100644 (file)
@@ -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<NamespaceDecl *, NamespaceDecl *> OrigToLatest;
+    for (DeclContext::specific_decl_iterator<NamespaceDecl> 
+           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<NamespaceDecl *, NamespaceDecl *>::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<NamespaceDecl>(ND);
+}
+
+/// \brief Determines whether the given declaration is a namespace or 
+/// namespace alias.
+bool CodeCompleteConsumer::IsNamespaceOrAlias(NamedDecl *ND) const {
+  return isa<NamespaceDecl>(ND) || isa<NamespaceAliasDecl>(ND);
+}
+
 namespace {
   struct VISIBILITY_HIDDEN SortCodeCompleteResult {
     typedef CodeCompleteConsumer::Result Result;
index 2952c843f45110c092ebfee8c3fa8e40103e9739..8bec9d54f5d39afa2fe98d15d4c667617028964f 100644 (file)
@@ -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);
   //@}
   
   //===--------------------------------------------------------------------===//
index bfee4d8b7676f19a012bec37c1696045d447e802..50b8ffd1f49bba3554b56d450b1d42d21ae9caf5 100644 (file)
@@ -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 (file)
index 0000000..8d70c45
--- /dev/null
@@ -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 (file)
index 0000000..db84124
--- /dev/null
@@ -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 (file)
index 0000000..b30b0bc
--- /dev/null
@@ -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 (file)
index 0000000..7bef353
--- /dev/null
@@ -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
+    
+