forked from OSchip/llvm-project
[clangd] Skip .inc headers when canonicalizing header #include.
Summary: This assumes that .inc files are supposed to be included via headers that include them. Reviewers: sammccall Reviewed By: sammccall Subscribers: klimek, ilya-biryukov, MaskRay, jkorous, cfe-commits Differential Revision: https://reviews.llvm.org/D47187 llvm-svn: 333188
This commit is contained in:
parent
d34b765cb2
commit
3cee95e723
|
@ -8,6 +8,8 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "CanonicalIncludes.h"
|
#include "CanonicalIncludes.h"
|
||||||
|
#include "../Headers.h"
|
||||||
|
#include "clang/Driver/Types.h"
|
||||||
#include "llvm/Support/Regex.h"
|
#include "llvm/Support/Regex.h"
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
|
@ -33,12 +35,33 @@ void CanonicalIncludes::addSymbolMapping(llvm::StringRef QualifiedName,
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::StringRef
|
llvm::StringRef
|
||||||
CanonicalIncludes::mapHeader(llvm::StringRef Header,
|
CanonicalIncludes::mapHeader(llvm::ArrayRef<std::string> Headers,
|
||||||
llvm::StringRef QualifiedName) const {
|
llvm::StringRef QualifiedName) const {
|
||||||
|
assert(!Headers.empty());
|
||||||
auto SE = SymbolMapping.find(QualifiedName);
|
auto SE = SymbolMapping.find(QualifiedName);
|
||||||
if (SE != SymbolMapping.end())
|
if (SE != SymbolMapping.end())
|
||||||
return SE->second;
|
return SE->second;
|
||||||
std::lock_guard<std::mutex> Lock(RegexMutex);
|
std::lock_guard<std::mutex> Lock(RegexMutex);
|
||||||
|
// Find the first header such that the extension is not '.inc', and isn't a
|
||||||
|
// recognized non-header file
|
||||||
|
auto I =
|
||||||
|
std::find_if(Headers.begin(), Headers.end(), [](llvm::StringRef Include) {
|
||||||
|
// Skip .inc file whose including header file should
|
||||||
|
// be #included instead.
|
||||||
|
return !Include.endswith(".inc");
|
||||||
|
});
|
||||||
|
if (I == Headers.end())
|
||||||
|
return Headers[0]; // Fallback to the declaring header.
|
||||||
|
StringRef Header = *I;
|
||||||
|
// If Header is not expected be included (e.g. .cc file), we fall back to
|
||||||
|
// the declaring header.
|
||||||
|
StringRef Ext = llvm::sys::path::extension(Header).trim('.');
|
||||||
|
// Include-able headers must have precompile type. Treat files with
|
||||||
|
// non-recognized extenstions (TY_INVALID) as headers.
|
||||||
|
auto ExtType = driver::types::lookupTypeForExtension(Ext);
|
||||||
|
if ((ExtType != driver::types::TY_INVALID) &&
|
||||||
|
!driver::types::onlyPrecompileType(ExtType))
|
||||||
|
return Headers[0];
|
||||||
for (auto &Entry : RegexHeaderMappingTable) {
|
for (auto &Entry : RegexHeaderMappingTable) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
std::string Dummy;
|
std::string Dummy;
|
||||||
|
@ -65,9 +88,8 @@ collectIWYUHeaderMaps(CanonicalIncludes *Includes) {
|
||||||
// FIXME(ioeric): resolve the header and store actual file path. For now,
|
// FIXME(ioeric): resolve the header and store actual file path. For now,
|
||||||
// we simply assume the written header is suitable to be #included.
|
// we simply assume the written header is suitable to be #included.
|
||||||
Includes->addMapping(PP.getSourceManager().getFilename(Range.getBegin()),
|
Includes->addMapping(PP.getSourceManager().getFilename(Range.getBegin()),
|
||||||
(Text.startswith("<") || Text.startswith("\""))
|
isLiteralInclude(Text) ? Text.str()
|
||||||
? Text.str()
|
: ("\"" + Text + "\"").str());
|
||||||
: ("\"" + Text + "\"").str());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,9 +48,10 @@ public:
|
||||||
void addSymbolMapping(llvm::StringRef QualifiedName,
|
void addSymbolMapping(llvm::StringRef QualifiedName,
|
||||||
llvm::StringRef CanonicalPath);
|
llvm::StringRef CanonicalPath);
|
||||||
|
|
||||||
/// Returns the canonical include for symbol with \p QualifiedName, which is
|
/// Returns the canonical include for symbol with \p QualifiedName.
|
||||||
/// declared in \p Header
|
/// \p Headers is the include stack: Headers.front() is the file declaring the
|
||||||
llvm::StringRef mapHeader(llvm::StringRef Header,
|
/// symbol, and Headers.back() is the main file.
|
||||||
|
llvm::StringRef mapHeader(llvm::ArrayRef<std::string> Headers,
|
||||||
llvm::StringRef QualifiedName) const;
|
llvm::StringRef QualifiedName) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -200,23 +200,31 @@ bool shouldCollectIncludePath(index::SymbolKind Kind) {
|
||||||
/// Gets a canonical include (URI of the header or <header> or "header") for
|
/// Gets a canonical include (URI of the header or <header> or "header") for
|
||||||
/// header of \p Loc.
|
/// header of \p Loc.
|
||||||
/// Returns None if fails to get include header for \p Loc.
|
/// Returns None if fails to get include header for \p Loc.
|
||||||
/// FIXME: we should handle .inc files whose symbols are expected be exported by
|
|
||||||
/// their containing headers.
|
|
||||||
llvm::Optional<std::string>
|
llvm::Optional<std::string>
|
||||||
getIncludeHeader(llvm::StringRef QName, const SourceManager &SM,
|
getIncludeHeader(llvm::StringRef QName, const SourceManager &SM,
|
||||||
SourceLocation Loc, const SymbolCollector::Options &Opts) {
|
SourceLocation Loc, const SymbolCollector::Options &Opts) {
|
||||||
llvm::StringRef FilePath = SM.getFilename(Loc);
|
std::vector<std::string> Headers;
|
||||||
if (FilePath.empty())
|
// Collect the #include stack.
|
||||||
return llvm::None;
|
while (true) {
|
||||||
if (Opts.Includes) {
|
if (!Loc.isValid())
|
||||||
llvm::StringRef Mapped = Opts.Includes->mapHeader(FilePath, QName);
|
break;
|
||||||
if (Mapped != FilePath)
|
auto FilePath = SM.getFilename(Loc);
|
||||||
return (Mapped.startswith("<") || Mapped.startswith("\""))
|
if (FilePath.empty())
|
||||||
? Mapped.str()
|
break;
|
||||||
: ("\"" + Mapped + "\"").str();
|
Headers.push_back(FilePath);
|
||||||
|
if (SM.isInMainFile(Loc))
|
||||||
|
break;
|
||||||
|
Loc = SM.getIncludeLoc(SM.getFileID(Loc));
|
||||||
}
|
}
|
||||||
|
if (Headers.empty())
|
||||||
return toURI(SM, SM.getFilename(Loc), Opts);
|
return llvm::None;
|
||||||
|
llvm::StringRef Header = Headers[0];
|
||||||
|
if (Opts.Includes) {
|
||||||
|
Header = Opts.Includes->mapHeader(Headers, QName);
|
||||||
|
if (Header.startswith("<") || Header.startswith("\""))
|
||||||
|
return Header.str();
|
||||||
|
}
|
||||||
|
return toURI(SM, Header, Opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the symbol location of the given declaration `D`.
|
// Return the symbol location of the given declaration `D`.
|
||||||
|
|
|
@ -131,9 +131,9 @@ public:
|
||||||
auto Factory = llvm::make_unique<SymbolIndexActionFactory>(
|
auto Factory = llvm::make_unique<SymbolIndexActionFactory>(
|
||||||
CollectorOpts, PragmaHandler.get());
|
CollectorOpts, PragmaHandler.get());
|
||||||
|
|
||||||
std::vector<std::string> Args = {"symbol_collector", "-fsyntax-only",
|
std::vector<std::string> Args = {
|
||||||
"-std=c++11", "-include",
|
"symbol_collector", "-fsyntax-only", "-xc++", "-std=c++11",
|
||||||
TestHeaderName, TestFileName};
|
"-include", TestHeaderName, TestFileName};
|
||||||
Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
|
Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
|
||||||
tooling::ToolInvocation Invocation(
|
tooling::ToolInvocation Invocation(
|
||||||
Args,
|
Args,
|
||||||
|
@ -666,6 +666,70 @@ TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
|
||||||
IncludeHeader("\"the/good/header.h\""))));
|
IncludeHeader("\"the/good/header.h\""))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SymbolCollectorTest, SkipIncFileWhenCanonicalizeHeaders) {
|
||||||
|
CollectorOpts.CollectIncludePath = true;
|
||||||
|
CanonicalIncludes Includes;
|
||||||
|
Includes.addMapping(TestHeaderName, "<canonical>");
|
||||||
|
CollectorOpts.Includes = &Includes;
|
||||||
|
auto IncFile = testPath("test.inc");
|
||||||
|
auto IncURI = URI::createFile(IncFile).toString();
|
||||||
|
InMemoryFileSystem->addFile(IncFile, 0,
|
||||||
|
llvm::MemoryBuffer::getMemBuffer("class X {};"));
|
||||||
|
runSymbolCollector("#include \"test.inc\"\nclass Y {};", /*Main=*/"",
|
||||||
|
/*ExtraArgs=*/{"-I", testRoot()});
|
||||||
|
EXPECT_THAT(Symbols,
|
||||||
|
UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
|
||||||
|
IncludeHeader("<canonical>")),
|
||||||
|
AllOf(QName("Y"), DeclURI(TestHeaderURI),
|
||||||
|
IncludeHeader("<canonical>"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SymbolCollectorTest, MainFileIsHeaderWhenSkipIncFile) {
|
||||||
|
CollectorOpts.CollectIncludePath = true;
|
||||||
|
CanonicalIncludes Includes;
|
||||||
|
CollectorOpts.Includes = &Includes;
|
||||||
|
TestFileName = testPath("main.h");
|
||||||
|
TestFileURI = URI::createFile(TestFileName).toString();
|
||||||
|
auto IncFile = testPath("test.inc");
|
||||||
|
auto IncURI = URI::createFile(IncFile).toString();
|
||||||
|
InMemoryFileSystem->addFile(IncFile, 0,
|
||||||
|
llvm::MemoryBuffer::getMemBuffer("class X {};"));
|
||||||
|
runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
|
||||||
|
/*ExtraArgs=*/{"-I", testRoot()});
|
||||||
|
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
|
||||||
|
IncludeHeader(TestFileURI))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SymbolCollectorTest, MainFileIsHeaderWithoutExtensionWhenSkipIncFile) {
|
||||||
|
CollectorOpts.CollectIncludePath = true;
|
||||||
|
CanonicalIncludes Includes;
|
||||||
|
CollectorOpts.Includes = &Includes;
|
||||||
|
TestFileName = testPath("no_ext_main");
|
||||||
|
TestFileURI = URI::createFile(TestFileName).toString();
|
||||||
|
auto IncFile = testPath("test.inc");
|
||||||
|
auto IncURI = URI::createFile(IncFile).toString();
|
||||||
|
InMemoryFileSystem->addFile(IncFile, 0,
|
||||||
|
llvm::MemoryBuffer::getMemBuffer("class X {};"));
|
||||||
|
runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
|
||||||
|
/*ExtraArgs=*/{"-I", testRoot()});
|
||||||
|
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
|
||||||
|
IncludeHeader(TestFileURI))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SymbolCollectorTest, FallbackToIncFileWhenIncludingFileIsCC) {
|
||||||
|
CollectorOpts.CollectIncludePath = true;
|
||||||
|
CanonicalIncludes Includes;
|
||||||
|
CollectorOpts.Includes = &Includes;
|
||||||
|
auto IncFile = testPath("test.inc");
|
||||||
|
auto IncURI = URI::createFile(IncFile).toString();
|
||||||
|
InMemoryFileSystem->addFile(IncFile, 0,
|
||||||
|
llvm::MemoryBuffer::getMemBuffer("class X {};"));
|
||||||
|
runSymbolCollector("", /*Main=*/"#include \"test.inc\"",
|
||||||
|
/*ExtraArgs=*/{"-I", testRoot()});
|
||||||
|
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), DeclURI(IncURI),
|
||||||
|
IncludeHeader(IncURI))));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
|
TEST_F(SymbolCollectorTest, AvoidUsingFwdDeclsAsCanonicalDecls) {
|
||||||
CollectorOpts.CollectIncludePath = true;
|
CollectorOpts.CollectIncludePath = true;
|
||||||
Annotations Header(R"(
|
Annotations Header(R"(
|
||||||
|
|
Loading…
Reference in New Issue