#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Capacity.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstddef>
+#include <vector>
namespace clang {
namespace clangd {
OS.flush();
return capitalize(std::move(Result));
}
+
+void setTags(clangd::Diag &D) {
+ static const auto *DeprecatedDiags = new llvm::DenseSet<unsigned>{
+ diag::warn_access_decl_deprecated,
+ diag::warn_atl_uuid_deprecated,
+ diag::warn_deprecated,
+ diag::warn_deprecated_altivec_src_compat,
+ diag::warn_deprecated_comma_subscript,
+ diag::warn_deprecated_compound_assign_volatile,
+ diag::warn_deprecated_copy,
+ diag::warn_deprecated_copy_with_dtor,
+ diag::warn_deprecated_copy_with_user_provided_copy,
+ diag::warn_deprecated_copy_with_user_provided_dtor,
+ diag::warn_deprecated_def,
+ diag::warn_deprecated_increment_decrement_volatile,
+ diag::warn_deprecated_message,
+ diag::warn_deprecated_redundant_constexpr_static_def,
+ diag::warn_deprecated_register,
+ diag::warn_deprecated_simple_assign_volatile,
+ diag::warn_deprecated_string_literal_conversion,
+ diag::warn_deprecated_this_capture,
+ diag::warn_deprecated_volatile_param,
+ diag::warn_deprecated_volatile_return,
+ diag::warn_deprecated_volatile_structured_binding,
+ diag::warn_opencl_attr_deprecated_ignored,
+ diag::warn_property_method_deprecated,
+ diag::warn_vector_mode_deprecated,
+ };
+ static const auto *UnusedDiags = new llvm::DenseSet<unsigned>{
+ diag::warn_opencl_attr_deprecated_ignored,
+ diag::warn_pragma_attribute_unused,
+ diag::warn_unused_but_set_parameter,
+ diag::warn_unused_but_set_variable,
+ diag::warn_unused_comparison,
+ diag::warn_unused_const_variable,
+ diag::warn_unused_exception_param,
+ diag::warn_unused_function,
+ diag::warn_unused_label,
+ diag::warn_unused_lambda_capture,
+ diag::warn_unused_local_typedef,
+ diag::warn_unused_member_function,
+ diag::warn_unused_parameter,
+ diag::warn_unused_private_field,
+ diag::warn_unused_property_backing_ivar,
+ diag::warn_unused_template,
+ diag::warn_unused_variable,
+ };
+ if (DeprecatedDiags->contains(D.ID)) {
+ D.Tags.push_back(DiagnosticTag::Deprecated);
+ } else if (UnusedDiags->contains(D.ID)) {
+ D.Tags.push_back(DiagnosticTag::Unnecessary);
+ }
+ // FIXME: Set tags for tidy-based diagnostics too.
+}
} // namespace
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const DiagBase &D) {
Main.relatedInformation->push_back(std::move(RelInfo));
}
}
+ Main.tags = D.Tags;
OutFn(std::move(Main), D.Fixes);
// If we didn't emit the notes as relatedLocations, emit separate diagnostics
// Fill in name/source now that we have all the context needed to map them.
for (auto &Diag : Output) {
+ setTags(Diag);
if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) {
// Warnings controlled by -Wfoo are better recognized by that name.
StringRef Warning = DiagnosticIDs::getWarningOptionForDiag(Diag.ID);
// duplicated messages due to various reasons (e.g. the check doesn't handle
// template instantiations well; clang-tidy alias checks).
std::set<std::pair<Range, std::string>> SeenDiags;
- llvm::erase_if(Output, [&](const Diag& D) {
+ llvm::erase_if(Output, [&](const Diag &D) {
return !SeenDiags.emplace(D.Range, D.Message).second;
});
return std::move(Output);
#include "support/MemoryTree.h"
#include "clang/Index/IndexSymbol.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/raw_ostream.h"
#include <bitset>
};
llvm::json::Value toJSON(const DiagnosticRelatedInformation &);
+enum DiagnosticTag {
+ /// Unused or unnecessary code.
+ ///
+ /// Clients are allowed to render diagnostics with this tag faded out instead
+ /// of having an error squiggle.
+ Unnecessary = 1,
+ /// Deprecated or obsolete code.
+ ///
+ /// Clients are allowed to rendered diagnostics with this tag strike through.
+ Deprecated = 2,
+};
+llvm::json::Value toJSON(DiagnosticTag Tag);
+
struct CodeAction;
struct Diagnostic {
/// The range at which the message applies.
/// The diagnostic's message.
std::string message;
+ /// Additional metadata about the diagnostic.
+ llvm::SmallVector<DiagnosticTag, 1> tags;
+
/// An array of related diagnostic information, e.g. when symbol-names within
/// a scope collide all definitions can be marked via this property.
llvm::Optional<std::vector<DiagnosticRelatedInformation>> relatedInformation;
return Field(&Diag::Notes, UnorderedElementsAre(NoteMatcher1, NoteMatcher2));
}
+::testing::Matcher<const Diag &>
+WithTag(::testing::Matcher<DiagnosticTag> TagMatcher) {
+ return Field(&Diag::Tags, Contains(TagMatcher));
+}
+
MATCHER_P2(Diag, Range, Message,
"Diag at " + llvm::to_string(Range) + " = [" + Message + "]") {
return arg.Range == Range && arg.Message == Message;
clangd::Diag D;
D.ID = clang::diag::err_undeclared_var_use;
+ D.Tags = {DiagnosticTag::Unnecessary};
D.Name = "undeclared_var_use";
D.Source = clangd::Diag::Clang;
D.Message = "something terrible happened";
../foo/baz/header.h:10:11:
note: declared somewhere in the header file)";
+ MainLSP.tags = {DiagnosticTag::Unnecessary};
clangd::Diagnostic NoteInMainLSP;
NoteInMainLSP.range = NoteInMain.Range;
testing::Contains(Diag(Code.range(), "no newline at end of file")));
}
}
+
+TEST(Diagnostics, Tags) {
+ TestTU TU;
+ TU.ExtraArgs = {"-Wunused", "-Wdeprecated"};
+ Annotations Test(R"cpp(
+ void bar() __attribute__((deprecated));
+ void foo() {
+ int $unused[[x]];
+ $deprecated[[bar]]();
+ })cpp");
+ TU.Code = Test.code().str();
+ EXPECT_THAT(*TU.build().getDiagnostics(),
+ UnorderedElementsAre(
+ AllOf(Diag(Test.range("unused"), "unused variable 'x'"),
+ WithTag(DiagnosticTag::Unnecessary)),
+ AllOf(Diag(Test.range("deprecated"), "'bar' is deprecated"),
+ WithTag(DiagnosticTag::Deprecated))));
+}
} // namespace
} // namespace clangd
} // namespace clang