[flang] FindOffsetLineAndColumn also uses SourcePosition.

New tests for COMMON and BLOCK.
Added CHECK-ONCE to test_any.sh. Make sure pattern only occurs once.

Original-commit: flang-compiler/f18@ad82dafcf9
Reviewed-on: https://github.com/flang-compiler/f18/pull/698
Tree-same-pre-rewrite: false
This commit is contained in:
Tin Huynh 2019-09-05 10:05:45 -07:00
parent 2f205a5f52
commit 69fd49a002
13 changed files with 208 additions and 67 deletions

View File

@ -239,18 +239,18 @@ void AllSources::EmitMessage(std::ostream &o,
[&](const Inclusion &inc) {
o << inc.source.path();
std::size_t offset{origin.covers.MemberOffset(range->start())};
std::pair<int, int> pos{inc.source.FindOffsetLineAndColumn(offset)};
o << ':' << pos.first << ':' << pos.second;
SourcePosition pos{inc.source.FindOffsetLineAndColumn(offset)};
o << ':' << pos.line << ':' << pos.column;
o << ": " << message << '\n';
if (echoSourceLine) {
const char *text{inc.source.content() +
inc.source.GetLineStartOffset(pos.first)};
inc.source.GetLineStartOffset(pos.line)};
o << " ";
for (const char *p{text}; *p != '\n'; ++p) {
o << *p;
}
o << "\n ";
for (int j{1}; j < pos.second; ++j) {
for (int j{1}; j < pos.column; ++j) {
char ch{text[j - 1]};
o << (ch == '\t' ? '\t' : ' ');
}
@ -260,8 +260,8 @@ void AllSources::EmitMessage(std::ostream &o,
if (&MapToOrigin(last) == &origin) {
auto endOffset{origin.covers.MemberOffset(last)};
auto endPos{inc.source.FindOffsetLineAndColumn(endOffset)};
if (pos.first == endPos.first) {
for (int j{pos.second}; j < endPos.second; ++j) {
if (pos.line == endPos.line) {
for (int j{pos.column}; j < endPos.column; ++j) {
o << '^';
}
}
@ -322,7 +322,7 @@ std::optional<SourcePosition> AllSources::GetSourcePosition(
const Origin &origin{MapToOrigin(prov)};
if (const auto *inc{std::get_if<Inclusion>(&origin.u)}) {
std::size_t offset{origin.covers.MemberOffset(prov)};
return SourcePosition{inc->source, offset};
return inc->source.FindOffsetLineAndColumn(offset);
} else {
return std::nullopt;
}
@ -345,7 +345,7 @@ std::string AllSources::GetPath(Provenance at) const {
int AllSources::GetLineNumber(Provenance at) const {
std::size_t offset{0};
const SourceFile *source{GetSourceFile(at, &offset)};
return source ? source->FindOffsetLineAndColumn(offset).first : 0;
return source ? source->FindOffsetLineAndColumn(offset).line : 0;
}
Provenance AllSources::CompilerInsertionProvenance(char ch) {

View File

@ -138,17 +138,6 @@ private:
std::vector<ContiguousProvenanceMapping> provenanceMap_;
};
struct SourcePosition {
SourcePosition(const SourceFile &file, int line, int column)
: file{file}, line{line}, column{column} {}
SourcePosition(const SourceFile &file, std::pair<int, int> pos)
: file{file}, line{pos.first}, column{pos.second} {}
SourcePosition(const SourceFile &, std::size_t);
const SourceFile &file;
int line, column;
};
// A singleton AllSources instance for the whole compilation
// is shared by reference.
class AllSources {

View File

@ -256,10 +256,10 @@ void SourceFile::Close() {
path_.clear();
}
std::pair<int, int> SourceFile::FindOffsetLineAndColumn(std::size_t at) const {
SourcePosition SourceFile::FindOffsetLineAndColumn(std::size_t at) const {
CHECK(at < bytes_);
if (lineStart_.empty()) {
return {1, static_cast<int>(at + 1)};
return {*this, 1, static_cast<int>(at + 1)};
}
std::size_t low{0}, count{lineStart_.size()};
while (count > 1) {
@ -271,7 +271,7 @@ std::pair<int, int> SourceFile::FindOffsetLineAndColumn(std::size_t at) const {
low = mid;
}
}
return {
static_cast<int>(low + 1), static_cast<int>(at - lineStart_[low] + 1)};
return {*this, static_cast<int>(low + 1),
static_cast<int>(at - lineStart_[low] + 1)};
}
}

View File

@ -33,6 +33,18 @@ std::string DirectoryName(std::string path);
std::string LocateSourceFile(
std::string name, const std::vector<std::string> &searchPath);
class SourceFile;
struct SourcePosition {
SourcePosition(const SourceFile &file, int line, int column)
: file{file}, line{line}, column{column} {}
SourcePosition(const SourceFile &file, std::pair<int, int> pos)
: file{file}, line{pos.first}, column{pos.second} {}
const SourceFile &file;
int line, column;
};
class SourceFile {
public:
explicit SourceFile(Encoding e) : encoding_{e} {}
@ -46,7 +58,7 @@ public:
bool Open(std::string path, std::stringstream *error);
bool ReadStandardInput(std::stringstream *error);
void Close();
std::pair<int, int> FindOffsetLineAndColumn(std::size_t) const;
SourcePosition FindOffsetLineAndColumn(std::size_t) const;
std::size_t GetLineStartOffset(int lineNumber) const {
return lineStart_.at(lineNumber - 1);
}

View File

@ -42,9 +42,21 @@ namespace Fortran::semantics {
using NameToSymbolMap = std::map<const char *, const Symbol *>;
static void DoDumpSymbols(std::ostream &, const Scope &, int indent = 0);
static void GetSymbolNames(const Scope &, NameToSymbolMap &);
static void PutIndent(std::ostream &, int indent);
static void GetSymbolNames(const Scope &scope, NameToSymbolMap &symbols) {
// Finds all symbol names in the scope without collecting duplicates.
for (const auto &pair : scope) {
symbols.emplace(pair.second->name().begin(), pair.second);
}
for (const auto &pair : scope.commonBlocks()) {
symbols.emplace(pair.second->name().begin(), pair.second);
}
for (const auto &child : scope.children()) {
GetSymbolNames(child, symbols);
}
}
// A parse tree visitor that calls Enter/Leave functions from each checker
// class C supplied as template parameters. Enter is called before the node's
// children are visited, Leave is called after. No two checkers may have the
@ -219,6 +231,23 @@ void Semantics::DumpSymbols(std::ostream &os) {
DoDumpSymbols(os, context_.globalScope());
}
void Semantics::DumpSymbolsSources(std::ostream &os) const {
NameToSymbolMap symbols;
GetSymbolNames(context_.globalScope(), symbols);
for (const auto pair : symbols) {
const Symbol &symbol{*pair.second};
auto sourceInfo{cooked_.GetSourcePositionRange(symbol.name())};
if (sourceInfo) {
os << symbol.name().ToString() << ": " << sourceInfo->first.file.path()
<< ", " << sourceInfo->first.line << ", " << sourceInfo->first.column
<< "-" << sourceInfo->second.column << "\n";
} else if (symbol.has<semantics::UseDetails>()) {
os << symbol.name().ToString() << ": "
<< symbol.GetUltimate().owner().symbol()->name().ToString() << "\n";
}
}
}
void DoDumpSymbols(std::ostream &os, const Scope &scope, int indent) {
PutIndent(os, indent);
os << Scope::EnumToString(scope.kind()) << " scope:";
@ -270,36 +299,6 @@ void DoDumpSymbols(std::ostream &os, const Scope &scope, int indent) {
--indent;
}
void Semantics::DumpSymbolsSources(std::ostream &os) const {
NameToSymbolMap symbols;
GetSymbolNames(context_.globalScope(), symbols);
for (const auto pair : symbols) {
const Symbol &symbol{*pair.second};
auto sourceInfo{cooked_.GetSourcePositionRange(symbol.name())};
if (sourceInfo) {
os << symbol.name().ToString() << ": " << sourceInfo->first.file.path()
<< ", " << sourceInfo->first.line << ", " << sourceInfo->first.column
<< "-" << sourceInfo->second.column << "\n";
} else if (symbol.has<semantics::UseDetails>()) {
os << symbol.name().ToString() << ": "
<< symbol.GetUltimate().owner().symbol()->name().ToString() << "\n";
}
}
}
void GetSymbolNames(const Scope &scope, NameToSymbolMap &symbols) {
// Finds all symbol names in the scope without collecting duplicates.
for (const auto &pair : scope) {
symbols.emplace(pair.second->name().begin(), pair.second);
}
for (const auto &pair : scope.commonBlocks()) {
symbols.emplace(pair.second->name().begin(), pair.second);
}
for (const auto &child : scope.children()) {
GetSymbolNames(child, symbols);
}
}
static void PutIndent(std::ostream &os, int indent) {
for (int i = 0; i < indent; ++i) {
os << " ";

View File

@ -248,12 +248,16 @@ set(GETSYMBOLS_TESTS
getsymbols01.f90
getsymbols02-*.f90
getsymbols03-a.f90
getsymbols04.f90
getsymbols05.f90
)
set(GETDEFINITION_TESTS
getdefinition01.f90
getdefinition02.f
getdefinition03-a.f90
getdefinition04.f90
getdefinition05.f90
)
set(F18 $<TARGET_FILE:f18>)

View File

@ -0,0 +1,25 @@
! Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
! Tests -fget-definition with COMMON block with same name as variable.
program main
integer :: x
integer :: y
common /x/ y
x = y
end program
! RUN: ${F18} -fget-definition 21 3 4 -fparse-only -fdebug-semantics %s | ${FileCheck} %s
! CHECK:x:.*getdefinition04.f90, 18, 14-15

View File

@ -0,0 +1,35 @@
! Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
! Tests -fget-symbols-sources with BLOCK that contains same variable name as
! another in an outer scope.
program main
integer :: x
integer :: y
block
integer :: x
integer :: y
x = y
end block
x = y
end program
!! Inner x
! RUN: ${F18} -fget-definition 24 5 6 -fparse-only -fdebug-semantics %s > %t;
! CHECK:x:.*getdefinition05.f90, 22, 16-17
!! Outer y
! RUN: ${F18} -fget-definition 26 7 8 -fparse-only -fdebug-semantics %s >> %t;
! CHECK:y:.*getdefinition05.f90, 20, 14-15
! RUN: cat %t | ${FileCheck} %s;

View File

@ -43,10 +43,10 @@ contains
end module
! RUN: ${F18} -fget-symbols-sources -fparse-only -fdebug-semantics %s 2>&1 | ${FileCheck} %s
! CHECK:m:.*getsymbols01.f90, 18, 8-9
! CHECK:f:.*getsymbols01.f90, 37, 26-27
! CHECK:s:.*getsymbols01.f90, 25, 18-19
! CHECK:ss:.*getsymbols01.f90, 32, 19-21
! CHECK:x:.*getsymbols01.f90, 25, 21-22
! CHECK:y:.*getsymbols01.f90, 25, 24-25
! CHECK:x:.*getsymbols01.f90, 39, 24-25
! CHECK-ONCE:m:.*getsymbols01.f90, 18, 8-9
! CHECK-ONCE:f:.*getsymbols01.f90, 37, 26-27
! CHECK-ONCE:s:.*getsymbols01.f90, 25, 18-19
! CHECK-ONCE:ss:.*getsymbols01.f90, 32, 19-21
! CHECK-ONCE:x:.*getsymbols01.f90, 25, 21-22
! CHECK-ONCE:y:.*getsymbols01.f90, 25, 24-25
! CHECK-ONCE:x:.*getsymbols01.f90, 39, 24-25

View File

@ -0,0 +1,27 @@
! Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
! Tests -fget-symbols-sources with COMMON.
program main
integer :: x
integer :: y
common /x/ y
x = y
end program
! RUN: ${F18} -fget-symbols-sources -fparse-only -fdebug-semantics %s 2>&1 | ${FileCheck} %s
! CHECK:x:.*getsymbols04.f90, 18, 14-15
! CHECK:y:.*getsymbols04.f90, 19, 14-15
! CHECK:x:.*getsymbols04.f90, 20, 11-12

View File

@ -0,0 +1,30 @@
! Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
! Tests -fget-symbols-sources with COMMON.
program main
integer :: x
integer :: y
block
integer :: x
x = y
end block
x = y
end program
! RUN: ${F18} -fget-symbols-sources -fparse-only -fdebug-semantics %s 2>&1 | ${FileCheck} %s
! CHECK:x:.*getsymbols05.f90, 18, 14-15
! CHECK:y:.*getsymbols05.f90, 19, 14-15
! CHECK:x:.*getsymbols05.f90, 21, 16-17

View File

@ -45,6 +45,18 @@ function internal_check() {
r=false
fi
done < ${lcheck}
egrep '^[[:space:]]*![[:space:]]*CHECK-ONCE:[[:space:]]*' ${linput} | sed -e 's/^[[:space:]]*![[:space:]]*CHECK-ONCE:[[:space:]]*//' > ${lcheck} 2>/dev/null
while read p; do
count=$(egrep -o -e "${p}" ${lstdin} | wc -l)
if [ ${count} -eq 0 ]; then
echo "Not found: ${p}" >&2
r=false
fi
if [ ${count} -gt 1 ]; then
echo "Found duplicates: ${p}" >&2
r=false
fi
done < ${lcheck}
rm -f ${lstdin} ${lcheck}
${r}
}
@ -55,6 +67,7 @@ for input in ${srcdir}/$*; do
CMD=$(echo ${CMD} | sed -e "s:%s:${input}:g")
if egrep -q -e '%t' <<< ${CMD} ; then
temp=`mktemp`
trap "rm -f ${temp}" EXIT
CMD=$(echo ${CMD} | sed -e "s:%t:${temp}:g")
fi
if $(eval $CMD); then

View File

@ -76,6 +76,12 @@ void CleanUpAtExit() {
}
}
struct GetDefinitionArgs {
GetDefinitionArgs(int line, int startColumn, int endColumn)
: line{line}, startColumn{startColumn}, endColumn{endColumn} {}
int line, startColumn, endColumn;
};
struct DriverOptions {
DriverOptions() {}
bool verbose{false}; // -v
@ -102,7 +108,7 @@ struct DriverOptions {
std::vector<std::string> pgf90Args;
const char *prefix{nullptr};
bool getDefinition{false};
int getDefinitionArgs[3]; // line, startColumn, endColumn.
GetDefinitionArgs getDefinitionArgs{0, 0, 0};
bool getSymbolsSources{false};
};
@ -258,8 +264,8 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options,
if (driver.getDefinition) {
std::string notFoundText{"Symbol not found.\n"};
auto cb{parsing.cooked().GetCharBlockFromLineAndColumns(
driver.getDefinitionArgs[0], driver.getDefinitionArgs[1],
driver.getDefinitionArgs[2])};
driver.getDefinitionArgs.line, driver.getDefinitionArgs.startColumn,
driver.getDefinitionArgs.endColumn)};
if (!cb) {
std::cerr << notFoundText;
exitStatus = EXIT_FAILURE;
@ -526,13 +532,13 @@ int main(int argc, char *const argv[]) {
// Receives 3 arguments: line, startColumn, endColumn.
driver.getDefinition = true;
char *endptr;
int arguments[3];
for (int i = 0; i < 3; i++) {
if (args.empty()) {
std::cerr << "Must provide 3 arguments for -fget-definitions.\n";
return EXIT_FAILURE;
}
driver.getDefinitionArgs[i] =
std::strtol(args.front().c_str(), &endptr, 10);
arguments[i] = std::strtol(args.front().c_str(), &endptr, 10);
if (*endptr != '\0') {
std::cerr << "Invalid argument to -fget-definitions: " << args.front()
<< '\n';
@ -540,6 +546,7 @@ int main(int argc, char *const argv[]) {
}
args.pop_front();
}
driver.getDefinitionArgs = {arguments[0], arguments[1], arguments[2]};
} else if (arg == "-fget-symbols-sources") {
driver.getSymbolsSources = true;
} else if (arg == "-help" || arg == "--help" || arg == "-?") {