1 //===--- CodeCompletionStrings.cpp -------------------------------*- C++-*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===---------------------------------------------------------------------===// 9 10 #include "CodeCompletionStrings.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/AST/DeclObjC.h" 13 #include "clang/AST/RawCommentList.h" 14 #include "clang/Basic/SourceManager.h" 15 #include <utility> 16 17 namespace clang { 18 namespace clangd { 19 20 namespace { 21 22 bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) { 23 return Chunk.Kind == CodeCompletionString::CK_Informative && 24 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 bool canRequestComment(const ASTContext &Ctx, const NamedDecl &D, 36 bool CommentsFromHeaders) { 37 if (CommentsFromHeaders) 38 return true; 39 auto &SourceMgr = Ctx.getSourceManager(); 40 // Accessing comments for decls from invalid preamble can lead to crashes. 41 // So we only return comments from the main file when doing code completion. 42 // For indexing, we still read all the comments. 43 // FIXME: find a better fix, e.g. store file contents in the preamble or get 44 // doc comments from the index. 45 auto canRequestForDecl = [&](const NamedDecl &D) -> bool { 46 for (auto *Redecl : D.redecls()) { 47 auto Loc = SourceMgr.getSpellingLoc(Redecl->getLocation()); 48 if (!SourceMgr.isWrittenInMainFile(Loc)) 49 return false; 50 } 51 return true; 52 }; 53 // First, check the decl itself. 54 if (!canRequestForDecl(D)) 55 return false; 56 // Completion also returns comments for properties, corresponding to ObjC 57 // methods. 58 const ObjCMethodDecl *M = dyn_cast<ObjCMethodDecl>(&D); 59 const ObjCPropertyDecl *PDecl = M ? M->findPropertyDecl() : nullptr; 60 return !PDecl || canRequestForDecl(*PDecl); 61 } 62 63 bool LooksLikeDocComment(llvm::StringRef CommentText) { 64 // We don't report comments that only contain "special" chars. 65 // This avoids reporting various delimiters, like: 66 // ================= 67 // ----------------- 68 // ***************** 69 return CommentText.find_first_not_of("/*-= \t\r\n") != llvm::StringRef::npos; 70 } 71 72 } // namespace 73 74 std::string getDocComment(const ASTContext &Ctx, 75 const CodeCompletionResult &Result, 76 bool CommentsFromHeaders) { 77 // FIXME: clang's completion also returns documentation for RK_Pattern if they 78 // contain a pattern for ObjC properties. Unfortunately, there is no API to 79 // get this declaration, so we don't show documentation in that case. 80 if (Result.Kind != CodeCompletionResult::RK_Declaration) 81 return ""; 82 auto *Decl = Result.getDeclaration(); 83 if (!Decl || llvm::isa<NamespaceDecl>(Decl)) { 84 // Namespaces often have too many redecls for any particular redecl comment 85 // to be useful. Moreover, we often confuse file headers or generated 86 // comments with namespace comments. Therefore we choose to just ignore 87 // the comments for namespaces. 88 return ""; 89 } 90 if (!canRequestComment(Ctx, *Decl, CommentsFromHeaders)) 91 return ""; 92 93 const RawComment *RC = getCompletionComment(Ctx, Decl); 94 if (!RC) 95 return ""; 96 std::string Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); 97 if (!LooksLikeDocComment(Doc)) 98 return ""; 99 return Doc; 100 } 101 102 std::string 103 getParameterDocComment(const ASTContext &Ctx, 104 const CodeCompleteConsumer::OverloadCandidate &Result, 105 unsigned ArgIndex, bool CommentsFromHeaders) { 106 auto *Func = Result.getFunction(); 107 if (!Func || !canRequestComment(Ctx, *Func, CommentsFromHeaders)) 108 return ""; 109 const RawComment *RC = getParameterComment(Ctx, Result, ArgIndex); 110 if (!RC) 111 return ""; 112 std::string Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); 113 if (!LooksLikeDocComment(Doc)) 114 return ""; 115 return Doc; 116 } 117 118 void getSignature(const CodeCompletionString &CCS, std::string *Signature, 119 std::string *Snippet, std::string *RequiredQualifiers) { 120 unsigned ArgCount = 0; 121 for (const auto &Chunk : CCS) { 122 // Informative qualifier chunks only clutter completion results, skip 123 // them. 124 if (isInformativeQualifierChunk(Chunk)) 125 continue; 126 127 switch (Chunk.Kind) { 128 case CodeCompletionString::CK_TypedText: 129 // The typed-text chunk is the actual name. We don't record this chunk. 130 // In general our string looks like <qualifiers><name><signature>. 131 // So once we see the name, any text we recorded so far should be 132 // reclassified as qualifiers. 133 if (RequiredQualifiers) 134 *RequiredQualifiers = std::move(*Signature); 135 Signature->clear(); 136 Snippet->clear(); 137 break; 138 case CodeCompletionString::CK_Text: 139 *Signature += Chunk.Text; 140 *Snippet += Chunk.Text; 141 break; 142 case CodeCompletionString::CK_Optional: 143 break; 144 case CodeCompletionString::CK_Placeholder: 145 *Signature += Chunk.Text; 146 ++ArgCount; 147 *Snippet += "${" + std::to_string(ArgCount) + ':'; 148 appendEscapeSnippet(Chunk.Text, Snippet); 149 *Snippet += '}'; 150 break; 151 case CodeCompletionString::CK_Informative: 152 // For example, the word "const" for a const method, or the name of 153 // the base class for methods that are part of the base class. 154 *Signature += Chunk.Text; 155 // Don't put the informative chunks in the snippet. 156 break; 157 case CodeCompletionString::CK_ResultType: 158 // This is not part of the signature. 159 break; 160 case CodeCompletionString::CK_CurrentParameter: 161 // This should never be present while collecting completion items, 162 // only while collecting overload candidates. 163 llvm_unreachable("Unexpected CK_CurrentParameter while collecting " 164 "CompletionItems"); 165 break; 166 case CodeCompletionString::CK_LeftParen: 167 case CodeCompletionString::CK_RightParen: 168 case CodeCompletionString::CK_LeftBracket: 169 case CodeCompletionString::CK_RightBracket: 170 case CodeCompletionString::CK_LeftBrace: 171 case CodeCompletionString::CK_RightBrace: 172 case CodeCompletionString::CK_LeftAngle: 173 case CodeCompletionString::CK_RightAngle: 174 case CodeCompletionString::CK_Comma: 175 case CodeCompletionString::CK_Colon: 176 case CodeCompletionString::CK_SemiColon: 177 case CodeCompletionString::CK_Equal: 178 case CodeCompletionString::CK_HorizontalSpace: 179 *Signature += Chunk.Text; 180 *Snippet += Chunk.Text; 181 break; 182 case CodeCompletionString::CK_VerticalSpace: 183 *Snippet += Chunk.Text; 184 // Don't even add a space to the signature. 185 break; 186 } 187 } 188 } 189 190 std::string formatDocumentation(const CodeCompletionString &CCS, 191 llvm::StringRef DocComment) { 192 // Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this 193 // information in the documentation field. 194 std::string Result; 195 const unsigned AnnotationCount = CCS.getAnnotationCount(); 196 if (AnnotationCount > 0) { 197 Result += "Annotation"; 198 if (AnnotationCount == 1) { 199 Result += ": "; 200 } else /* AnnotationCount > 1 */ { 201 Result += "s: "; 202 } 203 for (unsigned I = 0; I < AnnotationCount; ++I) { 204 Result += CCS.getAnnotation(I); 205 Result.push_back(I == AnnotationCount - 1 ? '\n' : ' '); 206 } 207 } 208 // Add brief documentation (if there is any). 209 if (!DocComment.empty()) { 210 if (!Result.empty()) { 211 // This means we previously added annotations. Add an extra newline 212 // character to make the annotations stand out. 213 Result.push_back('\n'); 214 } 215 Result += DocComment; 216 } 217 return Result; 218 } 219 220 std::string getReturnType(const CodeCompletionString &CCS) { 221 for (const auto &Chunk : CCS) 222 if (Chunk.Kind == CodeCompletionString::CK_ResultType) 223 return Chunk.Text; 224 return ""; 225 } 226 227 } // namespace clangd 228 } // namespace clang 229