]> granicus.if.org Git - clang/commitdiff
Implement preprocessor code completion where a macro name is expected,
authorDouglas Gregor <dgregor@apple.com>
Tue, 24 Aug 2010 20:21:13 +0000 (20:21 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 24 Aug 2010 20:21:13 +0000 (20:21 +0000)
e.g., after #ifdef/#ifndef or #undef, or inside a defined <macroname>
expression in a preprocessor conditional.

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

12 files changed:
include/clang/Lex/CodeCompletionHandler.h
include/clang/Lex/Preprocessor.h
include/clang/Parse/Parser.h
include/clang/Sema/Action.h
include/clang/Sema/CodeCompleteConsumer.h
include/clang/Sema/Sema.h
lib/Frontend/ASTUnit.cpp
lib/Lex/PPDirectives.cpp
lib/Lex/PPExpressions.cpp
lib/Parse/Parser.cpp
lib/Sema/SemaCodeComplete.cpp
test/Index/complete-preprocessor.m

index 4cc3b12d4730077d46b40471d75523015f1b20ed..ee02a227e6bffc53af47fdbb71d98f93016ffaf1 100644 (file)
@@ -35,6 +35,13 @@ public:
   /// \brief Callback invoked when performing code completion within a block of
   /// code that was excluded due to preprocessor conditionals.
   virtual void CodeCompleteInConditionalExclusion() { }
+  
+  /// \brief Callback invoked when performing code completion in a context
+  /// where the name of a macro is expected.
+  ///
+  /// \param IsDefinition Whether this is the definition of a macro, e.g.,
+  /// in a #define.
+  virtual void CodeCompleteMacroName(bool IsDefinition) { }
 };
   
 }
index 712feaa42515717d26fa096a8c2d92270d1cb4de..543cb742262a26124086d1d0dac656336c60bfec 100644 (file)
@@ -382,6 +382,11 @@ public:
     CodeComplete = &Handler;
   }
   
+  /// \brief Retrieve the current code-completion handler.
+  CodeCompletionHandler *getCodeCompletionHandler() const {
+    return CodeComplete;
+  }
+  
   /// \brief Clear out the code completion handler.
   void clearCodeCompletionHandler() {
     CodeComplete = 0;
index 83b42fd172e9a2b1857f2b43e0f9a75a44022668..e4859630260c8193d2efb257ecd3426f82334a6f 100644 (file)
@@ -1535,6 +1535,7 @@ private:
   // Preprocessor code-completion pass-through
   virtual void CodeCompleteDirective(bool InConditional);
   virtual void CodeCompleteInConditionalExclusion();
+  virtual void CodeCompleteMacroName(bool IsDefinition);
 };
 
 }  // end namespace clang
index d407037515f21da9180f0e86f0529411169b2f54..aebb2b4d2b7f4171a837dbee04d8f57196a8b7bc 100644 (file)
@@ -3215,6 +3215,17 @@ public:
   /// \brief Code completion while in an area of the translation unit that was
   /// excluded due to preprocessor conditionals.
   virtual void CodeCompleteInPreprocessorConditionalExclusion(Scope *S) { }
+  
+  /// \brief Code completion in the preprocessor where an already-defined
+  /// macro name is expected, e.g., an #ifdef or #undef.
+  ///
+  /// \param S The scope in which the macro name occurs.
+  ///
+  /// \param IsDefinition Whether this code completion for a macro name occurs
+  /// in a definition of the macro (#define) or in another use that already
+  /// expects that the macro is defined (e.g., #undef or #ifdef).
+  virtual void CodeCompletePreprocessorMacroName(Scope *S, bool IsDefinition) { 
+  }
   //@}
 };
 
index 59d8a4fd04850a7fb08639e8cc72d445b408e483..f9cf48495307fd5ec6e287125a0e5c699d77009a 100644 (file)
@@ -169,7 +169,12 @@ public:
     CCC_Name,
     /// \brief Code completion occurred where a new name is expected and a
     /// qualified name is permissible.
-    CCC_PotentiallyQualifiedName
+    CCC_PotentiallyQualifiedName,
+    /// \brief Code completion occurred where an macro is being defined.
+    CCC_MacroName,
+    /// \brief Code completion occurred where a macro name is expected
+    /// (without any arguments, in the case of a function-like macro).
+    CCC_MacroNameUse
   };
 
 private:
index b4449ff45c9c571a536d9d18ed00679f71480540..1ad6ca690a62ece0caf933d40d52d4424981af2d 100644 (file)
@@ -4692,6 +4692,7 @@ public:
                                                   unsigned NumSelIdents);
   virtual void CodeCompletePreprocessorDirective(Scope *S, bool InConditional);
   virtual void CodeCompleteInPreprocessorConditionalExclusion(Scope *S);
+  virtual void CodeCompletePreprocessorMacroName(Scope *S, bool IsDefinition);
   void GatherGlobalCodeCompletions(
                   llvm::SmallVectorImpl<CodeCompleteConsumer::Result> &Results);
   //@}
index 8a15f92045e089940d03da18ff6fc015a5938920..384edb4502b1c7169846e1a9ca117b8bd1698bab 100644 (file)
@@ -277,7 +277,8 @@ void ASTUnit::CacheCodeCompletionResults() {
         | (1 << (CodeCompletionContext::CCC_ClassStructUnion - 1))
         | (1 << (CodeCompletionContext::CCC_Statement - 1))
         | (1 << (CodeCompletionContext::CCC_Expression - 1))
-        | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1));
+        | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1))
+        | (1 << (CodeCompletionContext::CCC_MacroNameUse - 1));
       
       CachedResult.Priority = Results[I].Priority;
       CachedResult.Kind = Results[I].CursorKind;
@@ -1547,7 +1548,9 @@ void CalculateHiddenNames(const CodeCompletionContext &Context,
     break;
     
   case CodeCompletionContext::CCC_ObjCProtocolName:
-    // If we're just looking for protocol names, nothing can hide them.
+  case CodeCompletionContext::CCC_MacroName:
+  case CodeCompletionContext::CCC_MacroNameUse:
+    // If we're just looking for protocol or macro names, nothing can hide them.
     return;
   }
   
@@ -1595,7 +1598,7 @@ void AugmentedCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &S,
 
   // Contains the set of names that are hidden by "local" completion results.
   llvm::StringSet<> HiddenNames;
-  
+  llvm::SmallVector<CodeCompletionString *, 4> StringsToDestroy;
   typedef CodeCompleteConsumer::Result Result;
   llvm::SmallVector<Result, 8> AllResults;
   for (ASTUnit::cached_completion_iterator 
@@ -1623,10 +1626,11 @@ void AugmentedCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &S,
     
     // Adjust priority based on similar type classes.
     unsigned Priority = C->Priority;
+    CodeCompletionString *Completion = C->Completion;
     if (!Context.getPreferredType().isNull()) {
       if (C->Kind == CXCursor_MacroDefinition) {
         Priority = getMacroUsagePriority(C->Completion->getTypedText(),
-                               Context.getPreferredType()->isAnyPointerType());
+                               Context.getPreferredType()->isAnyPointerType());        
       } else if (C->Type) {
         CanQualType Expected
           = S.Context.getCanonicalType(
@@ -1646,7 +1650,17 @@ void AugmentedCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &S,
       }
     }
     
-    AllResults.push_back(Result(C->Completion, Priority, C->Kind,
+    // Adjust the completion string, if required.
+    if (C->Kind == CXCursor_MacroDefinition &&
+        Context.getKind() == CodeCompletionContext::CCC_MacroNameUse) {
+      // Create a new code-completion string that just contains the
+      // macro name, without its arguments.
+      Completion = new CodeCompletionString;
+      Completion->AddTypedTextChunk(C->Completion->getTypedText());
+      StringsToDestroy.push_back(Completion);
+    }
+    
+    AllResults.push_back(Result(Completion, Priority, C->Kind, 
                                 C->Availability));
   }
   
@@ -1659,6 +1673,9 @@ void AugmentedCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &S,
   
   Next.ProcessCodeCompleteResults(S, Context, AllResults.data(),
                                   AllResults.size());
+  
+  for (unsigned I = 0, N = StringsToDestroy.size(); I != N; ++I)
+    delete StringsToDestroy[I];
 }
 
 
index e0729f123f0e983cb7bb543b9e8887bdcb4ab3e9..8da7def9ed4d2303fdc3fcc89a3d01a265b0bcdd 100644 (file)
@@ -75,6 +75,13 @@ void Preprocessor::ReadMacroName(Token &MacroNameTok, char isDefineUndef) {
   // Read the token, don't allow macro expansion on it.
   LexUnexpandedToken(MacroNameTok);
 
+  if (MacroNameTok.is(tok::code_completion)) {
+    if (CodeComplete)
+      CodeComplete->CodeCompleteMacroName(isDefineUndef == 1);
+    LexUnexpandedToken(MacroNameTok);
+    return;
+  }
+  
   // Missing macro name?
   if (MacroNameTok.is(tok::eom)) {
     Diag(MacroNameTok, diag::err_pp_missing_macro_name);
index 9920c4cb7cf85331ee607555a59e889278c57789..73f3d4ee7a0f7f4418a830ebbf37bc6e0dba0b72 100644 (file)
@@ -19,6 +19,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Lex/MacroInfo.h"
 #include "clang/Lex/LiteralSupport.h"
+#include "clang/Lex/CodeCompletionHandler.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Lex/LexDiagnostic.h"
 #include "llvm/ADT/APSInt.h"
@@ -92,6 +93,12 @@ static bool EvaluateDefined(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
     PP.LexUnexpandedToken(PeekTok);
   }
 
+  if (PeekTok.is(tok::code_completion)) {
+    if (PP.getCodeCompletionHandler())
+      PP.getCodeCompletionHandler()->CodeCompleteMacroName(false);
+    PP.LexUnexpandedToken(PeekTok);
+  }
+  
   // If we don't have a pp-identifier now, this is an error.
   if ((II = PeekTok.getIdentifierInfo()) == 0) {
     PP.Diag(PeekTok, diag::err_pp_defined_requires_identifier);
@@ -697,7 +704,7 @@ EvaluateDirectiveExpression(IdentifierInfo *&IfNDefMacro) {
   // Peek ahead one token.
   Token Tok;
   Lex(Tok);
-
+  
   // C99 6.10.1p3 - All expressions are evaluated as intmax_t or uintmax_t.
   unsigned BitWidth = getTargetInfo().getIntMaxTWidth();
 
index 230506d85f73cf4fbfef1435dff2028731776922..13db742f18fc29555a47c7d4b5ca3d569104de0e 100644 (file)
@@ -1139,3 +1139,7 @@ void Parser::CodeCompleteDirective(bool InConditional) {
 void Parser::CodeCompleteInConditionalExclusion() {
   Actions.CodeCompleteInPreprocessorConditionalExclusion(getCurScope());
 }
+
+void Parser::CodeCompleteMacroName(bool IsDefinition) {
+  Actions.CodeCompletePreprocessorMacroName(getCurScope(), IsDefinition);
+}
index 14043e8b62c354e466b7ed7149b23106ae06eeb0..97742654ae79d2106deef54d4d434ea716396b54 100644 (file)
@@ -4787,6 +4787,30 @@ void Sema::CodeCompleteInPreprocessorConditionalExclusion(Scope *S) {
   CodeCompleteOrdinaryName(S, Action::PCC_RecoveryInFunction);
 }
 
+void Sema::CodeCompletePreprocessorMacroName(Scope *S, bool IsDefinition) {
+  typedef CodeCompleteConsumer::Result Result;
+  ResultBuilder Results(*this);
+  if (!IsDefinition && (!CodeCompleter || CodeCompleter->includeMacros())) {
+    // Add just the names of macros, not their arguments.    
+    Results.EnterNewScope();
+    for (Preprocessor::macro_iterator M = PP.macro_begin(), 
+                                   MEnd = PP.macro_end();
+         M != MEnd; ++M) {
+      CodeCompletionString *Pattern = new CodeCompletionString;
+      Pattern->AddTypedTextChunk(M->first->getName());
+      Results.AddResult(Pattern);
+    }
+    Results.ExitScope();
+  } else if (IsDefinition) {
+    // FIXME: Can we detect when the user just wrote an include guard above?
+  }
+  
+  HandleCodeCompleteResults(this, CodeCompleter, 
+                      IsDefinition? CodeCompletionContext::CCC_MacroName
+                                  : CodeCompletionContext::CCC_MacroNameUse,
+                            Results.data(), Results.size()); 
+}
+
 void Sema::GatherGlobalCodeCompletions(
                  llvm::SmallVectorImpl<CodeCompleteConsumer::Result> &Results) {
   ResultBuilder Builder(*this);
index b951c1622e88a04a9ccb4971e1dca0e62bcabe74..14c4c150b316cb035c515019e81fe6fc9c60d885 100644 (file)
@@ -4,7 +4,12 @@
 #if 1
 #endif
 
-
+#define FOO(a, b) a##b
+#define BAR
+#ifdef FOO
+#endif
+#if defined(FOO)
+#endif
 
 // RUN: c-index-test -code-completion-at=%s:4:2 %s | FileCheck -check-prefix=CHECK-CC1 %s
 // CHECK-CC1: NotImplemented:{TypedText define}{HorizontalSpace  }{Placeholder macro} (30)
@@ -45,3 +50,8 @@
 // CHECK-CC2-NEXT: NotImplemented:{TypedText pragma}{HorizontalSpace  }{Placeholder arguments} (30)
 // CHECK-CC2-NEXT: NotImplemented:{TypedText undef}{HorizontalSpace  }{Placeholder macro} (30)
 // CHECK-CC2-NEXT: NotImplemented:{TypedText warning}{HorizontalSpace  }{Placeholder message} (30)
+// RUN: c-index-test -code-completion-at=%s:9:8 %s | FileCheck -check-prefix=CHECK-CC3 %s
+// CHECK-CC3: NotImplemented:{TypedText BAR} (30)
+// CHECK-CC3: NotImplemented:{TypedText FOO} (30)
+// RUN: c-index-test -code-completion-at=%s:11:12 %s | FileCheck -check-prefix=CHECK-CC3 %s
+// RUN: c-index-test -code-completion-at=%s:11:13 %s | FileCheck -check-prefix=CHECK-CC3 %s