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 9 #include "CodeCompletionStrings.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/AST/DeclObjC.h" 12 #include "clang/AST/RawCommentList.h" 13 #include "clang/Basic/SourceManager.h" 14 #include <utility> 15 16 namespace clang { 17 namespace clangd { 18 namespace { 19 20 bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) { 21 return Chunk.Kind == CodeCompletionString::CK_Informative && 22 llvm::StringRef(Chunk.Text).endswith("::"); 23 } 24 25 void appendEscapeSnippet(const llvm::StringRef Text, std::string *Out) { 26 for (const auto Character : Text) { 27 if (Character == '$' || Character == '}' || Character == '\\') 28 Out->push_back('\\'); 29 Out->push_back(Character); 30 } 31 } 32 33 bool looksLikeDocComment(llvm::StringRef CommentText) { 34 // We don't report comments that only contain "special" chars. 35 // This avoids reporting various delimiters, like: 36 // ================= 37 // ----------------- 38 // ***************** 39 return CommentText.find_first_not_of("/*-= \t\r\n") != llvm::StringRef::npos; 40 } 41 42 } // namespace 43 44 std::string getDocComment(const ASTContext &Ctx, 45 const CodeCompletionResult &Result, 46 bool CommentsFromHeaders) { 47 // FIXME: clang's completion also returns documentation for RK_Pattern if they 48 // contain a pattern for ObjC properties. Unfortunately, there is no API to 49 // get this declaration, so we don't show documentation in that case. 50 if (Result.Kind != CodeCompletionResult::RK_Declaration) 51 return ""; 52 return Result.getDeclaration() ? getDeclComment(Ctx, *Result.getDeclaration()) 53 : ""; 54 } 55 56 std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl) { 57 if (isa<NamespaceDecl>(Decl)) { 58 // Namespaces often have too many redecls for any particular redecl comment 59 // to be useful. Moreover, we often confuse file headers or generated 60 // comments with namespace comments. Therefore we choose to just ignore 61 // the comments for namespaces. 62 return ""; 63 } 64 const RawComment *RC = getCompletionComment(Ctx, &Decl); 65 if (!RC) 66 return ""; 67 // Sanity check that the comment does not come from the PCH. We choose to not 68 // write them into PCH, because they are racy and slow to load. 69 assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc())); 70 std::string Doc = 71 RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); 72 return looksLikeDocComment(Doc) ? Doc : ""; 73 } 74 75 void getSignature(const CodeCompletionString &CCS, std::string *Signature, 76 std::string *Snippet, std::string *RequiredQualifiers) { 77 unsigned ArgCount = 0; 78 bool HadObjCArguments = false; 79 for (const auto &Chunk : CCS) { 80 // Informative qualifier chunks only clutter completion results, skip 81 // them. 82 if (isInformativeQualifierChunk(Chunk)) 83 continue; 84 85 switch (Chunk.Kind) { 86 case CodeCompletionString::CK_TypedText: 87 // The typed-text chunk is the actual name. We don't record this chunk. 88 // C++: 89 // In general our string looks like <qualifiers><name><signature>. 90 // So once we see the name, any text we recorded so far should be 91 // reclassified as qualifiers. 92 // 93 // Objective-C: 94 // Objective-C methods may have multiple typed-text chunks, so we must 95 // treat them carefully. For Objective-C methods, all typed-text chunks 96 // will end in ':' (unless there are no arguments, in which case we 97 // can safely treat them as C++). 98 if (!llvm::StringRef(Chunk.Text).endswith(":")) { // Treat as C++. 99 if (RequiredQualifiers) 100 *RequiredQualifiers = std::move(*Signature); 101 Signature->clear(); 102 Snippet->clear(); 103 } else { // Objective-C method with args. 104 // If this is the first TypedText to the Objective-C method, discard any 105 // text that we've previously seen (such as previous parameter selector, 106 // which will be marked as Informative text). 107 // 108 // TODO: Make previous parameters part of the signature for Objective-C 109 // methods. 110 if (!HadObjCArguments) { 111 HadObjCArguments = true; 112 Signature->clear(); 113 } else { // Subsequent argument, considered part of snippet/signature. 114 *Signature += Chunk.Text; 115 *Snippet += Chunk.Text; 116 } 117 } 118 break; 119 case CodeCompletionString::CK_Text: 120 *Signature += Chunk.Text; 121 *Snippet += Chunk.Text; 122 break; 123 case CodeCompletionString::CK_Optional: 124 break; 125 case CodeCompletionString::CK_Placeholder: 126 *Signature += Chunk.Text; 127 ++ArgCount; 128 *Snippet += "${" + std::to_string(ArgCount) + ':'; 129 appendEscapeSnippet(Chunk.Text, Snippet); 130 *Snippet += '}'; 131 break; 132 case CodeCompletionString::CK_Informative: 133 // For example, the word "const" for a const method, or the name of 134 // the base class for methods that are part of the base class. 135 *Signature += Chunk.Text; 136 // Don't put the informative chunks in the snippet. 137 break; 138 case CodeCompletionString::CK_ResultType: 139 // This is not part of the signature. 140 break; 141 case CodeCompletionString::CK_CurrentParameter: 142 // This should never be present while collecting completion items, 143 // only while collecting overload candidates. 144 llvm_unreachable("Unexpected CK_CurrentParameter while collecting " 145 "CompletionItems"); 146 break; 147 case CodeCompletionString::CK_LeftParen: 148 case CodeCompletionString::CK_RightParen: 149 case CodeCompletionString::CK_LeftBracket: 150 case CodeCompletionString::CK_RightBracket: 151 case CodeCompletionString::CK_LeftBrace: 152 case CodeCompletionString::CK_RightBrace: 153 case CodeCompletionString::CK_LeftAngle: 154 case CodeCompletionString::CK_RightAngle: 155 case CodeCompletionString::CK_Comma: 156 case CodeCompletionString::CK_Colon: 157 case CodeCompletionString::CK_SemiColon: 158 case CodeCompletionString::CK_Equal: 159 case CodeCompletionString::CK_HorizontalSpace: 160 *Signature += Chunk.Text; 161 *Snippet += Chunk.Text; 162 break; 163 case CodeCompletionString::CK_VerticalSpace: 164 *Snippet += Chunk.Text; 165 // Don't even add a space to the signature. 166 break; 167 } 168 } 169 } 170 171 std::string formatDocumentation(const CodeCompletionString &CCS, 172 llvm::StringRef DocComment) { 173 // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this 174 // information in the documentation field. 175 std::string Result; 176 const unsigned AnnotationCount = CCS.getAnnotationCount(); 177 if (AnnotationCount > 0) { 178 Result += "Annotation"; 179 if (AnnotationCount == 1) { 180 Result += ": "; 181 } else /* AnnotationCount > 1 */ { 182 Result += "s: "; 183 } 184 for (unsigned I = 0; I < AnnotationCount; ++I) { 185 Result += CCS.getAnnotation(I); 186 Result.push_back(I == AnnotationCount - 1 ? '\n' : ' '); 187 } 188 } 189 // Add brief documentation (if there is any). 190 if (!DocComment.empty()) { 191 if (!Result.empty()) { 192 // This means we previously added annotations. Add an extra newline 193 // character to make the annotations stand out. 194 Result.push_back('\n'); 195 } 196 Result += DocComment; 197 } 198 return Result; 199 } 200 201 std::string getReturnType(const CodeCompletionString &CCS) { 202 for (const auto &Chunk : CCS) 203 if (Chunk.Kind == CodeCompletionString::CK_ResultType) 204 return Chunk.Text; 205 return ""; 206 } 207 208 } // namespace clangd 209 } // namespace clang 210