@@ -259,6 +259,57 @@ 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 (size_t idx{n.size ()}; idx > 0 ; --idx) {
274+ if (isspace (n[idx - 1 ])) {
275+ n.erase (idx - 1 , 1 );
276+ }
277+ }
278+ return n;
279+ };
280+
281+ std::string lowerNoWS{removeSpaces (
282+ parser::ToLowerCaseLetters ({spelling.begin (), spelling.size ()}))};
283+ llvm::StringRef ref (lowerNoWS);
284+ if (ref.starts_with (" end" )) {
285+ ref = ref.drop_front (3 );
286+ }
287+
288+ unsigned version{context_.langOptions ().OpenMPVersion };
289+
290+ // For every "future" version v, check if the check if the corresponding
291+ // spelling of id was introduced later than the current version. If so,
292+ // and if that spelling matches the source spelling, issue a warning.
293+ for (unsigned v : llvm::omp::getOpenMPVersions ()) {
294+ if (v <= version) {
295+ continue ;
296+ }
297+ llvm::StringRef name{llvm::omp::getOpenMPDirectiveName (id, v)};
298+ auto [kind, versions]{llvm::omp::getOpenMPDirectiveKindAndVersions (name)};
299+ assert (kind == id && " Directive kind mismatch" );
300+
301+ if (static_cast <int >(version) >= versions.Min ) {
302+ continue ;
303+ }
304+ if (ref == removeSpaces (name)) {
305+ context_.Say (spelling,
306+ " Directive spelling '%s' is introduced in a later OpenMP version, %s" _warn_en_US,
307+ parser::ToUpperCaseLetters (ref), TryVersion (versions.Min ));
308+ break ;
309+ }
310+ }
311+ }
312+
262313void OmpStructureChecker::CheckMultipleOccurrence (
263314 semantics::UnorderedSymbolSet &listVars,
264315 const std::list<parser::Name> &nameList, const parser::CharBlock &item,
@@ -436,7 +487,133 @@ void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification &) {
436487 }
437488}
438489
490+ template <typename Checker> struct DirectiveSpellingVisitor {
491+ using Directive = llvm::omp::Directive;
492+
493+ DirectiveSpellingVisitor (Checker &&checker) : checker_(std::move(checker)) {}
494+
495+ template <typename T> bool Pre (const T &) { return true ; }
496+ template <typename T> void Post (const T &) {}
497+
498+ bool Pre (const parser::OmpSectionsDirective &x) {
499+ checker_ (x.source , x.v );
500+ return false ;
501+ }
502+ bool Pre (const parser::OpenMPDeclarativeAllocate &x) {
503+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_allocate);
504+ return false ;
505+ }
506+ bool Pre (const parser::OmpDispatchDirective &x) {
507+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_dispatch);
508+ return false ;
509+ }
510+ bool Pre (const parser::OmpErrorDirective &x) {
511+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_error);
512+ return false ;
513+ }
514+ bool Pre (const parser::OmpNothingDirective &x) {
515+ checker_ (x.source , Directive::OMPD_nothing);
516+ return false ;
517+ }
518+ bool Pre (const parser::OpenMPExecutableAllocate &x) {
519+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_allocate);
520+ return false ;
521+ }
522+ bool Pre (const parser::OpenMPAllocatorsConstruct &x) {
523+ checker_ (
524+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_allocators);
525+ return false ;
526+ }
527+ bool Pre (const parser::OmpAssumeDirective &x) {
528+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_assume);
529+ return false ;
530+ }
531+ bool Pre (const parser::OmpEndAssumeDirective &x) {
532+ checker_ (x.v .source , Directive::OMPD_assume);
533+ return false ;
534+ }
535+ bool Pre (const parser::OmpCriticalDirective &x) {
536+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_critical);
537+ return false ;
538+ }
539+ bool Pre (const parser::OmpEndCriticalDirective &x) {
540+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_critical);
541+ return false ;
542+ }
543+ bool Pre (const parser::OmpMetadirectiveDirective &x) {
544+ checker_ (
545+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_metadirective);
546+ return false ;
547+ }
548+ bool Pre (const parser::OpenMPDeclarativeAssumes &x) {
549+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_assumes);
550+ return false ;
551+ }
552+ bool Pre (const parser::OpenMPDeclareMapperConstruct &x) {
553+ checker_ (
554+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_declare_mapper);
555+ return false ;
556+ }
557+ bool Pre (const parser::OpenMPDeclareReductionConstruct &x) {
558+ checker_ (std::get<parser::Verbatim>(x.t ).source ,
559+ Directive::OMPD_declare_reduction);
560+ return false ;
561+ }
562+ bool Pre (const parser::OpenMPDeclareSimdConstruct &x) {
563+ checker_ (
564+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_declare_simd);
565+ return false ;
566+ }
567+ bool Pre (const parser::OpenMPDeclareTargetConstruct &x) {
568+ checker_ (
569+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_declare_target);
570+ return false ;
571+ }
572+ bool Pre (const parser::OmpDeclareVariantDirective &x) {
573+ checker_ (std::get<parser::Verbatim>(x.t ).source ,
574+ Directive::OMPD_declare_variant);
575+ return false ;
576+ }
577+ bool Pre (const parser::OpenMPThreadprivate &x) {
578+ checker_ (
579+ std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_threadprivate);
580+ return false ;
581+ }
582+ bool Pre (const parser::OpenMPRequiresConstruct &x) {
583+ checker_ (std::get<parser::Verbatim>(x.t ).source , Directive::OMPD_requires);
584+ return false ;
585+ }
586+
587+ bool Pre (const parser::OmpBlockDirective &x) {
588+ checker_ (x.source , x.v );
589+ return false ;
590+ }
591+
592+ bool Pre (const parser::OmpLoopDirective &x) {
593+ checker_ (x.source , x.v );
594+ return false ;
595+ }
596+
597+ bool Pre (const parser::OmpDirectiveSpecification &x) {
598+ auto &name = std::get<parser::OmpDirectiveName>(x.t );
599+ checker_ (name.source , name.v );
600+ return false ;
601+ }
602+
603+ private:
604+ Checker checker_;
605+ };
606+
607+ template <typename T>
608+ DirectiveSpellingVisitor (T &&) -> DirectiveSpellingVisitor<T>;
609+
439610void OmpStructureChecker::Enter (const parser::OpenMPConstruct &x) {
611+ DirectiveSpellingVisitor visitor (
612+ [this ](parser::CharBlock source, llvm::omp::Directive id) {
613+ return CheckDirectiveSpelling (source, id);
614+ });
615+ parser::Walk (x, visitor);
616+
440617 // Simd Construct with Ordered Construct Nesting check
441618 // We cannot use CurrentDirectiveIsNested() here because
442619 // PushContextAndClauseSets() has not been called yet, it is
@@ -461,6 +638,12 @@ void OmpStructureChecker::Leave(const parser::OpenMPConstruct &) {
461638}
462639
463640void OmpStructureChecker::Enter (const parser::OpenMPDeclarativeConstruct &x) {
641+ DirectiveSpellingVisitor visitor (
642+ [this ](parser::CharBlock source, llvm::omp::Directive id) {
643+ return CheckDirectiveSpelling (source, id);
644+ });
645+ parser::Walk (x, visitor);
646+
464647 EnterDirectiveNest (DeclarativeNest);
465648}
466649
0 commit comments