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 @@ -4381,6 +4381,9 @@ //===----------------------------------------------------------------------===// let Flags = [FC1Option, FlangOnlyOption] in { +def fget_definition : MultiArg<["-"], "fget-definition", 3>, + HelpText<"Get the symbol definition from ">, + Group; 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, 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 @@ -116,6 +116,10 @@ void ExecuteAction() override; }; +class GetDefinitionAction : public PrescanAndSemaAction { + void ExecuteAction() override; +}; + class GetSymbolsSourcesAction : public PrescanAndSemaAction { void ExecuteAction() override; }; 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 @@ -67,6 +67,9 @@ /// Parse, run semantics and then output the pre-FIR tree DebugPreFIRTree, + /// `-fget-definition` + GetDefinition, + /// Parse, run semantics and then dump symbol sources map GetSymbolsSources @@ -206,6 +209,14 @@ /// compilation. unsigned needProvenanceRangeToCharBlockMappings_ : 1; + /// Input values from `-fget-definition` + struct GetDefinitionVals { + unsigned line; + unsigned startColumn; + unsigned endColumn; + }; + GetDefinitionVals getDefVals_; + /// The input files and their types. std::vector inputs_; 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 @@ -95,12 +95,14 @@ if (opts.programAction_ == DebugDumpParsingLog) opts.instrumentedParse_ = true; - if (opts.programAction_ == DebugDumpProvenance) + if (opts.programAction_ == DebugDumpProvenance || + opts.programAction_ == Fortran::frontend::GetDefinition) opts.needProvenanceRangeToCharBlockMappings_ = true; } -static void ParseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args, +static bool ParseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args, clang::DiagnosticsEngine &diags) { + unsigned numErrorsBefore = diags.getNumErrors(); // By default the frontend driver creates a ParseSyntaxOnly action. opts.programAction_ = ParseSyntaxOnly; @@ -157,6 +159,9 @@ case clang::driver::options::OPT_fget_symbols_sources: opts.programAction_ = GetSymbolsSources; break; + case clang::driver::options::OPT_fget_definition: + opts.programAction_ = GetDefinition; + break; // TODO: // case calng::driver::options::OPT_emit_llvm: @@ -165,6 +170,27 @@ // case clang::driver::options::OPT_emit_module: // (...) } + + // Parse the values provided with `-fget-definition` (there should be 3 + // integers) + if (llvm::opt::OptSpecifier(a->getOption().getID()) == + clang::driver::options::OPT_fget_definition) { + unsigned optVals[3] = {0, 0, 0}; + + for (unsigned i = 0; i < 3; i++) { + llvm::StringRef val = a->getValue(i); + + if (val.getAsInteger(10, optVals[i])) { + // A non-integer was encountered - that's an error. + diags.Report(clang::diag::err_drv_invalid_value) + << a->getOption().getName() << val; + break; + } + } + opts.getDefVals_.line = optVals[0]; + opts.getDefVals_.startColumn = optVals[1]; + opts.getDefVals_.endColumn = optVals[2]; + } } opts.outputFile_ = args.getLastArgValue(clang::driver::options::OPT_o); @@ -293,6 +319,8 @@ setUpFrontendBasedOnAction(opts); opts.dashX_ = dashX; + + return diags.getNumErrors() == numErrorsBefore; } // Generate the path to look for intrinsic modules @@ -481,8 +509,7 @@ success = false; } - // Parse the frontend args - ParseFrontendArgs(res.frontendOpts(), args, diags); + success &= ParseFrontendArgs(res.frontendOpts(), args, diags); parsePreprocessorArgs(res.preprocessorOpts(), args); success &= parseSemaArgs(res, args, diags); success &= parseDialectArgs(res, args, diags); 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 @@ -21,6 +21,7 @@ #include "flang/Semantics/semantics.h" #include "flang/Semantics/unparse-with-symbols.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" #include #include @@ -371,6 +372,53 @@ ci.parsing().DumpParsingLog(llvm::outs()); } +void GetDefinitionAction::ExecuteAction() { + // Report and exit if fatal semantic errors are present + if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), + GetCurrentFileOrBufferName())) + return; + + CompilerInstance &ci = this->instance(); + parser::AllCookedSources &cs = ci.allCookedSources(); + unsigned diagID = ci.diagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, "Symbol not found"); + + auto gdv = ci.invocation().frontendOpts().getDefVals_; + auto charBlock{cs.GetCharBlockFromLineAndColumns( + gdv.line, gdv.startColumn, gdv.endColumn)}; + if (!charBlock) { + ci.diagnostics().Report(diagID); + return; + } + + llvm::outs() << "String range: >" << charBlock->ToString() << "<\n"; + + auto *symbol{ci.invocation() + .semanticsContext() + .FindScope(*charBlock) + .FindSymbol(*charBlock)}; + if (!symbol) { + ci.diagnostics().Report(diagID); + return; + } + + llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; + + auto sourceInfo{cs.GetSourcePositionRange(symbol->name())}; + if (!sourceInfo) { + llvm_unreachable( + "Failed to obtain SourcePosition." + "TODO: Please, write a test and replace this with a diagnostic!"); + return; + } + + llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n"; + llvm::outs() << symbol->name().ToString() << ": " + << sourceInfo->first.file.path() << ", " + << sourceInfo->first.line << ", " << sourceInfo->first.column + << "-" << sourceInfo->second.column << "\n"; +} + void GetSymbolsSourcesAction::ExecuteAction() { // Report and exit if fatal semantic errors are present if (reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), 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 @@ -67,6 +67,9 @@ case DebugPreFIRTree: return std::make_unique(); break; + case GetDefinition: + return std::make_unique(); + break; case GetSymbolsSources: 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 @@ -91,6 +91,8 @@ ! HELP-FC1-NEXT: -ffixed-line-length= ! HELP-FC1-NEXT: Use as character line width in fixed mode ! HELP-FC1-NEXT: -ffree-form Process source files in free form +! HELP-FC1-NEXT: -fget-definition +! HELP-FC1-NEXT: Get the symbol definition from ! HELP-FC1-NEXT: -fget-symbols-sources Dump symbols and their source code locations ! HELP-FC1-NEXT: -fimplicit-none No implicit typing allowed unless overridden by IMPLICIT statements ! HELP-FC1-NEXT: -finput-charset= Specify the default character set for source files diff --git a/flang/test/Driver/get-definition.f90 b/flang/test/Driver/get-definition.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/get-definition.f90 @@ -0,0 +1,46 @@ +! Verify that the driver correctly rejects invalid values for -fget-definition + +!----------- +! RUN LINES +!----------- +! RUN: not %flang_fc1 -fsyntax-only -fget-definition 45 1 2 %s 2>&1 | FileCheck --check-prefix=OK %s +! RUN: not %flang_fc1 -fsyntax-only -fget-definition a 1 1 %s 2>&1 | FileCheck --check-prefix=ERROR-a %s +! RUN: not %flang_fc1 -fsyntax-only -fget-definition 1 b 1 %s 2>&1 | FileCheck --check-prefix=ERROR-b %s +! RUN: not %flang_fc1 -fsyntax-only -fget-definition 1 1 c %s 2>&1 | FileCheck --check-prefix=ERROR-c %s +! RUN: not %flang_fc1 -fsyntax-only -fget-definition a b 1 %s 2>&1 | FileCheck --check-prefix=ERROR-ab %s +! RUN: not %flang_fc1 -fsyntax-only -fget-definition a b c %s 2>&1 | FileCheck --check-prefix=ERROR-abc %s +! RUN: not %flang_fc1 -fsyntax-only -fget-definition 1 b c %s 2>&1 | FileCheck --check-prefix=ERROR-bc %s +! RUN: not %flang_fc1 -fsyntax-only -fget-definition a 1 c %s 2>&1 | FileCheck --check-prefix=ERROR-ac %s + +!----------------- +! EXPECTED OUTPUT +!----------------- +! OK: String range: >m< +! OK-NOT: error + +! ERROR-a: error: invalid value 'a' in 'fget-definition' +! ERROR-a-NOT: String range: >m< + +! ERROR-b: error: invalid value 'b' in 'fget-definition' +! ERROR-b-NOT: String range: >m< + +! ERROR-c: error: invalid value 'c' in 'fget-definition' +! ERROR-c-NOT: String range: >m< + +! ERROR-ab: error: invalid value 'a' in 'fget-definition' +! ERROR-ab-NOT: String range: >m< + +! ERROR-ac: error: invalid value 'a' in 'fget-definition' +! ERROR-ac-NOT: String range: >m< + +! ERROR-bc: error: invalid value 'b' in 'fget-definition' +! ERROR-bc-NOT: String range: >m< + +! ERROR-abc: error: invalid value 'a' in 'fget-definition' +! ERROR-abc-NOT: String range: >m< + +!------- +! INPUT +!------- +module m +end module diff --git a/flang/test/Semantics/getdefinition01.f90 b/flang/test/Semantics/getdefinition01.f90 --- a/flang/test/Semantics/getdefinition01.f90 +++ b/flang/test/Semantics/getdefinition01.f90 @@ -16,12 +16,9 @@ end module ! RUN and CHECK lines at the bottom as this test is sensitive to line numbers -! RUN: %f18 -fget-definition 6 17 18 -fsyntax-only %s | FileCheck --check-prefix=CHECK1 %s -! RUN: %f18 -fget-definition 7 20 23 -fsyntax-only %s | FileCheck --check-prefix=CHECK2 %s -! RUN: %f18 -fget-definition 14 3 4 -fsyntax-only %s | FileCheck --check-prefix=CHECK3 %s +! RUN: %flang_fc1 -fsyntax-only -fget-definition 6 17 18 %s | FileCheck --check-prefix=CHECK1 %s +! RUN: %flang_fc1 -fsyntax-only -fget-definition 7 20 23 %s | FileCheck --check-prefix=CHECK2 %s +! RUN: %flang_fc1 -fsyntax-only -fget-definition 14 3 4 %s | FileCheck --check-prefix=CHECK3 %s ! CHECK1: x:{{.*}}getdefinition01.f90, 5, 21-22 ! CHECK2: yyy:{{.*}}getdefinition01.f90, 5, 24-27 ! CHECK3: x:{{.*}}getdefinition01.f90, 13, 24-25 - -! RUN: not %f18 -fget-definition -fsyntax-only %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s -! CHECK-ERROR: Invalid argument to -fget-definitions diff --git a/flang/test/Semantics/getdefinition02.f b/flang/test/Semantics/getdefinition02.f --- a/flang/test/Semantics/getdefinition02.f +++ b/flang/test/Semantics/getdefinition02.f @@ -17,9 +17,9 @@ end module ! RUN and CHECK lines here as test is sensitive to line numbers -! RUN: %f18 -fget-definition 7 9 10 -fsyntax-only %s 2>&1 | FileCheck --check-prefix=CHECK1 %s -! RUN: %f18 -fget-definition 8 26 29 -fsyntax-only %s 2>&1 | FileCheck --check-prefix=CHECK2 %s -! RUN: %f18 -fget-definition 15 9 10 -fsyntax-only %s 2>&1 | FileCheck --check-prefix=CHECK3 %s +! RUN: %flang_fc1 -fsyntax-only -fget-definition 7 9 10 %s 2>&1 | FileCheck --check-prefix=CHECK1 %s +! RUN: %flang_fc1 -fsyntax-only -fget-definition 8 26 29 %s 2>&1 | FileCheck --check-prefix=CHECK2 %s +! RUN: %flang_fc1 -fsyntax-only -fget-definition 15 9 10 %s 2>&1 | FileCheck --check-prefix=CHECK3 %s ! CHECK1: x:{{.*}}getdefinition02.f, 5, 27-28 ! CHECK2: yyy:{{.*}}getdefinition02.f, 5, 30-33 ! CHECK3: x:{{.*}}getdefinition02.f, 14, 30-31 diff --git a/flang/test/Semantics/getdefinition03-a.f90 b/flang/test/Semantics/getdefinition03-a.f90 --- a/flang/test/Semantics/getdefinition03-a.f90 +++ b/flang/test/Semantics/getdefinition03-a.f90 @@ -7,7 +7,7 @@ x = f end program -! RUN: %f18 -fget-definition 7 6 7 -fsyntax-only %s | FileCheck --check-prefix=CHECK1 %s -! RUN: %f18 -fget-definition 7 2 3 -fsyntax-only %s | FileCheck --check-prefix=CHECK2 %s +! RUN: %flang_fc1 -fsyntax-only -fget-definition 7 6 7 %s | FileCheck --check-prefix=CHECK1 %s +! RUN: %flang_fc1 -fsyntax-only -fget-definition 7 2 3 %s | FileCheck --check-prefix=CHECK2 %s ! CHECK1: f:{{.*}}getdefinition03-b.f90, 2, 12-13 ! CHECK2: x:{{.*}}getdefinition03-a.f90, 6, 13-14 diff --git a/flang/test/Semantics/getdefinition04.f90 b/flang/test/Semantics/getdefinition04.f90 --- a/flang/test/Semantics/getdefinition04.f90 +++ b/flang/test/Semantics/getdefinition04.f90 @@ -6,5 +6,5 @@ x = y end program -! RUN: %f18 -fget-definition 6 3 4 -fsyntax-only %s | FileCheck %s +! RUN: %flang_fc1 -fsyntax-only -fget-definition 6 3 4 %s | FileCheck %s ! CHECK: x:{{.*}}getdefinition04.f90, 3, 14-15 diff --git a/flang/test/Semantics/getdefinition05.f90 b/flang/test/Semantics/getdefinition05.f90 --- a/flang/test/Semantics/getdefinition05.f90 +++ b/flang/test/Semantics/getdefinition05.f90 @@ -12,8 +12,8 @@ end program !! Inner x -! RUN: %f18 -fget-definition 9 5 6 -fsyntax-only %s | FileCheck --check-prefix=CHECK1 %s +! RUN: %flang_fc1 -fsyntax-only -fget-definition 9 5 6 %s | FileCheck --check-prefix=CHECK1 %s ! CHECK1: x:{{.*}}getdefinition05.f90, 7, 16-17 !! Outer y -! RUN: %f18 -fget-definition 11 7 8 -fsyntax-only %s | FileCheck --check-prefix=CHECK2 %s +! RUN: %flang_fc1 -fsyntax-only -fget-definition 11 7 8 %s | FileCheck --check-prefix=CHECK2 %s ! CHECK2: y:{{.*}}getdefinition05.f90, 5, 14-15 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 @@ -658,8 +658,8 @@ } arguments[i] = std::strtol(args.front().c_str(), &endptr, 10); if (*endptr != '\0') { - llvm::errs() << "Invalid argument to -fget-definitions: " - << args.front() << '\n'; + llvm::errs() << "error: invalid value '" << args.front() + << "' in 'fget-definition'" << '\n'; return EXIT_FAILURE; } args.pop_front();