]> granicus.if.org Git - clang/commitdiff
Initial implementation of a code-completion interface in Clang. In
authorDouglas Gregor <dgregor@apple.com>
Thu, 17 Sep 2009 21:32:03 +0000 (21:32 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 17 Sep 2009 21:32:03 +0000 (21:32 +0000)
essence, code completion is triggered by a magic "code completion"
token produced by the lexer [*], which the parser recognizes at
certain points in the grammar. The parser then calls into the Action
object with the appropriate CodeCompletionXXX action.

Sema implements the CodeCompletionXXX callbacks by performing minimal
translation, then forwarding them to a CodeCompletionConsumer
subclass, which uses the results of semantic analysis to provide
code-completion results. At present, only a single, "printing" code
completion consumer is available, for regression testing and
debugging. However, the design is meant to permit other
code-completion consumers.

This initial commit contains two code-completion actions: one for
member access, e.g., "x." or "p->", and one for
nested-name-specifiers, e.g., "std::". More code-completion actions
will follow, along with improved gathering of code-completion results
for the various contexts.

[*] In the current -code-completion-dump testing/debugging mode, the
file is truncated at the completion point and EOF is translated into
"code completion".

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82166 91177308-0d34-0410-b5e6-96231b3b80d8

23 files changed:
include/clang/Basic/Diagnostic.h
include/clang/Basic/TokenKinds.def
include/clang/Lex/Lexer.h
include/clang/Lex/Preprocessor.h
include/clang/Parse/Action.h
include/clang/Sema/CodeCompleteConsumer.h [new file with mode: 0644]
include/clang/Sema/ParseAST.h
lib/Basic/Diagnostic.cpp
lib/Lex/Lexer.cpp
lib/Lex/Preprocessor.cpp
lib/Parse/ParseExpr.cpp
lib/Parse/ParseExprCXX.cpp
lib/Sema/CMakeLists.txt
lib/Sema/CodeCompleteConsumer.cpp [new file with mode: 0644]
lib/Sema/ParseAST.cpp
lib/Sema/Sema.cpp
lib/Sema/Sema.h
lib/Sema/SemaCodeComplete.cpp [new file with mode: 0644]
test/CMakeLists.txt
test/CodeCompletion/member-access.c [new file with mode: 0644]
test/CodeCompletion/member-access.cpp [new file with mode: 0644]
test/CodeCompletion/nested-name-specifier.cpp [new file with mode: 0644]
tools/clang-cc/clang-cc.cpp

index dc854b18302e2f3fd299761270fdb985a2a27b23..518c97b5c44c8c4e67facb957b9f54994932c13d 100644 (file)
@@ -166,6 +166,7 @@ private:
   bool IgnoreAllWarnings;        // Ignore all warnings: -w
   bool WarningsAsErrors;         // Treat warnings like errors:
   bool SuppressSystemWarnings;   // Suppress warnings in system headers.
+  bool SuppressAllDiagnostics;   // Suppress all diagnostics.
   ExtensionHandling ExtBehavior; // Map extensions onto warnings or errors?
   DiagnosticClient *Client;
 
@@ -245,6 +246,14 @@ public:
   void setSuppressSystemWarnings(bool Val) { SuppressSystemWarnings = Val; }
   bool getSuppressSystemWarnings() const { return SuppressSystemWarnings; }
 
+  /// \brief Suppress all diagnostics, to silence the front end when we 
+  /// know that we don't want any more diagnostics to be passed along to the
+  /// client
+  void setSuppressAllDiagnostics(bool Val = true) { 
+    SuppressAllDiagnostics = Val; 
+  }
+  bool getSuppressAllDiagnostics() const { return SuppressAllDiagnostics; }
+  
   /// setExtensionHandlingBehavior - This controls whether otherwise-unmapped
   /// extension diagnostics are mapped onto ignore/warning/error.  This
   /// corresponds to the GCC -pedantic and -pedantic-errors option.
index e711996cf26dadcf30f9564bad148ad86ef6e6c7..239712c08caff49fe69f5ce43a977ccf9421fece 100644 (file)
@@ -92,6 +92,7 @@ PPKEYWORD(unassert)
 TOK(unknown)             // Not a token.
 TOK(eof)                 // End of file.
 TOK(eom)                 // End of macro (end of line inside a macro).
+TOK(code_completion)     // Code completion marker
 
 // C99 6.4.9: Comments.
 TOK(comment)             // Comment (only in -E -C[C] mode)
index 1443ef11841b78081b1715b29088d538c035b474..cd1c49d5d61c107f51cad1f7967a37ba2dda41ed 100644 (file)
@@ -39,7 +39,8 @@ class Lexer : public PreprocessorLexer {
   SourceLocation FileLoc;        // Location for start of file.
   LangOptions Features;          // Features enabled by this language (cache).
   bool Is_PragmaLexer;           // True if lexer for _Pragma handling.
-
+  bool IsEofCodeCompletion;      // True if EOF is treated as a code-completion.
+  
   //===--------------------------------------------------------------------===//
   // Context-specific lexing flags set by the preprocessor.
   //
@@ -178,6 +179,15 @@ public:
     ExtendedTokenMode = Mode ? 1 : 0;
   }
 
+  /// \brief Specify that end-of-file is to be considered a code-completion
+  /// token.
+  ///
+  /// When in this mode, the end-of-file token will be immediately preceded
+  /// by a code-completion token.
+  void SetEofIsCodeCompletion() {
+    IsEofCodeCompletion = true;
+  }
+  
   const char *getBufferStart() const { return BufferStart; }
 
   /// ReadToEndOfLine - Read the rest of the current preprocessor line as an
index 0765ac391be3f3a53c5d8f1267c80eab0cd46e71..17823cd25774d110515619d058440f54046588c3 100644 (file)
@@ -92,6 +92,10 @@ class Preprocessor {
   bool DisableMacroExpansion : 1;  // True if macro expansion is disabled.
   bool InMacroArgs : 1;            // True if parsing fn macro invocation args.
 
+  /// \brief True if the end-of-file of the main file should be treated as
+  /// a code-completion token.
+  bool IsMainFileEofCodeCompletion : 1;
+
   /// Identifiers - This is mapping/lookup information for all identifiers in
   /// the program, including program keywords.
   IdentifierTable Identifiers;
@@ -259,6 +263,12 @@ public:
     Callbacks = C;
   }
 
+  /// \brief Note that, for the main source file, the end-of-file should be
+  /// treated as a code-completion token.
+  void SetMainFileEofCodeCompletion() {
+    IsMainFileEofCodeCompletion = true;
+  }
+  
   /// getMacroInfo - Given an identifier, return the MacroInfo it is #defined to
   /// or null if it isn't #define'd.
   MacroInfo *getMacroInfo(IdentifierInfo *II) const {
index 627ed20055592f226337e3b80c07ed484dc624eb..6a2ff62839e2d62e6ffd840ac93adfcc10f42de7 100644 (file)
@@ -2185,6 +2185,49 @@ public:
                                     SourceLocation AliasNameLoc) {
     return;
   }
+  
+  /// \name Code completion actions
+  ///
+  /// These actions are used to signal that a code-completion token has been
+  /// found at a point in the grammar where the Action implementation is
+  /// likely to be able to provide a list of possible completions, e.g.,
+  /// after the "." or "->" of a member access expression.
+  ///
+  //@{
+  
+  /// \brief Code completion for a member access expression.
+  ///
+  /// This code completion action is invoked when the code-completion token
+  /// is found after the "." or "->" of a member access expression.
+  ///
+  /// \param S the scope in which the member access expression occurs.
+  ///
+  /// \param Base the base expression (e.g., the x in "x.foo") of the member
+  /// access.
+  ///
+  /// \param OpLoc the location of the "." or "->" operator.
+  ///
+  /// \param IsArrow true when the operator is "->", false when it is ".".
+  virtual void CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *Base,
+                                               SourceLocation OpLoc,
+                                               bool IsArrow) {
+  }
+  
+  /// \brief Code completion for a C++ nested-name-specifier that precedes a
+  /// qualified-id of some form.
+  ///
+  /// This code completion action is invoked when the code-completion token
+  /// is found after the "::" of a nested-name-specifier.
+  ///
+  /// \param S the scope in which the nested-name-specifier occurs.
+  /// 
+  /// \param SS the scope specifier ending with "::".
+  ///
+  /// \parameter EnteringContext whether we're entering the context of this
+  /// scope specifier.
+  virtual void CodeCompleteQualifiedId(Scope *S, const CXXScopeSpec &SS,
+                                       bool EnteringContext) { }
+  //@}
 };
 
 /// MinimalAction - Minimal actions are used by light-weight clients of the
diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h
new file mode 100644 (file)
index 0000000..c9e989e
--- /dev/null
@@ -0,0 +1,183 @@
+//===---- CodeCompleteConsumer.h - Code Completion Interface ----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the CodeCompleteConsumer class.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_SEMA_CODECOMPLETECONSUMER_H
+#define LLVM_CLANG_SEMA_CODECOMPLETECONSUMER_H
+
+#include "clang/AST/DeclarationName.h"
+#include "clang/AST/Type.h"
+#include <list>
+#include <map>
+#include <vector>
+
+namespace llvm {
+class raw_ostream;
+}
+
+namespace clang {
+  
+class DeclContext;
+class NamedDecl;
+class Scope;
+class Sema;
+  
+/// \brief Abstract interface for a consumer of code-completion 
+/// information.
+class CodeCompleteConsumer {
+  /// \brief The semantic-analysis object to which this code-completion
+  /// consumer is attached.
+  Sema &SemaRef;
+  
+public:
+  /// \brief Captures a result of code completion.
+  struct Result {
+    /// \brief Describes the kind of result generated.
+    enum ResultKind {
+      RK_Declaration = 0, //< Refers to a declaration
+      RK_Keyword,        //< Refers to a keyword or symbol.
+    };
+    
+    /// \brief The kind of result stored here.
+    ResultKind Kind;
+    
+    union {
+      /// \brief When Kind == RK_Declaration, the declaration we are referring
+      /// to.
+      NamedDecl *Declaration;
+      
+      /// \brief When Kind == RK_Keyword, the string representing the keyword 
+      /// or symbol's spelling.
+      const char *Keyword;
+    };
+    
+    /// \brief Describes how good this result is, with zero being the best
+    /// result and progressively higher numbers representing poorer results.
+    unsigned Rank;
+    
+    /// \brief Whether this result is hidden by another name.
+    bool Hidden : 1;
+    
+    /// \brief Build a result that refers to a declaration.
+    Result(NamedDecl *Declaration, unsigned Rank)
+      : Kind(RK_Declaration), Declaration(Declaration), Rank(Rank), 
+        Hidden(false) { }
+    
+    /// \brief Build a result that refers to a keyword or symbol.
+    Result(const char *Keyword, unsigned Rank)
+      : Kind(RK_Keyword), Keyword(Keyword), Rank(Rank), Hidden(false) { }
+  };
+  
+  /// \brief A container of code-completion results.
+  class ResultSet {
+    /// \brief The actual results we have found.
+    std::vector<Result> Results;
+
+    /// \brief A mapping from declaration names to the declarations that have
+    /// this name within a particular scope and their index within the list of
+    /// results.
+    typedef std::multimap<DeclarationName, 
+                          std::pair<NamedDecl *, unsigned> > ShadowMap;
+    
+    /// \brief A list of shadow maps, which is used to model name hiding at
+    /// different levels of, e.g., the inheritance hierarchy.
+    std::list<ShadowMap> ShadowMaps;
+    
+  public:
+    typedef std::vector<Result>::iterator iterator;
+    iterator begin() { return Results.begin(); }
+    iterator end() { return Results.end(); }
+    
+    Result *data() { return Results.empty()? 0 : &Results.front(); }
+    unsigned size() const { return Results.size(); }
+    bool empty() const { return Results.empty(); }
+    
+    /// \brief Add a new result to this result set (if it isn't already in one
+    /// of the shadow maps), or replace an existing result (for, e.g., a 
+    /// redeclaration).
+    void MaybeAddResult(Result R);
+    
+    /// \brief Enter into a new scope.
+    void EnterNewScope();
+    
+    /// \brief Exit from the current scope.
+    void ExitScope();
+  };
+  
+  /// \brief Create a new code-completion consumer and registers it with
+  /// the given semantic-analysis object.
+  explicit CodeCompleteConsumer(Sema &S);
+  
+  /// \brief Deregisters and destroys this code-completion consumer.
+  virtual ~CodeCompleteConsumer();
+  
+  /// \brief Retrieve the semantic-analysis object to which this code-completion
+  /// consumer is attached.
+  Sema &getSema() const { return SemaRef; }
+  
+  /// \name Code-completion callbacks
+  //@{
+  
+  /// \brief Process the finalized code-completion results.
+  virtual void ProcessCodeCompleteResults(Result *Results, 
+                                          unsigned NumResults) { }
+  
+  /// \brief Code completion for a member access expression, e.g., "x->" or
+  /// "x.".
+  ///
+  /// \param S is the scope in which code-completion occurs.
+  ///
+  /// \param BaseType is the type whose members are being accessed.
+  ///
+  /// \param IsArrow whether this member referenced was written with an
+  /// arrow ("->") or a period (".").
+  virtual void CodeCompleteMemberReferenceExpr(Scope *S, QualType BaseType,
+                                               bool IsArrow);
+  
+  /// \brief Code completion for a qualified-id, e.g., "std::"
+  ///
+  /// \param S the scope in which the nested-name-specifier occurs.
+  ///
+  /// \param NNS the nested-name-specifier before the code-completion location.
+  ///
+  /// \param EnteringContext whether the parser will be entering the scope of
+  /// the qualified-id.
+  virtual void CodeCompleteQualifiedId(Scope *S, NestedNameSpecifier *NNS,
+                                       bool EnteringContext);
+  //@}
+  
+  /// \name Utility functions
+  //@{
+  unsigned CollectMemberResults(DeclContext *Ctx, unsigned InitialRank, 
+                                ResultSet &Results);
+  //@}
+};
+  
+/// \brief A simple code-completion consumer that prints the results it 
+/// receives in a simple format.
+class PrintingCodeCompleteConsumer : public CodeCompleteConsumer {
+  /// \brief The raw output stream.
+  llvm::raw_ostream &OS;
+  
+public:
+  /// \brief Create a new printing code-completion consumer that prints its
+  /// results to the given raw output stream.
+  PrintingCodeCompleteConsumer(Sema &S, llvm::raw_ostream &OS)
+    : CodeCompleteConsumer(S), OS(OS) { }
+  
+  /// \brief Prints the finalized code-completion results.
+  virtual void ProcessCodeCompleteResults(Result *Results, 
+                                          unsigned NumResults);
+};
+  
+} // end namespace clang
+
+#endif // LLVM_CLANG_SEMA_CODECOMPLETECONSUMER_H
index debe75bb85dd661202edd12b5d3cddbda069caac..36b79266e2d3f3dfb945f918b682a6f62157a01d 100644 (file)
@@ -18,7 +18,9 @@ namespace clang {
   class Preprocessor;
   class ASTConsumer;
   class ASTContext;
-
+  class CodeCompleteConsumer;
+  class Sema;
+  
   /// \brief Parse the entire file specified, notifying the ASTConsumer as
   /// the file is parsed.
   ///
@@ -30,7 +32,9 @@ namespace clang {
   /// end-of-translation-unit wrapup will be performed.
   void ParseAST(Preprocessor &pp, ASTConsumer *C,
                 ASTContext &Ctx, bool PrintStats = false,
-                bool CompleteTranslationUnit = true);
+                bool CompleteTranslationUnit = true,
+            CodeCompleteConsumer *(CreateCodeCompleter)(Sema &, void *Data) = 0,
+                void *CreateCodeCompleterData = 0);
 
 }  // end namespace clang
 
index 81d19cc8264727ff4d3476fff0d747c9cada443a..19f43e36a2f06956e0299e3ea27d009c8a4f329b 100644 (file)
@@ -196,6 +196,7 @@ Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) {
   IgnoreAllWarnings = false;
   WarningsAsErrors = false;
   SuppressSystemWarnings = false;
+  SuppressAllDiagnostics = false;
   ExtBehavior = Ext_Ignore;
 
   ErrorOccurred = false;
@@ -424,6 +425,9 @@ bool Diagnostic::setDiagnosticGroupMapping(const char *Group,
 bool Diagnostic::ProcessDiag() {
   DiagnosticInfo Info(this);
 
+  if (SuppressAllDiagnostics)
+    return false;
+  
   // Figure out the diagnostic level of this message.
   Diagnostic::Level DiagLevel;
   unsigned DiagID = Info.getID();
index 23ba6e1ca79befd7e218874aa34a05fa79a90453..351b63f6bb77700e5e849f3bdced507520d68321 100644 (file)
@@ -70,7 +70,8 @@ void Lexer::InitLexer(const char *BufStart, const char *BufPtr,
          " to simplify lexing!");
 
   Is_PragmaLexer = false;
-
+  IsEofCodeCompletion = false;
+  
   // Start of the file is a start of line.
   IsAtStartOfLine = true;
 
@@ -1309,6 +1310,18 @@ bool Lexer::LexEndOfFile(Token &Result, const char *CurPtr) {
     return true;  // Have a token.
   }
 
+  if (IsEofCodeCompletion) {
+    // We're at the end of the file, but we've been asked to conside the
+    // end of the file to be a code-completion token. Return the
+    // code-completion token.
+    Result.startToken();
+    FormTokenWithChars(Result, CurPtr, tok::code_completion);
+
+    // Only do the eof -> code_completion translation once.
+    IsEofCodeCompletion = false;
+    return true;
+  }
+  
   // If we are in raw mode, return this event as an EOF token.  Let the caller
   // that put us in raw mode handle the event.
   if (isLexingRawMode()) {
index bfa090a09e870d2d1a0f58fa6e4cf057d3441a95..4e522cbb8a4ada8f61a0f6c497f28a766703e0d0 100644 (file)
@@ -71,6 +71,7 @@ Preprocessor::Preprocessor(Diagnostic &diags, const LangOptions &opts,
   // Macro expansion is enabled.
   DisableMacroExpansion = false;
   InMacroArgs = false;
+  IsMainFileEofCodeCompletion = false;
   NumCachedTokenLexers = 0;
 
   CachedLexPos = 0;
@@ -368,6 +369,13 @@ void Preprocessor::EnterMainSourceFile() {
   // Enter the main file source buffer.
   EnterSourceFile(MainFileID, 0);
 
+  if (IsMainFileEofCodeCompletion) {
+    // Tell our newly-created lexer that it should treat its end-of-file as
+    // a code-completion token.
+    IsMainFileEofCodeCompletion = false;
+    static_cast<Lexer *>(getCurrentFileLexer())->SetEofIsCodeCompletion();
+  }
+  
   // Tell the header info that the main file was entered.  If the file is later
   // #imported, it won't be re-entered.
   if (const FileEntry *FE = SourceMgr.getFileEntryForID(MainFileID))
index 60c81929658b25ea37c28669d36f86de5db6924f..db507c2d3adb5e528452d619e41b32c65da09ce0 100644 (file)
@@ -935,6 +935,14 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) {
         ParseOptionalCXXScopeSpecifier(SS, ObjectType, false);
       }
 
+      if (Tok.is(tok::code_completion)) {
+        // Code completion for a member access expression.
+        Actions.CodeCompleteMemberReferenceExpr(CurScope, LHS.get(),
+                                                OpLoc, OpKind == tok::arrow);
+        
+        ConsumeToken();
+      }
+      
       if (Tok.is(tok::identifier)) {
         if (!LHS.isInvalid())
           LHS = Actions.ActOnMemberReferenceExpr(CurScope, move(LHS), OpLoc,
index a68ed6a8037026ac4393d2cdaf13784fa6a5c8aa..4c0f699b3f0a98f5bcde97b7c02bba2c75f1549e 100644 (file)
@@ -85,6 +85,13 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
       // To implement this, we clear out the object type as soon as we've
       // seen a leading '::' or part of a nested-name-specifier.
       ObjectType = 0;
+      
+      if (Tok.is(tok::code_completion)) {
+        // Code completion for a nested-name-specifier, where the code
+        // code completion token follows the '::'.
+        Actions.CodeCompleteQualifiedId(CurScope, SS, EnteringContext);
+        ConsumeToken();
+      }
     }
 
     // nested-name-specifier:
index 73ecc987fafe1e49a1dfbcf2f9324134d96ad176..d766fbb01c2ba0d2feb8902394125c058a410f4b 100644 (file)
@@ -1,6 +1,7 @@
 set(LLVM_NO_RTTI 1)
 
 add_clang_library(clangSema
+  CodeCompleteConsumer.cpp
   IdentifierResolver.cpp
   JumpDiagnostics.cpp
   ParseAST.cpp
@@ -10,6 +11,7 @@ add_clang_library(clangSema
   SemaCXXCast.cpp
   SemaCXXScopeSpec.cpp
   SemaChecking.cpp
+  SemaCodeComplete.cpp
   SemaDecl.cpp
   SemaDeclAttr.cpp
   SemaDeclCXX.cpp
diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp
new file mode 100644 (file)
index 0000000..7464bc1
--- /dev/null
@@ -0,0 +1,314 @@
+//===---- CodeCompleteConsumer.h - Code Completion Interface ----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file implements the CodeCompleteConsumer class.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "Sema.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <string.h>
+using namespace clang;
+
+CodeCompleteConsumer::CodeCompleteConsumer(Sema &S) : SemaRef(S) {
+  SemaRef.setCodeCompleteConsumer(this);
+}
+
+CodeCompleteConsumer::~CodeCompleteConsumer() {
+  SemaRef.setCodeCompleteConsumer(0);
+}
+
+void 
+CodeCompleteConsumer::CodeCompleteMemberReferenceExpr(Scope *S, 
+                                                      QualType BaseType,
+                                                      bool IsArrow) {
+  if (IsArrow) {
+    if (const PointerType *Ptr = BaseType->getAs<PointerType>())
+      BaseType = Ptr->getPointeeType();
+    else if (BaseType->isObjCObjectPointerType())
+    /*Do nothing*/ ;
+    else
+      return;
+  }
+  
+  ResultSet Results;
+  unsigned NextRank = 0;
+  
+  if (const RecordType *Record = BaseType->getAs<RecordType>()) {
+    NextRank = CollectMemberResults(Record->getDecl(), NextRank, Results);
+
+    if (getSema().getLangOptions().CPlusPlus) {
+      if (!Results.empty())
+        // The "template" keyword can follow "->" or "." in the grammar.
+        Results.MaybeAddResult(Result("template", NextRank++));
+
+      // FIXME: For C++, we also need to look into the current scope, since
+      // we could have the start of a nested-name-specifier.
+    }
+
+    // Hand off the results found for code completion.
+    ProcessCodeCompleteResults(Results.data(), Results.size());
+    
+    // We're done!
+    return;
+  }
+}
+
+void 
+CodeCompleteConsumer::CodeCompleteQualifiedId(Scope *S, 
+                                              NestedNameSpecifier *NNS,
+                                              bool EnteringContext) {
+  CXXScopeSpec SS;
+  SS.setScopeRep(NNS);
+  DeclContext *Ctx = getSema().computeDeclContext(SS, EnteringContext);
+  if (!Ctx)
+    return;
+  
+  ResultSet Results;
+  unsigned NextRank = CollectMemberResults(Ctx, 0, Results);
+  
+  // The "template" keyword can follow "::" in the grammar
+  if (!Results.empty())
+    Results.MaybeAddResult(Result("template", NextRank));
+  
+  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.
+    Results.push_back(R);
+    return;
+  }
+  
+  // FIXME: Using declarations
+  
+  Decl *CanonDecl = R.Declaration->getCanonicalDecl();
+  unsigned IDNS = CanonDecl->getIdentifierNamespace();
+
+  // Friend declarations and declarations introduced due to friends are never
+  // added as results.
+  if (isa<FriendDecl>(CanonDecl) || 
+      (IDNS & (Decl::IDNS_OrdinaryFriend | Decl::IDNS_TagFriend)))
+    return;
+
+  ShadowMap &SMap = ShadowMaps.back();
+  ShadowMap::iterator I, IEnd;
+  for (llvm::tie(I, IEnd) = SMap.equal_range(R.Declaration->getDeclName());
+       I != IEnd; ++I) {
+    NamedDecl *ND = I->second.first;
+    unsigned Index = I->second.second;
+    if (ND->getCanonicalDecl() == CanonDecl) {
+      // This is a redeclaration. Always pick the newer declaration.
+      I->second.first = R.Declaration;
+      Results[Index].Declaration = R.Declaration;
+      
+      // Pick the best rank of the two.
+      Results[Index].Rank = std::min(Results[Index].Rank, R.Rank);
+      
+      // We're done.
+      return;
+    }
+  }
+  
+  // This is a new declaration in this scope. However, check whether this
+  // declaration name is hidden by a similarly-named declaration in an outer
+  // scope.
+  std::list<ShadowMap>::iterator SM, SMEnd = ShadowMaps.end();
+  --SMEnd;
+  for (SM = ShadowMaps.begin(); SM != SMEnd; ++SM) {
+    for (llvm::tie(I, IEnd) = SM->equal_range(R.Declaration->getDeclName());
+         I != IEnd; ++I) {
+      // A tag declaration does not hide a non-tag declaration.
+      if (I->second.first->getIdentifierNamespace() == Decl::IDNS_Tag &&
+          (IDNS & (Decl::IDNS_Member | Decl::IDNS_Ordinary | 
+                   Decl::IDNS_ObjCProtocol)))
+        continue;
+      
+      // Protocols are in distinct namespaces from everything else.
+      if (((I->second.first->getIdentifierNamespace() & Decl::IDNS_ObjCProtocol)
+           || (IDNS & Decl::IDNS_ObjCProtocol)) &&
+          I->second.first->getIdentifierNamespace() != IDNS)
+        continue;
+      
+      // The newly-added result is hidden by an entry in the shadow map.
+      R.Hidden = true;
+      break;
+    }
+  }
+  
+  // Insert this result into the set of results and into the current shadow
+  // map.
+  SMap.insert(std::make_pair(R.Declaration->getDeclName(),
+                             std::make_pair(R.Declaration, Results.size())));
+  Results.push_back(R);
+}
+
+/// \brief Enter into a new scope.
+void CodeCompleteConsumer::ResultSet::EnterNewScope() {
+  ShadowMaps.push_back(ShadowMap());
+}
+
+/// \brief Exit from the current scope.
+void CodeCompleteConsumer::ResultSet::ExitScope() {
+  ShadowMaps.pop_back();
+}
+
+/// \brief Collect the results of searching for members within the given
+/// declaration context.
+///
+/// \param Ctx the declaration context from which we will gather results.
+///
+/// \param InitialRank the initial rank given to results in this tag
+/// declaration. Larger rank values will be used for, e.g., members found
+/// in base classes.
+///
+/// \param Results the result set that will be extended with any results
+/// found within this declaration context (and, for a C++ class, its bases).
+///
+/// \returns the next higher rank value, after considering all of the
+/// names within this declaration context.
+unsigned CodeCompleteConsumer::CollectMemberResults(DeclContext *Ctx, 
+                                                    unsigned InitialRank,
+                                                    ResultSet &Results) {
+  // Enumerate all of the results in this context.
+  Results.EnterNewScope();
+  for (DeclContext *CurCtx = Ctx->getPrimaryContext(); CurCtx; 
+       CurCtx = CurCtx->getNextContext()) {
+    for (DeclContext::decl_iterator D = CurCtx->decls_begin(), 
+                                 DEnd = CurCtx->decls_end();
+         D != DEnd; ++D) {
+      if (NamedDecl *ND = dyn_cast<NamedDecl>(*D)) {
+        // FIXME: Apply a filter to the results
+        Results.MaybeAddResult(Result(ND, InitialRank));
+      }
+    }
+  }
+  
+  // Traverse the contexts of inherited classes.
+  unsigned NextRank = InitialRank;
+  if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Ctx)) {
+    for (CXXRecordDecl::base_class_iterator B = Record->bases_begin(),
+                                         BEnd = Record->bases_end();
+         B != BEnd; ++B) {
+      QualType BaseType = B->getType();
+      
+      // Don't look into dependent bases, because name lookup can't look
+      // there anyway.
+      if (BaseType->isDependentType())
+        continue;
+      
+      const RecordType *Record = BaseType->getAs<RecordType>();
+      if (!Record)
+        continue;
+      
+      // FIXME: We should keep track of the virtual bases we visit, so 
+      // that we don't visit them more than once.
+      
+      // FIXME: It would be nice to be able to determine whether referencing
+      // a particular member would be ambiguous. For example, given
+      //
+      //   struct A { int member; };
+      //   struct B { int member; };
+      //   struct C : A, B { };
+      //
+      //   void f(C *c) { c->### }
+      // accessing 'member' would result in an ambiguity. However, code
+      // completion could be smart enough to qualify the member with the
+      // base class, e.g.,
+      //
+      //   c->B::member
+      //
+      // or
+      //
+      //   c->A::member
+      
+      // Collect results from this base class (and its bases).
+      NextRank = std::max(NextRank, 
+                          CollectMemberResults(Record->getDecl(), 
+                                               InitialRank + 1, 
+                                               Results));
+    }
+  }
+  
+  // FIXME: Look into base classes in Objective-C!
+
+  Results.ExitScope();
+  return NextRank;
+}
+
+namespace {
+  struct VISIBILITY_HIDDEN SortCodeCompleteResult {
+    typedef CodeCompleteConsumer::Result Result;
+    
+    bool operator()(const Result &X, const Result &Y) const {
+      // Sort first by rank.
+      if (X.Rank < Y.Rank)
+        return true;
+      else if (X.Rank > Y.Rank)
+        return false;
+      
+      // Result kinds are ordered by decreasing importance.
+      if (X.Kind < Y.Kind)
+        return true;
+      else if (X.Kind > Y.Kind)
+        return false;
+
+      // Non-hidden names precede hidden names.
+      if (X.Hidden != Y.Hidden)
+        return !X.Hidden;
+      
+      // Ordering depends on the kind of result.
+      switch (X.Kind) {
+      case Result::RK_Declaration:
+        // Order based on the declaration names.
+        return X.Declaration->getDeclName() < Y.Declaration->getDeclName();
+          
+      case Result::RK_Keyword:
+        return strcmp(X.Keyword, Y.Keyword) == -1;
+      }
+      
+      // If only our C++ compiler did control-flow warnings properly.
+      return false;
+    }
+  };
+}
+
+void 
+PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Result *Results, 
+                                                         unsigned NumResults) {
+  // Sort the results by rank/kind/etc.
+  std::stable_sort(Results, Results + NumResults, SortCodeCompleteResult());
+  
+  // Print the results.
+  for (unsigned I = 0; I != NumResults; ++I) {
+    switch (Results[I].Kind) {
+    case Result::RK_Declaration:
+      OS << Results[I].Declaration->getNameAsString() << " : " 
+         << Results[I].Rank;
+      if (Results[I].Hidden)
+        OS << " (Hidden)";
+      OS << '\n';
+      break;
+      
+    case Result::RK_Keyword:
+      OS << Results[I].Keyword << " : " << Results[I].Rank << '\n';
+      break;
+    }
+  }
+  
+  // Once we've printed the code-completion results, suppress remaining
+  // diagnostics.
+  // FIXME: Move this somewhere else!
+  getSema().PP.getDiagnostics().setSuppressAllDiagnostics();
+}
index 196c1c1b857937800d320718116bf6ac95aa13f3..be19b7e3d9861a9bb2d739463d0d2b6a664d52b2 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "clang/Sema/ParseAST.h"
 #include "Sema.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Sema/SemaConsumer.h"
 #include "clang/Sema/ExternalSemaSource.h"
 #include "clang/AST/ASTConsumer.h"
@@ -33,7 +34,9 @@ using namespace clang;
 ///
 void clang::ParseAST(Preprocessor &PP, ASTConsumer *Consumer,
                      ASTContext &Ctx, bool PrintStats,
-                     bool CompleteTranslationUnit) {
+                     bool CompleteTranslationUnit,
+                CodeCompleteConsumer *(CreateCodeCompleter)(Sema &, void *Data),
+                     void *CreateCodeCompleterData) {
   // Collect global stats on Decls/Stmts (until we have a module streamer).
   if (PrintStats) {
     Decl::CollectingStats(true);
@@ -60,6 +63,10 @@ void clang::ParseAST(Preprocessor &PP, ASTConsumer *Consumer,
     External->StartTranslationUnit(Consumer);
   }
 
+  CodeCompleteConsumer *CodeCompleter = 0;
+  if (CreateCodeCompleter)
+    CodeCompleter = CreateCodeCompleter(S, CreateCodeCompleterData);
+  
   Parser::DeclGroupPtrTy ADecl;
 
   while (!P.ParseTopLevelDecl(ADecl)) {  // Not end of file.
@@ -78,6 +85,9 @@ void clang::ParseAST(Preprocessor &PP, ASTConsumer *Consumer,
 
   Consumer->HandleTranslationUnit(Ctx);
 
+  if (CreateCodeCompleter)
+    delete CodeCompleter;
+  
   if (PrintStats) {
     fprintf(stderr, "\nSTATISTICS:\n");
     P.getActions().PrintStats();
index 3f734105d8affcb9027b06034332b2a9536345d0..63b252a536ae33aed338bd4139c30db4bde347fa 100644 (file)
@@ -191,9 +191,9 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
            bool CompleteTranslationUnit)
   : LangOpts(pp.getLangOptions()), PP(pp), Context(ctxt), Consumer(consumer),
     Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()),
-    ExternalSource(0), CurContext(0), PreDeclaratorDC(0),
-    CurBlock(0), PackContext(0), IdResolver(pp.getLangOptions()),
-    StdNamespace(0), StdBadAlloc(0),
+    ExternalSource(0), CodeCompleter(0), CurContext(0), 
+    PreDeclaratorDC(0), CurBlock(0), PackContext(0), 
+    IdResolver(pp.getLangOptions()), StdNamespace(0), StdBadAlloc(0),
     GlobalNewDeleteDeclared(false), ExprEvalContext(PotentiallyEvaluated),
     CompleteTranslationUnit(CompleteTranslationUnit),
     NumSFINAEErrors(0), CurrentInstantiationScope(0) {
index 3e99871dd118057d2a9d79b1c30162cb7ccf266a..915a85990c04d63796dbaac471b542f7d1a02085 100644 (file)
@@ -42,6 +42,7 @@ namespace llvm {
 namespace clang {
   class ASTContext;
   class ASTConsumer;
+  class CodeCompleteConsumer;
   class Preprocessor;
   class Decl;
   class DeclContext;
@@ -178,6 +179,9 @@ public:
   /// \brief Source of additional semantic information.
   ExternalSemaSource *ExternalSource;
 
+  /// \brief Code-completion consumer.
+  CodeCompleteConsumer *CodeCompleter;
+
   /// CurContext - This is the current declaration context of parsing.
   DeclContext *CurContext;
 
@@ -3618,6 +3622,23 @@ public:
 
     return T.getUnqualifiedType();
   }
+
+  /// \name Code completion
+  //@{
+private:
+  friend class CodeCompleteConsumer;
+  
+  void setCodeCompleteConsumer(CodeCompleteConsumer *CCC);
+  
+public:
+  virtual void CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *Base,
+                                               SourceLocation OpLoc,
+                                               bool IsArrow);
+  
+  virtual void CodeCompleteQualifiedId(Scope *S, const CXXScopeSpec &SS,
+                                       bool EnteringContext);
+  //@}
+  
   //===--------------------------------------------------------------------===//
   // Extra semantic analysis beyond the C type system
 private:
diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp
new file mode 100644 (file)
index 0000000..2183cfa
--- /dev/null
@@ -0,0 +1,45 @@
+//===---------------- SemaCodeComplete.cpp - Code Completion ----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the code-completion semantic actions.
+//
+//===----------------------------------------------------------------------===//
+#include "Sema.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
+
+using namespace clang;
+
+/// \brief Set the code-completion consumer for semantic analysis.
+void Sema::setCodeCompleteConsumer(CodeCompleteConsumer *CCC) {
+  assert(((CodeCompleter != 0) != (CCC != 0)) && 
+         "Already set or cleared a code-completion consumer?");
+  CodeCompleter = CCC;
+}
+
+void Sema::CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *BaseE,
+                                           SourceLocation OpLoc,
+                                           bool IsArrow) {
+  if (!BaseE || !CodeCompleter)
+    return;
+  
+  Expr *Base = static_cast<Expr *>(BaseE);
+  QualType BaseType = Base->getType();
+   
+  CodeCompleter->CodeCompleteMemberReferenceExpr(S, BaseType, IsArrow);
+}
+
+void Sema::CodeCompleteQualifiedId(Scope *S, const CXXScopeSpec &SS,
+                                   bool EnteringContext) {
+  if (!SS.getScopeRep() || !CodeCompleter)
+    return;
+  
+  CodeCompleter->CodeCompleteQualifiedId(S, 
+                                      (NestedNameSpecifier *)SS.getScopeRep(),
+                                         EnteringContext);
+}
index e6cd13cc2d16bf1c7607d177b8d5e74d8ac9565d..1c9567f8bd61acf176e93c11577126f98a0d02a7 100644 (file)
@@ -1,5 +1,6 @@
 set(CLANG_TEST_DIRECTORIES
   "Analysis"
+  "CodeCompletion"
   "CodeGen"
   "CodeGenCXX"
   "CodeGenObjC"
diff --git a/test/CodeCompletion/member-access.c b/test/CodeCompletion/member-access.c
new file mode 100644 (file)
index 0000000..25b2b9c
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
+// RUN: true
+struct Point {
+  float x;
+  float y;
+  float z;
+};
+
+void test(struct Point *p) {
+  // CHECK-CC1: x
+  // CHECK-CC1: y
+  // CHECK-CC1: z
+  p->
\ No newline at end of file
diff --git a/test/CodeCompletion/member-access.cpp b/test/CodeCompletion/member-access.cpp
new file mode 100644 (file)
index 0000000..234a631
--- /dev/null
@@ -0,0 +1,42 @@
+// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
+// RUN: true
+
+struct Base1 {
+  int member1;
+  float member2;
+};
+
+struct Base2 {
+  int member1;
+  double member3;
+  void memfun1(int);
+};
+
+struct Base3 : Base1, Base2 {
+  void memfun1(float);
+  void memfun1(double);
+  void memfun2(int);
+};
+
+struct Derived : Base3 {
+  int member4;
+  int memfun3(int);
+};
+
+class Proxy {
+public:
+  Derived *operator->() const;
+};
+
+void test(const Proxy &p) {
+  // CHECK-CC1: member4 : 0
+  // CHECK-CC1: memfun3 : 0
+  // CHECK-CC1: memfun1 : 1
+  // CHECK-CC1: memfun1 : 1
+  // CHECK-CC1: memfun2 : 1
+  // CHECK-CC1: member1 : 2
+  // CHECK-CC1: member1 : 2
+  // CHECK-CC1: member2 : 2
+  // CHECK-CC1: member3 : 2
+  // CHECK-CC1: memfun1 : 2 (Hidden)
+  p->
\ No newline at end of file
diff --git a/test/CodeCompletion/nested-name-specifier.cpp b/test/CodeCompletion/nested-name-specifier.cpp
new file mode 100644 (file)
index 0000000..f418164
--- /dev/null
@@ -0,0 +1,19 @@
+// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
+// RUN: true
+
+namespace N {
+  struct A { };
+  namespace M { 
+    struct C { };
+  };
+}
+
+namespace N {
+  struct B { };
+}
+
+// CHECK-CC1: A : 0
+// CHECK-CC1: B : 0
+// CHECK-CC1: M : 0
+// CHECK-CC1: template : 0
+N::
\ No newline at end of file
index 78a38c0f9f9036700dd85bec062a006dfb300e6c..4d8db2399d680c886618deca02a9dbf6f979f83e 100644 (file)
@@ -38,6 +38,7 @@
 #include "clang/Frontend/Utils.h"
 #include "clang/Analysis/PathDiagnostic.h"
 #include "clang/CodeGen/ModuleBuilder.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Sema/ParseAST.h"
 #include "clang/Sema/SemaDiagnostic.h"
 #include "clang/AST/ASTConsumer.h"
@@ -213,6 +214,17 @@ OutputFile("o",
  llvm::cl::desc("Specify output file"));
 
 
+static llvm::cl::opt<int>
+DumpCodeCompletion("code-completion-dump",
+                   llvm::cl::value_desc("N"),
+                   llvm::cl::desc("Dump code-completion information at $$N$$"));
+
+/// \brief Buld a new code-completion consumer that prints the results of
+/// code completion to standard output.
+static CodeCompleteConsumer *BuildPrintingCodeCompleter(Sema &S, void *) {
+  return new PrintingCodeCompleteConsumer(S, llvm::outs());
+}
+
 //===----------------------------------------------------------------------===//
 // PTH.
 //===----------------------------------------------------------------------===//
@@ -2046,12 +2058,29 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF,
     if (InitializeSourceManager(PP, InFile))
       return;
   }
-
-
+  
   // If we have an ASTConsumer, run the parser with it.
-  if (Consumer)
+  if (Consumer) {
+    CodeCompleteConsumer *(*CreateCodeCompleter)(Sema &, void *) = 0;
+    void *CreateCodeCompleterData = 0;
+    
+    if (DumpCodeCompletion) {
+      // To dump code-completion information, we chop off the file at the
+      // location of the string $$N$$, where N is the value provided to
+      // -code-completion-dump, and then tell the lexer to return a 
+      // code-completion token before it hits the end of the file.
+      // FIXME: Find $$N$$ in the main file buffer
+      
+      PP.SetMainFileEofCodeCompletion();
+      
+      // Set up the creation routine for code-completion.
+      CreateCodeCompleter = BuildPrintingCodeCompleter;
+    }
+
     ParseAST(PP, Consumer.get(), *ContextOwner.get(), Stats,
-             CompleteTranslationUnit);
+             CompleteTranslationUnit,
+             CreateCodeCompleter, CreateCodeCompleterData);
+  }
 
   if (PA == RunPreprocessorOnly) {    // Just lex as fast as we can, no output.
     llvm::TimeRegion Timer(ClangFrontendTimer);