[flang][driver] Add support for -fsyntax-only

The behaviour triggered with this flag is consistent with `-fparse-only`
in `flang` (i.e. the throwaway driver). This new spelling is consistent
with Clang and gfortran, and was proposed and agreed on for the new
driver in [1].

This patch also adds some minimal logic to communicate whether the
semantic checks have failed or not. When semantic checks fail, a
frontend driver error is generated. The return code from the frontend
driver is then determined by checking the driver diagnostics - the
presence of driver errors means that the compilation has failed. This
logic is consistent with `clang -cc1`.

[1] http://lists.llvm.org/pipermail/flang-dev/2020-November/000588.html

Differential Revision: https://reviews.llvm.org/D92854
This commit is contained in:
Andrzej Warzynski 2020-12-08 16:27:46 +00:00
parent 95d3cc67ca
commit 7d246cb19d
11 changed files with 156 additions and 4 deletions

View File

@ -2287,7 +2287,7 @@ defm strict_vtable_pointers : BoolFOption<"strict-vtable-pointers",
ResetBy<NegFlag>>;
def fstrict_overflow : Flag<["-"], "fstrict-overflow">, Group<f_Group>;
def fsyntax_only : Flag<["-"], "fsyntax-only">,
Flags<[NoXarchOption,CoreOption,CC1Option]>, Group<Action_Group>;
Flags<[NoXarchOption,CoreOption,CC1Option,FC1Option]>, Group<Action_Group>;
def ftabstop_EQ : Joined<["-"], "ftabstop=">, Group<f_Group>;
def ftemplate_depth_EQ : Joined<["-"], "ftemplate-depth=">, Group<f_Group>;
def ftemplate_depth_ : Joined<["-"], "ftemplate-depth-">, Group<f_Group>;

View File

@ -12,6 +12,7 @@
#include "flang/Frontend/FrontendAction.h"
#include "flang/Parser/parsing.h"
#include "flang/Parser/provenance.h"
#include "flang/Semantics/semantics.h"
#include "llvm/Support/raw_ostream.h"
namespace Fortran::frontend {
@ -28,6 +29,12 @@ class CompilerInstance {
std::shared_ptr<Fortran::parser::Parsing> parsing_;
/// The stream for diagnostics from Semantics
llvm::raw_ostream *semaOutputStream_ = &llvm::errs();
/// The stream for diagnostics from Semantics if owned, otherwise nullptr.
std::unique_ptr<llvm::raw_ostream> ownedSemaOutputStream_;
/// The diagnostics engine instance.
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_;
@ -77,6 +84,11 @@ public:
bool HasAllSources() const { return allSources_ != nullptr; }
parser::AllCookedSources &allCookedSources() {
assert(allCookedSources_ && "Compiler instance has no AllCookedSources!");
return *allCookedSources_;
};
/// }
/// @name Parser Operations
/// {
@ -84,6 +96,19 @@ public:
/// Return parsing to be used by Actions.
Fortran::parser::Parsing &parsing() const { return *parsing_; }
/// }
/// @name Semantic analysis
/// {
/// Replace the current stream for verbose output.
void set_semaOutputStream(llvm::raw_ostream &Value);
/// Replace the current stream for verbose output.
void set_semaOutputStream(std::unique_ptr<llvm::raw_ostream> Value);
/// Get the current stream for verbose output.
llvm::raw_ostream &semaOutputStream() { return *semaOutputStream_; }
/// }
/// @name High-Level Operations
/// {

View File

@ -25,6 +25,10 @@ class PrintPreprocessedAction : public FrontendAction {
void ExecuteAction() override;
};
class ParseSyntaxOnlyAction : public FrontendAction {
void ExecuteAction() override;
};
} // namespace Fortran::frontend
#endif // LLVM_FLANG_FRONTEND_FRONTENDACTIONS_H

View File

@ -1,4 +1,4 @@
//===- FrontendOptions.h ----------------------------------------*- C -*-===//
//===- FrontendOptions.h ----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@ -24,7 +24,11 @@ enum ActionKind {
/// -E mode.
PrintPreprocessedInput,
/// TODO: RunPreprocessor, ParserSyntaxOnly, EmitLLVM, EmitLLVMOnly,
/// -fsyntax-only
ParseSyntaxOnly,
/// TODO: RunPreprocessor, EmitLLVM, EmitLLVMOnly,
/// EmitCodeGenOnly, EmitAssembly, (...)
};
@ -34,6 +38,8 @@ inline const char *GetActionKindName(const ActionKind ak) {
return "InputOutputTest";
case PrintPreprocessedInput:
return "PrintPreprocessedInput";
case ParseSyntaxOnly:
return "ParseSyntaxOnly";
default:
return "<unknown ActionKind>";
// TODO:

View File

@ -13,6 +13,8 @@ add_flang_library(flangFrontend
LINK_LIBS
FortranParser
FortranSemantics
FortranCommon
clangBasic
clangDriver

View File

@ -11,6 +11,7 @@
#include "flang/Frontend/TextDiagnosticPrinter.h"
#include "flang/Parser/parsing.h"
#include "flang/Parser/provenance.h"
#include "flang/Semantics/semantics.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
@ -39,6 +40,17 @@ void CompilerInstance::set_invocation(
invocation_ = std::move(value);
}
void CompilerInstance::set_semaOutputStream(raw_ostream &Value) {
ownedSemaOutputStream_.release();
semaOutputStream_ = &Value;
}
void CompilerInstance::set_semaOutputStream(
std::unique_ptr<raw_ostream> Value) {
ownedSemaOutputStream_.swap(Value);
semaOutputStream_ = ownedSemaOutputStream_.get();
}
void CompilerInstance::AddOutputFile(OutputFile &&outFile) {
outputFiles_.push_back(std::move(outFile));
}
@ -140,7 +152,7 @@ bool CompilerInstance::ExecuteAction(FrontendAction &act) {
act.EndSourceFile();
}
}
return true;
return !diagnostics().getClient()->getNumErrors();
}
void CompilerInstance::CreateDiagnostics(

View File

@ -93,6 +93,9 @@ static InputKind ParseFrontendArgs(FrontendOptions &opts,
case clang::driver::options::OPT_E:
opts.programAction_ = PrintPreprocessedInput;
break;
case clang::driver::options::OPT_fsyntax_only:
opts.programAction_ = ParseSyntaxOnly;
break;
// TODO:
// case clang::driver::options::OPT_emit_obj:

View File

@ -7,10 +7,12 @@
//===----------------------------------------------------------------------===//
#include "flang/Frontend/FrontendActions.h"
#include "flang/Common/default-kinds.h"
#include "flang/Frontend/CompilerInstance.h"
#include "flang/Parser/parsing.h"
#include "flang/Parser/provenance.h"
#include "flang/Parser/source.h"
#include "flang/Semantics/semantics.h"
using namespace Fortran::frontend;
@ -68,3 +70,33 @@ void PrintPreprocessedAction::ExecuteAction() {
return;
}
}
void ParseSyntaxOnlyAction::ExecuteAction() {
CompilerInstance &ci = this->instance();
// TODO: These should be specifiable by users. For now just use the defaults.
common::LanguageFeatureControl features;
Fortran::common::IntrinsicTypeDefaultKinds defaultKinds;
// Parse
ci.parsing().Parse(llvm::outs());
auto &parseTree{*ci.parsing().parseTree()};
// Prepare semantics
Fortran::semantics::SemanticsContext semanticsContext{
defaultKinds, features, ci.allCookedSources()};
Fortran::semantics::Semantics semantics{
semanticsContext, parseTree, ci.parsing().cooked().AsCharBlock()};
// Run semantic checks
semantics.Perform();
// Report the diagnostics from the semantic checks
semantics.EmitMessages(ci.semaOutputStream());
if (semantics.AnyFatalError()) {
unsigned DiagID = ci.diagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "semantic errors in %0");
ci.diagnostics().Report(DiagID) << GetCurrentFileOrBufferName();
}
}

View File

@ -32,6 +32,9 @@ static std::unique_ptr<FrontendAction> CreateFrontendBaseAction(
case PrintPreprocessedInput:
return std::make_unique<PrintPreprocessedAction>();
break;
case ParseSyntaxOnly:
return std::make_unique<ParseSyntaxOnlyAction>();
break;
default:
break;
// TODO:

View File

@ -0,0 +1,9 @@
! RUN: not %flang-new -fc1 -fsyntax-only %s 2>&1 | FileCheck %s
! RUN: not %f18 -fparse-only %s 2>&1 | FileCheck %s
! REQUIRES: new-flang-driver
! CHECK: IF statement is not allowed in IF statement
! CHECK: semantic errors in {{.*}}syntax-only.f90
IF (A > 0.0) IF (B < 0.0) A = LOG (A)
END

View File

@ -76,4 +76,60 @@ TEST(FrontendAction, PrintPreprocessedInput) {
llvm::sys::fs::remove(inputFile);
compInst.ClearOutputFiles(/*EraseFiles=*/true);
}
TEST(FrontendAction, ParseSyntaxOnly) {
std::string inputFile = "test-file.f";
std::error_code ec;
// 1. Create the input file for the file manager
// AllSources (which is used to manage files inside every compiler instance),
// works with paths. This means that it requires a physical file. Create one.
std::unique_ptr<llvm::raw_fd_ostream> os{
new llvm::raw_fd_ostream(inputFile, ec, llvm::sys::fs::OF_None)};
if (ec)
FAIL() << "Fail to create the file need by the test";
// Populate the input file with the pre-defined input and flush it.
*(os) << "! if_stmt.f90:\n"
<< "IF (A > 0.0) IF (B < 0.0) A = LOG (A)\n"
<< "END";
os.reset();
// Get the path of the input file
llvm::SmallString<64> cwd;
if (std::error_code ec = llvm::sys::fs::current_path(cwd))
FAIL() << "Failed to obtain the current working directory";
std::string testFilePath(cwd.c_str());
testFilePath += "/" + inputFile;
// 2. Prepare the compiler (CompilerInvocation + CompilerInstance)
CompilerInstance compInst;
compInst.CreateDiagnostics();
auto invocation = std::make_shared<CompilerInvocation>();
invocation->frontendOpts().programAction_ = ParseSyntaxOnly;
compInst.set_invocation(std::move(invocation));
compInst.frontendOpts().inputs_.push_back(
FrontendInputFile(testFilePath, Language::Fortran));
// 3. Set-up the output stream for the semantic diagnostics.
llvm::SmallVector<char, 256> outputDiagBuffer;
std::unique_ptr<llvm::raw_pwrite_stream> outputStream(
new llvm::raw_svector_ostream(outputDiagBuffer));
compInst.set_semaOutputStream(std::move(outputStream));
// 4. Execute the ParseSyntaxOnly action
bool success = ExecuteCompilerInvocation(&compInst);
// 5. Validate the expected output
EXPECT_FALSE(success);
EXPECT_TRUE(!outputDiagBuffer.empty());
EXPECT_TRUE(
llvm::StringRef(outputDiagBuffer.data())
.startswith(
":2:14: error: IF statement is not allowed in IF statement\n"));
// 6. Clear the input files.
llvm::sys::fs::remove(inputFile);
}
} // namespace