clang-tools  15.0.0git
CodeCompletionStrings.cpp
Go to the documentation of this file.
1 //===--- CodeCompletionStrings.cpp -------------------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/RawCommentList.h"
12 #include "clang/Basic/SourceManager.h"
13 #include "clang/Sema/CodeCompleteConsumer.h"
14 #include "llvm/Support/JSON.h"
15 #include <limits>
16 #include <utility>
17 
18 namespace clang {
19 namespace clangd {
20 namespace {
21 
22 bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) {
23  return Chunk.Kind == CodeCompletionString::CK_Informative &&
24  llvm::StringRef(Chunk.Text).endswith("::");
25 }
26 
27 void appendEscapeSnippet(const llvm::StringRef Text, std::string *Out) {
28  for (const auto Character : Text) {
29  if (Character == '$' || Character == '}' || Character == '\\')
30  Out->push_back('\\');
31  Out->push_back(Character);
32  }
33 }
34 
35 void appendOptionalChunk(const CodeCompletionString &CCS, std::string *Out) {
36  for (const CodeCompletionString::Chunk &C : CCS) {
37  switch (C.Kind) {
38  case CodeCompletionString::CK_Optional:
39  assert(C.Optional &&
40  "Expected the optional code completion string to be non-null.");
41  appendOptionalChunk(*C.Optional, Out);
42  break;
43  default:
44  *Out += C.Text;
45  break;
46  }
47  }
48 }
49 
50 bool looksLikeDocComment(llvm::StringRef CommentText) {
51  // We don't report comments that only contain "special" chars.
52  // This avoids reporting various delimiters, like:
53  // =================
54  // -----------------
55  // *****************
56  return CommentText.find_first_not_of("/*-= \t\r\n") != llvm::StringRef::npos;
57 }
58 
59 } // namespace
60 
61 std::string getDocComment(const ASTContext &Ctx,
62  const CodeCompletionResult &Result,
63  bool CommentsFromHeaders) {
64  // FIXME: clang's completion also returns documentation for RK_Pattern if they
65  // contain a pattern for ObjC properties. Unfortunately, there is no API to
66  // get this declaration, so we don't show documentation in that case.
67  if (Result.Kind != CodeCompletionResult::RK_Declaration)
68  return "";
69  return Result.getDeclaration() ? getDeclComment(Ctx, *Result.getDeclaration())
70  : "";
71 }
72 
73 std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl) {
74  if (isa<NamespaceDecl>(Decl)) {
75  // Namespaces often have too many redecls for any particular redecl comment
76  // to be useful. Moreover, we often confuse file headers or generated
77  // comments with namespace comments. Therefore we choose to just ignore
78  // the comments for namespaces.
79  return "";
80  }
81  const RawComment *RC = getCompletionComment(Ctx, &Decl);
82  if (!RC)
83  return "";
84  // Sanity check that the comment does not come from the PCH. We choose to not
85  // write them into PCH, because they are racy and slow to load.
86  assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc()));
87  std::string Doc =
88  RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics());
89  if (!looksLikeDocComment(Doc))
90  return "";
91  // Clang requires source to be UTF-8, but doesn't enforce this in comments.
92  if (!llvm::json::isUTF8(Doc))
93  Doc = llvm::json::fixUTF8(Doc);
94  return Doc;
95 }
96 
97 void getSignature(const CodeCompletionString &CCS, std::string *Signature,
98  std::string *Snippet, std::string *RequiredQualifiers,
99  bool CompletingPattern) {
100  // Placeholder with this index will be ${0:…} to mark final cursor position.
101  // Usually we do not add $0, so the cursor is placed at end of completed text.
102  unsigned CursorSnippetArg = std::numeric_limits<unsigned>::max();
103  if (CompletingPattern) {
104  // In patterns, it's best to place the cursor at the last placeholder, to
105  // handle cases like
106  // namespace ${1:name} {
107  // ${0:decls}
108  // }
109  CursorSnippetArg =
110  llvm::count_if(CCS, [](const CodeCompletionString::Chunk &C) {
111  return C.Kind == CodeCompletionString::CK_Placeholder;
112  });
113  }
114  unsigned SnippetArg = 0;
115  bool HadObjCArguments = false;
116  bool HadInformativeChunks = false;
117  for (const auto &Chunk : CCS) {
118  // Informative qualifier chunks only clutter completion results, skip
119  // them.
120  if (isInformativeQualifierChunk(Chunk))
121  continue;
122 
123  switch (Chunk.Kind) {
124  case CodeCompletionString::CK_TypedText:
125  // The typed-text chunk is the actual name. We don't record this chunk.
126  // C++:
127  // In general our string looks like <qualifiers><name><signature>.
128  // So once we see the name, any text we recorded so far should be
129  // reclassified as qualifiers.
130  //
131  // Objective-C:
132  // Objective-C methods expressions may have multiple typed-text chunks,
133  // so we must treat them carefully. For Objective-C methods, all
134  // typed-text and informative chunks will end in ':' (unless there are
135  // no arguments, in which case we can safely treat them as C++).
136  //
137  // Completing a method declaration itself (not a method expression) is
138  // similar except that we use the `RequiredQualifiers` to store the
139  // text before the selector, e.g. `- (void)`.
140  if (!llvm::StringRef(Chunk.Text).endswith(":")) { // Treat as C++.
141  if (RequiredQualifiers)
142  *RequiredQualifiers = std::move(*Signature);
143  Signature->clear();
144  Snippet->clear();
145  } else { // Objective-C method with args.
146  // If this is the first TypedText to the Objective-C method, discard any
147  // text that we've previously seen (such as previous parameter selector,
148  // which will be marked as Informative text).
149  //
150  // TODO: Make previous parameters part of the signature for Objective-C
151  // methods.
152  if (!HadObjCArguments) {
153  HadObjCArguments = true;
154  // If we have no previous informative chunks (informative selector
155  // fragments in practice), we treat any previous chunks as
156  // `RequiredQualifiers` so they will be added as a prefix during the
157  // completion.
158  //
159  // e.g. to complete `- (void)doSomething:(id)argument`:
160  // - Completion name: `doSomething:`
161  // - RequiredQualifiers: `- (void)`
162  // - Snippet/Signature suffix: `(id)argument`
163  //
164  // This differs from the case when we're completing a method
165  // expression with a previous informative selector fragment.
166  //
167  // e.g. to complete `[self doSomething:nil ^somethingElse:(id)]`:
168  // - Previous Informative Chunk: `doSomething:`
169  // - Completion name: `somethingElse:`
170  // - Snippet/Signature suffix: `(id)`
171  if (!HadInformativeChunks) {
172  if (RequiredQualifiers)
173  *RequiredQualifiers = std::move(*Signature);
174  Snippet->clear();
175  }
176  Signature->clear();
177  } else { // Subsequent argument, considered part of snippet/signature.
178  *Signature += Chunk.Text;
179  *Snippet += Chunk.Text;
180  }
181  }
182  break;
183  case CodeCompletionString::CK_Text:
184  *Signature += Chunk.Text;
185  *Snippet += Chunk.Text;
186  break;
187  case CodeCompletionString::CK_Optional:
188  assert(Chunk.Optional);
189  // No need to create placeholders for default arguments in Snippet.
190  appendOptionalChunk(*Chunk.Optional, Signature);
191  break;
192  case CodeCompletionString::CK_Placeholder:
193  *Signature += Chunk.Text;
194  ++SnippetArg;
195  *Snippet +=
196  "${" +
197  std::to_string(SnippetArg == CursorSnippetArg ? 0 : SnippetArg) + ':';
198  appendEscapeSnippet(Chunk.Text, Snippet);
199  *Snippet += '}';
200  break;
201  case CodeCompletionString::CK_Informative:
202  HadInformativeChunks = true;
203  // For example, the word "const" for a const method, or the name of
204  // the base class for methods that are part of the base class.
205  *Signature += Chunk.Text;
206  // Don't put the informative chunks in the snippet.
207  break;
208  case CodeCompletionString::CK_ResultType:
209  // This is not part of the signature.
210  break;
211  case CodeCompletionString::CK_CurrentParameter:
212  // This should never be present while collecting completion items,
213  // only while collecting overload candidates.
214  llvm_unreachable("Unexpected CK_CurrentParameter while collecting "
215  "CompletionItems");
216  break;
217  case CodeCompletionString::CK_LeftParen:
218  case CodeCompletionString::CK_RightParen:
219  case CodeCompletionString::CK_LeftBracket:
220  case CodeCompletionString::CK_RightBracket:
221  case CodeCompletionString::CK_LeftBrace:
222  case CodeCompletionString::CK_RightBrace:
223  case CodeCompletionString::CK_LeftAngle:
224  case CodeCompletionString::CK_RightAngle:
225  case CodeCompletionString::CK_Comma:
226  case CodeCompletionString::CK_Colon:
227  case CodeCompletionString::CK_SemiColon:
228  case CodeCompletionString::CK_Equal:
229  case CodeCompletionString::CK_HorizontalSpace:
230  *Signature += Chunk.Text;
231  *Snippet += Chunk.Text;
232  break;
233  case CodeCompletionString::CK_VerticalSpace:
234  *Snippet += Chunk.Text;
235  // Don't even add a space to the signature.
236  break;
237  }
238  }
239 }
240 
241 std::string formatDocumentation(const CodeCompletionString &CCS,
242  llvm::StringRef DocComment) {
243  // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this
244  // information in the documentation field.
245  std::string Result;
246  const unsigned AnnotationCount = CCS.getAnnotationCount();
247  if (AnnotationCount > 0) {
248  Result += "Annotation";
249  if (AnnotationCount == 1) {
250  Result += ": ";
251  } else /* AnnotationCount > 1 */ {
252  Result += "s: ";
253  }
254  for (unsigned I = 0; I < AnnotationCount; ++I) {
255  Result += CCS.getAnnotation(I);
256  Result.push_back(I == AnnotationCount - 1 ? '\n' : ' ');
257  }
258  }
259  // Add brief documentation (if there is any).
260  if (!DocComment.empty()) {
261  if (!Result.empty()) {
262  // This means we previously added annotations. Add an extra newline
263  // character to make the annotations stand out.
264  Result.push_back('\n');
265  }
266  Result += DocComment;
267  }
268  return Result;
269 }
270 
271 std::string getReturnType(const CodeCompletionString &CCS) {
272  for (const auto &Chunk : CCS)
273  if (Chunk.Kind == CodeCompletionString::CK_ResultType)
274  return Chunk.Text;
275  return "";
276 }
277 
278 } // namespace clangd
279 } // namespace clang
clang::clangd::getDeclComment
std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl)
Similar to getDocComment, but returns the comment for a NamedDecl.
Definition: CodeCompletionStrings.cpp:73
Ctx
Context Ctx
Definition: TUScheduler.cpp:495
Snippet
std::string Snippet
Definition: CodeCompletionStringsTests.cpp:38
clang::clangd::remote::Character
char Character
Definition: Header.h:5
clang::clangd::getDocComment
std::string getDocComment(const ASTContext &Ctx, const CodeCompletionResult &Result, bool CommentsFromHeaders)
Gets a minimally formatted documentation comment of Result, with comment markers stripped.
Definition: CodeCompletionStrings.cpp:61
clang::clangd::formatDocumentation
std::string formatDocumentation(const CodeCompletionString &CCS, llvm::StringRef DocComment)
Assembles formatted documentation for a completion result.
Definition: CodeCompletionStrings.cpp:241
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
CodeCompletionStrings.h
clang::clangd::getSignature
void getSignature(const CodeCompletionString &CCS, std::string *Signature, std::string *Snippet, std::string *RequiredQualifiers, bool CompletingPattern)
Formats the signature for an item, as a display string and snippet.
Definition: CodeCompletionStrings.cpp:97
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::getReturnType
std::string getReturnType(const CodeCompletionString &CCS)
Gets detail to be used as the detail field in an LSP completion item.
Definition: CodeCompletionStrings.cpp:271
Out
CompiledFragmentImpl & Out
Definition: ConfigCompile.cpp:99
Signature
std::string Signature
Definition: CodeComplete.cpp:447