@@ -259,6 +259,55 @@ void OmpStructureChecker::CheckVariableListItem(
259259 }
260260}
261261
262+ void OmpStructureChecker::CheckDirectiveSpelling (
263+ parser::CharBlock spelling, llvm::omp::Directive id) {
264+ // Directive names that contain spaces can be spelled in the source without
265+ // any of the spaces. Because of that getOpenMPKind* is not guaranteed to
266+ // work with the source spelling as the argument.
267+ //
268+ // To verify the source spellings, we have to get the spelling for a given
269+ // version, remove spaces and compare it with the source spelling (also
270+ // with spaces removed).
271+ auto removeSpaces = [](llvm::StringRef s) {
272+ std::string n{s.str ()};
273+ for (auto it{n.begin ()}; it != n.end ();) {
274+ it = isspace (*it) ? n.erase (it) : std::next (it);
275+ }
276+ return n;
277+ };
278+
279+ std::string lowerNoWS{removeSpaces (
280+ parser::ToLowerCaseLetters ({spelling.begin (), spelling.size ()}))};
281+ llvm::StringRef ref (lowerNoWS);
282+ if (ref.starts_with (" end" )) {
283+ ref = ref.drop_front (3 );
284+ }
285+
286+ unsigned version{context_.langOptions ().OpenMPVersion };
287+
288+ // For every "future" version v, check if the check if the corresponding
289+ // spelling of id was introduced later than the current version. If so,
290+ // and if that spelling matches the source spelling, issue a warning.
291+ for (unsigned v : llvm::omp::getOpenMPVersions ()) {
292+ if (v <= version) {
293+ continue ;
294+ }
295+ llvm::StringRef name{llvm::omp::getOpenMPDirectiveName (id, v)};
296+ auto [kind, versions]{llvm::omp::getOpenMPDirectiveKindAndVersions (name)};
297+ assert (kind == id && " Directive kind mismatch" );
298+
299+ if (static_cast <int >(version) >= versions.Min ) {
300+ continue ;
301+ }
302+ if (ref == removeSpaces (name)) {
303+ context_.Say (spelling,
304+ " Directive spelling '%s' is introduced in a later OpenMP version, %s" _warn_en_US,
305+ parser::ToUpperCaseLetters (ref), TryVersion (versions.Min ));
306+ break ;
307+ }
308+ }
309+ }
310+
262311void OmpStructureChecker::CheckMultipleOccurrence (
263312 semantics::UnorderedSymbolSet &listVars,
264313 const std::list<parser::Name> &nameList, const parser::CharBlock &item,
@@ -436,7 +485,133 @@ void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification &) {
436485 }
437486}
438487
488+ template <typename Checker> struct DirectiveSpellingVisitor {
489+ using Directive = llvm::omp::Directive;
490+
491+ DirectiveSpellingVisitor (Checker &&checker) : checker_(std::move(checker)) {}
492+
493+ template <typename T> bool Pre (const T &) { return true ; }
494+ template <typename T> void Post (const T &) {}
495+
496+ bool Pre (const parser::OmpSectionsDirective &x) {
497+ checker_ (x.source , x.v );
498+ return false ;
499+ }
500+ bool Pre (const parser::OpenMPDeclarativeAllocate &x) {
501+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_allocate);
502+ return false ;
503+ }
504+ bool Pre (const parser::OmpDispatchDirective &x) {
505+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_dispatch);
506+ return false ;
507+ }
508+ bool Pre (const parser::OmpErrorDirective &x) {
509+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_error);
510+ return false ;
511+ }
512+ bool Pre (const parser::OmpNothingDirective &x) {
513+ checker_ (x.source , Directive::OMPD_nothing);
514+ return false ;
515+ }
516+ bool Pre (const parser::OpenMPExecutableAllocate &x) {
517+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_allocate);
518+ return false ;
519+ }
520+ bool Pre (const parser::OpenMPAllocatorsConstruct &x) {
521+ checker_ (
522+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_allocators);
523+ return false ;
524+ }
525+ bool Pre (const parser::OmpAssumeDirective &x) {
526+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_assume);
527+ return false ;
528+ }
529+ bool Pre (const parser::OmpEndAssumeDirective &x) {
530+ checker_ (x.v .source , Directive::OMPD_assume);
531+ return false ;
532+ }
533+ bool Pre (const parser::OmpCriticalDirective &x) {
534+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_critical);
535+ return false ;
536+ }
537+ bool Pre (const parser::OmpEndCriticalDirective &x) {
538+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_critical);
539+ return false ;
540+ }
541+ bool Pre (const parser::OmpMetadirectiveDirective &x) {
542+ checker_ (
543+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_metadirective);
544+ return false ;
545+ }
546+ bool Pre (const parser::OpenMPDeclarativeAssumes &x) {
547+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_assumes);
548+ return false ;
549+ }
550+ bool Pre (const parser::OpenMPDeclareMapperConstruct &x) {
551+ checker_ (
552+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_declare_mapper);
553+ return false ;
554+ }
555+ bool Pre (const parser::OpenMPDeclareReductionConstruct &x) {
556+ checker_ (std::get<parser::Verbatim>(x.t ).source ,
557+ Directive::OMPD_declare_reduction);
558+ return false ;
559+ }
560+ bool Pre (const parser::OpenMPDeclareSimdConstruct &x) {
561+ checker_ (
562+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_declare_simd);
563+ return false ;
564+ }
565+ bool Pre (const parser::OpenMPDeclareTargetConstruct &x) {
566+ checker_ (
567+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_declare_target);
568+ return false ;
569+ }
570+ bool Pre (const parser::OmpDeclareVariantDirective &x) {
571+ checker_ (std::get<parser::Verbatim>(x.t ).source ,
572+ Directive::OMPD_declare_variant);
573+ return false ;
574+ }
575+ bool Pre (const parser::OpenMPThreadprivate &x) {
576+ checker_ (
577+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_threadprivate);
578+ return false ;
579+ }
580+ bool Pre (const parser::OpenMPRequiresConstruct &x) {
581+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_requires);
582+ return false ;
583+ }
584+
585+ bool Pre (const parser::OmpBlockDirective &x) {
586+ checker_ (x.source , x.v );
587+ return false ;
588+ }
589+
590+ bool Pre (const parser::OmpLoopDirective &x) {
591+ checker_ (x.source , x.v );
592+ return false ;
593+ }
594+
595+ bool Pre (const parser::OmpDirectiveSpecification &x) {
596+ auto &name = std::get<parser::OmpDirectiveName>(x.t );
597+ checker_ (name.source , name.v );
598+ return false ;
599+ }
600+
601+ private:
602+ Checker checker_;
603+ };
604+
605+ template <typename T>
606+ DirectiveSpellingVisitor (T &&) -> DirectiveSpellingVisitor<T>;
607+
439608void OmpStructureChecker::Enter (const parser::OpenMPConstruct &x) {
609+ DirectiveSpellingVisitor visitor (
610+ [this ](parser::CharBlock source, llvm::omp::Directive id) {
611+ return CheckDirectiveSpelling (source, id);
612+ });
613+ parser::Walk (x, visitor);
614+
440615 // Simd Construct with Ordered Construct Nesting check
441616 // We cannot use CurrentDirectiveIsNested() here because
442617 // PushContextAndClauseSets() has not been called yet, it is
@@ -461,6 +636,12 @@ void OmpStructureChecker::Leave(const parser::OpenMPConstruct &) {
461636}
462637
463638void OmpStructureChecker::Enter (const parser::OpenMPDeclarativeConstruct &x) {
639+ DirectiveSpellingVisitor visitor (
640+ [this ](parser::CharBlock source, llvm::omp::Directive id) {
641+ return CheckDirectiveSpelling (source, id);
642+ });
643+ parser::Walk (x, visitor);
644+
464645 EnterDirectiveNest (DeclarativeNest);
465646}
466647
0 commit comments