Skip to content

Commit 359ef0c

Browse files
sam-mccalltru
authored andcommitted
[clangd] Improve inlay hints of things expanded from macros
When we aim a hint at some expanded tokens, we're only willing to attach it to spelled tokens that exactly corresponde. e.g. int zoom(int x, int y, int z); int dummy = zoom(NUMBERS); Here we want to place a hint "x:" on the expanded "1", but we shouldn't be willing to place it on NUMBERS, because it doesn't *exactly* correspond (it has more tokens). Fortunately we don't even have to implement this algorithm from scratch, TokenBuffer has it. Fixes clangd/clangd#1289 Fixes clangd/clangd#1118 Fixes clangd/clangd#1018 Differential Revision: https://reviews.llvm.org/D133982 (cherry picked from commit 924974a)
1 parent bd5722b commit 359ef0c

File tree

2 files changed

+38
-30
lines changed

2 files changed

+38
-30
lines changed

clang-tools-extra/clangd/InlayHints.cpp

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "Config.h"
1111
#include "HeuristicResolver.h"
1212
#include "ParsedAST.h"
13+
#include "SourceCode.h"
1314
#include "clang/AST/Decl.h"
1415
#include "clang/AST/DeclarationName.h"
1516
#include "clang/AST/ExprCXX.h"
@@ -192,8 +193,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
192193
public:
193194
InlayHintVisitor(std::vector<InlayHint> &Results, ParsedAST &AST,
194195
const Config &Cfg, llvm::Optional<Range> RestrictRange)
195-
: Results(Results), AST(AST.getASTContext()), Cfg(Cfg),
196-
RestrictRange(std::move(RestrictRange)),
196+
: Results(Results), AST(AST.getASTContext()), Tokens(AST.getTokens()),
197+
Cfg(Cfg), RestrictRange(std::move(RestrictRange)),
197198
MainFileID(AST.getSourceManager().getMainFileID()),
198199
Resolver(AST.getHeuristicResolver()),
199200
TypeHintPolicy(this->AST.getPrintingPolicy()),
@@ -227,8 +228,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
227228
return true;
228229
}
229230

230-
processCall(E->getParenOrBraceRange().getBegin(), E->getConstructor(),
231-
{E->getArgs(), E->getNumArgs()});
231+
processCall(E->getConstructor(), {E->getArgs(), E->getNumArgs()});
232232
return true;
233233
}
234234

@@ -254,7 +254,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
254254
if (!Callee)
255255
return true;
256256

257-
processCall(E->getRParenLoc(), Callee, {E->getArgs(), E->getNumArgs()});
257+
processCall(Callee, {E->getArgs(), E->getNumArgs()});
258258
return true;
259259
}
260260

@@ -278,11 +278,11 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
278278
return true;
279279
}
280280

281-
void addReturnTypeHint(FunctionDecl *D, SourceLocation Loc) {
281+
void addReturnTypeHint(FunctionDecl *D, SourceRange Range) {
282282
auto *AT = D->getReturnType()->getContainedAutoType();
283283
if (!AT || AT->getDeducedType().isNull())
284284
return;
285-
addTypeHint(Loc, D->getReturnType(), /*Prefix=*/"-> ");
285+
addTypeHint(Range, D->getReturnType(), /*Prefix=*/"-> ");
286286
}
287287

288288
bool VisitVarDecl(VarDecl *D) {
@@ -375,21 +375,11 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
375375
private:
376376
using NameVec = SmallVector<StringRef, 8>;
377377

378-
// The purpose of Anchor is to deal with macros. It should be the call's
379-
// opening or closing parenthesis or brace. (Always using the opening would
380-
// make more sense but CallExpr only exposes the closing.) We heuristically
381-
// assume that if this location does not come from a macro definition, then
382-
// the entire argument list likely appears in the main file and can be hinted.
383-
void processCall(SourceLocation Anchor, const FunctionDecl *Callee,
378+
void processCall(const FunctionDecl *Callee,
384379
llvm::ArrayRef<const Expr *> Args) {
385380
if (!Cfg.InlayHints.Parameters || Args.size() == 0 || !Callee)
386381
return;
387382

388-
// If the anchor location comes from a macro defintion, there's nowhere to
389-
// put hints.
390-
if (!AST.getSourceManager().getTopMacroCallerLoc(Anchor).isFileID())
391-
return;
392-
393383
// The parameter name of a move or copy constructor is not very interesting.
394384
if (auto *Ctor = dyn_cast<CXXConstructorDecl>(Callee))
395385
if (Ctor->isCopyOrMoveConstructor())
@@ -637,25 +627,33 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
637627
#undef CHECK_KIND
638628
}
639629

640-
auto FileRange =
641-
toHalfOpenFileRange(AST.getSourceManager(), AST.getLangOpts(), R);
642-
if (!FileRange)
630+
auto LSPRange = getHintRange(R);
631+
if (!LSPRange)
643632
return;
644-
Range LSPRange{
645-
sourceLocToPosition(AST.getSourceManager(), FileRange->getBegin()),
646-
sourceLocToPosition(AST.getSourceManager(), FileRange->getEnd())};
647-
Position LSPPos = Side == HintSide::Left ? LSPRange.start : LSPRange.end;
633+
Position LSPPos = Side == HintSide::Left ? LSPRange->start : LSPRange->end;
648634
if (RestrictRange &&
649635
(LSPPos < RestrictRange->start || !(LSPPos < RestrictRange->end)))
650636
return;
651-
// The hint may be in a file other than the main file (for example, a header
652-
// file that was included after the preamble), do not show in that case.
653-
if (!AST.getSourceManager().isWrittenInMainFile(FileRange->getBegin()))
654-
return;
655637
bool PadLeft = Prefix.consume_front(" ");
656638
bool PadRight = Suffix.consume_back(" ");
657639
Results.push_back(InlayHint{LSPPos, (Prefix + Label + Suffix).str(), Kind,
658-
PadLeft, PadRight, LSPRange});
640+
PadLeft, PadRight, *LSPRange});
641+
}
642+
643+
// Get the range of the main file that *exactly* corresponds to R.
644+
llvm::Optional<Range> getHintRange(SourceRange R) {
645+
const auto &SM = AST.getSourceManager();
646+
auto Spelled = Tokens.spelledForExpanded(Tokens.expandedTokens(R));
647+
// TokenBuffer will return null if e.g. R corresponds to only part of a
648+
// macro expansion.
649+
if (!Spelled || Spelled->empty())
650+
return llvm::None;
651+
// Hint must be within the main file, not e.g. a non-preamble include.
652+
if (SM.getFileID(Spelled->front().location()) != SM.getMainFileID() ||
653+
SM.getFileID(Spelled->back().location()) != SM.getMainFileID())
654+
return llvm::None;
655+
return Range{sourceLocToPosition(SM, Spelled->front().location()),
656+
sourceLocToPosition(SM, Spelled->back().endLocation())};
659657
}
660658

661659
void addTypeHint(SourceRange R, QualType T, llvm::StringRef Prefix) {
@@ -680,6 +678,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
680678

681679
std::vector<InlayHint> &Results;
682680
ASTContext &AST;
681+
const syntax::TokenBuffer &Tokens;
683682
const Config &Cfg;
684683
llvm::Optional<Range> RestrictRange;
685684
FileID MainFileID;

clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,15 @@ TEST(ParameterHints, Macros) {
820820
}
821821
)cpp",
822822
ExpectedHint{"param: ", "param"});
823+
824+
// If the macro expands to multiple arguments, don't hint it.
825+
assertParameterHints(R"cpp(
826+
void foo(double x, double y);
827+
#define CONSTANTS 3.14, 2.72
828+
void bar() {
829+
foo(CONSTANTS);
830+
}
831+
)cpp");
823832
}
824833

825834
TEST(ParameterHints, ConstructorParens) {

0 commit comments

Comments
 (0)