Index: include/clang/AST/DeclBase.h =================================================================== --- include/clang/AST/DeclBase.h +++ include/clang/AST/DeclBase.h @@ -52,6 +52,7 @@ class LinkageSpecDecl; class Module; class NamedDecl; +class NestedNameSpecifier; class ObjCCategoryDecl; class ObjCCategoryImplDecl; class ObjCContainerDecl; @@ -1938,6 +1939,13 @@ return const_cast(this)->getOuterLexicalRecordContext(); } + /// Compute the qualification required to get from the current context to + /// the target context. Useful for suggesting spellings (e.g. in FixItHints). + /// + /// \returns a nested name specifier that refers into the target context, or + /// nullptr if no qualification is needed or possible. + NestedNameSpecifier *getQualifierFor(const DeclContext *Target) const; + /// Test if this context is part of the enclosing namespace set of /// the context NS, as defined in C++0x [namespace.def]p9. If either context /// isn't a namespace, this is equivalent to Equals(). Index: lib/AST/DeclBase.cpp =================================================================== --- lib/AST/DeclBase.cpp +++ lib/AST/DeclBase.cpp @@ -1770,6 +1770,38 @@ return OutermostRD; } +NestedNameSpecifier * +DeclContext::getQualifierFor(const DeclContext *Target) const { + SmallVector TargetParents; + + for (const DeclContext *CommonAncestor = Target; + CommonAncestor && !CommonAncestor->Encloses(this); + CommonAncestor = CommonAncestor->getLookupParent()) { + if (CommonAncestor->isTransparentContext() || + CommonAncestor->isFunctionOrMethod()) + continue; + + TargetParents.push_back(CommonAncestor); + } + + NestedNameSpecifier *Result = nullptr; + while (!TargetParents.empty()) { + const DeclContext *Parent = TargetParents.pop_back_val(); + + if (const auto *Namespace = dyn_cast(Parent)) { + if (Namespace->isAnonymousNamespace() || Namespace->isInline()) + continue; + + Result = + NestedNameSpecifier::Create(getParentASTContext(), Result, Namespace); + } else if (const auto *TD = dyn_cast(Parent)) + Result = NestedNameSpecifier::Create( + getParentASTContext(), Result, false, + getParentASTContext().getTypeDeclType(TD).getTypePtr()); + } + return Result; +} + bool DeclContext::InEnclosingNamespaceSetOf(const DeclContext *O) const { // For non-file contexts, this is equivalent to Equals. if (!isFileContext()) Index: lib/Sema/SemaCodeComplete.cpp =================================================================== --- lib/Sema/SemaCodeComplete.cpp +++ lib/Sema/SemaCodeComplete.cpp @@ -648,50 +648,6 @@ return iterator(DeclOrVector.get()->end()); } -/// Compute the qualification required to get from the current context -/// (\p CurContext) to the target context (\p TargetContext). -/// -/// \param Context the AST context in which the qualification will be used. -/// -/// \param CurContext the context where an entity is being named, which is -/// typically based on the current scope. -/// -/// \param TargetContext the context in which the named entity actually -/// resides. -/// -/// \returns a nested name specifier that refers into the target context, or -/// NULL if no qualification is needed. -static NestedNameSpecifier * -getRequiredQualification(ASTContext &Context, const DeclContext *CurContext, - const DeclContext *TargetContext) { - SmallVector TargetParents; - - for (const DeclContext *CommonAncestor = TargetContext; - CommonAncestor && !CommonAncestor->Encloses(CurContext); - CommonAncestor = CommonAncestor->getLookupParent()) { - if (CommonAncestor->isTransparentContext() || - CommonAncestor->isFunctionOrMethod()) - continue; - - TargetParents.push_back(CommonAncestor); - } - - NestedNameSpecifier *Result = nullptr; - while (!TargetParents.empty()) { - const DeclContext *Parent = TargetParents.pop_back_val(); - - if (const auto *Namespace = dyn_cast(Parent)) { - if (!Namespace->getIdentifier()) - continue; - - Result = NestedNameSpecifier::Create(Context, Result, Namespace); - } else if (const auto *TD = dyn_cast(Parent)) - Result = NestedNameSpecifier::Create( - Context, Result, false, Context.getTypeDeclType(TD).getTypePtr()); - } - return Result; -} - /// Determine whether \p Id is a name reserved for the implementation (C99 /// 7.1.3, C++ [lib.global.names]). static bool isReservedName(const IdentifierInfo *Id, @@ -801,8 +757,7 @@ R.QualifierIsInformative = false; if (!R.Qualifier) - R.Qualifier = getRequiredQualification(SemaRef.Context, CurContext, - R.Declaration->getDeclContext()); + R.Qualifier = CurContext->getQualifierFor(R.Declaration->getDeclContext()); return false; } @@ -3970,8 +3925,8 @@ // If we need a nested-name-specifier, add one now. if (!InContext) { - NestedNameSpecifier *NNS = getRequiredQualification( - S.Context, CurContext, Overridden->getDeclContext()); + NestedNameSpecifier *NNS = + CurContext->getQualifierFor(Overridden->getDeclContext()); if (NNS) { std::string Str; llvm::raw_string_ostream OS(Str); @@ -4241,7 +4196,7 @@ // 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); + Qualifier = CurContext->getQualifierFor(Enum); } Results.EnterNewScope(); Index: lib/Sema/SemaLookup.cpp =================================================================== --- lib/Sema/SemaLookup.cpp +++ lib/Sema/SemaLookup.cpp @@ -20,6 +20,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/NestedNameSpecifier.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/LangOptions.h" #include "clang/Lex/HeaderSearch.h" Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -1193,14 +1193,31 @@ // Produce a nice diagnostic if multiple values aren't handled. if (!UnhandledNames.empty()) { - DiagnosticBuilder DB = Diag(CondExpr->getExprLoc(), - TheDefaultStmt ? diag::warn_def_missing_case - : diag::warn_missing_case) - << (int)UnhandledNames.size(); + DiagnosticBuilder DB = + Diag(CondExpr->getExprLoc(), TheDefaultStmt + ? diag::warn_def_missing_case + : diag::warn_missing_case) + << (int)UnhandledNames.size(); for (size_t I = 0, E = std::min(UnhandledNames.size(), (size_t)3); I != E; ++I) DB << UnhandledNames[I]; + + std::string NewCases; + llvm::raw_string_ostream NewCasesOS(NewCases); + // Include \n to separate new cases added if there are many. + // This suppresses printing the (long) insert text. + char Sep = UnhandledNames.size() > 3 ? '\n' : ' '; + const NestedNameSpecifier *Qual = + CurContext->getQualifierFor(ED->isScoped() ? ED : ED->getParent()); + for (const auto& Name : UnhandledNames) { + NewCasesOS << "case "; + if (Qual) + Qual->print(NewCasesOS, Context.getPrintingPolicy()); + NewCasesOS << Name << ':' << Sep; + } + NewCasesOS << "break;"; + DB << FixItHint::CreateInsertion(SS->getEndLoc(), NewCasesOS.str()); } if (!hasCasesNotInSwitch) Index: test/FixIt/fixit-enum.cpp =================================================================== --- /dev/null +++ test/FixIt/fixit-enum.cpp @@ -0,0 +1,19 @@ +// RUN: cp %s %t.cpp +// RUN: not %clang_cc1 -fixit %t.cpp -Werror +// RUN: %clang_cc1 %t.cpp -Werror + +namespace ns { +inline namespace q { namespace { enum { A, B, C }; } } +} // namespace ns + +namespace ns2 { +enum class F { X, Y, Z }; + +void test(F f, decltype(ns::A) g) { + switch (f) {} + // CHECK: YYY + + switch (g) {} + // CHECK: ZZZ +} +} // namespace ns2