[PATCH] D116147: [clangd] Respect .clang-tidy ExtraArgs (-Wfoo only) when producing diagnostics

2022-01-03 Thread Sam McCall via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
sammccall marked an inline comment as done.
Closed by commit rG9e6f88b31a7f: [clangd] Respect .clang-tidy ExtraArgs (-Wfoo 
only) when producing diagnostics (authored by sammccall).

Changed prior to commit:
  https://reviews.llvm.org/D116147?vs=395858=397076#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D116147/new/

https://reviews.llvm.org/D116147

Files:
  clang-tools-extra/clangd/ParsedAST.cpp
  clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp

Index: clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
===
--- clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
+++ clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
@@ -517,6 +517,80 @@
   DiagSeverity(DiagnosticsEngine::Error);
 }
 
+TidyProvider addClangArgs(std::vector ExtraArgs) {
+  return [ExtraArgs = std::move(ExtraArgs)](tidy::ClangTidyOptions ,
+llvm::StringRef) {
+if (!Opts.ExtraArgs)
+  Opts.ExtraArgs.emplace();
+for (llvm::StringRef Arg : ExtraArgs)
+  Opts.ExtraArgs->emplace_back(Arg);
+  };
+}
+
+TEST(DiagnosticTest, ClangTidyEnablesClangWarning) {
+  Annotations Main(R"cpp( // error-ok
+static void [[foo]]() {}
+  )cpp");
+  TestTU TU = TestTU::withCode(Main.code());
+  // This is always emitted as a clang warning, not a clang-tidy diagnostic.
+  auto UnusedFooWarning =
+  AllOf(Diag(Main.range(), "unused function 'foo'"),
+DiagName("-Wunused-function"), DiagSource(Diag::Clang),
+DiagSeverity(DiagnosticsEngine::Warning));
+
+  // Check the -Wunused warning isn't initially on.
+  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+
+  // We enable warnings based on clang-tidy extra args.
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
+
+  // But we don't respect other args.
+  TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Dfoo=bar"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
+  << "Not unused function 'bar'!";
+
+  // -Werror doesn't apply to warnings enabled by clang-tidy extra args.
+  TU.ExtraArgs = {"-Werror"};
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(),
+  ElementsAre(DiagSeverity(DiagnosticsEngine::Warning)));
+
+  // But clang-tidy extra args won't *downgrade* errors to warnings either.
+  TU.ExtraArgs = {"-Wunused", "-Werror"};
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(),
+  ElementsAre(DiagSeverity(DiagnosticsEngine::Error)));
+
+  // FIXME: we're erroneously downgrading the whole group, this should be Error.
+  TU.ExtraArgs = {"-Wunused-function", "-Werror"};
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(),
+  ElementsAre(DiagSeverity(DiagnosticsEngine::Warning)));
+
+  // This looks silly, but it's the typical result if a warning is enabled by a
+  // high-level .clang-tidy file and disabled by a low-level one.
+  TU.ExtraArgs = {};
+  TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+
+  // Overriding only works in the proper order.
+  TU.ClangTidyProvider = addClangArgs({"-Wno-unused", "-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), SizeIs(1));
+
+  // More specific vs less-specific: match clang behavior
+  TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused-function"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  TU.ClangTidyProvider = addClangArgs({"-Wunused-function", "-Wno-unused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+
+  // We do allow clang-tidy config to disable warnings from the compile command.
+  // It's unclear this is ideal, but it's hard to avoid.
+  TU.ExtraArgs = {"-Wunused"};
+  TU.ClangTidyProvider = addClangArgs({"-Wno-unused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+}
+
 TEST(DiagnosticTest, LongFixMessages) {
   // We limit the size of printed code.
   Annotations Source(R"cpp(
Index: clang-tools-extra/clangd/ParsedAST.cpp
===
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -246,6 +246,49 @@
   std::vector MainFileTokens;
 };
 
+// Find -W and -Wno- options in ExtraArgs and apply them to Diags.
+//
+// This is used to handle ExtraArgs in clang-tidy configuration.
+// We don't use clang's standard handling of this as we want slightly different
+// behavior (e.g. we want to exclude these from -Wno-error).
+void applyWarningOptions(llvm::ArrayRef ExtraArgs,
+ DiagnosticsEngine ) {
+  for (llvm::StringRef 

[PATCH] D116147: [clangd] Respect .clang-tidy ExtraArgs (-Wfoo only) when producing diagnostics

2022-01-03 Thread Kadir Cetinkaya via Phabricator via cfe-commits
kadircet accepted this revision.
kadircet added a comment.
This revision is now accepted and ready to land.

thanks, lgtm!




Comment at: clang-tools-extra/clangd/ParsedAST.cpp:270
+// We don't want this, so keep track of them to fix afterwards.
+llvm::DenseSet NeedsWerrorExclusion;
+for (diag::kind ID : Members) {

why do we need a set here, rather than a flag?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D116147/new/

https://reviews.llvm.org/D116147

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D116147: [clangd] Respect .clang-tidy ExtraArgs (-Wfoo only) when producing diagnostics

2021-12-22 Thread Sam McCall via Phabricator via cfe-commits
sammccall updated this revision to Diff 395858.
sammccall added a comment.

Also handle -Wno- flags


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D116147/new/

https://reviews.llvm.org/D116147

Files:
  clang-tools-extra/clangd/ParsedAST.cpp
  clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp

Index: clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
===
--- clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
+++ clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
@@ -517,6 +517,80 @@
   DiagSeverity(DiagnosticsEngine::Error);
 }
 
+TidyProvider addClangArgs(std::vector ExtraArgs) {
+  return [ExtraArgs = std::move(ExtraArgs)](tidy::ClangTidyOptions ,
+llvm::StringRef) {
+if (!Opts.ExtraArgs)
+  Opts.ExtraArgs.emplace();
+for (llvm::StringRef Arg : ExtraArgs)
+  Opts.ExtraArgs->emplace_back(Arg);
+  };
+}
+
+TEST(DiagnosticTest, ClangTidyEnablesClangWarning) {
+  Annotations Main(R"cpp( // error-ok
+static void [[foo]]() {}
+  )cpp");
+  TestTU TU = TestTU::withCode(Main.code());
+  // This is always emitted as a clang warning, not a clang-tidy diagnostic.
+  auto UnusedFooWarning =
+  AllOf(Diag(Main.range(), "unused function 'foo'"),
+DiagName("-Wunused-function"), DiagSource(Diag::Clang),
+DiagSeverity(DiagnosticsEngine::Warning));
+
+  // Check the -Wunused warning isn't initially on.
+  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+
+  // We enable warnings based on clang-tidy extra args.
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
+
+  // But we don't respect other args.
+  TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Dfoo=bar"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
+  << "Not unused function 'bar'!";
+
+  // -Werror doesn't apply to warnings enabled by clang-tidy extra args.
+  TU.ExtraArgs = {"-Werror"};
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(),
+  ElementsAre(DiagSeverity(DiagnosticsEngine::Warning)));
+
+  // But clang-tidy extra args won't *downgrade* errors to warnings either.
+  TU.ExtraArgs = {"-Wunused", "-Werror"};
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(),
+  ElementsAre(DiagSeverity(DiagnosticsEngine::Error)));
+
+  // FIXME: we're erroneously downgrading the whole group, this should be Error.
+  TU.ExtraArgs = {"-Wunused-function", "-Werror"};
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(),
+  ElementsAre(DiagSeverity(DiagnosticsEngine::Warning)));
+
+  // This looks silly, but it's the typical result if a warning is enabled by a
+  // high-level .clang-tidy file and disabled by a low-level one.
+  TU.ExtraArgs = {};
+  TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+
+  // Overriding only works in the proper order.
+  TU.ClangTidyProvider = addClangArgs({"-Wno-unused", "-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), SizeIs(1));
+
+  // More specific vs less-specific: match clang behavior
+  TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused-function"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+  TU.ClangTidyProvider = addClangArgs({"-Wunused-function", "-Wno-unused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+
+  // We do allow clang-tidy config to disable warnings from the compile command.
+  // It's unclear this is ideal, but it's hard to avoid.
+  TU.ExtraArgs = {"-Wunused"};
+  TU.ClangTidyProvider = addClangArgs({"-Wno-unused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+}
+
 TEST(DiagnosticTest, LongFixMessages) {
   // We limit the size of printed code.
   Annotations Source(R"cpp(
Index: clang-tools-extra/clangd/ParsedAST.cpp
===
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -246,6 +246,49 @@
   std::vector MainFileTokens;
 };
 
+// Find -W and -Wno- options in ExtraArgs and apply them to Diags.
+//
+// This is used to handle ExtraArgs in clang-tidy configuration.
+// We don't use clang's standard handling of this as we want slightly different
+// behavior (e.g. we want to exclude these from -Wno-error).
+void applyWarningOptions(llvm::ArrayRef ExtraArgs,
+ DiagnosticsEngine ) {
+  for (llvm::StringRef Group : ExtraArgs) {
+// Only handle args that are of the form -W[no-].
+// Other flags are possible but rare and deliberately out of scope.
+llvm::SmallVector Members;
+if (!Group.consume_front("-W") || Group.empty())
+ 

[PATCH] D116147: [clangd] Respect .clang-tidy ExtraArgs (-Wfoo only) when producing diagnostics

2021-12-22 Thread Sam McCall via Phabricator via cfe-commits
sammccall planned changes to this revision.
sammccall added a comment.

Actually we should probably handle `-Wno-` too.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D116147/new/

https://reviews.llvm.org/D116147

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D116147: [clangd] Respect .clang-tidy ExtraArgs (-Wfoo only) when producing diagnostics

2021-12-22 Thread Sam McCall via Phabricator via cfe-commits
sammccall created this revision.
sammccall added a reviewer: kadircet.
Herald added subscribers: usaxena95, arphaman.
sammccall requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

This mechanism is used almost exclusively to enable extra warnings in clang-tidy
using ExtraArgs=-Wfoo, Checks="clang-diagnostic-foo".
Its presence is a strong signal that these flags are useful.

We choose not to actually emit them as clang-tidy diagnostics, but under their
"main" name - this ensures we show the same diagnostic in a consistent way.

We don't add the ExtraArgs to the compile command in general, but rather just
handle the -W flags, which is the common case and avoids unexpected
side-effects.
And we only do this for the main file parse, when producing diagnostics.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D116147

Files:
  clang-tools-extra/clangd/ParsedAST.cpp
  clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp

Index: clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
===
--- clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
+++ clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
@@ -517,6 +517,58 @@
   DiagSeverity(DiagnosticsEngine::Error);
 }
 
+TidyProvider addClangArgs(std::vector ExtraArgs) {
+  return [ExtraArgs = std::move(ExtraArgs)](tidy::ClangTidyOptions ,
+llvm::StringRef) {
+if (!Opts.ExtraArgs)
+  Opts.ExtraArgs.emplace();
+for (llvm::StringRef Arg : ExtraArgs)
+  Opts.ExtraArgs->emplace_back(Arg);
+  };
+}
+
+TEST(DiagnosticTest, ClangTidyEnablesClangWarning) {
+  Annotations Main(R"cpp( // error-ok
+static void [[foo]]() {}
+  )cpp");
+  TestTU TU = TestTU::withCode(Main.code());
+  // This is always emitted as a clang warning, not a clang-tidy diagnostic.
+  auto UnusedFooWarning =
+  AllOf(Diag(Main.range(), "unused function 'foo'"),
+DiagName("-Wunused-function"), DiagSource(Diag::Clang),
+DiagSeverity(DiagnosticsEngine::Warning));
+
+  // Check the -Wunused warning isn't initially on.
+  EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+
+  // We enable warnings based on clang-tidy extra args.
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
+
+  // But we don't respect other args.
+  TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Dfoo=bar"});
+  EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
+  << "Not unused function 'bar'!";
+
+  // -Werror doesn't apply to warnings enabled by clang-tidy extra args.
+  TU.ExtraArgs = {"-Werror"};
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(),
+  ElementsAre(DiagSeverity(DiagnosticsEngine::Warning)));
+
+  // But clang-tidy extra args won't *downgrade* errors to warnings either.
+  TU.ExtraArgs = {"-Wunused", "-Werror"};
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(),
+  ElementsAre(DiagSeverity(DiagnosticsEngine::Error)));
+
+  // FIXME: we're erroneously downgrading the whole group, this should be Error.
+  TU.ExtraArgs = {"-Wunused-function", "-Werror"};
+  TU.ClangTidyProvider = addClangArgs({"-Wunused"});
+  EXPECT_THAT(*TU.build().getDiagnostics(),
+  ElementsAre(DiagSeverity(DiagnosticsEngine::Warning)));
+}
+
 TEST(DiagnosticTest, LongFixMessages) {
   // We limit the size of printed code.
   Annotations Source(R"cpp(
Index: clang-tools-extra/clangd/ParsedAST.cpp
===
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -311,7 +311,63 @@
 : "unknown error");
 return None;
   }
-  if (!PreserveDiags) {
+  tidy::ClangTidyOptions ClangTidyOpts;
+  if (PreserveDiags) {
+trace::Span Tracer("ClangTidyOpts");
+ClangTidyOpts = getTidyOptionsForFile(Inputs.ClangTidyProvider, Filename);
+dlog("ClangTidy configuration for file {0}: {1}", Filename,
+ tidy::configurationAsText(ClangTidyOpts));
+
+// If clang-tidy is configured to emit clang warnings, we should too.
+//
+// Such clang-tidy configuration consists of two parts:
+//   - ExtraArgs: ["-Wfoo"] causes clang to produce the warnings
+//   - Checks: "clang-diagnostic-foo" prevents clang-tidy filtering them out
+//
+// We treat these as clang warnings, so the Checks part is not relevant.
+// We must enable the warnings specified in ExtraArgs.
+//
+// We *don't* want to change the compile command directly. this can have
+// too many unexpected effects: breaking the command, interactions with
+// -- and -Werror, etc. Besides, we've already parsed the command.
+