diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4377,14 +4377,26 @@ def test_io : Flag<["-"], "test-io">, Group, HelpText<"Run the InputOuputTest action. Use for development and testing only.">; +def fdebug_unparse_no_sema : Flag<["-"], "fdebug-unparse-no-sema">, Group, + HelpText<"Unparse and stop (skips the semantic checks)">, + DocBrief<[{Only run the parser, then unparse the parse-tree and output the +generated Fortran source file. Semantic checks are disabled.}]>; def fdebug_unparse : Flag<["-"], "fdebug-unparse">, Group, - HelpText<"Unparse and stop.">; + HelpText<"Unparse and stop.">, + DocBrief<[{Run the parser and the semantic checks. Then unparse the +parse-tree and output the generated Fortran source file.}]>; def fdebug_unparse_with_symbols : Flag<["-"], "fdebug-unparse-with-symbols">, Group, HelpText<"Unparse and stop.">; def fdebug_dump_symbols : Flag<["-"], "fdebug-dump-symbols">, Group, HelpText<"Dump symbols after the semantic analysis">; def fdebug_dump_parse_tree : Flag<["-"], "fdebug-dump-parse-tree">, Group, - HelpText<"Dump the parse tree">; + HelpText<"Dump the parse tree">, + DocBrief<[{Run the Parser and the semantic checks, and then output the +parse tree.}]>; +def fdebug_dump_parse_tree_no_sema : Flag<["-"], "fdebug-dump-parse-tree-no-sema">, Group, + HelpText<"Dump the parse tree (skips the semantic checks)">, + DocBrief<[{Run the Parser and then output the parse tree. Semantic +checks are disabled.}]>; def fdebug_dump_provenance : Flag<["-"], "fdebug-dump-provenance">, Group, HelpText<"Dump provenance">; def fdebug_dump_parsing_log : Flag<["-"], "fdebug-dump-parsing-log">, Group, diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h --- a/flang/include/flang/Frontend/FrontendActions.h +++ b/flang/include/flang/Frontend/FrontendActions.h @@ -62,6 +62,22 @@ void ExecuteAction() override; }; +//===----------------------------------------------------------------------===// +// PrescanAndParse Actions +//===----------------------------------------------------------------------===// +class PrescanAndParseAction : public FrontendAction { + void ExecuteAction() override = 0; + bool BeginSourceFileAction(CompilerInstance &ci) override; +}; + +class DebugUnparseNoSemaAction : public PrescanAndParseAction { + void ExecuteAction() override; +}; + +class DebugDumpParseTreeNoSemaAction : public PrescanAndParseAction { + void ExecuteAction() override; +}; + //===----------------------------------------------------------------------===// // PrescanAndSema Actions //===----------------------------------------------------------------------===// diff --git a/flang/include/flang/Frontend/FrontendOptions.h b/flang/include/flang/Frontend/FrontendOptions.h --- a/flang/include/flang/Frontend/FrontendOptions.h +++ b/flang/include/flang/Frontend/FrontendOptions.h @@ -37,6 +37,10 @@ /// Parse, unparse the parse-tree and output a Fortran source file DebugUnparse, + /// Parse, unparse the parse-tree and output a Fortran source file, skip the + /// semantic checks + DebugUnparseNoSema, + /// Parse, resolve the sybmols, unparse the parse-tree and then output a /// Fortran source file DebugUnparseWithSymbols, @@ -47,6 +51,9 @@ /// Parse, run semantics and then output the parse tree DebugDumpParseTree, + /// Parse and then output the parse tree, skip the semantic checks + DebugDumpParseTreeNoSema, + /// Dump provenance DebugDumpProvenance, diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -127,6 +127,9 @@ case clang::driver::options::OPT_fdebug_unparse: opts.programAction_ = DebugUnparse; break; + case clang::driver::options::OPT_fdebug_unparse_no_sema: + opts.programAction_ = DebugUnparseNoSema; + break; case clang::driver::options::OPT_fdebug_unparse_with_symbols: opts.programAction_ = DebugUnparseWithSymbols; break; @@ -136,6 +139,9 @@ case clang::driver::options::OPT_fdebug_dump_parse_tree: opts.programAction_ = DebugDumpParseTree; break; + case clang::driver::options::OPT_fdebug_dump_parse_tree_no_sema: + opts.programAction_ = DebugDumpParseTreeNoSema; + break; case clang::driver::options::OPT_fdebug_dump_provenance: opts.programAction_ = DebugDumpProvenance; break; diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -67,6 +67,55 @@ return true; } +bool PrescanAndParseAction::BeginSourceFileAction(CompilerInstance &c1) { + CompilerInstance &ci = this->instance(); + + std::string currentInputPath{GetCurrentFileOrBufferName()}; + + Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); + + if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) { + // Switch between fixed and free form format based on the input file + // extension. + // + // Ideally we should have all Fortran options set before entering this + // method (i.e. before processing any specific input files). However, we + // can't decide between fixed and free form based on the file extension + // earlier than this. + parserOptions.isFixedForm = currentInput().IsFixedForm(); + } + + // Prescan. In case of failure, report and return. + ci.parsing().Prescan(currentInputPath, parserOptions); + + if (ci.parsing().messages().AnyFatalError()) { + const unsigned diagID = ci.diagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, "Could not scan %0"); + ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); + ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); + + return false; + } + + // Parse. In case of failure, report and return. + ci.parsing().Parse(llvm::outs()); + + if (ci.parsing().messages().AnyFatalError()) { + unsigned diagID = ci.diagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, "Could not parse %0"); + ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName(); + + ci.parsing().messages().Emit( + llvm::errs(), this->instance().allCookedSources()); + return false; + } + + // Report the diagnostics from parsing + ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources()); + + return true; +} + bool PrescanAndSemaAction::BeginSourceFileAction(CompilerInstance &c1) { CompilerInstance &ci = this->instance(); std::string currentInputPath{GetCurrentFileOrBufferName()}; @@ -117,6 +166,7 @@ // Report the diagnostics from the semantic checks semantics.EmitMessages(ci.semaOutputStream()); + return true; } @@ -191,6 +241,19 @@ GetCurrentFileOrBufferName()); } +void DebugUnparseNoSemaAction::ExecuteAction() { + auto &parseTree{instance().parsing().parseTree()}; + + Fortran::parser::AnalyzedObjectsAsFortran asFortran = + Fortran::frontend::getBasicAsFortran(); + + // TODO: Options should come from CompilerInvocation + Unparse(llvm::outs(), *parseTree, + /*encoding=*/Fortran::parser::Encoding::UTF_8, + /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, + /*preStatement=*/nullptr, &asFortran); +} + void DebugUnparseAction::ExecuteAction() { auto &parseTree{instance().parsing().parseTree()}; Fortran::parser::AnalyzedObjectsAsFortran asFortran = @@ -228,6 +291,15 @@ semantics, this->instance().diagnostics(), GetCurrentFileOrBufferName()); } +void DebugDumpParseTreeNoSemaAction::ExecuteAction() { + auto &parseTree{instance().parsing().parseTree()}; + Fortran::parser::AnalyzedObjectsAsFortran asFortran = + Fortran::frontend::getBasicAsFortran(); + + // Dump parse tree + Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); +} + void DebugDumpParseTreeAction::ExecuteAction() { auto &parseTree{instance().parsing().parseTree()}; Fortran::parser::AnalyzedObjectsAsFortran asFortran = diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp --- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -40,6 +40,9 @@ case DebugUnparse: return std::make_unique(); break; + case DebugUnparseNoSema: + return std::make_unique(); + break; case DebugUnparseWithSymbols: return std::make_unique(); break; @@ -49,6 +52,9 @@ case DebugDumpParseTree: return std::make_unique(); break; + case DebugDumpParseTreeNoSema: + return std::make_unique(); + break; case DebugDumpProvenance: return std::make_unique(); break; diff --git a/flang/test/Driver/driver-help.f90 b/flang/test/Driver/driver-help.f90 --- a/flang/test/Driver/driver-help.f90 +++ b/flang/test/Driver/driver-help.f90 @@ -69,6 +69,8 @@ ! HELP-FC1-NEXT: -falternative-parameter-statement ! HELP-FC1-NEXT: Enable the old style PARAMETER statement ! HELP-FC1-NEXT: -fbackslash Specify that backslash in string introduces an escape character +! HELP-FC1-NEXT: -fdebug-dump-parse-tree-no-sema +! HELP-FC1-NEXT: Dump the parse tree (skips the semantic checks) ! HELP-FC1-NEXT: -fdebug-dump-parse-tree Dump the parse tree ! HELP-FC1-NEXT: -fdebug-dump-parsing-log ! HELP-FC1-NEXT: Run instrumented parse and dump the parsing log @@ -78,6 +80,7 @@ ! HELP-FC1-NEXT: Measure the parse tree ! HELP-FC1-NEXT: -fdebug-module-writer Enable debug messages while writing module files ! HELP-FC1-NEXT: -fdebug-pre-fir-tree Dump the pre-FIR tree +! HELP-FC1-NEXT: -fdebug-unparse-no-sema Unparse and stop (skips the semantic checks) ! HELP-FC1-NEXT: -fdebug-unparse-with-symbols ! HELP-FC1-NEXT: Unparse and stop. ! HELP-FC1-NEXT: -fdebug-unparse Unparse and stop. diff --git a/flang/test/Driver/dump-parse-tree-no-sema.f90 b/flang/test/Driver/dump-parse-tree-no-sema.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/dump-parse-tree-no-sema.f90 @@ -0,0 +1,22 @@ +!---------- +! RUN lines +!---------- +! RUN: %flang_fc1 -fdebug-dump-parse-tree %s 2>&1 | FileCheck %s --check-prefix=SEMA_ON +! RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s --check-prefix=SEMA_OFF + +!----------------- +! EXPECTEED OUTPUT +!----------------- +! SEMA_ON: | | | NamedConstant -> Name = 'i' +! SEMA_ON-NEXT: | | | Constant -> Expr = '1_4' +! SEMA_ON-NEXT: | | | | LiteralConstant -> IntLiteralConstant = '1' + +! SEMA_OFF: | | | NamedConstant -> Name = 'i' +! SEMA_OFF-NEXT: | | | Constant -> Expr -> LiteralConstant -> IntLiteralConstant = '1' + +!------- +! INPUT +!------- +parameter(i=1) +integer :: j +end program diff --git a/flang/test/Parser/omp-allocate-unparse.f90 b/flang/test/Parser/omp-allocate-unparse.f90 --- a/flang/test/Parser/omp-allocate-unparse.f90 +++ b/flang/test/Parser/omp-allocate-unparse.f90 @@ -1,4 +1,4 @@ -! RUN: %f18 -fdebug-no-semantics -funparse -fopenmp %s | FileCheck %s +! RUN: %flang_fc1 -fdebug-unparse-no-sema -fopenmp %s | FileCheck %s ! Check Unparsing of OpenMP Allocate directive program allocate_unparse diff --git a/flang/test/Parser/omp-atomic-unparse.f90 b/flang/test/Parser/omp-atomic-unparse.f90 --- a/flang/test/Parser/omp-atomic-unparse.f90 +++ b/flang/test/Parser/omp-atomic-unparse.f90 @@ -1,4 +1,4 @@ -! RUN: %f18 -fdebug-no-semantics -funparse -fopenmp %s | FileCheck %s +! RUN: %flang_fc1 -fdebug-unparse-no-sema -fopenmp %s | FileCheck %s program main implicit none diff --git a/flang/tools/f18/f18.cpp b/flang/tools/f18/f18.cpp --- a/flang/tools/f18/f18.cpp +++ b/flang/tools/f18/f18.cpp @@ -564,6 +564,13 @@ options.instrumentedParse = true; } else if (arg == "-fdebug-no-semantics") { driver.debugNoSemantics = true; + } else if (arg == "-fdebug-unparse-no-sema") { + driver.debugNoSemantics = true; + driver.dumpUnparse = true; + } else if (arg == "-fdebug-dump-parse-tree-no-sema") { + driver.debugNoSemantics = true; + driver.dumpParseTree = true; + driver.syntaxOnly = true; } else if (arg == "-funparse" || arg == "-fdebug-unparse") { driver.dumpUnparse = true; } else if (arg == "-funparse-with-symbols" ||