kadircet created this revision. kadircet added reviewers: hokein, sammccall. Herald added a project: All. kadircet requested review of this revision. Herald added a project: clang-tools-extra. Herald added a subscriber: cfe-commits.
Introduces walkUsed, a very simple version of the public API to enable incremental development on rest of the pieces. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D136293 Files: clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h clang-tools-extra/include-cleaner/lib/WalkAST.cpp clang-tools-extra/include-cleaner/unittests/CMakeLists.txt clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
Index: clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp =================================================================== --- clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp +++ clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp @@ -1,15 +1,32 @@ +//===--- WalkASTTest.cpp ------------------------------------------- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// #include "AnalysisInternal.h" +#include "clang-include-cleaner/Analysis.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Frontend/TextDiagnostic.h" #include "clang/Testing/TestAST.h" +#include "clang/Tooling/Inclusions/StandardLibrary.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/Error.h" #include "llvm/Testing/Support/Annotations.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" +#include <cstddef> +#include <vector> -namespace clang { -namespace include_cleaner { +namespace clang::include_cleaner { namespace { +using testing::Pair; +using testing::UnorderedElementsAre; // Specifies a test of which symbols are referenced by a piece of code. // @@ -105,6 +122,44 @@ "^S *x;"); } +TEST(WalkUsed, Basic) { + llvm::Annotations HeaderCode(R"cpp( + void foo(); + namespace std { class vector {}; })cpp"); + llvm::Annotations Code(R"cpp( + void bar() { + $foo^foo(); + std::$vector^vector v; + } + )cpp"); + TestInputs Inputs(Code.code()); + Inputs.ExtraFiles["header.h"] = HeaderCode.code().str(); + Inputs.ExtraArgs.push_back("-include"); + Inputs.ExtraArgs.push_back("header.h"); + TestAST AST(Inputs); + + llvm::SmallVector<Decl *> TopLevelDecls; + for (Decl *D : AST.context().getTranslationUnitDecl()->decls()) { + TopLevelDecls.emplace_back(D); + } + + auto &SM = AST.sourceManager(); + llvm::DenseMap<size_t, std::vector<Header>> OffsetToProviders; + walkUsed(TopLevelDecls, [&](SourceLocation RefLoc, Symbol S, + llvm::ArrayRef<Header> Providers) { + auto [FID, Offset] = SM.getDecomposedLoc(RefLoc); + EXPECT_EQ(FID, SM.getMainFileID()); + OffsetToProviders.try_emplace(Offset, Providers.vec()); + }); + auto HeaderFile = AST.fileManager().getFile("header.h").get(); + EXPECT_THAT( + OffsetToProviders, + UnorderedElementsAre( + Pair(Code.point("foo"), UnorderedElementsAre(Header(HeaderFile))), + Pair(Code.point("vector"), + UnorderedElementsAre(Header( + tooling::stdlib::Header::named("<vector>").value()))))); +} + } // namespace -} // namespace include_cleaner -} // namespace clang +} // namespace clang::include_cleaner Index: clang-tools-extra/include-cleaner/unittests/CMakeLists.txt =================================================================== --- clang-tools-extra/include-cleaner/unittests/CMakeLists.txt +++ clang-tools-extra/include-cleaner/unittests/CMakeLists.txt @@ -18,6 +18,7 @@ clangAST clangBasic clangFrontend + clangToolingInclusionsStdlib ) target_link_libraries(ClangIncludeCleanerTests Index: clang-tools-extra/include-cleaner/lib/WalkAST.cpp =================================================================== --- clang-tools-extra/include-cleaner/lib/WalkAST.cpp +++ clang-tools-extra/include-cleaner/lib/WalkAST.cpp @@ -7,10 +7,15 @@ //===----------------------------------------------------------------------===// #include "AnalysisInternal.h" +#include "clang-include-cleaner/Analysis.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Tooling/Inclusions/StandardLibrary.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" -namespace clang { -namespace include_cleaner { +namespace clang::include_cleaner { namespace { using DeclCallback = llvm::function_ref<void(SourceLocation, NamedDecl &)>; @@ -37,11 +42,38 @@ } }; +llvm::SmallVector<Header> +toHeader(llvm::ArrayRef<tooling::stdlib::Header> Headers) { + llvm::SmallVector<Header> Result; + llvm::for_each(Headers, [&](tooling::stdlib::Header H) { + Result.emplace_back(Header(H)); + }); + return Result; +} + } // namespace void walkAST(Decl &Root, DeclCallback Callback) { ASTWalker(Callback).TraverseDecl(&Root); } -} // namespace include_cleaner -} // namespace clang +void walkUsed(llvm::ArrayRef<Decl *> ASTRoots, UsedSymbolVisitor CB) { + tooling::stdlib::Recognizer Recognizer; + for (auto *Root : ASTRoots) { + auto &SM = Root->getASTContext().getSourceManager(); + walkAST(*Root, [&](SourceLocation Loc, NamedDecl &ND) { + if (auto SS = Recognizer(&ND)) { + llvm::errs() << "returning stdlib\n"; + return CB(Loc, Symbol(*SS), toHeader(SS->headers())); + } + // FIXME: Extract locations from redecls. + // FIXME: Handle IWYU pragmas, non self-contained files. + llvm::errs() << "Returning fileentry\n"; + if (auto *FE = SM.getFileEntryForID(SM.getFileID(ND.getLocation()))) + return CB(Loc, Symbol(ND), {Header(FE)}); + }); + } + // FIXME: Handle references of macros. +} + +} // namespace clang::include_cleaner Index: clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h =================================================================== --- /dev/null +++ clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h @@ -0,0 +1,69 @@ +//===--- Analysis.h - Analyze symbol references in AST ------------- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_INCLUDE_CLEANER_ANALYSIS_H +#define CLANG_INCLUDE_CLEANER_ANALYSIS_H + +#include "clang/Tooling/Inclusions/StandardLibrary.h" +#include "llvm/ADT/STLFunctionalExtras.h" +#include <variant> + +namespace clang { +class SourceLocation; +class Decl; +class FileEntry; +namespace include_cleaner { + +/// An entity that can be referenced in the code. +struct Symbol { + Symbol(Decl &D) : Storage(&D) {} + Symbol(tooling::stdlib::Symbol S) : Storage(S) {} + +private: + // FIXME: Add support for macros. + std::variant<Decl *, tooling::stdlib::Symbol> Storage; +}; + +/// Represents a file that's providing some symbol. Might not be includeable. +struct Header { + Header(const FileEntry *FE) : Storage(FE) {} + Header(tooling::stdlib::Header H) : Storage(H) {} + + bool operator==(const Header &RHS) const { return Storage == RHS.Storage; } + +private: + // FIXME: Handle verbatim spellings. + std::variant<const FileEntry *, tooling::stdlib::Header> Storage; +}; +/// A UsedSymbolVisitor is a callback invoked for each symbol reference seen. +/// +/// References occur at a particular location, refer to a single symbol, and +/// that symbol may be provided by several headers. +/// FIXME: Provide signals about the reference type and providing headers. +using UsedSymbolVisitor = llvm::function_ref<void( + SourceLocation RefLoc, Symbol Target, llvm::ArrayRef<Header> Providers)>; + +/// Find and report all references to symbols in a region of code. +/// +/// The AST traversal is rooted at ASTRoots - typically top-level declarations +/// of a single source file. +/// FIXME: Handle macro uses. +/// +/// This is the main entrypoint of the include-cleaner library, and can be used: +/// - to diagnose missing includes: a referenced symbol is provided by +/// headers which don't match any #include in the main file +/// - to diagnose unused includes: an #include in the main file does not match +/// the headers for any referenced symbol +/// FIXME: Take in an include structure to improve location to header mappings +/// (e.g. IWYU pragmas). +void walkUsed(llvm::ArrayRef<Decl *> ASTRoots, UsedSymbolVisitor CB); + +} // namespace include_cleaner +} // namespace clang + +#endif
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits