diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -670,8 +670,11 @@ H.addAngleBracketTokens(E->getLAngleLoc(), E->getRAngleLoc()); return true; } + bool VisitMemberExpr(MemberExpr *E) { H.addAngleBracketTokens(E->getLAngleLoc(), E->getRAngleLoc()); + if (E->getType().isConstQualified()) + H.addExtraModifier(E->getMemberLoc(), HighlightingModifier::Readonly); return true; } diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp --- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -1154,6 +1154,94 @@ checkHighlightings(Test, {}, ScopeModifierMask); } +TEST(SemanticHighlighting, MemberReadonly) { + const char *TestCases[] = { + R"cpp( + struct $Class[[A]] { + int $Field[[x]]; + const int $Field_readonly[[y]]; + mutable int $Field[[z]]; + }; + void $Function[[foo]](){ + $Class[[A]] $LocalVariable[[a]]{}; + $LocalVariable[[a]].$Field[[x]]; + $LocalVariable[[a]].$Field_readonly[[y]]; + $LocalVariable[[a]].$Field[[z]]; + const $Class[[A]] $LocalVariable_readonly[[b]]{}; + $LocalVariable_readonly[[b]].$Field_readonly[[x]]; + $LocalVariable_readonly[[b]].$Field_readonly[[y]]; + $LocalVariable_readonly[[b]].$Field[[z]]; + } + )cpp", + R"cpp( + struct $Class[[A]] { + int $Field[[x]]; + const int $Field_readonly[[y]]; + mutable int $Field[[z]]; + }; + void $Function[[foo]](){ + $Class[[A]] *$LocalVariable[[a]] = $Operator[[new]] $Class[[A]]{}; + $LocalVariable[[a]]->$Field[[x]]; + $LocalVariable[[a]]->$Field_readonly[[y]]; + $LocalVariable[[a]]->$Field[[z]]; + const $Class[[A]] *$LocalVariable_readonly[[b]] = $Operator[[new]] $Class[[A]]{}; + $LocalVariable_readonly[[b]]->$Field_readonly[[x]]; + $LocalVariable_readonly[[b]]->$Field_readonly[[y]]; + $LocalVariable_readonly[[b]]->$Field[[z]]; + } + )cpp", + R"cpp( + struct $Class[[A]] { + int $Field[[x]]; + const int $Field_readonly[[y]]; + mutable int $Field[[z]]; + void $Method_readonly[[foo]]() const { + $Field_readonly[[x]]; + $Field_readonly[[y]]; + $Field[[z]]; + } + void $Method[[bar]]() { + $Field[[x]]; + $Field_readonly[[y]]; + $Field[[z]]; + } + }; + )cpp", + R"cpp( + struct $Class[[A]] { + int $Field[[x]]; + const int $Field_readonly[[y]]; + mutable int $Field[[z]]; + void $Method_readonly[[foo]]() const { + this->$Field_readonly[[x]]; + this->$Field_readonly[[y]]; + this->$Field[[z]]; + } + void $Method[[bar]]() { + this->$Field[[x]]; + this->$Field_readonly[[y]]; + this->$Field[[z]]; + } + }; + )cpp", + R"cpp( + struct $Class[[A]] { + void $Method_readonly[[foo]]() const {} + void $Method[[foo]]() {} + void $Method_readonly[[test]]() const { + $Method_readonly[[foo]](); + } + void $Method[[test]]() { + $Method[[foo]](); + } + }; + )cpp", + }; + + for (const char *Test : TestCases) + checkHighlightings(Test, {}, 1 << unsigned(HighlightingModifier::Readonly)); +} + // Ranges are highlighted as variables, unless highlighted as $Function etc. std::vector tokens(llvm::StringRef MarkedText) { Annotations A(MarkedText);