Skip to content

[flang] Add options -W[no-]unused-dummy-argument and -W[no-]unused-variable #127214

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

JDPailleux
Copy link
Contributor

Adding support of new warning flags -W[no-]unused-dummy-argument and -W[no-]unused-variable.
Used in ECRAD, but not a mandatory flags for the project (https://github.com/ecmwf-ifs/ecrad).
I think it could be usefull to develop various Fortran projects and avoiding unused dummy arguments / variables.

-Wunused-dummy-argument : Warn about unused dummy arguments.
-Wunused-variable : Warn whenever a local variable or non-constant static variable is unused aside from its declaration.

@llvmbot llvmbot added flang:driver flang Flang issues not falling into any other category flang:semantics labels Feb 14, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 14, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-flang-semantics

Author: Jean-Didier PAILLEUX (JDPailleux)

Changes

Adding support of new warning flags -W[no-]unused-dummy-argument and -W[no-]unused-variable.
Used in ECRAD, but not a mandatory flags for the project (https://github.com/ecmwf-ifs/ecrad).
I think it could be usefull to develop various Fortran projects and avoiding unused dummy arguments / variables.

-Wunused-dummy-argument : Warn about unused dummy arguments.
-Wunused-variable : Warn whenever a local variable or non-constant static variable is unused aside from its declaration.


Full diff: https://github.com/llvm/llvm-project/pull/127214.diff

14 Files Affected:

  • (modified) flang/include/flang/Semantics/symbol.h (+3)
  • (modified) flang/include/flang/Support/Fortran-features.h (+1-1)
  • (modified) flang/lib/Frontend/CompilerInvocation.cpp (+17-3)
  • (modified) flang/lib/Semantics/CMakeLists.txt (+1)
  • (added) flang/lib/Semantics/check-warning.cpp (+99)
  • (added) flang/lib/Semantics/check-warning.h (+40)
  • (modified) flang/lib/Semantics/expression.cpp (+7)
  • (modified) flang/lib/Semantics/resolve-names.cpp (+5)
  • (modified) flang/lib/Semantics/semantics.cpp (+8)
  • (modified) flang/lib/Support/Fortran-features.cpp (+2)
  • (modified) flang/test/Driver/werror-wrong.f90 (+1-1)
  • (modified) flang/test/Driver/wextra-ok.f90 (+1-1)
  • (added) flang/test/Semantics/wunused-dummy-argument.f90 (+54)
  • (added) flang/test/Semantics/wunused-variable.f90 (+72)
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 4ae2775c0f849..2c7e9bae5e652 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -293,10 +293,13 @@ class EntityDetails : public WithBindName {
   void set_isDummy(bool value = true) { isDummy_ = value; }
   bool isFuncResult() const { return isFuncResult_; }
   void set_funcResult(bool x) { isFuncResult_ = x; }
+  bool isUsed() const { return isUsed_; }
+  void set_isUsed() { isUsed_ = true; }
 
 private:
   bool isDummy_{false};
   bool isFuncResult_{false};
+  bool isUsed_{false};
   const DeclTypeSpec *type_{nullptr};
   friend llvm::raw_ostream &operator<<(
       llvm::raw_ostream &, const EntityDetails &);
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index 44ba6428e6c93..afccf76d5c025 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -54,7 +54,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
     PolymorphicActualAllocatableOrPointerToMonomorphicDummy, RelaxedPureDummy,
     UndefinableAsynchronousOrVolatileActual, AutomaticInMainProgram, PrintCptr,
     SavedLocalInSpecExpr, PrintNamelist, AssumedRankPassedToNonAssumedRank,
-    IgnoreIrrelevantAttributes, Unsigned)
+    IgnoreIrrelevantAttributes, Unsigned, UnusedDummyArgument, UnusedVariable)
 
 // Portability and suspicious usage warnings
 ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index f3d9432c62d3b..8b5f69d42bbb4 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -932,10 +932,24 @@ static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
     for (const auto &wArg : wArgs) {
       if (wArg == "error") {
         res.setWarnAsErr(true);
+      } else if (wArg == "unused-dummy-argument") {
+        res.getFrontendOpts().features.Enable(
+            Fortran::common::LanguageFeature::UnusedDummyArgument);
+      } else if (wArg == "no-unused-dummy-argument") {
+        res.getFrontendOpts().features.Enable(
+            Fortran::common::LanguageFeature::UnusedDummyArgument, false);
+      } else if (wArg == "unused-variable") {
+        res.getFrontendOpts().features.Enable(
+            Fortran::common::LanguageFeature::UnusedVariable);
+      } else if (wArg == "no-unused-variable") {
+        res.getFrontendOpts().features.Enable(
+            Fortran::common::LanguageFeature::UnusedVariable, false);
       } else {
-        const unsigned diagID =
-            diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
-                                  "Only `-Werror` is supported currently.");
+        const unsigned diagID = diags.getCustomDiagID(
+            clang::DiagnosticsEngine::Error,
+            "Only `-Werror`, `-W[no]unused-dummy-argument` "
+            "and `-W[no]unused-variable` are supported "
+            "currently.");
         diags.Report(diagID);
       }
     }
diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt
index 00108dde49dbd..366282361baf9 100644
--- a/flang/lib/Semantics/CMakeLists.txt
+++ b/flang/lib/Semantics/CMakeLists.txt
@@ -26,6 +26,7 @@ add_flang_library(FortranSemantics
   check-select-rank.cpp
   check-select-type.cpp
   check-stop.cpp
+  check-warning.cpp
   compute-offsets.cpp
   data-to-inits.cpp
   definable.cpp
diff --git a/flang/lib/Semantics/check-warning.cpp b/flang/lib/Semantics/check-warning.cpp
new file mode 100644
index 0000000000000..1604e7590429d
--- /dev/null
+++ b/flang/lib/Semantics/check-warning.cpp
@@ -0,0 +1,99 @@
+//===-- lib/Semantics/check-warning.cpp
+//-----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "check-warning.h"
+#include "flang/Semantics/tools.h"
+
+namespace Fortran::semantics {
+
+void WarningChecker::Enter(const parser::FunctionStmt &stmt) {
+  if (Wunused_dummy_argument) {
+    for (const auto &dummyName : std::get<std::list<parser::Name>>(stmt.t)) {
+      if (auto *detail = dummyName.symbol->detailsIf<ObjectEntityDetails>()) {
+        const Symbol *ownerSymbol{dummyName.symbol->owner().symbol()};
+        const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()};
+        bool inInterface{ownerSubp && ownerSubp->isInterface()};
+
+        if (!inInterface && !detail->isUsed()) {
+          context_.Say(dummyName.symbol->GetUltimate().name(),
+              "Unused dummy argument '%s' [-Wunused-dummy-argument]"_warn_en_US,
+              dummyName.ToString());
+        }
+      }
+    }
+  }
+  if (Wunused_variable) {
+    if (const auto &suffix{std::get<std::optional<parser::Suffix>>(stmt.t)}) {
+      if (suffix->resultName.has_value()) {
+        if (auto *detail =
+                suffix->resultName->symbol->detailsIf<ObjectEntityDetails>()) {
+          const Symbol *ownerSymbol{
+              suffix->resultName->symbol->owner().symbol()};
+          const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()};
+          bool inInterface{ownerSubp && ownerSubp->isInterface()};
+          if (!inInterface && !detail->isUsed()) {
+            context_.Say(suffix->resultName->source,
+                "Unused variable '%s' [-Wunused-variable]"_warn_en_US,
+                suffix->resultName->ToString());
+          }
+        }
+      }
+    }
+  }
+}
+
+void WarningChecker::Enter(const parser::SubroutineStmt &stmt) {
+  if (!Wunused_dummy_argument) {
+    return;
+  }
+  for (const auto &dummyArg : std::get<std::list<parser::DummyArg>>(stmt.t)) {
+    if (const auto *dummyName{std::get_if<parser::Name>(&dummyArg.u)}) {
+      if (const auto *symbol = dummyName->symbol) {
+
+        const Symbol *ownerSymbol{symbol->owner().symbol()};
+        const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()};
+        bool inInterface{ownerSubp && ownerSubp->isInterface()};
+
+        if (auto *detail = symbol->detailsIf<ObjectEntityDetails>()) {
+          if (!inInterface && !detail->isUsed()) {
+            context_.Say(symbol->GetUltimate().name(),
+                "Unused dummy argument '%s' [-Wunused-dummy-argument]"_warn_en_US,
+                dummyName->ToString());
+          }
+        }
+      }
+    }
+  }
+}
+
+void WarningChecker::Enter(const parser::EntityDecl &decl) {
+  if (!Wunused_variable) {
+    return;
+  }
+
+  const auto &name{std::get<parser::ObjectName>(decl.t)};
+  if (const auto *symbol = name.symbol) {
+    const Symbol *ownerSymbol{symbol->owner().symbol()};
+    const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()};
+    bool inInterface{ownerSubp && ownerSubp->isInterface()};
+    bool inModule{ownerSymbol && ownerSymbol->scope() &&
+        ownerSymbol->scope()->IsModule()};
+
+    if (auto *detail = symbol->detailsIf<ObjectEntityDetails>()) {
+      if (!inInterface && !inModule && !detail->isDummy() &&
+          !detail->isFuncResult() && !detail->isUsed()) {
+        context_.Say(symbol->name(),
+            "Unused variable '%s' [-Wunused-variable]"_warn_en_US,
+            name.ToString());
+      }
+    }
+  }
+}
+
+} // namespace Fortran::semantics
diff --git a/flang/lib/Semantics/check-warning.h b/flang/lib/Semantics/check-warning.h
new file mode 100644
index 0000000000000..f13d159392ab4
--- /dev/null
+++ b/flang/lib/Semantics/check-warning.h
@@ -0,0 +1,40 @@
+//===-- lib/Semantics/check-warning.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_SEMANTICS_CHECK_WARNING_H_
+#define FORTRAN_SEMANTICS_CHECK_WARNING_H_
+
+#include "flang/Semantics/semantics.h"
+
+namespace Fortran::parser {
+struct FunctionStmt;
+struct InterfaceBody;
+struct SubroutineStmt;
+struct EntityDecl;
+} // namespace Fortran::parser
+
+namespace Fortran::semantics {
+
+// Perform semantic checks on DummyArg on Function and Subroutine
+// TODO: Add checks for future warning options
+class WarningChecker : public virtual BaseChecker {
+public:
+  explicit WarningChecker(SemanticsContext &context) : context_{context} {}
+  void Enter(const parser::FunctionStmt &);
+  void Enter(const parser::SubroutineStmt &);
+  void Enter(const parser::EntityDecl &);
+
+private:
+  SemanticsContext &context_;
+  const bool Wunused_dummy_argument = context_.languageFeatures().IsEnabled(
+      common::LanguageFeature::UnusedDummyArgument);
+  const bool Wunused_variable = context_.languageFeatures().IsEnabled(
+      common::LanguageFeature::UnusedVariable);
+};
+} // namespace Fortran::semantics
+#endif
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 6949e5693d08f..be077fdceddf4 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -590,6 +590,13 @@ MaybeExpr ExpressionAnalyzer::FixMisparsedSubstring(
 }
 
 MaybeExpr ExpressionAnalyzer::Analyze(const parser::Designator &d) {
+  const auto &name = GetFirstName(d);
+  if (name.symbol) {
+    if (auto *detail =
+            name.symbol->detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
+      detail->set_isUsed();
+    }
+  }
   auto restorer{GetContextualMessages().SetLocation(d.source)};
   if (auto substringInquiry{FixMisparsedSubstring(d)}) {
     return substringInquiry;
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index e64abe6b50e78..488b0acfee8ff 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -6693,6 +6693,11 @@ bool DeclarationVisitor::Pre(const parser::CommonBlockObject &) {
 void DeclarationVisitor::Post(const parser::CommonBlockObject &x) {
   const auto &name{std::get<parser::Name>(x.t)};
   DeclareObjectEntity(name);
+  if (name.symbol) {
+    if (auto *detail{name.symbol->detailsIf<ObjectEntityDetails>()}) {
+      detail->set_isUsed();
+    }
+  }
   auto pair{specPartState_.commonBlockObjects.insert(name.source)};
   if (!pair.second) {
     const SourceName &prev{*pair.first};
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index 10a01039ea0ae..d3703b8fc2148 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -32,6 +32,7 @@
 #include "check-select-rank.h"
 #include "check-select-type.h"
 #include "check-stop.h"
+#include "check-warning.h"
 #include "compute-offsets.h"
 #include "mod-file.h"
 #include "resolve-labels.h"
@@ -202,6 +203,7 @@ using StatementSemanticsPass2 = SemanticsVisitor<AllocateChecker,
     MiscChecker, NamelistChecker, NullifyChecker, PurityChecker,
     ReturnStmtChecker, SelectRankConstructChecker, SelectTypeChecker,
     StopChecker>;
+using StatementSemanticsPass3 = SemanticsVisitor<WarningChecker>;
 
 static bool PerformStatementSemantics(
     SemanticsContext &context, parser::Program &program) {
@@ -221,6 +223,12 @@ static bool PerformStatementSemantics(
   if (context.languageFeatures().IsEnabled(common::LanguageFeature::CUDA)) {
     SemanticsVisitor<CUDAChecker>{context}.Walk(program);
   }
+  if (context.languageFeatures().IsEnabled(
+          common::LanguageFeature::UnusedDummyArgument) ||
+      context.languageFeatures().IsEnabled(
+          common::LanguageFeature::UnusedVariable)) {
+    StatementSemanticsPass3{context}.Walk(program);
+  }
   if (!context.messages().AnyFatalError()) {
     WarnUndefinedFunctionResult(context, context.globalScope());
   }
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index bbeb4b15a0486..056177d714423 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -31,6 +31,8 @@ LanguageFeatureControl::LanguageFeatureControl() {
   disable_.set(LanguageFeature::LogicalAbbreviations);
   disable_.set(LanguageFeature::XOROperator);
   disable_.set(LanguageFeature::OldStyleParameter);
+  disable_.set(LanguageFeature::UnusedDummyArgument);
+  disable_.set(LanguageFeature::UnusedVariable);
   // Possibly an accidental "feature" of nvfortran.
   disable_.set(LanguageFeature::AssumedRankPassedToNonAssumedRank);
   // These warnings are enabled by default, but only because they used
diff --git a/flang/test/Driver/werror-wrong.f90 b/flang/test/Driver/werror-wrong.f90
index 58adf6f745d5e..364be4a0e6e11 100644
--- a/flang/test/Driver/werror-wrong.f90
+++ b/flang/test/Driver/werror-wrong.f90
@@ -3,4 +3,4 @@
 ! RUN: not %flang_fc1 -fsyntax-only -Wall %s  2>&1 | FileCheck %s --check-prefix=WRONG
 ! RUN: not %flang_fc1 -fsyntax-only -WX %s  2>&1 | FileCheck %s --check-prefix=WRONG
 
-! WRONG: Only `-Werror` is supported currently.
+! WRONG: Only `-Werror`, `-W[no]unused-dummy-argument` and `-W[no]unused-variable` are supported currently.
diff --git a/flang/test/Driver/wextra-ok.f90 b/flang/test/Driver/wextra-ok.f90
index 441029aa0af27..9b391d5b91a59 100644
--- a/flang/test/Driver/wextra-ok.f90
+++ b/flang/test/Driver/wextra-ok.f90
@@ -5,7 +5,7 @@
 ! RUN: not %flang -std=f2018 -Wblah -Wextra %s -c 2>&1 | FileCheck %s --check-prefix=WRONG
 
 ! CHECK-OK: the warning option '-Wextra' is not supported
-! WRONG: Only `-Werror` is supported currently.
+! WRONG: Only `-Werror`, `-W[no]unused-dummy-argument` and `-W[no]unused-variable` are supported currently.
 
 program wextra_ok
 end program wextra_ok
diff --git a/flang/test/Semantics/wunused-dummy-argument.f90 b/flang/test/Semantics/wunused-dummy-argument.f90
new file mode 100644
index 0000000000000..bad7720f48f2c
--- /dev/null
+++ b/flang/test/Semantics/wunused-dummy-argument.f90
@@ -0,0 +1,54 @@
+! RUN: %flang_fc1 -Wunused-dummy-argument %s 2>&1 | FileCheck %s
+! RUN: not %flang_fc1 -Wunused-dummy-argument -Werror %s
+! RUN: %flang_fc1 -Wno-unused-dummy-argument %s 2>&1 | FileCheck %s --check-prefix=NOWARN
+
+! CHECK: warning: Unused dummy argument 'a4' [-Wunused-dummy-argument]
+! CHECK: warning: Unused dummy argument 'b4' [-Wunused-dummy-argument]
+! CHECK: warning: Unused dummy argument 'a6' [-Wunused-dummy-argument]
+! CHECK: warning: Unused dummy argument 'b6' [-Wunused-dummy-argument]
+
+! NOWARN-NOT: warning: Unused dummy argument 'a4' [-Wunused-dummy-argument]
+! NOWARN-NOT: warning: Unused dummy argument 'b4' [-Wunused-dummy-argument]
+! NOWARN-NOT: warning: Unused dummy argument 'a6' [-Wunused-dummy-argument]
+! NOWARN-NOT: warning: Unused dummy argument 'b6' [-Wunused-dummy-argument]
+
+program main
+        type :: my_type
+                integer :: val
+        end type
+        integer :: not_dummy_arg
+        interface
+                subroutine subroutine_interface(a)
+                        integer, intent(in) :: a
+                end subroutine
+
+                function function_interface(a2)
+                        integer, intent(in) :: a2
+                end function
+        end interface
+contains
+        subroutine subroutine_all_used(a3, b3)
+                integer, intent(inout) :: a3, b3
+                a3 = a3 + b3
+        end subroutine
+
+        subroutine subroutine_unused_both(a4, b4)
+                integer, intent(inout) :: a4(10)
+                type(my_type) :: b4
+        end subroutine
+
+
+        function function_used_all(a5, b5) result(c1)
+                integer, intent(inout) :: a5(10)
+                type(my_type), intent(in) :: b5
+                integer :: c1
+                a5(1) = b5%val
+                c1 = a5(2)
+        end function 
+
+        function function_unused_both(a6, b6) result(c2)
+                integer, intent(inout) :: a6(10)
+                type(my_type) :: b6
+                integer :: c2
+        end function
+end program
diff --git a/flang/test/Semantics/wunused-variable.f90 b/flang/test/Semantics/wunused-variable.f90
new file mode 100644
index 0000000000000..146f943f83a35
--- /dev/null
+++ b/flang/test/Semantics/wunused-variable.f90
@@ -0,0 +1,72 @@
+! RUN: %flang_fc1 -Wunused-variable %s 2>&1 | FileCheck %s
+! RUN: not %flang_fc1 -Wunused-variable -Werror %s
+! RUN: %flang_fc1 -Wno-unused-variable %s 2>&1 | FileCheck %s --check-prefix=NOWARN
+
+! CHECK: warning: Unused variable 'unused_var_in_submod_subroutine' [-Wunused-variable]
+! CHECK: warning: Unused variable 'my_type_var1' [-Wunused-variable]
+! CHECK: warning: Unused variable 'not_dummy_arg' [-Wunused-variable]
+! CHECK: warning: Unused variable 'in_subroutine' [-Wunused-variable]
+! CHECK: warning: Unused variable 'c1' [-Wunused-variable]
+
+! NOWARN-NOT: warning: Unused variable 'unused_var_in_submod_subroutine' [-Wunused-variable]
+! NOWARN-NOT: warning: Unused variable 'my_type_var1' [-Wunused-variable]
+! NOWARN-NOT: warning: Unused variable 'not_dummy_arg' [-Wunused-variable]
+! NOWARN-NOT: warning: Unused variable 'in_subroutine' [-Wunused-variable]
+! NOWARN-NOT: warning: Unused variable 'c1' [-Wunused-variable]
+module test
+        integer :: var_in_module
+        contains
+        subroutine module_subroutine(a)
+                integer :: unused_var_in_submod_subroutine
+                integer :: a
+        end subroutine
+end module test
+
+program main
+        type :: my_type
+                integer :: val
+                integer :: unused_val
+        end type
+        interface
+                subroutine subroutine_in_interface()
+                        integer :: w
+                end subroutine
+                function function_in_interface() result(j)
+                        integer :: x
+                        integer :: j
+                end function
+        end interface
+        type(my_type) :: my_type_var1
+        type(my_type) :: my_type_var2
+        integer :: not_dummy_arg
+
+        integer :: variable_common
+        common variable_common
+
+        my_type_var2%val = 12
+
+
+
+        print *, function_used_all()
+
+        contains
+        subroutine subroutine_all_used(a3)
+                integer, intent(in) :: a3
+                integer :: in_subroutine_used
+                in_subroutine_used = a3
+        end subroutine
+
+        subroutine subroutine_unused(a4)
+                integer, intent(in) :: a4
+                integer :: in_subroutine
+        end subroutine
+
+        function function_used_all() result(c1)
+                integer :: in_function
+                integer :: c1
+                c1 = in_function
+        end function 
+
+        function function_unused_all() result(c1)
+        end function 
+end program

@llvmbot
Copy link
Member

llvmbot commented Feb 14, 2025

@llvm/pr-subscribers-flang-driver

Author: Jean-Didier PAILLEUX (JDPailleux)

Changes

Adding support of new warning flags -W[no-]unused-dummy-argument and -W[no-]unused-variable.
Used in ECRAD, but not a mandatory flags for the project (https://github.com/ecmwf-ifs/ecrad).
I think it could be usefull to develop various Fortran projects and avoiding unused dummy arguments / variables.

-Wunused-dummy-argument : Warn about unused dummy arguments.
-Wunused-variable : Warn whenever a local variable or non-constant static variable is unused aside from its declaration.


Full diff: https://github.com/llvm/llvm-project/pull/127214.diff

14 Files Affected:

  • (modified) flang/include/flang/Semantics/symbol.h (+3)
  • (modified) flang/include/flang/Support/Fortran-features.h (+1-1)
  • (modified) flang/lib/Frontend/CompilerInvocation.cpp (+17-3)
  • (modified) flang/lib/Semantics/CMakeLists.txt (+1)
  • (added) flang/lib/Semantics/check-warning.cpp (+99)
  • (added) flang/lib/Semantics/check-warning.h (+40)
  • (modified) flang/lib/Semantics/expression.cpp (+7)
  • (modified) flang/lib/Semantics/resolve-names.cpp (+5)
  • (modified) flang/lib/Semantics/semantics.cpp (+8)
  • (modified) flang/lib/Support/Fortran-features.cpp (+2)
  • (modified) flang/test/Driver/werror-wrong.f90 (+1-1)
  • (modified) flang/test/Driver/wextra-ok.f90 (+1-1)
  • (added) flang/test/Semantics/wunused-dummy-argument.f90 (+54)
  • (added) flang/test/Semantics/wunused-variable.f90 (+72)
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index 4ae2775c0f849..2c7e9bae5e652 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -293,10 +293,13 @@ class EntityDetails : public WithBindName {
   void set_isDummy(bool value = true) { isDummy_ = value; }
   bool isFuncResult() const { return isFuncResult_; }
   void set_funcResult(bool x) { isFuncResult_ = x; }
+  bool isUsed() const { return isUsed_; }
+  void set_isUsed() { isUsed_ = true; }
 
 private:
   bool isDummy_{false};
   bool isFuncResult_{false};
+  bool isUsed_{false};
   const DeclTypeSpec *type_{nullptr};
   friend llvm::raw_ostream &operator<<(
       llvm::raw_ostream &, const EntityDetails &);
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index 44ba6428e6c93..afccf76d5c025 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -54,7 +54,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
     PolymorphicActualAllocatableOrPointerToMonomorphicDummy, RelaxedPureDummy,
     UndefinableAsynchronousOrVolatileActual, AutomaticInMainProgram, PrintCptr,
     SavedLocalInSpecExpr, PrintNamelist, AssumedRankPassedToNonAssumedRank,
-    IgnoreIrrelevantAttributes, Unsigned)
+    IgnoreIrrelevantAttributes, Unsigned, UnusedDummyArgument, UnusedVariable)
 
 // Portability and suspicious usage warnings
 ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index f3d9432c62d3b..8b5f69d42bbb4 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -932,10 +932,24 @@ static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
     for (const auto &wArg : wArgs) {
       if (wArg == "error") {
         res.setWarnAsErr(true);
+      } else if (wArg == "unused-dummy-argument") {
+        res.getFrontendOpts().features.Enable(
+            Fortran::common::LanguageFeature::UnusedDummyArgument);
+      } else if (wArg == "no-unused-dummy-argument") {
+        res.getFrontendOpts().features.Enable(
+            Fortran::common::LanguageFeature::UnusedDummyArgument, false);
+      } else if (wArg == "unused-variable") {
+        res.getFrontendOpts().features.Enable(
+            Fortran::common::LanguageFeature::UnusedVariable);
+      } else if (wArg == "no-unused-variable") {
+        res.getFrontendOpts().features.Enable(
+            Fortran::common::LanguageFeature::UnusedVariable, false);
       } else {
-        const unsigned diagID =
-            diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
-                                  "Only `-Werror` is supported currently.");
+        const unsigned diagID = diags.getCustomDiagID(
+            clang::DiagnosticsEngine::Error,
+            "Only `-Werror`, `-W[no]unused-dummy-argument` "
+            "and `-W[no]unused-variable` are supported "
+            "currently.");
         diags.Report(diagID);
       }
     }
diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt
index 00108dde49dbd..366282361baf9 100644
--- a/flang/lib/Semantics/CMakeLists.txt
+++ b/flang/lib/Semantics/CMakeLists.txt
@@ -26,6 +26,7 @@ add_flang_library(FortranSemantics
   check-select-rank.cpp
   check-select-type.cpp
   check-stop.cpp
+  check-warning.cpp
   compute-offsets.cpp
   data-to-inits.cpp
   definable.cpp
diff --git a/flang/lib/Semantics/check-warning.cpp b/flang/lib/Semantics/check-warning.cpp
new file mode 100644
index 0000000000000..1604e7590429d
--- /dev/null
+++ b/flang/lib/Semantics/check-warning.cpp
@@ -0,0 +1,99 @@
+//===-- lib/Semantics/check-warning.cpp
+//-----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "check-warning.h"
+#include "flang/Semantics/tools.h"
+
+namespace Fortran::semantics {
+
+void WarningChecker::Enter(const parser::FunctionStmt &stmt) {
+  if (Wunused_dummy_argument) {
+    for (const auto &dummyName : std::get<std::list<parser::Name>>(stmt.t)) {
+      if (auto *detail = dummyName.symbol->detailsIf<ObjectEntityDetails>()) {
+        const Symbol *ownerSymbol{dummyName.symbol->owner().symbol()};
+        const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()};
+        bool inInterface{ownerSubp && ownerSubp->isInterface()};
+
+        if (!inInterface && !detail->isUsed()) {
+          context_.Say(dummyName.symbol->GetUltimate().name(),
+              "Unused dummy argument '%s' [-Wunused-dummy-argument]"_warn_en_US,
+              dummyName.ToString());
+        }
+      }
+    }
+  }
+  if (Wunused_variable) {
+    if (const auto &suffix{std::get<std::optional<parser::Suffix>>(stmt.t)}) {
+      if (suffix->resultName.has_value()) {
+        if (auto *detail =
+                suffix->resultName->symbol->detailsIf<ObjectEntityDetails>()) {
+          const Symbol *ownerSymbol{
+              suffix->resultName->symbol->owner().symbol()};
+          const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()};
+          bool inInterface{ownerSubp && ownerSubp->isInterface()};
+          if (!inInterface && !detail->isUsed()) {
+            context_.Say(suffix->resultName->source,
+                "Unused variable '%s' [-Wunused-variable]"_warn_en_US,
+                suffix->resultName->ToString());
+          }
+        }
+      }
+    }
+  }
+}
+
+void WarningChecker::Enter(const parser::SubroutineStmt &stmt) {
+  if (!Wunused_dummy_argument) {
+    return;
+  }
+  for (const auto &dummyArg : std::get<std::list<parser::DummyArg>>(stmt.t)) {
+    if (const auto *dummyName{std::get_if<parser::Name>(&dummyArg.u)}) {
+      if (const auto *symbol = dummyName->symbol) {
+
+        const Symbol *ownerSymbol{symbol->owner().symbol()};
+        const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()};
+        bool inInterface{ownerSubp && ownerSubp->isInterface()};
+
+        if (auto *detail = symbol->detailsIf<ObjectEntityDetails>()) {
+          if (!inInterface && !detail->isUsed()) {
+            context_.Say(symbol->GetUltimate().name(),
+                "Unused dummy argument '%s' [-Wunused-dummy-argument]"_warn_en_US,
+                dummyName->ToString());
+          }
+        }
+      }
+    }
+  }
+}
+
+void WarningChecker::Enter(const parser::EntityDecl &decl) {
+  if (!Wunused_variable) {
+    return;
+  }
+
+  const auto &name{std::get<parser::ObjectName>(decl.t)};
+  if (const auto *symbol = name.symbol) {
+    const Symbol *ownerSymbol{symbol->owner().symbol()};
+    const auto *ownerSubp{ownerSymbol->detailsIf<SubprogramDetails>()};
+    bool inInterface{ownerSubp && ownerSubp->isInterface()};
+    bool inModule{ownerSymbol && ownerSymbol->scope() &&
+        ownerSymbol->scope()->IsModule()};
+
+    if (auto *detail = symbol->detailsIf<ObjectEntityDetails>()) {
+      if (!inInterface && !inModule && !detail->isDummy() &&
+          !detail->isFuncResult() && !detail->isUsed()) {
+        context_.Say(symbol->name(),
+            "Unused variable '%s' [-Wunused-variable]"_warn_en_US,
+            name.ToString());
+      }
+    }
+  }
+}
+
+} // namespace Fortran::semantics
diff --git a/flang/lib/Semantics/check-warning.h b/flang/lib/Semantics/check-warning.h
new file mode 100644
index 0000000000000..f13d159392ab4
--- /dev/null
+++ b/flang/lib/Semantics/check-warning.h
@@ -0,0 +1,40 @@
+//===-- lib/Semantics/check-warning.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_SEMANTICS_CHECK_WARNING_H_
+#define FORTRAN_SEMANTICS_CHECK_WARNING_H_
+
+#include "flang/Semantics/semantics.h"
+
+namespace Fortran::parser {
+struct FunctionStmt;
+struct InterfaceBody;
+struct SubroutineStmt;
+struct EntityDecl;
+} // namespace Fortran::parser
+
+namespace Fortran::semantics {
+
+// Perform semantic checks on DummyArg on Function and Subroutine
+// TODO: Add checks for future warning options
+class WarningChecker : public virtual BaseChecker {
+public:
+  explicit WarningChecker(SemanticsContext &context) : context_{context} {}
+  void Enter(const parser::FunctionStmt &);
+  void Enter(const parser::SubroutineStmt &);
+  void Enter(const parser::EntityDecl &);
+
+private:
+  SemanticsContext &context_;
+  const bool Wunused_dummy_argument = context_.languageFeatures().IsEnabled(
+      common::LanguageFeature::UnusedDummyArgument);
+  const bool Wunused_variable = context_.languageFeatures().IsEnabled(
+      common::LanguageFeature::UnusedVariable);
+};
+} // namespace Fortran::semantics
+#endif
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 6949e5693d08f..be077fdceddf4 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -590,6 +590,13 @@ MaybeExpr ExpressionAnalyzer::FixMisparsedSubstring(
 }
 
 MaybeExpr ExpressionAnalyzer::Analyze(const parser::Designator &d) {
+  const auto &name = GetFirstName(d);
+  if (name.symbol) {
+    if (auto *detail =
+            name.symbol->detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
+      detail->set_isUsed();
+    }
+  }
   auto restorer{GetContextualMessages().SetLocation(d.source)};
   if (auto substringInquiry{FixMisparsedSubstring(d)}) {
     return substringInquiry;
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index e64abe6b50e78..488b0acfee8ff 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -6693,6 +6693,11 @@ bool DeclarationVisitor::Pre(const parser::CommonBlockObject &) {
 void DeclarationVisitor::Post(const parser::CommonBlockObject &x) {
   const auto &name{std::get<parser::Name>(x.t)};
   DeclareObjectEntity(name);
+  if (name.symbol) {
+    if (auto *detail{name.symbol->detailsIf<ObjectEntityDetails>()}) {
+      detail->set_isUsed();
+    }
+  }
   auto pair{specPartState_.commonBlockObjects.insert(name.source)};
   if (!pair.second) {
     const SourceName &prev{*pair.first};
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index 10a01039ea0ae..d3703b8fc2148 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -32,6 +32,7 @@
 #include "check-select-rank.h"
 #include "check-select-type.h"
 #include "check-stop.h"
+#include "check-warning.h"
 #include "compute-offsets.h"
 #include "mod-file.h"
 #include "resolve-labels.h"
@@ -202,6 +203,7 @@ using StatementSemanticsPass2 = SemanticsVisitor<AllocateChecker,
     MiscChecker, NamelistChecker, NullifyChecker, PurityChecker,
     ReturnStmtChecker, SelectRankConstructChecker, SelectTypeChecker,
     StopChecker>;
+using StatementSemanticsPass3 = SemanticsVisitor<WarningChecker>;
 
 static bool PerformStatementSemantics(
     SemanticsContext &context, parser::Program &program) {
@@ -221,6 +223,12 @@ static bool PerformStatementSemantics(
   if (context.languageFeatures().IsEnabled(common::LanguageFeature::CUDA)) {
     SemanticsVisitor<CUDAChecker>{context}.Walk(program);
   }
+  if (context.languageFeatures().IsEnabled(
+          common::LanguageFeature::UnusedDummyArgument) ||
+      context.languageFeatures().IsEnabled(
+          common::LanguageFeature::UnusedVariable)) {
+    StatementSemanticsPass3{context}.Walk(program);
+  }
   if (!context.messages().AnyFatalError()) {
     WarnUndefinedFunctionResult(context, context.globalScope());
   }
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index bbeb4b15a0486..056177d714423 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -31,6 +31,8 @@ LanguageFeatureControl::LanguageFeatureControl() {
   disable_.set(LanguageFeature::LogicalAbbreviations);
   disable_.set(LanguageFeature::XOROperator);
   disable_.set(LanguageFeature::OldStyleParameter);
+  disable_.set(LanguageFeature::UnusedDummyArgument);
+  disable_.set(LanguageFeature::UnusedVariable);
   // Possibly an accidental "feature" of nvfortran.
   disable_.set(LanguageFeature::AssumedRankPassedToNonAssumedRank);
   // These warnings are enabled by default, but only because they used
diff --git a/flang/test/Driver/werror-wrong.f90 b/flang/test/Driver/werror-wrong.f90
index 58adf6f745d5e..364be4a0e6e11 100644
--- a/flang/test/Driver/werror-wrong.f90
+++ b/flang/test/Driver/werror-wrong.f90
@@ -3,4 +3,4 @@
 ! RUN: not %flang_fc1 -fsyntax-only -Wall %s  2>&1 | FileCheck %s --check-prefix=WRONG
 ! RUN: not %flang_fc1 -fsyntax-only -WX %s  2>&1 | FileCheck %s --check-prefix=WRONG
 
-! WRONG: Only `-Werror` is supported currently.
+! WRONG: Only `-Werror`, `-W[no]unused-dummy-argument` and `-W[no]unused-variable` are supported currently.
diff --git a/flang/test/Driver/wextra-ok.f90 b/flang/test/Driver/wextra-ok.f90
index 441029aa0af27..9b391d5b91a59 100644
--- a/flang/test/Driver/wextra-ok.f90
+++ b/flang/test/Driver/wextra-ok.f90
@@ -5,7 +5,7 @@
 ! RUN: not %flang -std=f2018 -Wblah -Wextra %s -c 2>&1 | FileCheck %s --check-prefix=WRONG
 
 ! CHECK-OK: the warning option '-Wextra' is not supported
-! WRONG: Only `-Werror` is supported currently.
+! WRONG: Only `-Werror`, `-W[no]unused-dummy-argument` and `-W[no]unused-variable` are supported currently.
 
 program wextra_ok
 end program wextra_ok
diff --git a/flang/test/Semantics/wunused-dummy-argument.f90 b/flang/test/Semantics/wunused-dummy-argument.f90
new file mode 100644
index 0000000000000..bad7720f48f2c
--- /dev/null
+++ b/flang/test/Semantics/wunused-dummy-argument.f90
@@ -0,0 +1,54 @@
+! RUN: %flang_fc1 -Wunused-dummy-argument %s 2>&1 | FileCheck %s
+! RUN: not %flang_fc1 -Wunused-dummy-argument -Werror %s
+! RUN: %flang_fc1 -Wno-unused-dummy-argument %s 2>&1 | FileCheck %s --check-prefix=NOWARN
+
+! CHECK: warning: Unused dummy argument 'a4' [-Wunused-dummy-argument]
+! CHECK: warning: Unused dummy argument 'b4' [-Wunused-dummy-argument]
+! CHECK: warning: Unused dummy argument 'a6' [-Wunused-dummy-argument]
+! CHECK: warning: Unused dummy argument 'b6' [-Wunused-dummy-argument]
+
+! NOWARN-NOT: warning: Unused dummy argument 'a4' [-Wunused-dummy-argument]
+! NOWARN-NOT: warning: Unused dummy argument 'b4' [-Wunused-dummy-argument]
+! NOWARN-NOT: warning: Unused dummy argument 'a6' [-Wunused-dummy-argument]
+! NOWARN-NOT: warning: Unused dummy argument 'b6' [-Wunused-dummy-argument]
+
+program main
+        type :: my_type
+                integer :: val
+        end type
+        integer :: not_dummy_arg
+        interface
+                subroutine subroutine_interface(a)
+                        integer, intent(in) :: a
+                end subroutine
+
+                function function_interface(a2)
+                        integer, intent(in) :: a2
+                end function
+        end interface
+contains
+        subroutine subroutine_all_used(a3, b3)
+                integer, intent(inout) :: a3, b3
+                a3 = a3 + b3
+        end subroutine
+
+        subroutine subroutine_unused_both(a4, b4)
+                integer, intent(inout) :: a4(10)
+                type(my_type) :: b4
+        end subroutine
+
+
+        function function_used_all(a5, b5) result(c1)
+                integer, intent(inout) :: a5(10)
+                type(my_type), intent(in) :: b5
+                integer :: c1
+                a5(1) = b5%val
+                c1 = a5(2)
+        end function 
+
+        function function_unused_both(a6, b6) result(c2)
+                integer, intent(inout) :: a6(10)
+                type(my_type) :: b6
+                integer :: c2
+        end function
+end program
diff --git a/flang/test/Semantics/wunused-variable.f90 b/flang/test/Semantics/wunused-variable.f90
new file mode 100644
index 0000000000000..146f943f83a35
--- /dev/null
+++ b/flang/test/Semantics/wunused-variable.f90
@@ -0,0 +1,72 @@
+! RUN: %flang_fc1 -Wunused-variable %s 2>&1 | FileCheck %s
+! RUN: not %flang_fc1 -Wunused-variable -Werror %s
+! RUN: %flang_fc1 -Wno-unused-variable %s 2>&1 | FileCheck %s --check-prefix=NOWARN
+
+! CHECK: warning: Unused variable 'unused_var_in_submod_subroutine' [-Wunused-variable]
+! CHECK: warning: Unused variable 'my_type_var1' [-Wunused-variable]
+! CHECK: warning: Unused variable 'not_dummy_arg' [-Wunused-variable]
+! CHECK: warning: Unused variable 'in_subroutine' [-Wunused-variable]
+! CHECK: warning: Unused variable 'c1' [-Wunused-variable]
+
+! NOWARN-NOT: warning: Unused variable 'unused_var_in_submod_subroutine' [-Wunused-variable]
+! NOWARN-NOT: warning: Unused variable 'my_type_var1' [-Wunused-variable]
+! NOWARN-NOT: warning: Unused variable 'not_dummy_arg' [-Wunused-variable]
+! NOWARN-NOT: warning: Unused variable 'in_subroutine' [-Wunused-variable]
+! NOWARN-NOT: warning: Unused variable 'c1' [-Wunused-variable]
+module test
+        integer :: var_in_module
+        contains
+        subroutine module_subroutine(a)
+                integer :: unused_var_in_submod_subroutine
+                integer :: a
+        end subroutine
+end module test
+
+program main
+        type :: my_type
+                integer :: val
+                integer :: unused_val
+        end type
+        interface
+                subroutine subroutine_in_interface()
+                        integer :: w
+                end subroutine
+                function function_in_interface() result(j)
+                        integer :: x
+                        integer :: j
+                end function
+        end interface
+        type(my_type) :: my_type_var1
+        type(my_type) :: my_type_var2
+        integer :: not_dummy_arg
+
+        integer :: variable_common
+        common variable_common
+
+        my_type_var2%val = 12
+
+
+
+        print *, function_used_all()
+
+        contains
+        subroutine subroutine_all_used(a3)
+                integer, intent(in) :: a3
+                integer :: in_subroutine_used
+                in_subroutine_used = a3
+        end subroutine
+
+        subroutine subroutine_unused(a4)
+                integer, intent(in) :: a4
+                integer :: in_subroutine
+        end subroutine
+
+        function function_used_all() result(c1)
+                integer :: in_function
+                integer :: c1
+                c1 = in_function
+        end function 
+
+        function function_unused_all() result(c1)
+        end function 
+end program

@tarunprabhu
Copy link
Contributor

Does support for these options need to be added in the main driver?

@JDPailleux
Copy link
Contributor Author

JDPailleux commented Feb 18, 2025

Does support for these options need to be added in the main driver?

There's no need to add these options warning in the main driver, as OPT_W_Joined already takes care of this and will now accept [no]unused-dummy-argument and [no]unused-variable as argument.

Comment on lines 935 to 952
} else if (wArg == "unused-dummy-argument") {
res.getFrontendOpts().features.Enable(
Fortran::common::LanguageFeature::UnusedDummyArgument);
} else if (wArg == "no-unused-dummy-argument") {
res.getFrontendOpts().features.Enable(
Fortran::common::LanguageFeature::UnusedDummyArgument, false);
} else if (wArg == "unused-variable") {
res.getFrontendOpts().features.Enable(
Fortran::common::LanguageFeature::UnusedVariable);
} else if (wArg == "no-unused-variable") {
res.getFrontendOpts().features.Enable(
Fortran::common::LanguageFeature::UnusedVariable, false);
} else {
const unsigned diagID =
diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
"Only `-Werror` is supported currently.");
const unsigned diagID = diags.getCustomDiagID(
clang::DiagnosticsEngine::Error,
"Only `-Werror`, `-W[no]unused-dummy-argument` "
"and `-W[no]unused-variable` are supported "
"currently.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look like it is scalable. For every warning option that is added, we would have to add a check and change the diagnostic line. Besides, the comment above suggests that this was intended to be a temporary measure. This code predates my involvement with this project. Perhaps those who have been around longer have some insight on how the warning classes were intended to be handled: @banach-space @kiranchandramohan @jeanPerier

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we implemented this part, Flang had no mechanism to control diagnostics or warnings, which is why this logic is so basic. I see that this is slowly changing.

This doesn't look like it is scalable.

Agreed - this is not a sustainable long-term approach. One way forward could be to take inspiration from Clang, for example: clang/include/clang/Basic/DiagnosticGroups.td#L863.

My perspective on changes like this is that we shouldn't block them unless we can propose a concrete alternative. In practice, that means someone needs to design and implement a better approach.

@JDPailleux, are we expecting more patches like this in the near future? It would be helpful to understand the urgency of updating this logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@banach-space Yes, probably, because in the near future we'd like to add more in order to support -Wall in Flang. And I agree that in the long term, adding all the warning flags should be improved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, basically, it would be better to add an entry for each warning flag in the main driver? And therefore remove/rework the use of OPT_W_Joined in a future PR? I'll modify this PR to do that ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just realised that the link that I shared was wrong, I meant this:

def UnusedArgument : DiagGroup<"unused-argument">;

it would be better to add an entry for each warning flag in the main driver?

Adding things in the main driver makes sense if it's going to be re-used by Clang. Is it? IIRC, Clang ties these "warning" flags to various diagnostic groups (hence the link above), as opposed to using https://github.com/llvm/llvm-project/blob/main/clang/include/clang/Driver/Options.td.

But please keep in mind that I've not touched this in ~3yrs and these are just bits from my memory. In practice, we should re-visit the logic in Clang and see whether there's scope for re-use.

  • If yes, great, just do it!
  • If not, then Flang needs something bespoke.

As I no longer actively work in this area, I am unable to make a specific suggestion. But hopefully this provides a good starting point.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tip, I'll look into it!

@tarunprabhu
Copy link
Contributor

Does support for these options need to be added in the main driver?

There's no need to add these options warning in the main driver, as OPT_W_Joined already takes care of this and will now accept [no]unused-dummy-argument and [no]unused-variable as argument.

Do these warning flags appear in flang --help?

@JDPailleux
Copy link
Contributor Author

Does support for these options need to be added in the main driver?

There's no need to add these options warning in the main driver, as OPT_W_Joined already takes care of this and will now accept [no]unused-dummy-argument and [no]unused-variable as argument.

Do these warning flags appear in flang --help?

Yes here : -W<warning> Enable the specified warning (like in clang)

@tarunprabhu
Copy link
Contributor

Yes here : -W<warning> Enable the specified warning (like in clang)

If a user doesn't know of the existence of these warning flags, how would they find it?

Having said that, it seems that one can't do that with clang either. Ideally, we should support a page like this at least, but that's separate from this PR.

@banach-space
Copy link
Contributor

Having said that, it seems that one can't do that with clang either. Ideally, we should support a page like this at least, but that's separate from this PR.

That's likely linked to

def UnusedArgument : DiagGroup<"unused-argument">;
(or similar *.td file)

@tarunprabhu
Copy link
Contributor

(or similar *.td file)

From clang/docs/CMakeLists.txt

gen_rst_file_from_td(DiagnosticsReference.rst -gen-diag-docs ../include/clang/Basic/Diagnostic.td "${docs_targets}")

@JDPailleux, will you be looking into a different way of structuring the warning flags as part of this patch?

I have requested reviews from others for the non-driver parts of this PR.

@JDPailleux
Copy link
Contributor Author

(or similar *.td file)

From clang/docs/CMakeLists.txt

gen_rst_file_from_td(DiagnosticsReference.rst -gen-diag-docs ../include/clang/Basic/Diagnostic.td "${docs_targets}")

@JDPailleux, will you be looking into a different way of structuring the warning flags as part of this patch?

I have requested reviews from others for the non-driver parts of this PR.

@tarunprabhu Yes, it's planned to restructure the warnings flags in this patch by using the Clang Diagnostics or a similar mechanism.

@tarunprabhu
Copy link
Contributor

@tarunprabhu Yes, it's planned to restructure the warnings flags in this patch by using the Clang Diagnostics or a similar mechanism.

Ok, thanks 👍🏾

Feel free to open a different PR for those changes if that feels more appropriate.

@JDPailleux JDPailleux force-pushed the jdp/flang/wunused-dummy-argument-and-unused-variable-flags branch from 900b32a to 46f8e51 Compare March 7, 2025 07:25
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Mar 7, 2025
Copy link

github-actions bot commented Mar 7, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@JDPailleux JDPailleux force-pushed the jdp/flang/wunused-dummy-argument-and-unused-variable-flags branch from 46f8e51 to ab53339 Compare March 7, 2025 07:42
…riable

Addition of two new warning options and a diagnostic system for Flang
based on the Clang's diagnostics.
@JDPailleux JDPailleux force-pushed the jdp/flang/wunused-dummy-argument-and-unused-variable-flags branch from ab53339 to 86e34cf Compare March 7, 2025 09:42
@tarunprabhu
Copy link
Contributor

Thanks for working on this @JDPailleux.

This looks like a substantial change including to parts of clang. Is it possible to split this into two. One PR for just the "infrastructure" and another for the flang-specifc warnings. This would make it easier to focus the discussion on the design. Could you also provide a high-level description of the approach that you have taken?

Thanks.

@JDPailleux
Copy link
Contributor Author

Thanks for working on this @JDPailleux.

This looks like a substantial change including to parts of clang. Is it possible to split this into two. One PR for just the "infrastructure" and another for the flang-specifc warnings. This would make it easier to focus the discussion on the design. Could you also provide a high-level description of the approach that you have taken?

Thanks.

Yes of course, I'll split this in 2 PRs.

@JDPailleux
Copy link
Contributor Author

JDPailleux commented Mar 10, 2025

Hi, A PR has been created to support a diagnostic system for Flang here: #130593.
I'm open to discuss and apply modifications or rework if necessary. I will update this PR, after validation of this one later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category flang:driver flang:semantics flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants