diff --git a/src/collectors/include_graph/include_graph_util.cpp b/src/collectors/include_graph/include_graph_util.cpp index cb4f291..41d1e82 100644 --- a/src/collectors/include_graph/include_graph_util.cpp +++ b/src/collectors/include_graph/include_graph_util.cpp @@ -196,6 +196,17 @@ void add_redeclaration(clang::CompilerInstance *ci, IncludeGraphData *data, data->redeclarations); } +// Gets a sensible clang::SourceLocation even in the presence of a macro. +clang::SourceLocation get_canonical_location(clang::SourceManager &sm, + clang::SourceLocation loc) { + clang::SourceLocation spellingLoc = sm.getSpellingLoc(loc); + // this usually happens when there is token pasting + if (sm.isWrittenInScratchSpace(spellingLoc)) { + loc = sm.getExpansionRange(loc).getBegin(); + } + return loc; +} + void add_decl_reference(clang::CompilerInstance *ci, IncludeGraphData *data, const clang::DeclRefExpr *e) { @@ -205,9 +216,10 @@ void add_decl_reference(clang::CompilerInstance *ci, IncludeGraphData *data, if (!d) return; clang::SourceLocation locDef = d->getLocation(); - clang::SourceLocation expansionLoc = ci->getSourceManager().getExpansionLoc(locUse); + clang::SourceLocation locUseCanonical = + get_canonical_location(ci->getSourceManager(), locUse); - add_usage(ci, data, expansionLoc, locDef, e, data->decl_references); + add_usage(ci, data, locUseCanonical, locDef, e, data->decl_references); } template @@ -234,7 +246,7 @@ bool check_for_first_end(clang::CompilerInstance *ci, IncludeGraphData *data, } void add_type_reference(clang::CompilerInstance *ci, IncludeGraphData *data, - const clang::TypeLoc *n, const clang::Decl* decl) { + const clang::TypeLoc *n, const clang::Decl *decl) { const clang::Type *t = n->getTypePtr(); if (!decl) @@ -258,8 +270,9 @@ void add_type_reference(clang::CompilerInstance *ci, IncludeGraphData *data, return; } - add_usage(ci, data, ci->getSourceManager().getExpansionLoc(n->getBeginLoc()), - decl->getLocation(), n, data->type_references); + clang::SourceLocation locUse = + get_canonical_location(ci->getSourceManager(), n->getBeginLoc()); + add_usage(ci, data, locUse, decl->getLocation(), n, data->type_references); } } // namespace include_graph } // namespace collectors diff --git a/t/043-variable-access-through-expansion.t.cpp b/t/043-variable-access-through-expansion.t.cpp index 8597515..46d12bd 100644 --- a/t/043-variable-access-through-expansion.t.cpp +++ b/t/043-variable-access-through-expansion.t.cpp @@ -2,27 +2,27 @@ #include -#include -#include #include +#include +#include #include -#include #include -#include +#include #include +#include #include class MyTool { private: - clang::CompilerInstance* ci; + clang::CompilerInstance *ci; clangmetatool::collectors::IncludeGraph graph; + public: - MyTool(clang::CompilerInstance* ci, clang::ast_matchers::MatchFinder *f) - :ci(ci), graph(ci, f) { - } - void postProcessing - (std::map &replacementsMap) { + MyTool(clang::CompilerInstance *ci, clang::ast_matchers::MatchFinder *f) + : ci(ci), graph(ci, f) {} + void postProcessing( + std::map &replacementsMap) { clangmetatool::collectors::IncludeGraphData *data = graph.getData(); // file ID 0 to 1, aka foo.cpp to paste.h @@ -33,56 +33,68 @@ class MyTool { // file ID from 0 to 2, aka foo.cpp to global.h edge = std::make_pair(0, 2); ASSERT_EQ(data->usage_reference_count.count(edge), 1); - // GLOBAL1 .. GLOBAL4 are referenced - ASSERT_EQ(data->usage_reference_count[edge], 4); + // see comments in foo.cpp about reference counts + ASSERT_EQ(data->usage_reference_count[edge], 6); - // file ID from 2 to 1, aka paste.h to global.h + // file ID from 0 to 3, aka foo.cpp to macro.h + edge = std::make_pair(0, 3); + // ANOTHER_PASTE & REFERENCE_GLOBAL1 + ASSERT_EQ(data->usage_reference_count[edge], 2); + + // file ID from 0 to 5, aka foo.cpp to indirect.h + edge = std::make_pair(0, 3); + // ANOTHER_PASTE & REFERENCE_GLOBAL1 + ASSERT_EQ(data->usage_reference_count[edge], 2); + + // file ID from 1 to 2, aka paste.h to global.h edge = std::make_pair(1, 2); ASSERT_EQ(data->usage_reference_count.count(edge), 1); + // the macro usages in foo.cpp have the reference counts ASSERT_EQ(data->usage_reference_count[edge], 0); - // file ID from 0 to 3, aka foo.cpp to macro.h - edge = std::make_pair(0, 3); - ASSERT_EQ(data->usage_reference_count[edge], 2); // USE 2 macros from here - // file ID from 3 to 2, aka macro.h to global.h edge = std::make_pair(3, 2); - // Even though macro.h has a "spelling" of GLOBAL3 in macro.h it does - // not include global.h and does not directly reference GLOBAL3 - // only callers of the macro actually reference global.h + // The call to REFERENCE_GLOBAL3 in foo.cpp has spelling of GLOBAL3 + // in macro.h ASSERT_EQ(data->usage_reference_count.count(edge), 1); + ASSERT_EQ(data->usage_reference_count[edge], 1); + + // file ID from 4 to 2, aka indirect.h to global.h + edge = std::make_pair(4, 2); + ASSERT_EQ(data->usage_reference_count.count(edge), 0); ASSERT_EQ(data->usage_reference_count[edge], 0); - } + + // file ID from 4 to 1, aka indirect.h to paste.h + edge = std::make_pair(4, 1); + ASSERT_EQ(data->usage_reference_count.count(edge), 1); + ASSERT_EQ(data->usage_reference_count[edge], 2); + } }; TEST(use_meta_tool, factory) { llvm::cl::OptionCategory MyToolCategory("my-tool options"); - const char* argv[] = { - "foo", - CMAKE_SOURCE_DIR "/t/data/043-variable-access-through-expansion/foo.cpp", - "--", - "-xc++" - }; + const char *argv[] = {"foo", + CMAKE_SOURCE_DIR + "/t/data/043-variable-access-through-expansion/foo.cpp", + "--", "-xc++"}; int argc = sizeof(argv) / sizeof(argv[0]); auto result = clang::tooling::CommonOptionsParser::create( - argc, argv, MyToolCategory, llvm::cl::OneOrMore); + argc, argv, MyToolCategory, llvm::cl::OneOrMore); ASSERT_TRUE(!!result); - clang::tooling::CommonOptionsParser& optionsParser = result.get(); + clang::tooling::CommonOptionsParser &optionsParser = result.get(); - clang::tooling::RefactoringTool tool - ( optionsParser.getCompilations(), - optionsParser.getSourcePathList()); + clang::tooling::RefactoringTool tool(optionsParser.getCompilations(), + optionsParser.getSourcePathList()); - clangmetatool::MetaToolFactory< clangmetatool::MetaTool > - raf(tool.getReplacements()); + clangmetatool::MetaToolFactory> raf( + tool.getReplacements()); int r = tool.runAndSave(&raf); ASSERT_EQ(0, r); } - // ---------------------------------------------------------------------------- // Copyright 2023 Bloomberg Finance L.P. // diff --git a/t/044-type-access-through-expansion.t.cpp b/t/044-type-access-through-expansion.t.cpp index 6b9e0e2..83ff696 100644 --- a/t/044-type-access-through-expansion.t.cpp +++ b/t/044-type-access-through-expansion.t.cpp @@ -2,70 +2,73 @@ #include -#include -#include #include +#include +#include #include -#include #include -#include +#include #include +#include #include class MyTool { private: - clang::CompilerInstance* ci; + clang::CompilerInstance *ci; clangmetatool::collectors::IncludeGraph graph; + public: - MyTool(clang::CompilerInstance* ci, clang::ast_matchers::MatchFinder *f) - :ci(ci), graph(ci, f) { - } - void postProcessing - (std::map &replacementsMap) { + MyTool(clang::CompilerInstance *ci, clang::ast_matchers::MatchFinder *f) + : ci(ci), graph(ci, f) {} + void postProcessing( + std::map &replacementsMap) { clangmetatool::collectors::IncludeGraphData *data = graph.getData(); // file ID 0 to 1, aka foo.cpp to foo.h auto edge = std::make_pair(0, 1); ASSERT_EQ(data->usage_reference_count.count(edge), 1); - ASSERT_EQ(data->usage_reference_count[edge], 1); + ASSERT_EQ(data->usage_reference_count[edge], 0); // file ID from 0 to 2, aka foo.cpp to macro.h edge = std::make_pair(0, 2); ASSERT_EQ(data->usage_reference_count.count(edge), 1); // We reference the macros in macro.h 3 times. ASSERT_EQ(data->usage_reference_count[edge], 3); - } + + // file ID from 1 to 2, aka macro.h to foo.h + edge = std::make_pair(2, 1); + ASSERT_EQ(data->usage_reference_count.count(edge), 1); + // We reference the type in foo.h 3 times, but type declarations are + // only stored once + ASSERT_EQ(data->usage_reference_count[edge], 1); + } }; TEST(use_meta_tool, factory) { llvm::cl::OptionCategory MyToolCategory("my-tool options"); - const char* argv[] = { - "foo", - CMAKE_SOURCE_DIR "/t/data/044-type-access-through-expansion/foo.cpp", - "--", - "-xc++" - }; + const char *argv[] = {"foo", + CMAKE_SOURCE_DIR + "/t/data/044-type-access-through-expansion/foo.cpp", + "--", "-xc++"}; int argc = sizeof(argv) / sizeof(argv[0]); auto result = clang::tooling::CommonOptionsParser::create( - argc, argv, MyToolCategory, llvm::cl::OneOrMore); + argc, argv, MyToolCategory, llvm::cl::OneOrMore); ASSERT_TRUE(!!result); - clang::tooling::CommonOptionsParser& optionsParser = result.get(); + clang::tooling::CommonOptionsParser &optionsParser = result.get(); - clang::tooling::RefactoringTool tool - ( optionsParser.getCompilations(), - optionsParser.getSourcePathList()); + clang::tooling::RefactoringTool tool(optionsParser.getCompilations(), + optionsParser.getSourcePathList()); - clangmetatool::MetaToolFactory< clangmetatool::MetaTool > - raf(tool.getReplacements()); + clangmetatool::MetaToolFactory> raf( + tool.getReplacements()); int r = tool.runAndSave(&raf); ASSERT_EQ(0, r); } - // ---------------------------------------------------------------------------- // Copyright 2023 Bloomberg Finance L.P. // diff --git a/t/data/043-variable-access-through-expansion/foo.cpp b/t/data/043-variable-access-through-expansion/foo.cpp index d63a4a7..3ef1aa0 100644 --- a/t/data/043-variable-access-through-expansion/foo.cpp +++ b/t/data/043-variable-access-through-expansion/foo.cpp @@ -1,13 +1,32 @@ #include "paste.h" #include "macro.h" #include "global.h" +#include "indirect.h" + +#define REFERENCE_GLOBAL1() GLOBAL1 int bar() { - return PASTE(GLO, BAL1) + PASTE(GL, OBAL2); + return PASTE(GLO, BAL1) // 1st use of global.h + + PASTE(GL, OBAL2) // 2nd use of global.h + + REFERENCE_GLOBAL1() // 3rd use of global.h (no scratch space) + ; } int baz() { - return ANOTHER_PASTE(GL, OBAL4) + REFERENCE_GLOBAL3(); + return ANOTHER_PASTE(GL, OBAL4) // 4th use of global.h + + REFERENCE_GLOBAL3() // not a use of global.h (no scratch space) + ; +} + +int indirect() +{ + return INDIRECT_PASTE(GL, OBAL5); // 5th use of global.h +} + + +int indirect2() +{ + return INDIRECT_REFERENCE_GLOBAL6(); // 6th use of global.h } diff --git a/t/data/043-variable-access-through-expansion/global.h b/t/data/043-variable-access-through-expansion/global.h index 8006760..00ace54 100644 --- a/t/data/043-variable-access-through-expansion/global.h +++ b/t/data/043-variable-access-through-expansion/global.h @@ -5,5 +5,7 @@ int GLOBAL1; int GLOBAL2; int GLOBAL3; int GLOBAL4; +int GLOBAL5; +int GLOBAL6; #endif diff --git a/t/data/043-variable-access-through-expansion/indirect.h b/t/data/043-variable-access-through-expansion/indirect.h new file mode 100644 index 0000000..d7fb3bb --- /dev/null +++ b/t/data/043-variable-access-through-expansion/indirect.h @@ -0,0 +1,8 @@ +#include "paste.h" + +#define INDIRECT_PASTE(a, b) ( \ + PASTE(a, b) \ + ) + +// Does not counts as an access to GLOBAL6 +#define INDIRECT_REFERENCE_GLOBAL6() INDIRECT_PASTE(GLOB, AL6)