[cfe-commits] r82474 - in /cfe/trunk: include/clang/Sema/CodeCompleteConsumer.h lib/Sema/SemaCodeComplete.cpp test/CodeCompletion/enum-switch-case-qualified.cpp test/CodeCompletion/enum-switch-case.cpp
Douglas Gregor
dgregor at apple.com
Mon Sep 21 12:57:38 PDT 2009
Author: dgregor
Date: Mon Sep 21 14:57:38 2009
New Revision: 82474
URL: http://llvm.org/viewvc/llvm-project?rev=82474&view=rev
Log:
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.
Added:
cfe/trunk/test/CodeCompletion/enum-switch-case-qualified.cpp
cfe/trunk/test/CodeCompletion/enum-switch-case.cpp
Modified:
cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
cfe/trunk/lib/Sema/SemaCodeComplete.cpp
Modified: cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h?rev=82474&r1=82473&r2=82474&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h (original)
+++ cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h Mon Sep 21 14:57:38 2009
@@ -24,6 +24,7 @@
namespace clang {
class NamedDecl;
+class NestedNameSpecifier;
class Sema;
/// \brief A "string" used to describe how code completion can
@@ -155,10 +156,15 @@
/// \brief Whether this result is hidden by another name.
bool Hidden : 1;
+ /// \brief If the result requires a nested-name-specifier for name lookup
+ /// to function properly, this is the nested-name-specifier.
+ NestedNameSpecifier *Qualifier;
+
/// \brief Build a result that refers to a declaration.
- Result(NamedDecl *Declaration, unsigned Rank)
+ Result(NamedDecl *Declaration, unsigned Rank,
+ NestedNameSpecifier *Qualifier = 0)
: Kind(RK_Declaration), Declaration(Declaration), Rank(Rank),
- Hidden(false) { }
+ Hidden(false), Qualifier(Qualifier) { }
/// \brief Build a result that refers to a keyword or symbol.
Result(const char *Keyword, unsigned Rank)
Modified: cfe/trunk/lib/Sema/SemaCodeComplete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCodeComplete.cpp?rev=82474&r1=82473&r2=82474&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCodeComplete.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCodeComplete.cpp Mon Sep 21 14:57:38 2009
@@ -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 @@
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 @@
}
}
+/// \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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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());
}
Added: cfe/trunk/test/CodeCompletion/enum-switch-case-qualified.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeCompletion/enum-switch-case-qualified.cpp?rev=82474&view=auto
==============================================================================
--- cfe/trunk/test/CodeCompletion/enum-switch-case-qualified.cpp (added)
+++ cfe/trunk/test/CodeCompletion/enum-switch-case-qualified.cpp Mon Sep 21 14:57:38 2009
@@ -0,0 +1,33 @@
+// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
+// RUN: true
+
+namespace M {
+
+namespace N {
+ struct C {
+ enum Color {
+ Red,
+ Orange,
+ Yellow,
+ Green,
+ Blue,
+ Indigo,
+ Violet
+ };
+ };
+}
+
+}
+
+namespace M {
+
+void test(enum N::C::Color color) {
+ switch (color) {
+ // CHECK-NEXT-CC1: Blue : 0 : N::C::Blue
+ // CHECK-NEXT-CC1: Green : 0 : N::C::Green
+ // CHECK-NEXT-CC1: Indigo : 0 : N::C::Indigo
+ // CHECK-NEXT-CC1: Orange : 0 : N::C::Orange
+ // CHECK-NEXT-CC1: Red : 0 : N::C::Red
+ // CHECK-NEXT-CC1: Violet : 0 : N::C::Violet
+ // CHECK-NEXT-CC1: Yellow : 0 : N::C::Yellow
+ case
Added: cfe/trunk/test/CodeCompletion/enum-switch-case.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeCompletion/enum-switch-case.cpp?rev=82474&view=auto
==============================================================================
--- cfe/trunk/test/CodeCompletion/enum-switch-case.cpp (added)
+++ cfe/trunk/test/CodeCompletion/enum-switch-case.cpp Mon Sep 21 14:57:38 2009
@@ -0,0 +1,29 @@
+// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
+// RUN: true
+
+namespace N {
+ enum Color {
+ Red,
+ Orange,
+ Yellow,
+ Green,
+ Blue,
+ Indigo,
+ Violet
+ };
+}
+
+void test(enum N::Color color) {
+ switch (color) {
+ case N::Red:
+ break;
+
+ case N::Yellow:
+ break;
+
+ // CHECK-CC1: Blue : 0 : N::Blue
+ // CHECK-NEXT-CC1: Green : 0 : N::Green
+ // CHECK-NEXT-CC1: Indigo : 0 : N::Indigo
+ // CHECK-NEXT-CC1: Orange : 0 : N::Orange
+ // CHECK-NEXT-CC1: Violet : 0 : N::Violet
+ case
More information about the cfe-commits
mailing list