aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/SemaCodeComplete.cpp
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2009-09-21 19:57:38 +0000
committerDouglas Gregor <dgregor@apple.com>2009-09-21 19:57:38 +0000
commitb9d0ef76347574a147b8456b018fe94dc1e2be26 (patch)
treefe0995cc5b39da6033b29a2636c1b8e280d7df56 /lib/Sema/SemaCodeComplete.cpp
parentfd02ed702e754f8dd0b4c979325ba99c2234f270 (diff)
Enhance "case" code completion in C++ to suggest qualified names for
enumerators when either the user intentionally wrote a qualified name (in which case we just use that nested-name-specifier to match the user's code) or when this is the first "case" statement and we need a qualified name to refer to an enumerator in a different scope. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82474 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaCodeComplete.cpp')
-rw-r--r--lib/Sema/SemaCodeComplete.cpp103
1 files changed, 91 insertions, 12 deletions
diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp
index c3efcdd05b..00d7fc5466 100644
--- a/lib/Sema/SemaCodeComplete.cpp
+++ b/lib/Sema/SemaCodeComplete.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "Sema.h"
#include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/AST/ExprCXX.h"
#include "llvm/ADT/SmallPtrSet.h"
#include <list>
#include <map>
@@ -328,6 +329,53 @@ static DeclContext *findOuterContext(Scope *S) {
return 0;
}
+/// \brief Compute the qualification required to get from the current context
+/// (\p CurContext) to the target context (\p TargetContext).
+///
+/// \param Context the AST context in which the qualification will be used.
+///
+/// \param CurContext the context where an entity is being named, which is
+/// typically based on the current scope.
+///
+/// \param TargetContext the context in which the named entity actually
+/// resides.
+///
+/// \returns a nested name specifier that refers into the target context, or
+/// NULL if no qualification is needed.
+static NestedNameSpecifier *
+getRequiredQualification(ASTContext &Context,
+ DeclContext *CurContext,
+ DeclContext *TargetContext) {
+ llvm::SmallVector<DeclContext *, 4> TargetParents;
+
+ for (DeclContext *CommonAncestor = TargetContext;
+ CommonAncestor && !CommonAncestor->Encloses(CurContext);
+ CommonAncestor = CommonAncestor->getLookupParent()) {
+ if (CommonAncestor->isTransparentContext() ||
+ CommonAncestor->isFunctionOrMethod())
+ continue;
+
+ TargetParents.push_back(CommonAncestor);
+ }
+
+ NestedNameSpecifier *Result = 0;
+ while (!TargetParents.empty()) {
+ DeclContext *Parent = TargetParents.back();
+ TargetParents.pop_back();
+
+ if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Parent))
+ Result = NestedNameSpecifier::Create(Context, Result, Namespace);
+ else if (TagDecl *TD = dyn_cast<TagDecl>(Parent))
+ Result = NestedNameSpecifier::Create(Context, Result,
+ false,
+ Context.getTypeDeclType(TD).getTypePtr());
+ else
+ assert(Parent->isTranslationUnit());
+ }
+
+ return Result;
+}
+
/// \brief Collect the results of searching for members within the given
/// declaration context.
///
@@ -651,6 +699,22 @@ static void AddTemplateParameterChunks(ASTContext &Context,
}
}
+/// \brief Add a qualifier to the given code-completion string, if the
+/// provided nested-name-specifier is non-NULL.
+void AddQualifierToCompletionString(CodeCompletionString *Result,
+ NestedNameSpecifier *Qualifier,
+ ASTContext &Context) {
+ if (!Qualifier)
+ return;
+
+ std::string PrintedNNS;
+ {
+ llvm::raw_string_ostream OS(PrintedNNS);
+ Qualifier->print(OS, Context.PrintingPolicy);
+ }
+ Result->AddTextChunk(PrintedNNS.c_str());
+}
+
/// \brief If possible, create a new code completion string for the given
/// result.
///
@@ -666,6 +730,7 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
+ AddQualifierToCompletionString(Result, Qualifier, S.Context);
Result->AddTextChunk(Function->getNameAsString().c_str());
Result->AddTextChunk("(");
AddFunctionParameterChunks(S.Context, Function, Result);
@@ -675,6 +740,7 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
+ AddQualifierToCompletionString(Result, Qualifier, S.Context);
FunctionDecl *Function = FunTmpl->getTemplatedDecl();
Result->AddTextChunk(Function->getNameAsString().c_str());
@@ -727,6 +793,7 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
if (TemplateDecl *Template = dyn_cast<TemplateDecl>(ND)) {
CodeCompletionString *Result = new CodeCompletionString;
+ AddQualifierToCompletionString(Result, Qualifier, S.Context);
Result->AddTextChunk(Template->getNameAsString().c_str());
Result->AddTextChunk("<");
AddTemplateParameterChunks(S.Context, Template, Result);
@@ -734,6 +801,13 @@ CodeCompleteConsumer::Result::CreateCodeCompletionString(Sema &S) {
return Result;
}
+ if (Qualifier) {
+ CodeCompletionString *Result = new CodeCompletionString;
+ AddQualifierToCompletionString(Result, Qualifier, S.Context);
+ Result->AddTextChunk(ND->getNameAsString().c_str());
+ return Result;
+ }
+
return 0;
}
@@ -900,6 +974,7 @@ void Sema::CodeCompleteCase(Scope *S) {
// token, in case we are code-completing in the middle of the switch and not
// at the end. However, we aren't able to do so at the moment.
llvm::SmallPtrSet<EnumConstantDecl *, 8> EnumeratorsSeen;
+ NestedNameSpecifier *Qualifier = 0;
for (SwitchCase *SC = Switch->getSwitchCaseList(); SC;
SC = SC->getNextSwitchCase()) {
CaseStmt *Case = dyn_cast<CaseStmt>(SC);
@@ -918,21 +993,32 @@ void Sema::CodeCompleteCase(Scope *S) {
// template are type- and value-dependent.
EnumeratorsSeen.insert(Enumerator);
- // FIXME: If this is a qualified-id, should we keep track of the
- // nested-name-specifier so we can reproduce it as part of code
- // completion? e.g.,
+ // If this is a qualified-id, keep track of the nested-name-specifier
+ // so that we can reproduce it as part of code completion, e.g.,
//
// switch (TagD.getKind()) {
// case TagDecl::TK_enum:
// break;
// case XXX
//
- // At the XXX, we would like our completions to be TagDecl::TK_union,
+ // At the XXX, our completions are TagDecl::TK_union,
// TagDecl::TK_struct, and TagDecl::TK_class, rather than TK_union,
// TK_struct, and TK_class.
+ if (QualifiedDeclRefExpr *QDRE = dyn_cast<QualifiedDeclRefExpr>(DRE))
+ Qualifier = QDRE->getQualifier();
}
}
+ if (getLangOptions().CPlusPlus && !Qualifier && EnumeratorsSeen.empty()) {
+ // If there are no prior enumerators in C++, check whether we have to
+ // qualify the names of the enumerators that we suggest, because they
+ // may not be visible in this scope.
+ Qualifier = getRequiredQualification(Context, CurContext,
+ Enum->getDeclContext());
+
+ // FIXME: Scoped enums need to start with "EnumDecl" as the context!
+ }
+
// Add any enumerators that have not yet been mentioned.
ResultBuilder Results(*this);
Results.EnterNewScope();
@@ -942,17 +1028,10 @@ void Sema::CodeCompleteCase(Scope *S) {
if (EnumeratorsSeen.count(*E))
continue;
- Results.MaybeAddResult(CodeCompleteConsumer::Result(*E, 0));
+ Results.MaybeAddResult(CodeCompleteConsumer::Result(*E, 0, Qualifier));
}
Results.ExitScope();
- // In C++, add nested-name-specifiers.
- if (getLangOptions().CPlusPlus) {
- Results.setFilter(&ResultBuilder::IsNestedNameSpecifier);
- CollectLookupResults(S, Context.getTranslationUnitDecl(), 1,
- Results);
- }
-
HandleCodeCompleteResults(CodeCompleter, Results.data(), Results.size());
}