Skip to content

Commit

Permalink
[clangd] Add a distinct highlighting for local variables
Browse files Browse the repository at this point in the history
Summary:
It's useful to be able to distinguish local variables from namespace
scope variables.

Reviewers: hokein, jvikstrom

Reviewed By: hokein

Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D66723

llvm-svn: 370103
  • Loading branch information
HighCommander4 committed Aug 27, 2019
1 parent 2694522 commit 86a4a53
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 68 deletions.
17 changes: 10 additions & 7 deletions clang-tools-extra/clangd/SemanticHighlighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,9 @@ class HighlightingTokenCollector
addToken(Loc, HighlightingKind::Parameter);
return;
}
if (isa<VarDecl>(D)) {
addToken(Loc, HighlightingKind::Variable);
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
addToken(Loc, VD->isLocalVarDecl() ? HighlightingKind::LocalVariable
: HighlightingKind::Variable);
return;
}
if (isa<FunctionDecl>(D)) {
Expand Down Expand Up @@ -256,7 +257,7 @@ class HighlightingTokenCollector
}

void addToken(SourceLocation Loc, HighlightingKind Kind) {
if(Loc.isMacroID()) {
if (Loc.isMacroID()) {
// Only intereseted in highlighting arguments in macros (DEF_X(arg)).
if (!SM.isMacroArgExpansion(Loc))
return;
Expand All @@ -266,8 +267,8 @@ class HighlightingTokenCollector
// Non top level decls that are included from a header are not filtered by
// topLevelDecls. (example: method declarations being included from another
// file for a class from another file)
// There are also cases with macros where the spelling loc will not be in the
// main file and the highlighting would be incorrect.
// There are also cases with macros where the spelling loc will not be in
// the main file and the highlighting would be incorrect.
if (!isInsideMainFile(Loc, SM))
return;

Expand Down Expand Up @@ -367,9 +368,9 @@ diffHighlightings(ArrayRef<HighlightingToken> New,
auto OldEnd = Old.end();
auto NextLineNumber = [&]() {
int NextNew = NewLine.end() != NewEnd ? NewLine.end()->R.start.line
: std::numeric_limits<int>::max();
: std::numeric_limits<int>::max();
int NextOld = OldLine.end() != OldEnd ? OldLine.end()->R.start.line
: std::numeric_limits<int>::max();
: std::numeric_limits<int>::max();
return std::min(NextNew, NextOld);
};

Expand Down Expand Up @@ -436,6 +437,8 @@ llvm::StringRef toTextMateScope(HighlightingKind Kind) {
return "entity.name.function.method.cpp";
case HighlightingKind::Variable:
return "variable.other.cpp";
case HighlightingKind::LocalVariable:
return "variable.other.local.cpp";
case HighlightingKind::Parameter:
return "variable.parameter.cpp";
case HighlightingKind::Field:
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/SemanticHighlighting.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace clangd {

enum class HighlightingKind {
Variable = 0,
LocalVariable,
Parameter,
Function,
Method,
Expand Down
11 changes: 7 additions & 4 deletions clang-tools-extra/clangd/test/semantic-highlighting.test
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
# CHECK-NEXT: "variable.other.cpp"
# CHECK-NEXT: ],
# CHECK-NEXT: [
# CHECK-NEXT: "variable.other.local.cpp"
# CHECK-NEXT: ],
# CHECK-NEXT: [
# CHECK-NEXT: "variable.parameter.cpp"
# CHECK-NEXT: ],
# CHECK-NEXT: [
Expand Down Expand Up @@ -46,7 +49,7 @@
# CHECK-NEXT: "lines": [
# CHECK-NEXT: {
# CHECK-NEXT: "line": 0,
# CHECK-NEXT: "tokens": "AAAAAAADAAoAAAAEAAEAAA=="
# CHECK-NEXT: "tokens": "AAAAAAADAAsAAAAEAAEAAA=="
# CHECK-NEXT: }
# CHECK-NEXT: ],
# CHECK-NEXT: "textDocument": {
Expand All @@ -61,11 +64,11 @@
# CHECK-NEXT: "lines": [
# CHECK-NEXT: {
# CHECK-NEXT: "line": 0,
# CHECK-NEXT: "tokens": "AAAAAAADAAoAAAAEAAEAAA=="
# CHECK-NEXT: "tokens": "AAAAAAADAAsAAAAEAAEAAA=="
# CHECK-NEXT: }
# CHECK-NEXT: {
# CHECK-NEXT: "line": 1,
# CHECK-NEXT: "tokens": "AAAAAAADAAoAAAAEAAEAAA=="
# CHECK-NEXT: "tokens": "AAAAAAADAAsAAAAEAAEAAA=="
# CHECK-NEXT: }
# CHECK-NEXT: ],
# CHECK-NEXT: "textDocument": {
Expand All @@ -80,7 +83,7 @@
# CHECK-NEXT: "lines": [
# CHECK-NEXT: {
# CHECK-NEXT: "line": 1,
# CHECK-NEXT: "tokens": "AAAAAAADAAoAAAAEAAEAAA=="
# CHECK-NEXT: "tokens": "AAAAAAADAAsAAAAEAAEAAA=="
# CHECK-NEXT: }
# CHECK-NEXT: ],
# CHECK-NEXT: "textDocument": {
Expand Down
115 changes: 58 additions & 57 deletions clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ std::vector<HighlightingToken> getExpectedTokens(Annotations &Test) {
{HighlightingKind::Field, "Field"},
{HighlightingKind::Method, "Method"},
{HighlightingKind::TemplateParameter, "TemplateParameter"},
{HighlightingKind::Primitive, "Primitive"}};
{HighlightingKind::Primitive, "Primitive"},
{HighlightingKind::LocalVariable, "LocalVariable"}};
std::vector<HighlightingToken> ExpectedTokens;
for (const auto &KindString : KindToString) {
std::vector<HighlightingToken> Toks = makeHighlightingTokens(
Expand Down Expand Up @@ -103,31 +104,31 @@ void checkDiffedHighlights(llvm::StringRef OldCode, llvm::StringRef NewCode) {

TEST(SemanticHighlighting, GetsCorrectTokens) {
const char *TestCases[] = {
R"cpp(
R"cpp(
struct $Class[[AS]] {
$Primitive[[double]] $Field[[SomeMember]];
};
struct {
} $Variable[[S]];
$Primitive[[void]] $Function[[foo]]($Primitive[[int]] $Parameter[[A]], $Class[[AS]] $Parameter[[As]]) {
$Primitive[[auto]] $Variable[[VeryLongVariableName]] = 12312;
$Class[[AS]] $Variable[[AA]];
$Primitive[[auto]] $Variable[[L]] = $Variable[[AA]].$Field[[SomeMember]] + $Parameter[[A]];
auto $Variable[[FN]] = [ $Variable[[AA]]]($Primitive[[int]] $Parameter[[A]]) -> $Primitive[[void]] {};
$Variable[[FN]](12312);
$Primitive[[auto]] $LocalVariable[[VeryLongVariableName]] = 12312;
$Class[[AS]] $LocalVariable[[AA]];
$Primitive[[auto]] $LocalVariable[[L]] = $LocalVariable[[AA]].$Field[[SomeMember]] + $Parameter[[A]];
auto $LocalVariable[[FN]] = [ $LocalVariable[[AA]]]($Primitive[[int]] $Parameter[[A]]) -> $Primitive[[void]] {};
$LocalVariable[[FN]](12312);
}
)cpp",
R"cpp(
R"cpp(
$Primitive[[void]] $Function[[foo]]($Primitive[[int]]);
$Primitive[[void]] $Function[[Gah]]();
$Primitive[[void]] $Function[[foo]]() {
auto $Variable[[Bou]] = $Function[[Gah]];
auto $LocalVariable[[Bou]] = $Function[[Gah]];
}
struct $Class[[A]] {
$Primitive[[void]] $Method[[abc]]();
};
)cpp",
R"cpp(
R"cpp(
namespace $Namespace[[abc]] {
template<typename $TemplateParameter[[T]]>
struct $Class[[A]] {
Expand All @@ -149,12 +150,12 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
$Class[[B]]::$Class[[B]]() {}
$Class[[B]]::~$Class[[B]]() {}
$Primitive[[void]] $Function[[f]] () {
$Class[[B]] $Variable[[BB]] = $Class[[B]]();
$Variable[[BB]].~$Class[[B]]();
$Class[[B]] $LocalVariable[[BB]] = $Class[[B]]();
$LocalVariable[[BB]].~$Class[[B]]();
$Class[[B]]();
}
)cpp",
R"cpp(
R"cpp(
enum class $Enum[[E]] {
$EnumConstant[[A]],
$EnumConstant[[B]],
Expand All @@ -169,7 +170,7 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
$Primitive[[int]] $Variable[[I]] = $EnumConstant[[Hi]];
$Enum[[E]] $Variable[[L]] = $Enum[[E]]::$EnumConstant[[B]];
)cpp",
R"cpp(
R"cpp(
namespace $Namespace[[abc]] {
namespace {}
namespace $Namespace[[bcd]] {
Expand All @@ -192,7 +193,7 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
::$Namespace[[vwz]]::$Class[[A]] $Variable[[B]];
::$Namespace[[abc]]::$Namespace[[bcd]]::$Class[[A]] $Variable[[BB]];
)cpp",
R"cpp(
R"cpp(
struct $Class[[D]] {
$Primitive[[double]] $Field[[C]];
};
Expand All @@ -209,21 +210,21 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
}
};
$Primitive[[void]] $Function[[foo]]() {
$Class[[A]] $Variable[[AA]];
$Variable[[AA]].$Field[[B]] += 2;
$Variable[[AA]].$Method[[foo]]();
$Variable[[AA]].$Field[[E]].$Field[[C]];
$Class[[A]] $LocalVariable[[AA]];
$LocalVariable[[AA]].$Field[[B]] += 2;
$LocalVariable[[AA]].$Method[[foo]]();
$LocalVariable[[AA]].$Field[[E]].$Field[[C]];
$Class[[A]]::$Variable[[S]] = 90;
}
)cpp",
R"cpp(
R"cpp(
struct $Class[[AA]] {
$Primitive[[int]] $Field[[A]];
}
$Primitive[[int]] $Variable[[B]];
$Class[[AA]] $Variable[[A]]{$Variable[[B]]};
)cpp",
R"cpp(
R"cpp(
namespace $Namespace[[a]] {
struct $Class[[A]] {};
typedef $Primitive[[char]] $Primitive[[C]];
Expand All @@ -239,7 +240,7 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
typedef $Namespace[[a]]::$Primitive[[C]] $Primitive[[PC]];
typedef $Primitive[[float]] $Primitive[[F]];
)cpp",
R"cpp(
R"cpp(
template<typename $TemplateParameter[[T]], typename = $Primitive[[void]]>
class $Class[[A]] {
$TemplateParameter[[T]] $Field[[AA]];
Expand All @@ -265,30 +266,30 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
template<typename $TemplateParameter[[T]]>
$Primitive[[void]] $Function[[foo]]($TemplateParameter[[T]] ...);
)cpp",
R"cpp(
R"cpp(
template <class $TemplateParameter[[T]]>
struct $Class[[Tmpl]] {$TemplateParameter[[T]] $Field[[x]] = 0;};
extern template struct $Class[[Tmpl]]<$Primitive[[float]]>;
template struct $Class[[Tmpl]]<$Primitive[[double]]>;
)cpp",
// This test is to guard against highlightings disappearing when using
// conversion operators as their behaviour in the clang AST differ from
// other CXXMethodDecls.
R"cpp(
// This test is to guard against highlightings disappearing when using
// conversion operators as their behaviour in the clang AST differ from
// other CXXMethodDecls.
R"cpp(
class $Class[[Foo]] {};
struct $Class[[Bar]] {
explicit operator $Class[[Foo]]*() const;
explicit operator $Primitive[[int]]() const;
operator $Class[[Foo]]();
};
$Primitive[[void]] $Function[[f]]() {
$Class[[Bar]] $Variable[[B]];
$Class[[Foo]] $Variable[[F]] = $Variable[[B]];
$Class[[Foo]] *$Variable[[FP]] = ($Class[[Foo]]*)$Variable[[B]];
$Primitive[[int]] $Variable[[I]] = ($Primitive[[int]])$Variable[[B]];
$Class[[Bar]] $LocalVariable[[B]];
$Class[[Foo]] $LocalVariable[[F]] = $LocalVariable[[B]];
$Class[[Foo]] *$LocalVariable[[FP]] = ($Class[[Foo]]*)$LocalVariable[[B]];
$Primitive[[int]] $LocalVariable[[I]] = ($Primitive[[int]])$LocalVariable[[B]];
}
)cpp"
R"cpp(
R"cpp(
struct $Class[[B]] {};
struct $Class[[A]] {
$Class[[B]] $Field[[BB]];
Expand All @@ -297,7 +298,7 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
$Class[[A]] &$Class[[A]]::operator=($Class[[A]] &&$Parameter[[O]]) = default;
)cpp",
R"cpp(
R"cpp(
enum $Enum[[En]] {
$EnumConstant[[EC]],
};
Expand All @@ -315,7 +316,7 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
$Class[[Bar2]]() : $Class[[Bar]]($Class[[Foo]](), $EnumConstant[[EC]]) {}
};
)cpp",
R"cpp(
R"cpp(
enum $Enum[[E]] {
$EnumConstant[[E]],
};
Expand All @@ -329,7 +330,7 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
$Primitive[[decltype]]($Variable[[Form]]) $Variable[[F]] = 10;
auto $Variable[[Fun]] = []()->$Primitive[[void]]{};
)cpp",
R"cpp(
R"cpp(
class $Class[[G]] {};
template<$Class[[G]] *$TemplateParameter[[U]]>
class $Class[[GP]] {};
Expand All @@ -344,19 +345,19 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
template<$Primitive[[unsigned]] $TemplateParameter[[U]] = 2>
class $Class[[Foo]] {
$Primitive[[void]] $Method[[f]]() {
for($Primitive[[int]] $Variable[[I]] = 0;
$Variable[[I]] < $TemplateParameter[[U]];) {}
for($Primitive[[int]] $LocalVariable[[I]] = 0;
$LocalVariable[[I]] < $TemplateParameter[[U]];) {}
}
};
$Class[[G]] $Variable[[L]];
$Primitive[[void]] $Function[[f]]() {
$Class[[Foo]]<123> $Variable[[F]];
$Class[[GP]]<&$Variable[[L]]> $Variable[[LL]];
$Class[[GR]]<$Variable[[L]]> $Variable[[LLL]];
$Class[[Foo]]<123> $LocalVariable[[F]];
$Class[[GP]]<&$Variable[[L]]> $LocalVariable[[LL]];
$Class[[GR]]<$Variable[[L]]> $LocalVariable[[LLL]];
}
)cpp",
R"cpp(
R"cpp(
template<typename $TemplateParameter[[T]],
$Primitive[[void]] (T::*$TemplateParameter[[method]])($Primitive[[int]])>
struct $Class[[G]] {
Expand All @@ -376,14 +377,14 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
};
$Primitive[[void]] $Function[[foo]]() {
$Class[[F]] $Variable[[FF]];
$Class[[G]]<$Class[[F]], &$Class[[F]]::$Method[[f]]> $Variable[[GG]];
$Variable[[GG]].$Method[[foo]](&$Variable[[FF]]);
$Class[[A]]<$Function[[foo]]> $Variable[[AA]];
$Class[[F]] $LocalVariable[[FF]];
$Class[[G]]<$Class[[F]], &$Class[[F]]::$Method[[f]]> $LocalVariable[[GG]];
$LocalVariable[[GG]].$Method[[foo]](&$LocalVariable[[FF]]);
$Class[[A]]<$Function[[foo]]> $LocalVariable[[AA]];
)cpp",
// Tokens that share a source range but have conflicting Kinds are not
// highlighted.
R"cpp(
// Tokens that share a source range but have conflicting Kinds are not
// highlighted.
R"cpp(
#define DEF_MULTIPLE(X) namespace X { class X { int X; }; }
#define DEF_CLASS(T) class T {};
DEF_MULTIPLE(XYZ);
Expand All @@ -399,17 +400,17 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
#define SOME_NAME_SET variable2 = 123
#define INC_VAR(X) X += 2
$Primitive[[void]] $Function[[foo]]() {
DEF_VAR($Variable[[X]], 123);
DEF_VAR_REV(908, $Variable[[XY]]);
$Primitive[[int]] CPY( $Variable[[XX]] );
DEF_VAR_TYPE($Class[[A]], $Variable[[AA]]);
DEF_VAR($LocalVariable[[X]], 123);
DEF_VAR_REV(908, $LocalVariable[[XY]]);
$Primitive[[int]] CPY( $LocalVariable[[XX]] );
DEF_VAR_TYPE($Class[[A]], $LocalVariable[[AA]]);
$Primitive[[double]] SOME_NAME;
$Primitive[[int]] SOME_NAME_SET;
$Variable[[variable]] = 20.1;
$LocalVariable[[variable]] = 20.1;
MACRO_CONCAT(var, 2, $Primitive[[float]]);
DEF_VAR_T($Class[[A]], CPY(CPY($Variable[[Nested]])),
DEF_VAR_T($Class[[A]], CPY(CPY($LocalVariable[[Nested]])),
CPY($Class[[A]]()));
INC_VAR($Variable[[variable]]);
INC_VAR($LocalVariable[[variable]]);
}
$Primitive[[void]] SOME_NAME();
DEF_VAR($Variable[[XYZ]], 567);
Expand All @@ -421,7 +422,7 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
CALL_FN($Function[[foo]]);
}
)cpp",
R"cpp(
R"cpp(
#define fail(expr) expr
#define assert(COND) if (!(COND)) { fail("assertion failed" #COND); }
$Primitive[[int]] $Variable[[x]];
Expand Down Expand Up @@ -505,7 +506,7 @@ TEST(SemanticHighlighting, toSemanticHighlightingInformation) {
std::vector<SemanticHighlightingInformation> ActualResults =
toSemanticHighlightingInformation(Tokens);
std::vector<SemanticHighlightingInformation> ExpectedResults = {
{3, "AAAACAAEAAAAAAAEAAMAAg=="}, {1, "AAAAAQAEAAA="}};
{3, "AAAACAAEAAAAAAAEAAMAAw=="}, {1, "AAAAAQAEAAA="}};
EXPECT_EQ(ActualResults, ExpectedResults);
}

Expand Down

0 comments on commit 86a4a53

Please sign in to comment.