Index: lib/Sema/SemaCodeComplete.cpp =================================================================== --- lib/Sema/SemaCodeComplete.cpp +++ lib/Sema/SemaCodeComplete.cpp @@ -18,6 +18,7 @@ #include "clang/AST/QualTypeNames.h" #include "clang/AST/Type.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/Specifiers.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/MacroInfo.h" @@ -1984,6 +1985,81 @@ } } +namespace { +/// Information that allows to avoid completing redundant enumerators. +struct CoveredEnumerators { + llvm::SmallPtrSet Seen; + NestedNameSpecifier *SuggestedQualifier = nullptr; + + // Determine which enumerators we have already seen in the switch statement. + // FIXME: Ideally, we would also be able to look *past* the code-completion + // 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. + static CoveredEnumerators gather(SwitchStmt *Switch) { + CoveredEnumerators Enumerators; + for (SwitchCase *SC = Switch->getSwitchCaseList(); SC; + SC = SC->getNextSwitchCase()) { + CaseStmt *Case = dyn_cast(SC); + if (!Case) + continue; + + Expr *CaseVal = Case->getLHS()->IgnoreParenCasts(); + if (auto *DRE = dyn_cast(CaseVal)) + if (auto *Enumerator = dyn_cast(DRE->getDecl())) { + // We look into the AST of the case statement to determine which + // enumerator was named. Alternatively, we could compute the value of + // the integral constant expression, then compare it against the + // values of each enumerator. However, value-based approach would not + // work as well with C++ templates where enumerators declared within a + // template are type- and value-dependent. + Enumerators.Seen.insert(Enumerator); + + // 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, our completions are TagDecl::TK_union, + // TagDecl::TK_struct, and TagDecl::TK_class, rather than TK_union, + // TK_struct, and TK_class. + Enumerators.SuggestedQualifier = DRE->getQualifier(); + } + } + return Enumerators; + } +}; +} // namespace + +static void AddEnumerators(ResultBuilder &Results, ASTContext &Context, + EnumDecl *Enum, DeclContext *CurContext, + const CoveredEnumerators &Enumerators, + llvm::Optional FixIt = llvm::None) { + NestedNameSpecifier *Qualifier = Enumerators.SuggestedQualifier; + if (Context.getLangOpts().CPlusPlus && !Qualifier && Enumerators.Seen.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); + } + + CodeCompletionBuilder Builder(Results.getAllocator(), + Results.getCodeCompletionTUInfo()); + Results.EnterNewScope(); + for (auto *E : Enum->enumerators()) { + if (Enumerators.Seen.count(E)) + continue; + + CodeCompletionResult R(E, CCP_EnumInCase, Qualifier); + if (FixIt) + R.FixIts.push_back(*FixIt); + Results.AddResult(R, CurContext, nullptr, false); + } + Results.ExitScope(); +} + /// Add language constructs that show up for "ordinary" names. static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, Scope *S, Sema &SemaRef, ResultBuilder &Results) { @@ -2206,6 +2282,24 @@ // Switch-specific statements. if (SemaRef.getCurFunction() && !SemaRef.getCurFunction()->SwitchStack.empty()) { + EnumDecl *EnumDef = nullptr; + SwitchStmt *Switch = + SemaRef.getCurFunction()->SwitchStack.back().getPointer(); + if (Switch->getCond()) { + QualType SwitchType = Switch->getCond()->IgnoreImplicit()->getType(); + if (SwitchType->isEnumeralType()) + EnumDef = SwitchType->castAs()->getDecl()->getDefinition(); + } + if (EnumDef && SemaRef.CodeCompleter->includeFixIts()) { + // case + AddEnumerators( + Results, SemaRef.Context, EnumDef, SemaRef.CurContext, + CoveredEnumerators::gather(Switch), + FixItHint::CreateInsertion(SemaRef.getPreprocessor() + .getCodeCompletionTokenRange() + .getBegin(), + "case ")); + } // case expression: Builder.AddTypedTextChunk("case"); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); @@ -4225,36 +4319,6 @@ SmallVector IgnoreDecls; }; -namespace { -/// Information that allows to avoid completing redundant enumerators. -struct CoveredEnumerators { - llvm::SmallPtrSet Seen; - NestedNameSpecifier *SuggestedQualifier = nullptr; -}; -} // namespace - -static void AddEnumerators(ResultBuilder &Results, ASTContext &Context, - EnumDecl *Enum, DeclContext *CurContext, - const CoveredEnumerators &Enumerators) { - NestedNameSpecifier *Qualifier = Enumerators.SuggestedQualifier; - if (Context.getLangOpts().CPlusPlus && !Qualifier && Enumerators.Seen.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); - } - - Results.EnterNewScope(); - for (auto *E : Enum->enumerators()) { - if (Enumerators.Seen.count(E)) - continue; - - CodeCompletionResult R(E, CCP_EnumInCase, Qualifier); - Results.AddResult(R, CurContext, nullptr, false); - } - Results.ExitScope(); -} - /// Try to find a corresponding FunctionProtoType for function-like types (e.g. /// function pointers, std::function, etc). static const FunctionProtoType *TryDeconstructFunctionLike(QualType T) { @@ -4978,49 +5042,12 @@ if (EnumDecl *Def = Enum->getDefinition()) Enum = Def; - // Determine which enumerators we have already seen in the switch statement. - // FIXME: Ideally, we would also be able to look *past* the code-completion - // 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. - CoveredEnumerators Enumerators; - for (SwitchCase *SC = Switch->getSwitchCaseList(); SC; - SC = SC->getNextSwitchCase()) { - CaseStmt *Case = dyn_cast(SC); - if (!Case) - continue; - - Expr *CaseVal = Case->getLHS()->IgnoreParenCasts(); - if (auto *DRE = dyn_cast(CaseVal)) - if (auto *Enumerator = - dyn_cast(DRE->getDecl())) { - // We look into the AST of the case statement to determine which - // enumerator was named. Alternatively, we could compute the value of - // the integral constant expression, then compare it against the - // values of each enumerator. However, value-based approach would not - // work as well with C++ templates where enumerators declared within a - // template are type- and value-dependent. - Enumerators.Seen.insert(Enumerator); - - // 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, our completions are TagDecl::TK_union, - // TagDecl::TK_struct, and TagDecl::TK_class, rather than TK_union, - // TK_struct, and TK_class. - Enumerators.SuggestedQualifier = DRE->getQualifier(); - } - } - // Add any enumerators that have not yet been mentioned. ResultBuilder Results(*this, CodeCompleter->getAllocator(), CodeCompleter->getCodeCompletionTUInfo(), CodeCompletionContext::CCC_Expression); - AddEnumerators(Results, Context, Enum, CurContext, Enumerators); + AddEnumerators(Results, Context, Enum, CurContext, + CoveredEnumerators::gather(Switch)); if (CodeCompleter->includeMacros()) { AddMacroResults(PP, Results, CodeCompleter->loadExternal(), false); Index: test/CodeCompletion/enum-switch-case.cpp =================================================================== --- test/CodeCompletion/enum-switch-case.cpp +++ test/CodeCompletion/enum-switch-case.cpp @@ -18,11 +18,13 @@ case N::Yellow: break; - case + case N::Green: // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:21:8 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s // CHECK-CC1: Blue : [#N::Color#]N::Blue // CHECK-CC1-NEXT: Green : [#N::Color#]N::Green // CHECK-CC1-NEXT: Indigo : [#N::Color#]N::Indigo // CHECK-CC1-NEXT: Orange : [#N::Color#]N::Orange // CHECK-CC1-NEXT: Violet : [#N::Color#]N::Violet - + V + // RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:28:4 %s -o - | FileCheck -check-prefix=CHECK-UNQUAL %s + // CHECK-UNQUAL: Violet : [#N::Color#]N::Violet (requires fix-it: {28:3-28:3} to "case ")