#include "clang/Basic/CharInfo.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TokenKinds.h"
#include "clang/Format/Format.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Lex/ExternalPreprocessorSource.h"
+#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Sema/CodeCompleteConsumer.h"
const IncludeInserter &Includes,
llvm::StringRef FileName,
CodeCompletionContext::Kind ContextKind,
- const CodeCompleteOptions &Opts, bool GenerateSnippets)
+ const CodeCompleteOptions &Opts,
+ bool IsUsingDeclaration, tok::TokenKind NextTokenKind)
: ASTCtx(ASTCtx), ExtractDocumentation(Opts.IncludeComments),
EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets),
- GenerateSnippets(GenerateSnippets) {
+ IsUsingDeclaration(IsUsingDeclaration), NextTokenKind(NextTokenKind) {
add(C, SemaCCS);
if (C.SemaResult) {
assert(ASTCtx);
}
std::string summarizeSnippet() const {
- if (!GenerateSnippets)
+ if (IsUsingDeclaration)
+ return "";
+ // Suppress function argument snippets if args are already present.
+ if ((Completion.Kind == CompletionItemKind::Function ||
+ Completion.Kind == CompletionItemKind::Method ||
+ Completion.Kind == CompletionItemKind::Constructor) &&
+ NextTokenKind == tok::l_paren)
return "";
auto *Snippet = onlyValue<&BundledEntry::SnippetSuffix>();
if (!Snippet)
llvm::SmallVector<BundledEntry, 1> Bundled;
bool ExtractDocumentation;
bool EnableFunctionArgSnippets;
- /// When false, no snippets are generated argument lists.
- bool GenerateSnippets;
+ // No snippets will be generated for using declarations and when the function
+ // arguments are already present.
+ bool IsUsingDeclaration;
+ tok::TokenKind NextTokenKind;
};
// Determine the symbol ID for a Sema code completion result, if possible.
CompletionRecorder *Recorder = nullptr;
CodeCompletionContext::Kind CCContextKind = CodeCompletionContext::CCC_Other;
bool IsUsingDeclaration = false;
+ // The snippets will not be generated if the token following completion
+ // location is an opening parenthesis (tok::l_paren) because this would add
+ // extra parenthesis.
+ tok::TokenKind NextTokenKind = tok::eof;
// Counters for logging.
int NSema = 0, NIndex = 0, NSemaAndIndex = 0, NIdent = 0;
bool Incomplete = false; // Would more be available with a higher limit?
auto Style = getFormatStyleForFile(
SemaCCInput.FileName, SemaCCInput.ParseInput.Contents,
SemaCCInput.ParseInput.FSProvider->getFileSystem().get());
+ const auto NextToken = Lexer::findNextToken(
+ Recorder->CCSema->getPreprocessor().getCodeCompletionLoc(),
+ Recorder->CCSema->getSourceManager(), Recorder->CCSema->LangOpts);
+ if (NextToken)
+ NextTokenKind = NextToken->getKind();
// If preprocessor was run, inclusions from preprocessor callback should
// already be added to Includes.
Inserter.emplace(
if (!Builder)
Builder.emplace(Recorder ? &Recorder->CCSema->getASTContext() : nullptr,
Item, SemaCCS, QueryScopes, *Inserter, FileName,
- CCContextKind, Opts,
- /*GenerateSnippets=*/!IsUsingDeclaration);
+ CCContextKind, Opts, IsUsingDeclaration, NextTokenKind);
else
Builder->add(Item, SemaCCS);
}
}
}
+TEST(CompletionTest, FunctionArgsExist) {
+ clangd::CodeCompleteOptions Opts;
+ Opts.EnableSnippets = true;
+ std::string Context = R"cpp(
+ int foo(int A);
+ int bar();
+ struct Object {
+ Object(int B) {}
+ };
+ template <typename T>
+ struct Container {
+ Container(int Size) {}
+ };
+ )cpp";
+ EXPECT_THAT(completions(Context + "int y = fo^", {}, Opts).Completions,
+ UnorderedElementsAre(
+ AllOf(Labeled("foo(int A)"), SnippetSuffix("(${1:int A})"))));
+ EXPECT_THAT(
+ completions(Context + "int y = fo^(42)", {}, Opts).Completions,
+ UnorderedElementsAre(AllOf(Labeled("foo(int A)"), SnippetSuffix(""))));
+ // FIXME(kirillbobyrev): No snippet should be produced here.
+ EXPECT_THAT(completions(Context + "int y = fo^o(42)", {}, Opts).Completions,
+ UnorderedElementsAre(
+ AllOf(Labeled("foo(int A)"), SnippetSuffix("(${1:int A})"))));
+ EXPECT_THAT(
+ completions(Context + "int y = ba^", {}, Opts).Completions,
+ UnorderedElementsAre(AllOf(Labeled("bar()"), SnippetSuffix("()"))));
+ EXPECT_THAT(completions(Context + "int y = ba^()", {}, Opts).Completions,
+ UnorderedElementsAre(AllOf(Labeled("bar()"), SnippetSuffix(""))));
+ EXPECT_THAT(
+ completions(Context + "Object o = Obj^", {}, Opts).Completions,
+ Contains(AllOf(Labeled("Object(int B)"), SnippetSuffix("(${1:int B})"),
+ Kind(CompletionItemKind::Constructor))));
+ EXPECT_THAT(completions(Context + "Object o = Obj^()", {}, Opts).Completions,
+ Contains(AllOf(Labeled("Object(int B)"), SnippetSuffix(""),
+ Kind(CompletionItemKind::Constructor))));
+ EXPECT_THAT(
+ completions(Context + "Container c = Cont^", {}, Opts).Completions,
+ Contains(AllOf(Labeled("Container<typename T>(int Size)"),
+ SnippetSuffix("<${1:typename T}>(${2:int Size})"),
+ Kind(CompletionItemKind::Constructor))));
+ // FIXME(kirillbobyrev): It would be nice to still produce the template
+ // snippet part: in this case it should be "<${1:typename T}>".
+ EXPECT_THAT(
+ completions(Context + "Container c = Cont^()", {}, Opts).Completions,
+ Contains(AllOf(Labeled("Container<typename T>(int Size)"),
+ SnippetSuffix(""),
+ Kind(CompletionItemKind::Constructor))));
+}
+
} // namespace
} // namespace clangd
} // namespace clang