forked from OSchip/llvm-project
[flang] Search for #include "file" in right directory (take 2)
Make the #include "file" preprocessing directive begin its search in the same directory as the file containing the directive, as other preprocessors and our Fortran INCLUDE statement do. Avoid current working directory for all source files except the original. Resolve tests. Differential Revision: https://reviews.llvm.org/D95481
This commit is contained in:
parent
764a7a2155
commit
6110e7716c
|
@ -148,9 +148,9 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushSearchPathDirectory(std::string);
|
void AppendSearchPathDirectory(std::string); // new last directory
|
||||||
std::string PopSearchPathDirectory();
|
const SourceFile *Open(std::string path, llvm::raw_ostream &error,
|
||||||
const SourceFile *Open(std::string path, llvm::raw_ostream &error);
|
std::optional<std::string> &&prependPath = std::nullopt);
|
||||||
const SourceFile *ReadStandardInput(llvm::raw_ostream &error);
|
const SourceFile *ReadStandardInput(llvm::raw_ostream &error);
|
||||||
|
|
||||||
ProvenanceRange AddIncludedFile(
|
ProvenanceRange AddIncludedFile(
|
||||||
|
@ -210,7 +210,7 @@ private:
|
||||||
ProvenanceRange range_;
|
ProvenanceRange range_;
|
||||||
std::map<char, Provenance> compilerInsertionProvenance_;
|
std::map<char, Provenance> compilerInsertionProvenance_;
|
||||||
std::vector<std::unique_ptr<SourceFile>> ownedSourceFiles_;
|
std::vector<std::unique_ptr<SourceFile>> ownedSourceFiles_;
|
||||||
std::vector<std::string> searchPath_;
|
std::list<std::string> searchPath_;
|
||||||
Encoding encoding_{Encoding::UTF_8};
|
Encoding encoding_{Encoding::UTF_8};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include "characters.h"
|
#include "characters.h"
|
||||||
#include "llvm/Support/MemoryBuffer.h"
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -28,8 +30,8 @@ class raw_ostream;
|
||||||
namespace Fortran::parser {
|
namespace Fortran::parser {
|
||||||
|
|
||||||
std::string DirectoryName(std::string path);
|
std::string DirectoryName(std::string path);
|
||||||
std::string LocateSourceFile(
|
std::optional<std::string> LocateSourceFile(
|
||||||
std::string name, const std::vector<std::string> &searchPath);
|
std::string name, const std::list<std::string> &searchPath);
|
||||||
|
|
||||||
class SourceFile;
|
class SourceFile;
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ void InputOutputTestAction::ExecuteAction() {
|
||||||
CompilerInstance &ci = instance();
|
CompilerInstance &ci = instance();
|
||||||
Fortran::parser::AllSources &allSources{ci.allSources()};
|
Fortran::parser::AllSources &allSources{ci.allSources()};
|
||||||
const Fortran::parser::SourceFile *sf;
|
const Fortran::parser::SourceFile *sf;
|
||||||
sf = allSources.Open(path, error_stream);
|
sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s});
|
||||||
llvm::ArrayRef<char> fileContent = sf->content();
|
llvm::ArrayRef<char> fileContent = sf->content();
|
||||||
|
|
||||||
// Output file descriptor to receive the content of input file.
|
// Output file descriptor to receive the content of input file.
|
||||||
|
|
|
@ -25,7 +25,7 @@ const SourceFile *Parsing::Prescan(const std::string &path, Options options) {
|
||||||
AllSources &allSources{allCooked_.allSources()};
|
AllSources &allSources{allCooked_.allSources()};
|
||||||
if (options.isModuleFile) {
|
if (options.isModuleFile) {
|
||||||
for (const auto &path : options.searchDirectories) {
|
for (const auto &path : options.searchDirectories) {
|
||||||
allSources.PushSearchPathDirectory(path);
|
allSources.AppendSearchPathDirectory(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,8 @@ const SourceFile *Parsing::Prescan(const std::string &path, Options options) {
|
||||||
if (path == "-") {
|
if (path == "-") {
|
||||||
sourceFile = allSources.ReadStandardInput(fileError);
|
sourceFile = allSources.ReadStandardInput(fileError);
|
||||||
} else {
|
} else {
|
||||||
sourceFile = allSources.Open(path, fileError);
|
std::optional<std::string> currentDirectory{"."};
|
||||||
|
sourceFile = allSources.Open(path, fileError, std::move(currentDirectory));
|
||||||
}
|
}
|
||||||
if (!fileError.str().empty()) {
|
if (!fileError.str().empty()) {
|
||||||
ProvenanceRange range{allSources.AddCompilerInsertion(path)};
|
ProvenanceRange range{allSources.AddCompilerInsertion(path)};
|
||||||
|
@ -46,12 +47,12 @@ const SourceFile *Parsing::Prescan(const std::string &path, Options options) {
|
||||||
|
|
||||||
if (!options.isModuleFile) {
|
if (!options.isModuleFile) {
|
||||||
// For .mod files we always want to look in the search directories.
|
// For .mod files we always want to look in the search directories.
|
||||||
// For normal source files we don't push them until after the primary
|
// For normal source files we don't add them until after the primary
|
||||||
// source file has been opened. If foo.f is missing from the current
|
// source file has been opened. If foo.f is missing from the current
|
||||||
// working directory, we don't want to accidentally read another foo.f
|
// working directory, we don't want to accidentally read another foo.f
|
||||||
// from another directory that's on the search path.
|
// from another directory that's on the search path.
|
||||||
for (const auto &path : options.searchDirectories) {
|
for (const auto &path : options.searchDirectories) {
|
||||||
allSources.PushSearchPathDirectory(path);
|
allSources.AppendSearchPathDirectory(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -399,6 +399,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||||
if (j == tokens) {
|
if (j == tokens) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
CHECK(prescanner); // TODO: change to reference
|
||||||
if (dir.TokenAt(j).ToString() != "#") {
|
if (dir.TokenAt(j).ToString() != "#") {
|
||||||
prescanner->Say(dir.GetTokenProvenanceRange(j), "missing '#'"_err_en_US);
|
prescanner->Say(dir.GetTokenProvenanceRange(j), "missing '#'"_err_en_US);
|
||||||
return;
|
return;
|
||||||
|
@ -578,6 +579,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string include;
|
std::string include;
|
||||||
|
std::optional<std::string> prependPath;
|
||||||
if (dir.TokenAt(j).ToString() == "<") { // #include <foo>
|
if (dir.TokenAt(j).ToString() == "<") { // #include <foo>
|
||||||
std::size_t k{j + 1};
|
std::size_t k{j + 1};
|
||||||
if (k >= tokens) {
|
if (k >= tokens) {
|
||||||
|
@ -598,6 +600,12 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||||
} else if ((include = dir.TokenAt(j).ToString()).substr(0, 1) == "\"" &&
|
} else if ((include = dir.TokenAt(j).ToString()).substr(0, 1) == "\"" &&
|
||||||
include.substr(include.size() - 1, 1) == "\"") { // #include "foo"
|
include.substr(include.size() - 1, 1) == "\"") { // #include "foo"
|
||||||
include = include.substr(1, include.size() - 2);
|
include = include.substr(1, include.size() - 2);
|
||||||
|
// #include "foo" starts search in directory of file containing
|
||||||
|
// the directive
|
||||||
|
auto prov{dir.GetTokenProvenanceRange(dirOffset).start()};
|
||||||
|
if (const auto *currentFile{allSources_.GetSourceFile(prov)}) {
|
||||||
|
prependPath = DirectoryName(currentFile->path());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
prescanner->Say(dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1),
|
prescanner->Say(dir.GetTokenProvenanceRange(j < tokens ? j : tokens - 1),
|
||||||
"#include: expected name of file to include"_err_en_US);
|
"#include: expected name of file to include"_err_en_US);
|
||||||
|
@ -615,7 +623,8 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||||
}
|
}
|
||||||
std::string buf;
|
std::string buf;
|
||||||
llvm::raw_string_ostream error{buf};
|
llvm::raw_string_ostream error{buf};
|
||||||
const SourceFile *included{allSources_.Open(include, error)};
|
const SourceFile *included{
|
||||||
|
allSources_.Open(include, error, std::move(prependPath))};
|
||||||
if (!included) {
|
if (!included) {
|
||||||
prescanner->Say(dir.GetTokenProvenanceRange(dirOffset),
|
prescanner->Say(dir.GetTokenProvenanceRange(dirOffset),
|
||||||
"#include: %s"_err_en_US, error.str());
|
"#include: %s"_err_en_US, error.str());
|
||||||
|
|
|
@ -760,14 +760,12 @@ void Prescanner::FortranInclude(const char *firstQuote) {
|
||||||
std::string buf;
|
std::string buf;
|
||||||
llvm::raw_string_ostream error{buf};
|
llvm::raw_string_ostream error{buf};
|
||||||
Provenance provenance{GetProvenance(nextLine_)};
|
Provenance provenance{GetProvenance(nextLine_)};
|
||||||
const SourceFile *currentFile{allSources_.GetSourceFile(provenance)};
|
std::optional<std::string> prependPath;
|
||||||
if (currentFile) {
|
if (const SourceFile * currentFile{allSources_.GetSourceFile(provenance)}) {
|
||||||
allSources_.PushSearchPathDirectory(DirectoryName(currentFile->path()));
|
prependPath = DirectoryName(currentFile->path());
|
||||||
}
|
|
||||||
const SourceFile *included{allSources_.Open(path, error)};
|
|
||||||
if (currentFile) {
|
|
||||||
allSources_.PopSearchPathDirectory();
|
|
||||||
}
|
}
|
||||||
|
const SourceFile *included{
|
||||||
|
allSources_.Open(path, error, std::move(prependPath))};
|
||||||
if (!included) {
|
if (!included) {
|
||||||
Say(provenance, "INCLUDE: %s"_err_en_US, error.str());
|
Say(provenance, "INCLUDE: %s"_err_en_US, error.str());
|
||||||
} else if (included->bytes() > 0) {
|
} else if (included->bytes() > 0) {
|
||||||
|
|
|
@ -156,20 +156,28 @@ const char &AllSources::operator[](Provenance at) const {
|
||||||
return origin[origin.covers.MemberOffset(at)];
|
return origin[origin.covers.MemberOffset(at)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void AllSources::PushSearchPathDirectory(std::string directory) {
|
void AllSources::AppendSearchPathDirectory(std::string directory) {
|
||||||
// gfortran and ifort append to current path, PGI prepends
|
// gfortran and ifort append to current path, PGI prepends
|
||||||
searchPath_.push_back(directory);
|
searchPath_.push_back(directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AllSources::PopSearchPathDirectory() {
|
const SourceFile *AllSources::Open(std::string path, llvm::raw_ostream &error,
|
||||||
std::string directory{searchPath_.back()};
|
std::optional<std::string> &&prependPath) {
|
||||||
searchPath_.pop_back();
|
|
||||||
return directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SourceFile *AllSources::Open(std::string path, llvm::raw_ostream &error) {
|
|
||||||
std::unique_ptr<SourceFile> source{std::make_unique<SourceFile>(encoding_)};
|
std::unique_ptr<SourceFile> source{std::make_unique<SourceFile>(encoding_)};
|
||||||
if (source->Open(LocateSourceFile(path, searchPath_), error)) {
|
if (prependPath) {
|
||||||
|
// Set to "." for the initial source file; set to the directory name
|
||||||
|
// of the including file for #include "quoted-file" directives &
|
||||||
|
// INCLUDE statements.
|
||||||
|
searchPath_.emplace_front(std::move(*prependPath));
|
||||||
|
}
|
||||||
|
std::optional<std::string> found{LocateSourceFile(path, searchPath_)};
|
||||||
|
if (prependPath) {
|
||||||
|
searchPath_.pop_front();
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
error << "Source file '" << path << "' was not found";
|
||||||
|
return nullptr;
|
||||||
|
} else if (source->Open(*found, error)) {
|
||||||
return ownedSourceFiles_.emplace_back(std::move(source)).get();
|
return ownedSourceFiles_.emplace_back(std::move(source)).get();
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -56,9 +56,9 @@ std::string DirectoryName(std::string path) {
|
||||||
return pathBuf.str().str();
|
return pathBuf.str().str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string LocateSourceFile(
|
std::optional<std::string> LocateSourceFile(
|
||||||
std::string name, const std::vector<std::string> &searchPath) {
|
std::string name, const std::list<std::string> &searchPath) {
|
||||||
if (name.empty() || name == "-" || llvm::sys::path::is_absolute(name)) {
|
if (name == "-" || llvm::sys::path::is_absolute(name)) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
for (const std::string &dir : searchPath) {
|
for (const std::string &dir : searchPath) {
|
||||||
|
@ -70,7 +70,7 @@ std::string LocateSourceFile(
|
||||||
return path.str().str();
|
return path.str().str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return name;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t RemoveCarriageReturns(llvm::MutableArrayRef<char> buf) {
|
std::size_t RemoveCarriageReturns(llvm::MutableArrayRef<char> buf) {
|
||||||
|
@ -123,7 +123,6 @@ bool SourceFile::Open(std::string path, llvm::raw_ostream &error) {
|
||||||
bool SourceFile::ReadStandardInput(llvm::raw_ostream &error) {
|
bool SourceFile::ReadStandardInput(llvm::raw_ostream &error) {
|
||||||
Close();
|
Close();
|
||||||
path_ = "standard input";
|
path_ = "standard input";
|
||||||
|
|
||||||
auto buf_or = llvm::MemoryBuffer::getSTDIN();
|
auto buf_or = llvm::MemoryBuffer::getSTDIN();
|
||||||
if (!buf_or) {
|
if (!buf_or) {
|
||||||
auto err = buf_or.getError();
|
auto err = buf_or.getError();
|
||||||
|
@ -146,7 +145,6 @@ void SourceFile::ReadFile() {
|
||||||
auto tmp_buf{llvm::WritableMemoryBuffer::getNewUninitMemBuffer(
|
auto tmp_buf{llvm::WritableMemoryBuffer::getNewUninitMemBuffer(
|
||||||
content().size() + 1)};
|
content().size() + 1)};
|
||||||
llvm::copy(content(), tmp_buf->getBufferStart());
|
llvm::copy(content(), tmp_buf->getBufferStart());
|
||||||
Close();
|
|
||||||
buf_ = std::move(tmp_buf);
|
buf_ = std::move(tmp_buf);
|
||||||
}
|
}
|
||||||
buf_end_++;
|
buf_end_++;
|
||||||
|
|
|
@ -1 +1,5 @@
|
||||||
NOT A REAL MODULE FILE - USE THIS ONLY FOR TESTING THE DRIVER
|
!mod$ v1 sum:1f5a35ada852dc66
|
||||||
|
module basictestmoduleone
|
||||||
|
type::t1
|
||||||
|
end type
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
!mod$ v1 sum:449b70509dd4bce3
|
||||||
|
module basictestmoduleone
|
||||||
|
type::t2
|
||||||
|
end type
|
||||||
|
end
|
|
@ -1 +1,3 @@
|
||||||
NOT A REAL MODULE FILE - USE THIS ONLY FOR TESTING THE DRIVER
|
!mod$ v1 sum:563b9a1f049282d2
|
||||||
|
module basictestmoduletwo
|
||||||
|
end
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
!--------------------------------------------
|
!--------------------------------------------
|
||||||
! EXPECTED OUTPUT FOR MISSING INCLUDED FILE
|
! EXPECTED OUTPUT FOR MISSING INCLUDED FILE
|
||||||
!--------------------------------------------
|
!--------------------------------------------
|
||||||
! UNINCLUDED:No such file or directory
|
! UNINCLUDED:#include: Source file 'basic-header-one.h' was not found
|
||||||
! UNINCLUDED-NOT:program b
|
! UNINCLUDED-NOT:program b
|
||||||
! UNINCLUDED-NOT:program c
|
! UNINCLUDED-NOT:program c
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
! Ensure argument -I works as expected with module files.
|
! Ensure argument -I works as expected with module files.
|
||||||
! The module files for this test are not real module files.
|
|
||||||
|
|
||||||
! REQUIRES: new-flang-driver
|
! REQUIRES: new-flang-driver
|
||||||
|
|
||||||
|
@ -18,15 +17,21 @@
|
||||||
!-----------------------------------------
|
!-----------------------------------------
|
||||||
! EXPECTED OUTPUT FOR MISSING MODULE FILE
|
! EXPECTED OUTPUT FOR MISSING MODULE FILE
|
||||||
!-----------------------------------------
|
!-----------------------------------------
|
||||||
! SINGLEINCLUDE:No such file or directory
|
! SINGLEINCLUDE:Error reading module file for module 'basictestmoduletwo'
|
||||||
! SINGLEINCLUDE-NOT:Error reading module file for module 'basictestmoduletwo'
|
! SINGLEINCLUDE-NOT:Error reading module file for module 'basictestmoduletwo'
|
||||||
|
! SINGLEINCLUDE-NOT:error: Derived type 't1' not found
|
||||||
|
! SINGLEINCLUDE:error: Derived type 't2' not found
|
||||||
|
|
||||||
!---------------------------------------
|
!---------------------------------------
|
||||||
! EXPECTED OUTPUT FOR ALL MODULES FOUND
|
! EXPECTED OUTPUT FOR ALL MODULES FOUND
|
||||||
!---------------------------------------
|
!---------------------------------------
|
||||||
! INCLUDED-NOT:No such file or directory
|
! INCLUDED-NOT:Error reading module file
|
||||||
|
! INCLUDED-NOT:error: Derived type 't1' not found
|
||||||
|
! INCLUDED:error: Derived type 't2' not found
|
||||||
|
|
||||||
program test_dash_I_with_mod_files
|
program test_dash_I_with_mod_files
|
||||||
USE basictestmoduleone
|
USE basictestmoduleone
|
||||||
USE basictestmoduletwo
|
USE basictestmoduletwo
|
||||||
|
type(t1) :: x1 ! t1 defined in Inputs/basictestmoduleone.mod
|
||||||
|
type(t2) :: x2 ! t2 defined in Inputs/module-dir/basictestmoduleone.mod
|
||||||
end
|
end
|
||||||
|
|
|
@ -84,7 +84,7 @@ struct DriverOptions {
|
||||||
bool verbose{false}; // -v
|
bool verbose{false}; // -v
|
||||||
bool compileOnly{false}; // -c
|
bool compileOnly{false}; // -c
|
||||||
std::string outputPath; // -o path
|
std::string outputPath; // -o path
|
||||||
std::vector<std::string> searchDirectories{"."s}; // -I dir
|
std::vector<std::string> searchDirectories; // -I dir
|
||||||
std::string moduleDirectory{"."s}; // -module dir
|
std::string moduleDirectory{"."s}; // -module dir
|
||||||
std::string moduleFileSuffix{".mod"}; // -moduleSuffix suff
|
std::string moduleFileSuffix{".mod"}; // -moduleSuffix suff
|
||||||
bool forcedForm{false}; // -Mfixed or -Mfree appeared
|
bool forcedForm{false}; // -Mfixed or -Mfree appeared
|
||||||
|
|
|
@ -157,7 +157,7 @@ TEST_F(FrontendActionTest, ParseSyntaxOnly) {
|
||||||
EXPECT_TRUE(!outputDiagBuffer.empty());
|
EXPECT_TRUE(!outputDiagBuffer.empty());
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
llvm::StringRef(outputDiagBuffer.data())
|
llvm::StringRef(outputDiagBuffer.data())
|
||||||
.startswith(
|
.contains(
|
||||||
":1:14: error: IF statement is not allowed in IF statement\n"));
|
":1:14: error: IF statement is not allowed in IF statement\n"));
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
Loading…
Reference in New Issue