diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -299,19 +299,27 @@ return SymbolID(USR); } -// FIXME: This should be handled while printing underlying decls instead. std::string printType(const QualType QT, const DeclContext &CurContext) { std::string Result; llvm::raw_string_ostream OS(Result); - auto Decls = - explicitReferenceTargets(DynTypedNode::create(QT), DeclRelation::Alias); - if (!Decls.empty()) - OS << getQualification(CurContext.getParentASTContext(), &CurContext, - Decls.front(), - /*VisibleNamespaces=*/llvm::ArrayRef{}); PrintingPolicy PP(CurContext.getParentASTContext().getPrintingPolicy()); - PP.SuppressScope = true; PP.SuppressTagKeyword = true; + PP.SuppressUnwrittenScope = true; + + class PrintCB : public PrintingCallbacks { + public: + PrintCB(const DeclContext *CurContext) : CurContext(CurContext) {} + virtual ~PrintCB() {} + virtual bool isScopeVisible(const DeclContext *DC) const { + return DC->Encloses(CurContext); + } + + private: + const DeclContext *CurContext; + }; + PrintCB PCB(&CurContext); + PP.Callbacks = &PCB; + QT.print(OS, PP); return OS.str(); } diff --git a/clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp --- a/clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp +++ b/clang-tools-extra/clangd/unittests/tweaks/ExpandAutoTypeTests.cpp @@ -65,6 +65,9 @@ EXPECT_EQ(apply(R"cpp(au^to x = "test";)cpp"), R"cpp(const char * x = "test";)cpp"); + EXPECT_EQ(apply("ns::Class * foo() { au^to c = foo(); }"), + "ns::Class * foo() { ns::Class * c = foo(); }"); + EXPECT_UNAVAILABLE("dec^ltype(au^to) x = 10;"); // expanding types in structured bindings is syntactically invalid. EXPECT_UNAVAILABLE("const ^auto &[x,y] = (int[]){1,2};"); diff --git a/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp --- a/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp +++ b/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp @@ -97,6 +97,22 @@ })cpp"; EXPECT_EQ(apply(ConstCheckInput), ConstCheckOutput); + // Check const qualifier with namespace + std::string ConstNamespaceCheckInput = R"cpp( +namespace X { struct Y { int z; }; } +int f(const X::Y &y) { + [[return y.z + y.z;]] +})cpp"; + std::string ConstNamespaceCheckOutput = R"cpp( +namespace X { struct Y { int z; }; } +int extracted(const X::Y &y) { +return y.z + y.z; +} +int f(const X::Y &y) { + return extracted(y); +})cpp"; + EXPECT_EQ(apply(ConstNamespaceCheckInput), ConstNamespaceCheckOutput); + // Don't extract when we need to make a function as a parameter. EXPECT_THAT(apply("void f() { [[int a; f();]] }"), StartsWith("fail")); diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -18,6 +18,7 @@ namespace clang { +class DeclContext; class LangOptions; class SourceManager; class Stmt; @@ -39,6 +40,15 @@ virtual std::string remapPath(StringRef Path) const { return std::string(Path); } + + /// When printing type to be inserted into code in specific context, this + /// callback can be used to avoid printing the redundant part of the + /// qualifier. For example, when inserting code inside namespace foo, we + /// should print bar::SomeType instead of foo::bar::SomeType. + /// To do this, shouldPrintScope should return true on "foo" NamespaceDecl. + /// The printing stops at the first isScopeVisible() == true, so there will + /// be no calls with outer scopes. + virtual bool isScopeVisible(const DeclContext *NS) const { return false; } }; /// Describes how types, statements, expressions, and declarations should be diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1225,6 +1225,9 @@ if (DC->isFunctionOrMethod()) return; + if (Policy.Callbacks && Policy.Callbacks->isScopeVisible(DC)) + return; + if (const auto *NS = dyn_cast(DC)) { if (Policy.SuppressUnwrittenScope && NS->isAnonymousNamespace()) return AppendScope(DC->getParent(), OS, NameInScope);