Repository: llvm/llvm-project
File: flang/lib/Semantics/resolve-names.cpp (C++)

Code before:
  // Resolve mapper names in "from" clauses (e.g., for target update)
  auto &mods{OmpGetModifiers(x.v)};
  if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
    if (auto *symbol{FindSymbol(currScope(), mapper->v)}) {
      auto &ultimate{symbol->GetUltimate()};
      auto *misc{ultimate.detailsIf<MiscDetails>()};
      auto *md{ultimate.detailsIf<MapperDetails>()};
      if (!md && (!misc || misc->kind() != MiscDetails::Kind::ConstructName))
        context().Say(mapper->v.source,
            "Name '%s' should be a mapper name"_err_en_US, mapper->v.source);
      else
        mapper->v.symbol = symbol;
    } else {
      // Allow the special 'default' mapper identifier without prior
      // declaration so lowering can recognize and handle it. Emit an
      // error for any other missing mapper identifier.
      if (mapper->v.source.ToString() == "default") {
        mapper->v.symbol = &MakeSymbol(
            mapper->v, MiscDetails{MiscDetails::Kind::ConstructName});
      } else {
        context().Say(
            mapper->v.source, "'%s' not declared"_err_en_US, mapper->v.source);
      }
    }
  }
  return true;
}

void OmpVisitor::ProcessMapperSpecifier(const parser::OmpMapperSpecifier &spec,
    const parser::OmpClauseList &clauses) {
  // This "manually" walks the tree of the construct, because we need
  // to resolve the type before the map clauses are processed - when
  // just following the natural flow, the map clauses gets processed before
  // the type has been fully processed.
  BeginDeclTypeSpec();
  auto &mapperName{std::get<std::string>(spec.t)};
  // Create or update the mapper symbol with MapperDetails and
  // keep track of the declarative construct for module emission.
  SourceName mapperSource{context().SaveTempName(std::string{mapperName})};
  Symbol &mapperSym{MakeSymbol(mapperSource, Attrs{})};
  if (!mapperSym.detailsIf<MapperDetails>()) {
    mapperSym.set_details(MapperDetails{});
  }
  if (!context().langOptions().OpenMPSimd) {
    mapperSym.get<MapperDetails>().AddDecl(declaratives_.back());
  }
  PushScope(Scope::Kind::OtherConstruct, nullptr);
  Walk(std::get<parser::TypeSpec>(spec.t));
  auto &varName{std::get<parser::Name>(spec.t)};
  DeclareObjectEntity(varName);
  EndDeclTypeSpec();

Review comment (refactor):
The `Pre(OmpClause::To)` and `Pre(OmpClause::From)` methods are identical duplicates of each other and nearly identical to `Pre(OmpMapClause)` (lines 1850-1879). Consider extracting the mapper resolution logic into a shared helper method to reduce code duplication and improve maintainability. For example:

```cpp
void OmpVisitor::ResolveMapperModifier(const parser::OmpMapper &mapper) {
if (auto *symbol{FindSymbol(currScope(), mapper.v)}) {
auto &ultimate{symbol->GetUltimate()};
auto *misc{ultimate.detailsIf<MiscDetails>()};
auto *md{ultimate.detailsIf<MapperDetails>()};
if (!md && (!misc || misc->kind() != MiscDetails::Kind::ConstructName))
context().Say(mapper.v.source,
\"Name '%s' should be a mapper name\"_err_en_US, mapper.v.source);
else
mapper.v.symbol = symbol;
} else {
if (mapper.v.source.ToString() == \"default\") {
mapper.v.symbol = &MakeSymbol(
mapper.v, MiscDetails{MiscDetails::Kind::ConstructName});
} else {
context().Say(
mapper.v.source, \"'%s' not declared\"_err_en_US, mapper.v.source);
}
}
}
```

Then each handler would become:

```cpp
bool OmpVisitor::Pre(const parser::OmpClause::To &x) {
auto &mods{OmpGetModifiers(x.v)};
if (auto *mapper{OmpGetUniqueModifier<parser::OmpMapper>(mods)}) {
ResolveMapperModifier(*mapper);
}
return true;
}
```
```suggestion
    ResolveMapperModifier(*mapper);
  }
  return true;
}

void OmpVisitor::ResolveMapperModifier(const parser::OmpMapper &mapper) {
  if (auto *symbol{FindSymbol(currScope(), mapper.v)}) {
    auto &ultimate{symbol->GetUltimate()};
    auto *misc{ultimate.detailsIf<MiscDetails>()};
    auto *md{ultimate.detailsIf<MapperDetails>()};
    if (!md && (!misc || misc->kind() != MiscDetails::Kind::ConstructName))
      context().Say(mapper.v.source,
          "Name '%s' should be a mapper name"_err_en_US, mapper.v.source);
    else
      mapper.v.symbol = symbol;
  } else {
    // Allow the special 'default' mapper identifier without prior
    // declaration so lowering can recognize and handle it. Emit an
    // error for any other missing mapper identifier.
    if (mapper.v.source.ToString() == "default") {
      mapper.v.symbol = &MakeSymbol(
          mapper.v, MiscDetails{MiscDetails::Kind::ConstructName});
    } else {
      context().Say(
          mapper.v.source, "'%s' not declared"_err_en_US, mapper.v.source);
    }
  }
```

Code after:
  // keep track of the declarative construct for module emission.
  SourceName mapperSource{context().SaveTempName(std::string{mapperName})};
  Symbol &mapperSym{MakeSymbol(mapperSource, Attrs{})};
  if (!mapperSym.detailsIf<MapperDetails>()) {
    mapperSym.set_details(MapperDetails{});
  }
  if (!context().langOptions().OpenMPSimd) {
    mapperSym.get<MapperDetails>().AddDecl(declaratives_.back());
  }
  PushScope(Scope::Kind::OtherConstruct, nullptr);
  Walk(std::get<parser::TypeSpec>(spec.t));
  auto &varName{std::get<parser::Name>(spec.t)};
  DeclareObjectEntity(varName);
  EndDeclTypeSpec();

  Walk(clauses);
  PopScope();
}

parser::CharBlock MakeNameFromOperator(
    const parser::DefinedOperator::IntrinsicOperator &op,
    SemanticsContext &context) {
  switch (op) {
  case parser::DefinedOperator::IntrinsicOperator::Multiply:
    return parser::CharBlock{"op.*", 4};
  case parser::DefinedOperator::IntrinsicOperator::Add:
    return parser::CharBlock{"op.+", 4};
  case parser::DefinedOperator::IntrinsicOperator::Subtract:
    return parser::CharBlock{"op.-", 4};

  case parser::DefinedOperator::IntrinsicOperator::AND:
    return parser::CharBlock{"op.AND", 6};
  case parser::DefinedOperator::IntrinsicOperator::OR:
    return parser::CharBlock{"op.OR", 6};
  case parser::DefinedOperator::IntrinsicOperator::EQV:
    return parser::CharBlock{"op.EQV", 7};
  case parser::DefinedOperator::IntrinsicOperator::NEQV:
    return parser::CharBlock{"op.NEQV", 8};

  default:
    context.Say("Unsupported operator in DECLARE REDUCTION"_err_en_US);
    return parser::CharBlock{"op.?", 4};
  }
}

parser::CharBlock MangleSpecialFunctions(const parser::CharBlock &name) {
  return llvm::StringSwitch<parser::CharBlock>(name.ToString())
      .Case("max", {"op.max", 6})
      .Case("min", {"op.min", 6})
      .Case("iand", {"op.iand", 7})
      .Case("ior", {"op.ior", 6})