diff options
| author | Sam McCall <sam.mccall@gmail.com> | 2019-07-11 16:04:18 +0000 | 
|---|---|---|
| committer | Sam McCall <sam.mccall@gmail.com> | 2019-07-11 16:04:18 +0000 | 
| commit | 9470142ca59cfcf1e9184efbf408c5584a4a2a30 (patch) | |
| tree | b339089ad6d4a8a8e57f907678f6e10cde50514c | |
| parent | 5cc7c9ab93996d9e52d8c40cb0ef1f835f4f7e7c (diff) | |
| download | bcm5719-llvm-9470142ca59cfcf1e9184efbf408c5584a4a2a30.tar.gz bcm5719-llvm-9470142ca59cfcf1e9184efbf408c5584a4a2a30.zip  | |
[clangd] Implementation of auto type expansion.
Add a tweak for clangd to replace an auto keyword to the deduced type.
This way a user can declare something with auto and then have the
IDE/clangd replace auto with whatever type clangd thinks it is. In case
of long/complext types this makes is reduces writing effort for the
user.
The functionality is similar to the hover over the auto keyword.
Example (from the header):
```
/// Before:
///    auto x = Something();
///    ^^^^
/// After:
///    MyClass x = Something();
///    ^^^^^^^
```
Patch by kuhnel! (Christian Kühnel)
Differential Revision: https://reviews.llvm.org/D62855
llvm-svn: 365792
| -rw-r--r-- | clang-tools-extra/clangd/AST.cpp | 30 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/AST.h | 16 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/Selection.cpp | 13 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/Selection.h | 3 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/XRefs.cpp | 18 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/XRefs.h | 10 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp | 119 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/test/code-action-request.test | 70 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/unittests/ASTTests.cpp | 42 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/unittests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/unittests/TweakTests.cpp | 148 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/unittests/XRefsTests.cpp | 22 | 
13 files changed, 489 insertions, 4 deletions
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp index 113069a09eb..2f401f263b6 100644 --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -169,5 +169,35 @@ llvm::Optional<SymbolID> getSymbolID(const IdentifierInfo &II,    return SymbolID(USR);  } +std::string shortenNamespace(const llvm::StringRef OriginalName, +                             const llvm::StringRef CurrentNamespace) { +  llvm::SmallVector<llvm::StringRef, 8> OriginalParts; +  llvm::SmallVector<llvm::StringRef, 8> CurrentParts; +  llvm::SmallVector<llvm::StringRef, 8> Result; +  OriginalName.split(OriginalParts, "::"); +  CurrentNamespace.split(CurrentParts, "::"); +  auto MinLength = std::min(CurrentParts.size(), OriginalParts.size()); + +  unsigned DifferentAt = 0; +  while (DifferentAt < MinLength && +      CurrentParts[DifferentAt] == OriginalParts[DifferentAt]) { +    DifferentAt++; +  } + +  for (u_int i = DifferentAt; i < OriginalParts.size(); ++i) { +    Result.push_back(OriginalParts[i]); +  } +  return join(Result, "::"); +} + +std::string printType(const QualType QT, const DeclContext & Context){ +  PrintingPolicy PP(Context.getParentASTContext().getPrintingPolicy()); +  PP.SuppressTagKeyword = 1; +  return shortenNamespace( +      QT.getAsString(PP), +      printNamespaceScope(Context) ); +} + +  } // namespace clangd  } // namespace clang diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h index e0e84a0ffb1..d5edba7209b 100644 --- a/clang-tools-extra/clangd/AST.h +++ b/clang-tools-extra/clangd/AST.h @@ -67,6 +67,22 @@ llvm::Optional<SymbolID> getSymbolID(const IdentifierInfo &II,                                       const MacroInfo *MI,                                       const SourceManager &SM); +/// Returns a QualType as string. +std::string printType(const QualType QT, const DeclContext & Context); + +/// Try to shorten the OriginalName by removing namespaces from the left of +/// the string that are redundant in the CurrentNamespace. This way the type +/// idenfier become shorter and easier to read. +/// Limitation: It only handles the qualifier of the type itself, not that of +/// templates. +/// FIXME: change type of parameter CurrentNamespace to DeclContext , +/// take in to account using directives etc +/// Example: shortenNamespace("ns1::MyClass<ns1::OtherClass>", "ns1") +///    --> "MyClass<ns1::OtherClass>" +std::string  shortenNamespace(const llvm::StringRef OriginalName, +                              const llvm::StringRef CurrentNamespace); + +  } // namespace clangd  } // namespace clang diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp index 7e41de02758..d3e8fcf91f8 100644 --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -364,5 +364,18 @@ const Node *SelectionTree::commonAncestor() const {    return Ancestor;  } +const DeclContext& SelectionTree::Node::getDeclContext() const { +  for (const Node* CurrentNode = this; CurrentNode != nullptr; +       CurrentNode = CurrentNode->Parent) { +    if (const Decl* Current = CurrentNode->ASTNode.get<Decl>()) { +      if (CurrentNode != this) +        if (auto *DC = dyn_cast<DeclContext>(Current)) +          return *DC; +      return *Current->getDeclContext(); +    } +  } +  llvm_unreachable("A tree must always be rooted at TranslationUnitDecl."); +} +  } // namespace clangd  } // namespace clang diff --git a/clang-tools-extra/clangd/Selection.h b/clang-tools-extra/clangd/Selection.h index c7cee64a73b..04951c61f84 100644 --- a/clang-tools-extra/clangd/Selection.h +++ b/clang-tools-extra/clangd/Selection.h @@ -93,6 +93,9 @@ public:      ast_type_traits::DynTypedNode ASTNode;      // The extent to which this node is covered by the selection.      Selection Selected; +    // Walk up the AST to get the DeclContext of this Node, +    // which is not the node itself. +    const DeclContext& getDeclContext() const;    };    // The most specific common ancestor of all the selected nodes. diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 17a5a69545a..a59618902eb 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -870,7 +870,9 @@ public:  } // namespace  /// Retrieves the deduced type at a given location (auto, decltype). -bool hasDeducedType(ParsedAST &AST, SourceLocation SourceLocationBeg) { +/// SourceLocationBeg must point to the first character of the token +llvm::Optional<QualType> getDeducedType(ParsedAST &AST, +                                        SourceLocation SourceLocationBeg) {    Token Tok;    auto &ASTCtx = AST.getASTContext();    // Only try to find a deduced type if the token is auto or decltype. @@ -878,12 +880,20 @@ bool hasDeducedType(ParsedAST &AST, SourceLocation SourceLocationBeg) {        Lexer::getRawToken(SourceLocationBeg, Tok, ASTCtx.getSourceManager(),                           ASTCtx.getLangOpts(), false) ||        !Tok.is(tok::raw_identifier)) { -    return false; +    return {};    }    AST.getPreprocessor().LookUpIdentifierInfo(Tok);    if (!(Tok.is(tok::kw_auto) || Tok.is(tok::kw_decltype))) -    return false; -  return true; +    return {}; + +  DeducedTypeVisitor V(SourceLocationBeg); +  V.TraverseAST(AST.getASTContext()); +  return V.DeducedType; +} + +/// Retrieves the deduced type at a given location (auto, decltype). +bool hasDeducedType(ParsedAST &AST, SourceLocation SourceLocationBeg) { +  return (bool) getDeducedType(AST, SourceLocationBeg);  }  llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos, diff --git a/clang-tools-extra/clangd/XRefs.h b/clang-tools-extra/clangd/XRefs.h index 184cbefaf5a..318133a572a 100644 --- a/clang-tools-extra/clangd/XRefs.h +++ b/clang-tools-extra/clangd/XRefs.h @@ -141,6 +141,16 @@ llvm::Optional<TypeHierarchyItem> getTypeHierarchy(      ParsedAST &AST, Position Pos, int Resolve, TypeHierarchyDirection Direction,      const SymbolIndex *Index = nullptr, PathRef TUPath = PathRef{}); +/// Retrieves the deduced type at a given location (auto, decltype). +/// Retuns None unless SourceLocationBeg starts an auto/decltype token. +/// It will return the underlying type. +llvm::Optional<QualType> getDeducedType(ParsedAST &AST, +                                        SourceLocation SourceLocationBeg); + +/// Check if there is a deduced type at a given location (auto, decltype). +/// SourceLocationBeg must point to the first character of the token +bool hasDeducedType(ParsedAST &AST, SourceLocation SourceLocationBeg); +  } // namespace clangd  } // namespace clang diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt index 9b5aa035298..e7fcd5b3046 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt +++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt @@ -18,6 +18,7 @@ add_clang_library(clangDaemonTweaks OBJECT    RawStringLiteral.cpp    SwapIfBranches.cpp    ExtractVariable.cpp +  ExpandAutoType.cpp    LINK_LIBS    clangAST diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp new file mode 100644 index 00000000000..b37cda20fec --- /dev/null +++ b/clang-tools-extra/clangd/refactor/tweaks/ExpandAutoType.cpp @@ -0,0 +1,119 @@ +//===--- ReplaceAutoType.cpp -------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "refactor/Tweak.h" + +#include "Logger.h" +#include "clang/AST/Type.h" +#include "clang/AST/TypeLoc.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" +#include <climits> +#include <memory> +#include <string> +#include <AST.h> +#include "XRefs.h" +#include "llvm/ADT/StringExtras.h" + +namespace clang { +namespace clangd { + +/// Expand the "auto" type to the derived type +/// Before: +///    auto x = Something(); +///    ^^^^ +/// After: +///    MyClass x = Something(); +///    ^^^^^^^ +/// FIXME: Handle decltype as well +class ExpandAutoType : public Tweak { +public: +  const char *id() const final; +  Intent intent() const override { return Intent::Refactor;} +  bool prepare(const Selection &Inputs) override; +  Expected<Effect> apply(const Selection &Inputs) override; +  std::string title() const override; + +private: +  /// Cache the AutoTypeLoc, so that we do not need to search twice. +  llvm::Optional<clang::AutoTypeLoc> CachedLocation; + +  /// Create an error message with filename and line number in it +  llvm::Error createErrorMessage(const std::string& Message, +                                 const Selection &Inputs); + +}; + +REGISTER_TWEAK(ExpandAutoType) + +std::string ExpandAutoType::title() const { return "expand auto type"; } + +bool ExpandAutoType::prepare(const Selection& Inputs) { +  CachedLocation = llvm::None; +  if (auto *Node = Inputs.ASTSelection.commonAncestor()) { +    if (auto *TypeNode = Node->ASTNode.get<TypeLoc>()) { +      if (const AutoTypeLoc Result = TypeNode->getAs<AutoTypeLoc>()) { +        CachedLocation = Result; +      } +    } +  } +  return (bool) CachedLocation; +} + +Expected<Tweak::Effect> ExpandAutoType::apply(const Selection& Inputs) { +  auto& SrcMgr = Inputs.AST.getASTContext().getSourceManager(); + +  llvm::Optional<clang::QualType> DeducedType = +      getDeducedType(Inputs.AST, CachedLocation->getBeginLoc()); + +  // if we can't resolve the type, return an error message +  if (DeducedType == llvm::None || DeducedType->isNull()) { +    return createErrorMessage("Could not deduce type for 'auto' type", Inputs); +  } + +  // if it's a lambda expression, return an error message +  if (isa<RecordType>(*DeducedType) and +      dyn_cast<RecordType>(*DeducedType)->getDecl()->isLambda()) { +    return createErrorMessage("Could not expand type of lambda expression", +                              Inputs); +  } + +  // if it's a function expression, return an error message +  // naively replacing 'auto' with the type will break declarations. +  // FIXME: there are other types that have similar problems +  if (DeducedType->getTypePtr()->isFunctionPointerType()) { +    return createErrorMessage("Could not expand type of function pointer", +                              Inputs); +  } + +  std::string PrettyTypeName = printType(*DeducedType, +      Inputs.ASTSelection.commonAncestor()->getDeclContext()); + +  tooling::Replacement +      Expansion(SrcMgr, CharSourceRange(CachedLocation->getSourceRange(), true), +                PrettyTypeName); + +  return Tweak::Effect::applyEdit(tooling::Replacements(Expansion)); +} + +llvm::Error ExpandAutoType::createErrorMessage(const std::string& Message, +                                               const Selection& Inputs) { +  auto& SrcMgr = Inputs.AST.getASTContext().getSourceManager(); +  std::string ErrorMessage = +      Message + ": " + +          SrcMgr.getFilename(Inputs.Cursor).str() + " Line " + +          std::to_string(SrcMgr.getExpansionLineNumber(Inputs.Cursor)); + +  return llvm::createStringError(llvm::inconvertibleErrorCode(), +                                 ErrorMessage.c_str()); +} + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/test/code-action-request.test b/clang-tools-extra/clangd/test/code-action-request.test new file mode 100644 index 00000000000..5b7dd6b95b5 --- /dev/null +++ b/clang-tools-extra/clangd/test/code-action-request.test @@ -0,0 +1,70 @@ +# RUN: clangd -log=verbose -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"auto i = 0;"}}} +--- +{ +	"jsonrpc": "2.0", +	"id": 1, +	"method": "textDocument/codeAction", +	"params": { +		"textDocument": { +			"uri": "test:///main.cpp" +		}, +        "range": { +            "start": { +                "line": 0, +                "character": 0 +            }, +            "end": { +                "line": 0, +                "character": 4 +            } +        }, +        "context": { +            "diagnostics": [] +        } +    } +} +#      CHECK:  "id": 1, +# CHECK-NEXT:  "jsonrpc": "2.0", +# CHECK-NEXT:  "result": [ +# CHECK-NEXT:    { +# CHECK-NEXT:      "arguments": [ +# CHECK-NEXT:        { +# CHECK-NEXT:          "file": "file:///clangd-test/main.cpp", +# CHECK-NEXT:          "selection": { +# CHECK-NEXT:            "end": { +# CHECK-NEXT:              "character": 4, +# CHECK-NEXT:              "line": 0 +# CHECK-NEXT:            }, +# CHECK-NEXT:            "start": { +# CHECK-NEXT:              "character": 0, +# CHECK-NEXT:              "line": 0 +# CHECK-NEXT:            } +# CHECK-NEXT:          }, +# CHECK-NEXT:          "tweakID": "ExpandAutoType" +# CHECK-NEXT:        } +# CHECK-NEXT:      ], +# CHECK-NEXT:      "command": "clangd.applyTweak", +# CHECK-NEXT:      "title": "expand auto type" +# CHECK-NEXT:    } +# CHECK-NEXT:  ] +--- +{"jsonrpc":"2.0","id":4,"method":"workspace/executeCommand","params":{"command":"clangd.applyTweak","arguments":[{"file":"file:///clangd-test/main.cpp","selection":{"end":{"character":4,"line":0},"start":{"character":0,"line":0}},"tweakID":"ExpandAutoType"}]}} +#      CHECK:    "newText": "int", +# CHECK-NEXT:    "range": { +# CHECK-NEXT:      "end": { +# CHECK-NEXT:        "character": 4, +# CHECK-NEXT:        "line": 0 +# CHECK-NEXT:      }, +# CHECK-NEXT:      "start": { +# CHECK-NEXT:        "character": 0, +# CHECK-NEXT:        "line": 0 +# CHECK-NEXT:      } +# CHECK-NEXT:    } +--- +{"jsonrpc":"2.0","id":4,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} +---
\ No newline at end of file diff --git a/clang-tools-extra/clangd/unittests/ASTTests.cpp b/clang-tools-extra/clangd/unittests/ASTTests.cpp new file mode 100644 index 00000000000..1cca2658d4c --- /dev/null +++ b/clang-tools-extra/clangd/unittests/ASTTests.cpp @@ -0,0 +1,42 @@ +//===-- ASTTests.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "AST.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +TEST(ExpandAutoType, ShortenNamespace) { +  ASSERT_EQ("TestClass", shortenNamespace("TestClass", "")); + +  ASSERT_EQ("TestClass", shortenNamespace( +      "testnamespace::TestClass", "testnamespace")); + +  ASSERT_EQ( +      "namespace1::TestClass", +      shortenNamespace("namespace1::TestClass", "namespace2")); + +  ASSERT_EQ("TestClass", +            shortenNamespace("testns1::testns2::TestClass", +                             "testns1::testns2")); + +  ASSERT_EQ( +      "testns2::TestClass", +      shortenNamespace("testns1::testns2::TestClass", "testns1")); + +  ASSERT_EQ("TestClass<testns1::OtherClass>", +            shortenNamespace( +                "testns1::TestClass<testns1::OtherClass>", "testns1")); +} + + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt index eb2aa5fcff1..3749f9f0f9b 100644 --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -23,6 +23,7 @@ endif()  add_custom_target(ClangdUnitTests)  add_unittest(ClangdUnitTests ClangdTests    Annotations.cpp +  ASTTests.cpp    BackgroundIndexTests.cpp    CancellationTests.cpp    CanonicalIncludesTests.cpp diff --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp index 652afb0cec4..7a0d88405b4 100644 --- a/clang-tools-extra/clangd/unittests/TweakTests.cpp +++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp @@ -11,6 +11,7 @@  #include "TestTU.h"  #include "refactor/Tweak.h"  #include "clang/AST/Expr.h" +#include "clang/Basic/LLVM.h"  #include "clang/Rewrite/Core/Rewriter.h"  #include "clang/Tooling/Core/Replacement.h"  #include "llvm/ADT/StringRef.h" @@ -126,6 +127,19 @@ void checkTransform(llvm::StringRef ID, llvm::StringRef Input,    EXPECT_EQ(Output, std::string(*Result)) << Input;  } +/// Check if apply returns an error and that the @ErrorMessage is contained +/// in that error +void checkApplyContainsError(llvm::StringRef ID, llvm::StringRef Input, +                             const std::string& ErrorMessage) { +  auto Result = apply(ID, Input); +  ASSERT_FALSE(Result) << "expected error message:\n   " << ErrorMessage << +                       "\non input:" << Input; +  EXPECT_NE(std::string::npos, +            llvm::toString(Result.takeError()).find(ErrorMessage)) +            << "Wrong error message:\n  " << llvm::toString(Result.takeError()) +            << "\nexpected:\n  " << ErrorMessage; +} +  TEST(TweakTest, SwapIfBranches) {    llvm::StringLiteral ID = "SwapIfBranches"; @@ -517,6 +531,140 @@ int a = 123 EMPTY_FN(1) ;    )cpp");  } +TEST(TweakTest, ExpandAutoType) { +  llvm::StringLiteral ID = "ExpandAutoType"; + +  checkAvailable(ID, R"cpp( +    ^a^u^t^o^ i = 0; +  )cpp"); + +  checkNotAvailable(ID, R"cpp( +    auto ^i^ ^=^ ^0^;^ +  )cpp"); + +  llvm::StringLiteral Input = R"cpp( +    [[auto]] i = 0; +  )cpp"; +  llvm::StringLiteral Output = R"cpp( +    int i = 0; +  )cpp"; +  checkTransform(ID, Input, Output); + +  // check primitive type +  Input = R"cpp( +    au^to i = 0; +  )cpp"; +  Output = R"cpp( +    int i = 0; +  )cpp"; +  checkTransform(ID, Input, Output); + +  // check classes and namespaces +  Input = R"cpp( +    namespace testns { +      class TestClass { +        class SubClass {}; +      }; +    } +    ^auto C = testns::TestClass::SubClass(); +  )cpp"; +  Output = R"cpp( +    namespace testns { +      class TestClass { +        class SubClass {}; +      }; +    } +    testns::TestClass::SubClass C = testns::TestClass::SubClass(); +  )cpp"; +  checkTransform(ID, Input, Output); + +  // check that namespaces are shortened +  Input = R"cpp( +    namespace testns { +    class TestClass { +    }; +    void func() { ^auto C = TestClass(); } +    } +  )cpp"; +  Output = R"cpp( +    namespace testns { +    class TestClass { +    }; +    void func() { TestClass C = TestClass(); } +    } +  )cpp"; +  checkTransform(ID, Input, Output); + +  // unknown types in a template should not be replaced +  Input = R"cpp( +    template <typename T> void x() { +        ^auto y =  T::z(); +        } +  )cpp"; +  checkApplyContainsError(ID, Input, "Could not deduce type for 'auto' type"); + +  // undefined functions should not be replaced +  Input = R"cpp( +    a^uto x = doesnt_exist(); +  )cpp"; +  checkApplyContainsError(ID, Input, "Could not deduce type for 'auto' type"); + +  // function pointers should not be replaced +  Input = R"cpp( +    int foo(); +    au^to x = &foo; +  )cpp"; +  checkApplyContainsError(ID, Input, +      "Could not expand type of function pointer"); + +  // lambda types are not replaced +  Input = R"cpp( +    au^to x = []{}; +  )cpp"; +  checkApplyContainsError(ID, Input, +      "Could not expand type of lambda expression"); + +  // inline namespaces +  Input = R"cpp( +    inline namespace x { +      namespace { struct S; } +    } +    au^to y = S(); +  )cpp"; +  Output = R"cpp( +    inline namespace x { +      namespace { struct S; } +    } +    S y = S(); +  )cpp"; + +  // local class +  Input = R"cpp( +  namespace x { +    void y() { +      struct S{}; +      a^uto z = S(); +  }} +  )cpp"; +  Output = R"cpp( +  namespace x { +    void y() { +      struct S{}; +      S z = S(); +  }} +  )cpp"; +  checkTransform(ID, Input, Output); + +  // replace array types +  Input = R"cpp( +    au^to x = "test"; +  )cpp"; +  Output = R"cpp( +    const char * x = "test"; +  )cpp"; +  checkTransform(ID, Input, Output); +} +  } // namespace  } // namespace clangd  } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp index d77f0a22ab9..444edcf9834 100644 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -2139,6 +2139,28 @@ TEST(FindReferences, NoQueryForLocalSymbols) {    }  } +TEST(GetDeducedType, KwAutoExpansion) { +  struct Test { +    StringRef AnnotatedCode; +    const char *DeducedType; +  } Tests[] = { +      {"^auto i = 0;", "int"}, +      {"^auto f(){ return 1;};", "int"} +  }; +  for (Test T : Tests) { +    Annotations File(T.AnnotatedCode); +    auto AST = TestTU::withCode(File.code()).build(); +    ASSERT_TRUE(AST.getDiagnostics().empty()) << AST.getDiagnostics().begin()->Message; +    SourceManagerForFile SM("foo.cpp", File.code()); + +    for (Position Pos : File.points()) { +      auto Location = sourceLocationInMainFile(SM.get(), Pos); +      auto DeducedType = getDeducedType(AST, *Location); +      EXPECT_EQ(DeducedType->getAsString(), T.DeducedType); +    } +  } +} +  } // namespace  } // namespace clangd  } // namespace clang  | 

