From: Douglas Gregor Date: Tue, 24 Aug 2010 20:21:13 +0000 (+0000) Subject: Implement preprocessor code completion where a macro name is expected, X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1fbb447e9d43c2c676e94081fbfee7eb6cbe933b;p=clang Implement preprocessor code completion where a macro name is expected, e.g., after #ifdef/#ifndef or #undef, or inside a defined expression in a preprocessor conditional. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@111954 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Lex/CodeCompletionHandler.h b/include/clang/Lex/CodeCompletionHandler.h index 4cc3b12d47..ee02a227e6 100644 --- a/include/clang/Lex/CodeCompletionHandler.h +++ b/include/clang/Lex/CodeCompletionHandler.h @@ -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) { } }; } diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 712feaa425..543cb74226 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -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; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 83b42fd172..e485963026 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -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 diff --git a/include/clang/Sema/Action.h b/include/clang/Sema/Action.h index d407037515..aebb2b4d2b 100644 --- a/include/clang/Sema/Action.h +++ b/include/clang/Sema/Action.h @@ -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) { + } //@} }; diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index 59d8a4fd04..f9cf484953 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -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: diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index b4449ff45c..1ad6ca690a 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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 &Results); //@} diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 8a15f92045..384edb4502 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -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 StringsToDestroy; typedef CodeCompleteConsumer::Result Result; llvm::SmallVector 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]; } diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index e0729f123f..8da7def9ed 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -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); diff --git a/lib/Lex/PPExpressions.cpp b/lib/Lex/PPExpressions.cpp index 9920c4cb7c..73f3d4ee7a 100644 --- a/lib/Lex/PPExpressions.cpp +++ b/lib/Lex/PPExpressions.cpp @@ -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(); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 230506d85f..13db742f18 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1139,3 +1139,7 @@ void Parser::CodeCompleteDirective(bool InConditional) { void Parser::CodeCompleteInConditionalExclusion() { Actions.CodeCompleteInPreprocessorConditionalExclusion(getCurScope()); } + +void Parser::CodeCompleteMacroName(bool IsDefinition) { + Actions.CodeCompletePreprocessorMacroName(getCurScope(), IsDefinition); +} diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 14043e8b62..97742654ae 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -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 &Results) { ResultBuilder Builder(*this); diff --git a/test/Index/complete-preprocessor.m b/test/Index/complete-preprocessor.m index b951c1622e..14c4c150b3 100644 --- a/test/Index/complete-preprocessor.m +++ b/test/Index/complete-preprocessor.m @@ -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