diff --git a/clang/include/clang/Lex/CodeCompletionHandler.h b/clang/include/clang/Lex/CodeCompletionHandler.h index ee02a227e6bf..bb8705b9a63d 100644 --- a/clang/include/clang/Lex/CodeCompletionHandler.h +++ b/clang/include/clang/Lex/CodeCompletionHandler.h @@ -16,6 +16,9 @@ namespace clang { +class IdentifierInfo; +class MacroInfo; + /// \brief Callback handler that receives notifications when performing code /// completion within the preprocessor. class CodeCompletionHandler { @@ -42,6 +45,16 @@ public: /// \param IsDefinition Whether this is the definition of a macro, e.g., /// in a #define. virtual void CodeCompleteMacroName(bool IsDefinition) { } + + /// \brief Callback invoked when performing code completion in a preprocessor + /// expression, such as the condition of an #if or #elif directive. + virtual void CodeCompletePreprocessorExpression() { } + + /// \brief Callback invoked when performing code completion inside a + /// function-like macro argument. + virtual void CodeCompleteMacroArgument(IdentifierInfo *Macro, + MacroInfo *MacroInfo, + unsigned ArgumentIndex) { } }; } diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index e4859630260c..1f680f8854f9 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1536,6 +1536,10 @@ private: virtual void CodeCompleteDirective(bool InConditional); virtual void CodeCompleteInConditionalExclusion(); virtual void CodeCompleteMacroName(bool IsDefinition); + virtual void CodeCompletePreprocessorExpression(); + virtual void CodeCompleteMacroArgument(IdentifierInfo *Macro, + MacroInfo *MacroInfo, + unsigned ArgumentIndex); }; } // end namespace clang diff --git a/clang/include/clang/Sema/Action.h b/clang/include/clang/Sema/Action.h index aebb2b4d2b7f..3b7fc2cc6239 100644 --- a/clang/include/clang/Sema/Action.h +++ b/clang/include/clang/Sema/Action.h @@ -43,7 +43,8 @@ namespace clang { // Lex. class Preprocessor; class Token; - + class MacroInfo; + /// Action - As the parser reads the input file and recognizes the productions /// of the grammar, it invokes methods on this class to turn the parsed input /// into something useful: e.g. a parse tree. @@ -3207,9 +3208,8 @@ public: /// \brief Code completion for a preprocessor directive. /// - /// \brief S The scope in which the preprocessor directive is being parsed. /// \brief InConditional Whether we're inside a preprocessor conditional. - virtual void CodeCompletePreprocessorDirective(Scope *S, bool InConditional) { + virtual void CodeCompletePreprocessorDirective(bool InConditional) { } /// \brief Code completion while in an area of the translation unit that was @@ -3219,13 +3219,31 @@ public: /// \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) { + virtual void CodeCompletePreprocessorMacroName(bool IsDefinition) { } + + /// \brief Callback invoked when performing code completion in a preprocessor + /// expression, such as the condition of an #if or #elif directive. + virtual void CodeCompletePreprocessorExpression() { } + + /// \brief Callback invoked when performing code completion inside a + /// function-like macro argument. + /// + /// \param S The scope in which this macro is being expanded. + /// + /// \param Macro The name of the macro that is going to be expanded. + /// + /// \param MacroInfo Information about the macro that is going to be + /// expanded. + /// + /// \param Argument The argument in which the code-completion token occurs. + virtual void CodeCompletePreprocessorMacroArgument(Scope *S, + IdentifierInfo *Macro, + MacroInfo *MacroInfo, + unsigned Argument) { } //@} }; diff --git a/clang/include/clang/Sema/CodeCompleteConsumer.h b/clang/include/clang/Sema/CodeCompleteConsumer.h index f9cf48495307..8b5a741ebd1c 100644 --- a/clang/include/clang/Sema/CodeCompleteConsumer.h +++ b/clang/include/clang/Sema/CodeCompleteConsumer.h @@ -174,7 +174,9 @@ public: 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 + CCC_MacroNameUse, + /// \brief Code completion occurred within a preprocessor expression. + CCC_PreprocessorExpression }; private: diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index c7a83ef662c2..202da5a41e7c 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4633,9 +4633,14 @@ public: ParsedType ReturnType, IdentifierInfo **SelIdents, unsigned NumSelIdents); - virtual void CodeCompletePreprocessorDirective(Scope *S, bool InConditional); + virtual void CodeCompletePreprocessorDirective(bool InConditional); virtual void CodeCompleteInPreprocessorConditionalExclusion(Scope *S); - virtual void CodeCompletePreprocessorMacroName(Scope *S, bool IsDefinition); + virtual void CodeCompletePreprocessorMacroName(bool IsDefinition); + virtual void CodeCompletePreprocessorExpression(); + virtual void CodeCompletePreprocessorMacroArgument(Scope *S, + IdentifierInfo *Macro, + MacroInfo *MacroInfo, + unsigned Argument); void GatherGlobalCodeCompletions( llvm::SmallVectorImpl &Results); //@} diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index 384edb4502b1..117d3775c0e1 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -278,7 +278,8 @@ void ASTUnit::CacheCodeCompletionResults() { | (1 << (CodeCompletionContext::CCC_Statement - 1)) | (1 << (CodeCompletionContext::CCC_Expression - 1)) | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)) - | (1 << (CodeCompletionContext::CCC_MacroNameUse - 1)); + | (1 << (CodeCompletionContext::CCC_MacroNameUse - 1)) + | (1 << (CodeCompletionContext::CCC_PreprocessorExpression - 1)); CachedResult.Priority = Results[I].Priority; CachedResult.Kind = Results[I].CursorKind; @@ -1550,6 +1551,7 @@ void CalculateHiddenNames(const CodeCompletionContext &Context, case CodeCompletionContext::CCC_ObjCProtocolName: case CodeCompletionContext::CCC_MacroName: case CodeCompletionContext::CCC_MacroNameUse: + case CodeCompletionContext::CCC_PreprocessorExpression: // If we're just looking for protocol or macro names, nothing can hide them. return; } diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp index 73f3d4ee7a0f..163e869400aa 100644 --- a/clang/lib/Lex/PPExpressions.cpp +++ b/clang/lib/Lex/PPExpressions.cpp @@ -149,6 +149,12 @@ static bool EvaluateValue(PPValue &Result, Token &PeekTok, DefinedTracker &DT, bool ValueLive, Preprocessor &PP) { DT.State = DefinedTracker::Unknown; + if (PeekTok.is(tok::code_completion)) { + if (PP.getCodeCompletionHandler()) + PP.getCodeCompletionHandler()->CodeCompletePreprocessorExpression(); + PP.LexUnexpandedToken(PeekTok); + } + // If this token's spelling is a pp-identifier, check to see if it is // 'defined' or if it is a macro. Note that we check here because many // keywords are pp-identifiers, so we can't check the kind. diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index ad05b40e42a8..894dc368e4c8 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -19,6 +19,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/LexDiagnostic.h" +#include "clang/Lex/CodeCompletionHandler.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" #include @@ -323,6 +324,13 @@ MacroArgs *Preprocessor::ReadFunctionLikeMacroArgs(Token &MacroName, // an argument value in a macro could expand to ',' or '(' or ')'. LexUnexpandedToken(Tok); + if (Tok.is(tok::code_completion)) { + if (CodeComplete) + CodeComplete->CodeCompleteMacroArgument(MacroName.getIdentifierInfo(), + MI, NumActuals); + LexUnexpandedToken(Tok); + } + if (Tok.is(tok::eof) || Tok.is(tok::eom)) { // "#if f(" & "#if f(\n" Diag(MacroName, diag::err_unterm_macro_invoc); // Do not lose the EOF/EOM. Return it to the client. diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 13db742f18fc..dd244bb895a2 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1133,7 +1133,7 @@ void Parser::FieldCallback::_anchor() { // Code-completion pass-through functions void Parser::CodeCompleteDirective(bool InConditional) { - Actions.CodeCompletePreprocessorDirective(getCurScope(), InConditional); + Actions.CodeCompletePreprocessorDirective(InConditional); } void Parser::CodeCompleteInConditionalExclusion() { @@ -1141,5 +1141,16 @@ void Parser::CodeCompleteInConditionalExclusion() { } void Parser::CodeCompleteMacroName(bool IsDefinition) { - Actions.CodeCompletePreprocessorMacroName(getCurScope(), IsDefinition); + Actions.CodeCompletePreprocessorMacroName(IsDefinition); +} + +void Parser::CodeCompletePreprocessorExpression() { + Actions.CodeCompletePreprocessorExpression(); +} + +void Parser::CodeCompleteMacroArgument(IdentifierInfo *Macro, + MacroInfo *MacroInfo, + unsigned ArgumentIndex) { + Actions.CodeCompletePreprocessorMacroArgument(getCurScope(), Macro, MacroInfo, + ArgumentIndex); } diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 06e65784e85d..e25a47c2a553 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -4608,7 +4608,7 @@ void Sema::CodeCompleteObjCMethodDeclSelector(Scope *S, Results.data(),Results.size()); } -void Sema::CodeCompletePreprocessorDirective(Scope *S, bool InConditional) { +void Sema::CodeCompletePreprocessorDirective(bool InConditional) { ResultBuilder Results(*this); Results.EnterNewScope(); @@ -4785,11 +4785,12 @@ void Sema::CodeCompletePreprocessorDirective(Scope *S, bool InConditional) { } void Sema::CodeCompleteInPreprocessorConditionalExclusion(Scope *S) { - CodeCompleteOrdinaryName(S, Action::PCC_RecoveryInFunction); + CodeCompleteOrdinaryName(S, + S->getFnParent()? Action::PCC_RecoveryInFunction + : Action::PCC_Namespace); } -void Sema::CodeCompletePreprocessorMacroName(Scope *S, bool IsDefinition) { - typedef CodeCompleteConsumer::Result Result; +void Sema::CodeCompletePreprocessorMacroName(bool IsDefinition) { ResultBuilder Results(*this); if (!IsDefinition && (!CodeCompleter || CodeCompleter->includeMacros())) { // Add just the names of macros, not their arguments. @@ -4812,6 +4813,40 @@ void Sema::CodeCompletePreprocessorMacroName(Scope *S, bool IsDefinition) { Results.data(), Results.size()); } +void Sema::CodeCompletePreprocessorExpression() { + ResultBuilder Results(*this); + + if (!CodeCompleter || CodeCompleter->includeMacros()) + AddMacroResults(PP, Results); + + // defined () + Results.EnterNewScope(); + CodeCompletionString *Pattern = new CodeCompletionString; + Pattern->AddTypedTextChunk("defined"); + Pattern->AddChunk(CodeCompletionString::CK_HorizontalSpace); + Pattern->AddChunk(CodeCompletionString::CK_LeftParen); + Pattern->AddPlaceholderChunk("macro"); + Pattern->AddChunk(CodeCompletionString::CK_RightParen); + Results.AddResult(Pattern); + Results.ExitScope(); + + HandleCodeCompleteResults(this, CodeCompleter, + CodeCompletionContext::CCC_PreprocessorExpression, + Results.data(), Results.size()); +} + +void Sema::CodeCompletePreprocessorMacroArgument(Scope *S, + IdentifierInfo *Macro, + MacroInfo *MacroInfo, + unsigned Argument) { + // FIXME: In the future, we could provide "overload" results, much like we + // do for function calls. + + CodeCompleteOrdinaryName(S, + S->getFnParent()? Action::PCC_RecoveryInFunction + : Action::PCC_Namespace); +} + void Sema::GatherGlobalCodeCompletions( llvm::SmallVectorImpl &Results) { ResultBuilder Builder(*this); diff --git a/clang/test/Index/complete-preprocessor.m b/clang/test/Index/complete-preprocessor.m index 14c4c150b316..d3a07d6f0699 100644 --- a/clang/test/Index/complete-preprocessor.m +++ b/clang/test/Index/complete-preprocessor.m @@ -11,6 +11,8 @@ #if defined(FOO) #endif +FOO(in,t) value; + // 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) // CHECK-CC1-NEXT: NotImplemented:{TypedText define}{HorizontalSpace }{Placeholder macro}{LeftParen (}{Placeholder args}{RightParen )} (30) @@ -55,3 +57,18 @@ // 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 +// RUN: c-index-test -code-completion-at=%s:11:5 %s | FileCheck -check-prefix=CHECK-CC4 %s +// CHECK-CC4: macro definition:{TypedText BAR} (70) +// CHECK-CC4: macro definition:{TypedText FOO}{LeftParen (}{Placeholder a}{Comma , }{Placeholder b}{RightParen )} (70) +// RUN: c-index-test -code-completion-at=%s:14:5 %s | FileCheck -check-prefix=CHECK-CC5 %s +// CHECK-CC5: NotImplemented:{TypedText const} (40) +// CHECK-CC5: NotImplemented:{TypedText double} (40) +// CHECK-CC5: NotImplemented:{TypedText enum} (40) +// CHECK-CC5: NotImplemented:{TypedText extern} (30) +// CHECK-CC5: NotImplemented:{TypedText float} (40) +// CHECK-CC5: macro definition:{TypedText FOO}{LeftParen (}{Placeholder a}{Comma , }{Placeholder b}{RightParen )} (70) +// CHECK-CC5: macro definition:{TypedText i386} (70) +// CHECK-CC5: TypedefDecl:{TypedText id} (40) +// CHECK-CC5: NotImplemented:{TypedText inline} (30) +// CHECK-CC5: NotImplemented:{TypedText int} (40) +// CHECK-CC5: NotImplemented:{TypedText long} (40)