[flang][driver] Add support for `-fget-definition`

This patch adds `-fget-definition` to `flang-new`. The semantics of this
option are identical in both drivers. The error message in the
"throwaway" driver is updated so that it matches the one from
`flang-new` (which is auto-generated and cannot be changed easily).

Tests are updated accordingly. A dedicated test for error handling was
added: get-definition.f90 (for the sake of simplicity,
getdefinition01.f90 no longer tests for errors).

The `ParseFrontendArgs` function is updated so that it can return
errors. This change is required in order to report invalid values
following `-fget-definition`.

The actual implementation of `GetDefinitionAction::ExecuteAction()` was
extracted from f18.cpp (i.e. the bit that deals with
`-fget-definition`).

Depends on: https://reviews.llvm.org/D100556

Differential Revision: https://reviews.llvm.org/D100558
This commit is contained in:
Andrzej Warzynski 2021-04-14 10:43:14 +00:00
parent cd64273f5e
commit dc256a443a
14 changed files with 161 additions and 20 deletions

View File

@ -4381,6 +4381,9 @@ def J : JoinedOrSeparate<["-"], "J">,
//===----------------------------------------------------------------------===//
let Flags = [FC1Option, FlangOnlyOption] in {
def fget_definition : MultiArg<["-"], "fget-definition", 3>,
HelpText<"Get the symbol definition from <line> <start-column> <end-column>">,
Group<Action_Group>;
def test_io : Flag<["-"], "test-io">, Group<Action_Group>,
HelpText<"Run the InputOuputTest action. Use for development and testing only.">;
def fdebug_unparse_no_sema : Flag<["-"], "fdebug-unparse-no-sema">, Group<Action_Group>,

View File

@ -116,6 +116,10 @@ class DebugPreFIRTreeAction : public PrescanAndSemaAction {
void ExecuteAction() override;
};
class GetDefinitionAction : public PrescanAndSemaAction {
void ExecuteAction() override;
};
class GetSymbolsSourcesAction : public PrescanAndSemaAction {
void ExecuteAction() override;
};

View File

@ -67,6 +67,9 @@ enum ActionKind {
/// 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 @@ public:
/// 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<FrontendInputFile> inputs_;

View File

@ -95,12 +95,14 @@ static void setUpFrontendBasedOnAction(FrontendOptions &opts) {
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 @@ static void ParseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
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 @@ static void ParseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
// 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 @@ static void ParseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
setUpFrontendBasedOnAction(opts);
opts.dashX_ = dashX;
return diags.getNumErrors() == numErrorsBefore;
}
// Generate the path to look for intrinsic modules
@ -481,8 +509,7 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &res,
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);

View File

@ -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 <clang/Basic/Diagnostic.h>
#include <memory>
@ -371,6 +372,53 @@ void DebugDumpParsingLogAction::ExecuteAction() {
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(),

View File

@ -67,6 +67,9 @@ static std::unique_ptr<FrontendAction> CreateFrontendBaseAction(
case DebugPreFIRTree:
return std::make_unique<DebugPreFIRTreeAction>();
break;
case GetDefinition:
return std::make_unique<GetDefinitionAction>();
break;
case GetSymbolsSources:
return std::make_unique<GetSymbolsSourcesAction>();
break;

View File

@ -91,6 +91,8 @@
! HELP-FC1-NEXT: -ffixed-line-length=<value>
! HELP-FC1-NEXT: Use <value> as character line width in fixed mode
! HELP-FC1-NEXT: -ffree-form Process source files in free form
! HELP-FC1-NEXT: -fget-definition <value> <value> <value>
! HELP-FC1-NEXT: Get the symbol definition from <line> <start-column> <end-column>
! 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=<value> Specify the default character set for source files

View File

@ -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

View File

@ -16,12 +16,9 @@ contains
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

View File

@ -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

View File

@ -7,7 +7,7 @@ program main
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

View File

@ -6,5 +6,5 @@ program main
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

View File

@ -12,8 +12,8 @@ program main
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

View File

@ -658,8 +658,8 @@ int main(int argc, char *const argv[]) {
}
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();