[cfe-commits] r82222 - in /cfe/trunk: include/clang/Parse/Action.h include/clang/Sema/CodeCompleteConsumer.h lib/Parse/ParseDecl.cpp lib/Parse/ParseDeclCXX.cpp lib/Sema/CodeCompleteConsumer.cpp lib/Sema/Sema.h lib/Sema/SemaCXXScopeSpec.cpp lib/Sema/SemaCodeComplete.cpp test/CodeCompletion/tag.c

Douglas Gregor dgregor at apple.com
Fri Sep 18 08:37:18 PDT 2009


Author: dgregor
Date: Fri Sep 18 10:37:17 2009
New Revision: 82222

URL: http://llvm.org/viewvc/llvm-project?rev=82222&view=rev
Log:
Implement code completion for tags, e.g., code completion after "enum"
will provide the names of various enumerations currently
visible. Introduced filtering of code-completion results when we build
the result set, so that we can identify just the kinds of declarations
we want.

This implementation is incomplete for C++, since we don't consider
that the token after the tag keyword could start a
nested-name-specifier. 


Added:
    cfe/trunk/test/CodeCompletion/tag.c   (with props)
Modified:
    cfe/trunk/include/clang/Parse/Action.h
    cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
    cfe/trunk/lib/Sema/SemaCodeComplete.cpp

Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=82222&r1=82221&r2=82222&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Fri Sep 18 10:37:17 2009
@@ -2210,8 +2210,18 @@
   /// \param IsArrow true when the operator is "->", false when it is ".".
   virtual void CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *Base,
                                                SourceLocation OpLoc,
-                                               bool IsArrow) {
-  }
+                                               bool IsArrow) { }
+  
+  /// \brief Code completion for a reference to a tag.
+  ///
+  /// This code completion action is invoked when the code-completion
+  /// token is found after a tag keyword (struct, union, enum, or class).
+  ///
+  /// \para, S the scope in which the tag reference occurs.
+  ///
+  /// \param TagSpec an instance of DeclSpec::TST, indicating what kind of tag
+  /// this is (struct/union/enum/class).
+  virtual void CodeCompleteTag(Scope *S, unsigned TagSpec) { }
   
   /// \brief Code completion for a C++ nested-name-specifier that precedes a
   /// qualified-id of some form.

Modified: cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h?rev=82222&r1=82221&r2=82222&view=diff

==============================================================================
--- cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h (original)
+++ cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h Fri Sep 18 10:37:17 2009
@@ -75,9 +75,17 @@
     Result(const char *Keyword, unsigned Rank)
       : Kind(RK_Keyword), Keyword(Keyword), Rank(Rank), Hidden(false) { }
   };
-  
+    
   /// \brief A container of code-completion results.
   class ResultSet {
+  public:
+    /// \brief The type of a name-lookup filter, which can be provided to the
+    /// name-lookup routines to specify which declarations should be included in
+    /// the result set (when it returns true) and which declarations should be
+    /// filtered out (returns false).
+    typedef bool (CodeCompleteConsumer::*LookupFilter)(NamedDecl *) const;
+    
+  private:
     /// \brief The actual results we have found.
     std::vector<Result> Results;
 
@@ -87,11 +95,27 @@
     typedef std::multimap<DeclarationName, 
                           std::pair<NamedDecl *, unsigned> > ShadowMap;
     
+    /// \brief The code-completion consumer that is producing these results.
+    CodeCompleteConsumer &Completer;
+    
+    /// \brief If non-NULL, a filter function used to remove any code-completion
+    /// results that are not desirable.
+    LookupFilter Filter;
+    
     /// \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:
+    explicit ResultSet(CodeCompleteConsumer &Completer,
+                       LookupFilter Filter = 0)
+      : Completer(Completer), Filter(Filter) { }
+    
+    /// \brief Set the filter used for code-completion results.
+    void setFilter(LookupFilter Filter) {
+      this->Filter = Filter;
+    }
+    
     typedef std::vector<Result>::iterator iterator;
     iterator begin() { return Results.begin(); }
     iterator end() { return Results.end(); }
@@ -99,7 +123,7 @@
     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).
@@ -142,6 +166,10 @@
   virtual void CodeCompleteMemberReferenceExpr(Scope *S, QualType BaseType,
                                                bool IsArrow);
   
+  /// \brief Code completion for a tag name following an enum, class, struct,
+  /// or union keyword.
+  virtual void CodeCompleteTag(Scope *S, ElaboratedType::TagKind TK);
+  
   /// \brief Code completion for a qualified-id, e.g., "std::"
   ///
   /// \param S the scope in which the nested-name-specifier occurs.
@@ -154,10 +182,27 @@
                                        bool EnteringContext);
   //@}
   
-  /// \name Utility functions
+  /// \name Name lookup functions
+  ///
+  /// The name lookup functions in this group collect code-completion results
+  /// by performing a form of name looking into a scope or declaration context.
   //@{
-  unsigned CollectMemberResults(DeclContext *Ctx, unsigned InitialRank, 
+  unsigned CollectLookupResults(Scope *S, unsigned InitialRank,
                                 ResultSet &Results);
+  unsigned CollectMemberLookupResults(DeclContext *Ctx, unsigned InitialRank, 
+                                      ResultSet &Results);
+  //@}
+  
+  /// \name Name lookup predicates
+  ///
+  /// These predicates can be passed to the name lookup functions to filter the
+  /// results of name lookup. All of the predicates have the same type, so that
+  /// 
+  //@{
+  bool IsNestedNameSpecifier(NamedDecl *ND) const;
+  bool IsEnum(NamedDecl *ND) const;
+  bool IsClassOrStruct(NamedDecl *ND) const;
+  bool IsUnion(NamedDecl *ND) const;
   //@}
 };
   

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=82222&r1=82221&r2=82222&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Fri Sep 18 10:37:17 2009
@@ -1589,7 +1589,12 @@
 void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
                                 AccessSpecifier AS) {
   // Parse the tag portion of this.
-
+  if (Tok.is(tok::code_completion)) {
+    // Code completion for an enum name.
+    Actions.CodeCompleteTag(CurScope, DeclSpec::TST_enum);
+    ConsumeToken();
+  }
+  
   AttributeList *Attr = 0;
   // If attributes exist after tag, parse them.
   if (Tok.is(tok::kw___attribute))

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=82222&r1=82221&r2=82222&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Fri Sep 18 10:37:17 2009
@@ -527,6 +527,12 @@
     TagType = DeclSpec::TST_union;
   }
 
+  if (Tok.is(tok::code_completion)) {
+    // Code completion for a struct, class, or union name.
+    Actions.CodeCompleteTag(CurScope, TagType);
+    ConsumeToken();
+  }
+  
   AttributeList *Attr = 0;
   // If attributes exist after tag, parse them.
   if (Tok.is(tok::kw___attribute))

Modified: cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp?rev=82222&r1=82221&r2=82222&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp (original)
+++ cfe/trunk/lib/Sema/CodeCompleteConsumer.cpp Fri Sep 18 10:37:17 2009
@@ -11,6 +11,7 @@
 //
 //===----------------------------------------------------------------------===//
 #include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/Parse/Scope.h"
 #include "clang/Lex/Preprocessor.h"
 #include "Sema.h"
 #include "llvm/ADT/STLExtras.h"
@@ -41,11 +42,11 @@
       return;
   }
   
-  ResultSet Results;
+  ResultSet Results(*this);
   unsigned NextRank = 0;
   
   if (const RecordType *Record = BaseType->getAs<RecordType>()) {
-    NextRank = CollectMemberResults(Record->getDecl(), NextRank, Results);
+    NextRank = CollectMemberLookupResults(Record->getDecl(), NextRank, Results);
 
     if (getSema().getLangOptions().CPlusPlus) {
       if (!Results.empty())
@@ -64,6 +65,32 @@
   }
 }
 
+void CodeCompleteConsumer::CodeCompleteTag(Scope *S, ElaboratedType::TagKind TK) {
+  ResultSet::LookupFilter Filter = 0;
+  switch (TK) {
+  case ElaboratedType::TK_enum:
+    Filter = &CodeCompleteConsumer::IsEnum;
+    break;
+    
+  case ElaboratedType::TK_class:
+  case ElaboratedType::TK_struct:
+    Filter = &CodeCompleteConsumer::IsClassOrStruct;
+    break;
+    
+  case ElaboratedType::TK_union:
+    Filter = &CodeCompleteConsumer::IsUnion;
+    break;
+  }
+  
+  ResultSet Results(*this, Filter);
+  CollectLookupResults(S, 0, Results);
+  
+  // FIXME: In C++, we could have the start of a nested-name-specifier.
+  // Add those results (with a poorer rank, naturally).
+  
+  ProcessCodeCompleteResults(Results.data(), Results.size());
+}
+
 void 
 CodeCompleteConsumer::CodeCompleteQualifiedId(Scope *S, 
                                               NestedNameSpecifier *NNS,
@@ -74,8 +101,8 @@
   if (!Ctx)
     return;
   
-  ResultSet Results;
-  unsigned NextRank = CollectMemberResults(Ctx, 0, Results);
+  ResultSet Results(*this);
+  unsigned NextRank = CollectMemberLookupResults(Ctx, 0, Results);
   
   // The "template" keyword can follow "::" in the grammar
   if (!Results.empty())
@@ -92,6 +119,11 @@
   }
   
   // FIXME: Using declarations
+  // FIXME: Separate overload sets
+  
+  // Filter out any unwanted results.
+  if (Filter && !(Completer.*Filter)(R.Declaration))
+    return;
   
   Decl *CanonDecl = R.Declaration->getCanonicalDecl();
   unsigned IDNS = CanonDecl->getIdentifierNamespace();
@@ -164,23 +196,89 @@
   ShadowMaps.pop_back();
 }
 
+// Find the next outer declaration context corresponding to this scope.
+static DeclContext *findOuterContext(Scope *S) {
+  for (S = S->getParent(); S; S = S->getParent())
+    if (S->getEntity())
+      return static_cast<DeclContext *>(S->getEntity())->getPrimaryContext();
+  
+  return 0;
+}
+
+/// \brief Collect the results of searching for declarations within the given
+/// scope and its parent scopes.
+///
+/// \param S the scope in which we will start looking for declarations.
+///
+/// \param InitialRank the initial rank given to results in this scope.
+/// Larger rank values will be used for results found in parent scopes.
+unsigned CodeCompleteConsumer::CollectLookupResults(Scope *S, 
+                                                    unsigned InitialRank,
+                                                    ResultSet &Results) {
+  if (!S)
+    return InitialRank;
+  
+  // FIXME: Using directives!
+  
+  unsigned NextRank = InitialRank;
+  Results.EnterNewScope();
+  if (S->getEntity() && 
+      !((DeclContext *)S->getEntity())->isFunctionOrMethod()) {
+    // Look into this scope's declaration context, along with any of its
+    // parent lookup contexts (e.g., enclosing classes), up to the point
+    // where we hit the context stored in the next outer scope.
+    DeclContext *Ctx = (DeclContext *)S->getEntity();
+    DeclContext *OuterCtx = findOuterContext(S);
+    
+    for (; Ctx && Ctx->getPrimaryContext() != OuterCtx;
+         Ctx = Ctx->getLookupParent()) {
+      if (Ctx->isFunctionOrMethod())
+        continue;
+      
+      NextRank = CollectMemberLookupResults(Ctx, NextRank + 1, Results);
+    }
+  } else if (!S->getParent()) {
+    // Look into the translation unit scope. We walk through the translation
+    // unit's declaration context, because the Scope itself won't have all of
+    // the declarations if 
+    NextRank = CollectMemberLookupResults(
+                                    getSema().Context.getTranslationUnitDecl(), 
+                                          NextRank + 1, Results);
+  } else {
+    // Walk through the declarations in this Scope.
+    for (Scope::decl_iterator D = S->decl_begin(), DEnd = S->decl_end();
+         D != DEnd; ++D) {
+      if (NamedDecl *ND = dyn_cast<NamedDecl>((Decl *)((*D).get())))
+        Results.MaybeAddResult(Result(ND, NextRank));        
+    }
+    
+    NextRank = NextRank + 1;
+  }
+  
+  // Lookup names in the parent scope.
+  NextRank = CollectLookupResults(S->getParent(), NextRank, Results);
+  Results.ExitScope();
+  
+  return NextRank;
+}
+
 /// \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 InitialRank the initial rank given to results in this declaration
+/// context. 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) {
+unsigned CodeCompleteConsumer::CollectMemberLookupResults(DeclContext *Ctx, 
+                                                          unsigned InitialRank,
+                                                          ResultSet &Results) {
   // Enumerate all of the results in this context.
   Results.EnterNewScope();
   for (DeclContext *CurCtx = Ctx->getPrimaryContext(); CurCtx; 
@@ -188,10 +286,8 @@
     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
+      if (NamedDecl *ND = dyn_cast<NamedDecl>(*D))
         Results.MaybeAddResult(Result(ND, InitialRank));
-      }
     }
   }
   
@@ -235,9 +331,9 @@
       
       // Collect results from this base class (and its bases).
       NextRank = std::max(NextRank, 
-                          CollectMemberResults(Record->getDecl(), 
-                                               InitialRank + 1, 
-                                               Results));
+                          CollectMemberLookupResults(Record->getDecl(), 
+                                                     InitialRank + 1, 
+                                                     Results));
     }
   }
   
@@ -247,6 +343,46 @@
   return NextRank;
 }
 
+/// \brief Determines whether the given declaration is suitable as the 
+/// start of a C++ nested-name-specifier, e.g., a class or namespace.
+bool CodeCompleteConsumer::IsNestedNameSpecifier(NamedDecl *ND) const {
+  // Allow us to find class templates, too.
+  if (ClassTemplateDecl *ClassTemplate = dyn_cast<ClassTemplateDecl>(ND))
+    ND = ClassTemplate->getTemplatedDecl();
+  
+  return getSema().isAcceptableNestedNameSpecifier(ND);
+}
+
+/// \brief Determines whether the given declaration is an enumeration.
+bool CodeCompleteConsumer::IsEnum(NamedDecl *ND) const {
+  return isa<EnumDecl>(ND);
+}
+
+/// \brief Determines whether the given declaration is a class or struct.
+bool CodeCompleteConsumer::IsClassOrStruct(NamedDecl *ND) const {
+  // Allow us to find class templates, too.
+  if (ClassTemplateDecl *ClassTemplate = dyn_cast<ClassTemplateDecl>(ND))
+    ND = ClassTemplate->getTemplatedDecl();
+  
+  if (RecordDecl *RD = dyn_cast<RecordDecl>(ND))
+    return RD->getTagKind() == TagDecl::TK_class ||
+           RD->getTagKind() == TagDecl::TK_struct;
+  
+  return false;
+}
+
+/// \brief Determines whether the given declaration is a union.
+bool CodeCompleteConsumer::IsUnion(NamedDecl *ND) const {
+  // Allow us to find class templates, too.
+  if (ClassTemplateDecl *ClassTemplate = dyn_cast<ClassTemplateDecl>(ND))
+    ND = ClassTemplate->getTemplatedDecl();
+
+  if (RecordDecl *RD = dyn_cast<RecordDecl>(ND))
+    return RD->getTagKind() == TagDecl::TK_union;
+  
+  return false;
+}
+
 namespace {
   struct VISIBILITY_HIDDEN SortCodeCompleteResult {
     typedef CodeCompleteConsumer::Result Result;

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=82222&r1=82221&r2=82222&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Fri Sep 18 10:37:17 2009
@@ -2083,6 +2083,7 @@
   virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S,
                                                    SourceLocation CCLoc);
 
+  bool isAcceptableNestedNameSpecifier(NamedDecl *SD);
   NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS);
 
 
@@ -3635,6 +3636,8 @@
                                                SourceLocation OpLoc,
                                                bool IsArrow);
   
+  virtual void CodeCompleteTag(Scope *S, unsigned TagSpec);
+
   virtual void CodeCompleteQualifiedId(Scope *S, const CXXScopeSpec &SS,
                                        bool EnteringContext);
   //@}

Modified: cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp?rev=82222&r1=82221&r2=82222&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp Fri Sep 18 10:37:17 2009
@@ -260,7 +260,7 @@
 
 /// \brief Determines whether the given declaration is an valid acceptable
 /// result for name lookup of a nested-name-specifier.
-bool isAcceptableNestedNameSpecifier(ASTContext &Context, NamedDecl *SD) {
+bool Sema::isAcceptableNestedNameSpecifier(NamedDecl *SD) {
   if (!SD)
     return false;
 
@@ -307,7 +307,7 @@
   assert(!Found.isAmbiguous() && "Cannot handle ambiguities here yet");
 
   NamedDecl *Result = Found;
-  if (isAcceptableNestedNameSpecifier(Context, Result))
+  if (isAcceptableNestedNameSpecifier(Result))
     return Result;
 
   return 0;
@@ -406,7 +406,7 @@
 
   // FIXME: Deal with ambiguities cleanly.
   NamedDecl *SD = Found;
-  if (isAcceptableNestedNameSpecifier(Context, SD)) {
+  if (isAcceptableNestedNameSpecifier(SD)) {
     if (!ObjectType.isNull() && !ObjectTypeSearchedInScope) {
       // C++ [basic.lookup.classref]p4:
       //   [...] If the name is found in both contexts, the
@@ -425,7 +425,7 @@
 
       // FIXME: Handle ambiguities in FoundOuter!
       NamedDecl *OuterDecl = FoundOuter;
-      if (isAcceptableNestedNameSpecifier(Context, OuterDecl) &&
+      if (isAcceptableNestedNameSpecifier(OuterDecl) &&
           OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() &&
           (!isa<TypeDecl>(OuterDecl) || !isa<TypeDecl>(SD) ||
            !Context.hasSameType(

Modified: cfe/trunk/lib/Sema/SemaCodeComplete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCodeComplete.cpp?rev=82222&r1=82221&r2=82222&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaCodeComplete.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCodeComplete.cpp Fri Sep 18 10:37:17 2009
@@ -34,6 +34,35 @@
   CodeCompleter->CodeCompleteMemberReferenceExpr(S, BaseType, IsArrow);
 }
 
+void Sema::CodeCompleteTag(Scope *S, unsigned TagSpec) {
+  if (!CodeCompleter)
+    return;
+  
+  TagDecl::TagKind TK;
+  switch ((DeclSpec::TST)TagSpec) {
+  case DeclSpec::TST_enum:
+    TK = TagDecl::TK_enum;
+    break;
+    
+  case DeclSpec::TST_union:
+    TK = TagDecl::TK_union;
+    break;
+    
+  case DeclSpec::TST_struct:
+    TK = TagDecl::TK_struct;
+    break;
+
+  case DeclSpec::TST_class:
+    TK = TagDecl::TK_class;
+    break;
+    
+  default:
+    assert(false && "Unknown type specifier kind in CodeCompleteTag");
+    return;
+  }
+  CodeCompleter->CodeCompleteTag(S, TK);
+}
+
 void Sema::CodeCompleteQualifiedId(Scope *S, const CXXScopeSpec &SS,
                                    bool EnteringContext) {
   if (!SS.getScopeRep() || !CodeCompleter)

Added: cfe/trunk/test/CodeCompletion/tag.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeCompletion/tag.c?rev=82222&view=auto

==============================================================================
--- cfe/trunk/test/CodeCompletion/tag.c (added)
+++ cfe/trunk/test/CodeCompletion/tag.c Fri Sep 18 10:37:17 2009
@@ -0,0 +1,15 @@
+// RUN: clang-cc -fsyntax-only -code-completion-dump=1 %s -o - | FileCheck -check-prefix=CC1 %s &&
+// RUN: true
+
+enum X { x };
+enum Y { y };
+struct Z { };
+
+void X();
+
+void test() {
+  enum X { x };
+  // CHECK-CC1: X : 0
+  // CHECK-CC1: Y : 2
+  // CHECK-CC1: X : 2 (Hidden)
+  enum

Propchange: cfe/trunk/test/CodeCompletion/tag.c

------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/CodeCompletion/tag.c

------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/CodeCompletion/tag.c

------------------------------------------------------------------------------
    svn:mime-type = text/plain





More information about the cfe-commits mailing list