diff options
-rw-r--r-- | include/clang/Basic/Diagnostic.h | 9 | ||||
-rw-r--r-- | include/clang/Basic/TokenKinds.def | 1 | ||||
-rw-r--r-- | include/clang/Lex/Lexer.h | 12 | ||||
-rw-r--r-- | include/clang/Lex/Preprocessor.h | 10 | ||||
-rw-r--r-- | include/clang/Parse/Action.h | 43 | ||||
-rw-r--r-- | include/clang/Sema/CodeCompleteConsumer.h | 183 | ||||
-rw-r--r-- | include/clang/Sema/ParseAST.h | 8 | ||||
-rw-r--r-- | lib/Basic/Diagnostic.cpp | 4 | ||||
-rw-r--r-- | lib/Lex/Lexer.cpp | 15 | ||||
-rw-r--r-- | lib/Lex/Preprocessor.cpp | 8 | ||||
-rw-r--r-- | lib/Parse/ParseExpr.cpp | 8 | ||||
-rw-r--r-- | lib/Parse/ParseExprCXX.cpp | 7 | ||||
-rw-r--r-- | lib/Sema/CMakeLists.txt | 2 | ||||
-rw-r--r-- | lib/Sema/CodeCompleteConsumer.cpp | 314 | ||||
-rw-r--r-- | lib/Sema/ParseAST.cpp | 12 | ||||
-rw-r--r-- | lib/Sema/Sema.cpp | 6 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 21 | ||||
-rw-r--r-- | lib/Sema/SemaCodeComplete.cpp | 45 | ||||
-rw-r--r-- | test/CMakeLists.txt | 1 | ||||
-rw-r--r-- | test/CodeCompletion/member-access.c | 13 | ||||
-rw-r--r-- | test/CodeCompletion/member-access.cpp | 42 | ||||
-rw-r--r-- | test/CodeCompletion/nested-name-specifier.cpp | 19 | ||||
-rw-r--r-- | tools/clang-cc/clang-cc.cpp | 37 |
23 files changed, 808 insertions, 12 deletions
diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index dc854b1830..518c97b5c4 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -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. diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index e711996cf2..239712c08c 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -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) diff --git a/include/clang/Lex/Lexer.h b/include/clang/Lex/Lexer.h index 1443ef1184..cd1c49d5d6 100644 --- a/include/clang/Lex/Lexer.h +++ b/include/clang/Lex/Lexer.h @@ -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 diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 0765ac391b..17823cd257 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -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 { diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 627ed20055..6a2ff62839 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -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 index 0000000000..c9e989e3cc --- /dev/null +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -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 diff --git a/include/clang/Sema/ParseAST.h b/include/clang/Sema/ParseAST.h index debe75bb85..36b79266e2 100644 --- a/include/clang/Sema/ParseAST.h +++ b/include/clang/Sema/ParseAST.h @@ -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 diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index 81d19cc826..19f43e36a2 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -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(); diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp index 23ba6e1ca7..351b63f6bb 100644 --- a/lib/Lex/Lexer.cpp +++ b/lib/Lex/Lexer.cpp @@ -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()) { diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index bfa090a09e..4e522cbb8a 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -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)) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 60c8192965..db507c2d3a 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -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, diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index a68ed6a803..4c0f699b3f 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -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: diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 73ecc987fa..d766fbb01c 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -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 index 0000000000..7464bc1f88 --- /dev/null +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -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(); +} diff --git a/lib/Sema/ParseAST.cpp b/lib/Sema/ParseAST.cpp index 196c1c1b85..be19b7e3d9 100644 --- a/lib/Sema/ParseAST.cpp +++ b/lib/Sema/ParseAST.cpp @@ -13,6 +13,7 @@ #include "clang/Sema/ParseAST.h" #include "Sema.h" +#include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Sema/ |