diff --git a/clang-tools-extra/clangd/IncludeCleaner.h b/clang-tools-extra/clangd/IncludeCleaner.h --- a/clang-tools-extra/clangd/IncludeCleaner.h +++ b/clang-tools-extra/clangd/IncludeCleaner.h @@ -20,8 +20,8 @@ #include "Headers.h" #include "ParsedAST.h" +#include "Protocol.h" #include "clang-include-cleaner/Types.h" -#include "clang/Tooling/Syntax/Tokens.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringSet.h" #include @@ -33,7 +33,7 @@ // Data needed for missing include diagnostics. struct MissingIncludeDiagInfo { include_cleaner::Symbol Symbol; - syntax::FileRange SymRefRange; + clangd::Range SymRefRange; std::vector Providers; bool operator==(const MissingIncludeDiagInfo &Other) const { diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp --- a/clang-tools-extra/clangd/IncludeCleaner.cpp +++ b/clang-tools-extra/clangd/IncludeCleaner.cpp @@ -32,6 +32,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Format/Format.h" #include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/Core/Replacement.h" #include "clang/Tooling/Inclusions/HeaderIncludes.h" @@ -263,11 +264,7 @@ D.File = AST.tuPath(); D.InsideMainFile = true; D.Severity = DiagnosticsEngine::Warning; - D.Range = clangd::Range{ - offsetToPosition(Code, - SymbolWithMissingInclude.SymRefRange.beginOffset()), - offsetToPosition(Code, - SymbolWithMissingInclude.SymRefRange.endOffset())}; + D.Range = std::move(SymbolWithMissingInclude.SymRefRange); auto &F = D.Fixes.emplace_back(); F.Message = "#include " + Spelling; TextEdit Edit = replacementToEdit(Code, *Replacement); @@ -379,15 +376,12 @@ Ref.RT != include_cleaner::RefType::Explicit) return; - auto &Tokens = AST.getTokens(); - auto SpelledForExpanded = - Tokens.spelledForExpanded(Tokens.expandedTokens(Ref.RefLocation)); - if (!SpelledForExpanded) - return; + auto Loc = SM.getFileLoc(Ref.RefLocation); + auto Range = Lexer::makeFileCharRange( + CharSourceRange{SourceRange{Loc}, true}, SM, AST.getLangOpts()); - auto Range = syntax::Token::range(SM, SpelledForExpanded->front(), - SpelledForExpanded->back()); - MissingIncludeDiagInfo DiagInfo{Ref.Target, Range, Providers}; + MissingIncludeDiagInfo DiagInfo{Ref.Target, halfOpenToRange(SM, Range), + Providers}; MissingIncludes.push_back(std::move(DiagInfo)); }); std::vector UnusedIncludes = diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp --- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -160,12 +160,8 @@ ASSERT_TRUE(BDecl); include_cleaner::Symbol B{*BDecl}; auto Range = MainFile.range("b"); - size_t Start = llvm::cantFail(positionToOffset(MainFile.code(), Range.start)); - size_t End = llvm::cantFail(positionToOffset(MainFile.code(), Range.end)); - syntax::FileRange BRange{SM.getMainFileID(), static_cast(Start), - static_cast(End)}; include_cleaner::Header Header{*SM.getFileManager().getFile("b.h")}; - MissingIncludeDiagInfo BInfo{B, BRange, {Header}}; + MissingIncludeDiagInfo BInfo{B, Range, {Header}}; EXPECT_THAT(Findings.MissingIncludes, ElementsAre(BInfo)); } @@ -177,27 +173,39 @@ WithContextValue Ctx(Config::Key, std::move(Cfg)); Annotations MainFile(R"cpp( #include "a.h" +#include "all.h" $insert_b[[]]#include "baz.h" #include "dir/c.h" -$insert_d[[]]#include "fuzz.h" +$insert_d[[]]$insert_foo[[]]#include "fuzz.h" #include "header.h" $insert_foobar[[]]#include $insert_f[[]]$insert_vector[[]] - void foo() { - $b[[b]](); +#define DEF(X) const Foo *X; +#define BAZ(X) const X x - ns::$bar[[Bar]] bar; - bar.d(); - $f[[f]](); + void foo() { + $b[[b]](); - // this should not be diagnosed, because it's ignored in the config - buzz(); + ns::$bar[[Bar]] bar; + bar.d(); + $f[[f]](); - $foobar[[foobar]](); + // this should not be diagnosed, because it's ignored in the config + buzz(); - std::$vector[[vector]] v; - })cpp"); + $foobar[[foobar]](); + + std::$vector[[vector]] v; + + int var = $FOO[[FOO]]; + + $DEF[[DEF]](a); + + $BAR[[BAR]](b); + + BAZ($Foo[[Foo]]); +})cpp"); TestTU TU; TU.Filename = "foo.cpp"; @@ -224,6 +232,13 @@ namespace std { class vector {}; } )cpp"); + TU.AdditionalFiles["all.h"] = guard("#include \"foo.h\""); + TU.AdditionalFiles["foo.h"] = guard(R"cpp( + #define BAR(x) Foo *x + #define FOO 1 + struct Foo{}; + )cpp"); + TU.Code = MainFile.code(); ParsedAST AST = TU.build(); @@ -253,7 +268,23 @@ Diag(MainFile.range("vector"), "No header providing \"std::vector\" is directly included"), withFix(Fix(MainFile.range("insert_vector"), - "#include \n", "#include "))))); + "#include \n", "#include "))), + AllOf(Diag(MainFile.range("FOO"), + "No header providing \"FOO\" is directly included"), + withFix(Fix(MainFile.range("insert_foo"), + "#include \"foo.h\"\n", "#include \"foo.h\""))), + AllOf(Diag(MainFile.range("DEF"), + "No header providing \"Foo\" is directly included"), + withFix(Fix(MainFile.range("insert_foo"), + "#include \"foo.h\"\n", "#include \"foo.h\""))), + AllOf(Diag(MainFile.range("BAR"), + "No header providing \"BAR\" is directly included"), + withFix(Fix(MainFile.range("insert_foo"), + "#include \"foo.h\"\n", "#include \"foo.h\""))), + AllOf(Diag(MainFile.range("Foo"), + "No header providing \"Foo\" is directly included"), + withFix(Fix(MainFile.range("insert_foo"), + "#include \"foo.h\"\n", "#include \"foo.h\""))))); } TEST(IncludeCleaner, IWYUPragmas) { diff --git a/clang-tools-extra/include-cleaner/lib/Analysis.cpp b/clang-tools-extra/include-cleaner/lib/Analysis.cpp --- a/clang-tools-extra/include-cleaner/lib/Analysis.cpp +++ b/clang-tools-extra/include-cleaner/lib/Analysis.cpp @@ -33,9 +33,11 @@ tooling::stdlib::Recognizer Recognizer; for (auto *Root : ASTRoots) { walkAST(*Root, [&](SourceLocation Loc, NamedDecl &ND, RefType RT) { - if (!SM.isWrittenInMainFile(SM.getSpellingLoc(Loc))) + if (!SM.isWrittenInMainFile(SM.getSpellingLoc(Loc)) && + SM.getDecomposedLoc(SM.getSpellingLoc(Loc)).first != + SM.getPreambleFileID()) return; - // FIXME: Most of the work done here is repetative. It might be useful to + // FIXME: Most of the work done here is repetitive. It might be useful to // have a cache/batching. SymbolReference SymRef{Loc, ND, RT}; return CB(SymRef, headersForSymbol(ND, SM, PI));