diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp --- a/clang-tools-extra/clangd/Diagnostics.cpp +++ b/clang-tools-extra/clangd/Diagnostics.cpp @@ -31,6 +31,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include +#include #include #include @@ -463,7 +464,8 @@ // Main diagnostic should always refer to a range inside main file. If a // diagnostic made it so for, it means either itself or one of its notes is - // inside main file. + // inside main file. It's also possible that there's a fix in the main file, + // but we preserve fixes iff primary diagnostic is in the main file. if (D.InsideMainFile) { Main.range = D.Range; } else { @@ -701,13 +703,12 @@ return; } - bool InsideMainFile = isInsideMainFile(Info); SourceManager &SM = Info.getSourceManager(); auto FillDiagBase = [&](DiagBase &D) { fillNonLocationData(DiagLevel, Info, D); - D.InsideMainFile = InsideMainFile; + D.InsideMainFile = isInsideMainFile(Info); D.Range = diagnosticRange(Info, *LangOpts); D.File = std::string(SM.getFilename(Info.getLocation())); D.AbsFile = getCanonicalPath( @@ -719,9 +720,9 @@ auto AddFix = [&](bool SyntheticMessage) -> bool { assert(!Info.getFixItHints().empty() && "diagnostic does not have attached fix-its"); - if (!InsideMainFile) + // No point in generating fixes, if the diagnostic is for a different file. + if (!LastDiag->InsideMainFile) return false; - // Copy as we may modify the ranges. auto FixIts = Info.getFixItHints().vec(); llvm::SmallVector Edits; diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -22,6 +22,7 @@ #include "support/Path.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticSema.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/TargetSelect.h" #include "gmock/gmock.h" @@ -1680,6 +1681,28 @@ EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty()); } +TEST(DiagnosticsTest, FixItFromHeader) { + llvm::StringLiteral Header(R"cpp( + void foo(int *); + void foo(int *, int);)cpp"); + Annotations Source(R"cpp( + /*error-ok*/ + void bar() { + int x; + $diag[[foo]]($fix[[]]x, 1); + })cpp"); + TestTU TU; + TU.Code = Source.code().str(); + TU.HeaderCode = Header.str(); + EXPECT_THAT( + *TU.build().getDiagnostics(), + UnorderedElementsAre(AllOf( + Diag(Source.range("diag"), "no matching function for call to 'foo'"), + withFix(Fix(Source.range("fix"), "&", + "candidate function not viable: no known conversion from " + "'int' to 'int *' for 1st argument; take the address of " + "the argument with &"))))); +} } // namespace } // namespace clangd } // namespace clang