[flang-commits] [flang] [flang] Add options -W[no-]unused-dummy-argument and -W[no-]unused-variable (PR #127214)

via flang-commits flang-commits at lists.llvm.org
Fri Feb 14 07:20:22 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-driver

Author: Jean-Didier PAILLEUX (JDPailleux)

<details>
<summary>Changes</summary>

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
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

``````````

</details>


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


More information about the flang-commits mailing list