[flang-commits] [flang] [flang] Add new warnings for unused & undefined locals (PR #173504)
via flang-commits
flang-commits at lists.llvm.org
Wed Dec 24 10:37:00 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-parser
Author: Peter Klausler (klausler)
<details>
<summary>Changes</summary>
Add a requested warning for completely unused local variables. The implementation runs a scan over typed expressions during the existing expression semantics pass to detect variable uses, and a routine at the end of semantics to take a pass over the symbol tables to find unused locals.
The new infrastructure needed to detect variable uses, and the existing infrastructure that detects potential variable definitions, then makes it easy to detect variables that are used without any possible initialization or definition, so I did that too.
The warning for unused locals is off by default -- they might indicate a misspelling (that IMPLICIT NONE would have caught), but seem otherwise generally benign. The warning for uses of completely uninitialized and undefined variables, however, is enabled by default, since that's likely to indicate a program bug that should be investigated.
This patch touches a lot of files lightly. Many of these files are tests that would have produced needless warning noise; one new test was added.
Fixes https://github.com/llvm/llvm-project/issues/173276.
---
Patch is 23.74 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/173504.diff
18 Files Affected:
- (modified) flang/include/flang/Evaluate/tools.h (+6-2)
- (modified) flang/include/flang/Parser/parse-tree.h (+7-7)
- (modified) flang/include/flang/Parser/tools.h (+10-1)
- (modified) flang/include/flang/Semantics/expression.h (+29-12)
- (modified) flang/include/flang/Semantics/semantics.h (+3)
- (modified) flang/include/flang/Support/Fortran-features.h (+2-1)
- (modified) flang/lib/Evaluate/tools.cpp (+3)
- (modified) flang/lib/Lower/OpenMP/Atomic.cpp (+1-1)
- (modified) flang/lib/Semantics/check-omp-atomic.cpp (+2-2)
- (modified) flang/lib/Semantics/expression.cpp (+90)
- (modified) flang/lib/Semantics/semantics.cpp (+54-7)
- (modified) flang/lib/Support/Fortran-features.cpp (+1)
- (modified) flang/test/Driver/disable-diagnostic.f90 (+3-5)
- (modified) flang/test/Semantics/OpenMP/copying.f90 (+1-1)
- (modified) flang/test/Semantics/bindings03.f90 (+1)
- (modified) flang/test/Semantics/kinds05b.f90 (+1-1)
- (modified) flang/test/Semantics/long-name.f90 (+1-1)
- (added) flang/test/Semantics/unused.f90 (+25)
``````````diff
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index 4248e3a5461f5..798afc8266d85 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -1089,6 +1089,10 @@ extern template semantics::UnorderedSymbolSet CollectSymbols(
const Expr<SomeInteger> &);
extern template semantics::UnorderedSymbolSet CollectSymbols(
const Expr<SubscriptInteger> &);
+extern template semantics::UnorderedSymbolSet CollectSymbols(
+ const ProcedureDesignator &);
+extern template semantics::UnorderedSymbolSet CollectSymbols(
+ const Assignment &);
// Collects Symbols of interest for the CUDA data transfer in an expression
template <typename A>
@@ -1341,8 +1345,8 @@ template <typename A> inline bool HasCUDADeviceAttrs(const A &expr) {
// device attribute.
template <typename A, typename B>
inline bool IsCUDADataTransfer(const A &lhs, const B &rhs) {
- int lhsNbManagedSymbols = {GetNbOfCUDAManagedOrUnifiedSymbols(lhs)};
- int rhsNbManagedSymbols = {GetNbOfCUDAManagedOrUnifiedSymbols(rhs)};
+ int lhsNbManagedSymbols{GetNbOfCUDAManagedOrUnifiedSymbols(lhs)};
+ int rhsNbManagedSymbols{GetNbOfCUDAManagedOrUnifiedSymbols(rhs)};
int rhsNbSymbols{GetNbOfCUDADeviceSymbols(rhs)};
// Special cases perforemd on the host:
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index a31eb542a50d0..a8f64546f4047 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -1481,8 +1481,11 @@ WRAPPER_CLASS(ContiguousStmt, std::list<ObjectName>);
// R846 int-constant-subobject -> constant-subobject
using ConstantSubobject = Constant<common::Indirection<Designator>>;
-// Represents an analyzed expression
+// Represent an analyzed expression
using TypedExpr = common::ForwardOwningPointer<evaluate::GenericExprWrapper>;
+using TypedCall = common::ForwardOwningPointer<evaluate::ProcedureRef>;
+using TypedAssignment =
+ common::ForwardOwningPointer<evaluate::GenericAssignmentWrapper>;
// R845 data-stmt-constant ->
// scalar-constant | scalar-constant-subobject |
@@ -2025,8 +2028,6 @@ struct DeallocateStmt {
// R1032 assignment-stmt -> variable = expr
struct AssignmentStmt {
TUPLE_CLASS_BOILERPLATE(AssignmentStmt);
- using TypedAssignment =
- common::ForwardOwningPointer<evaluate::GenericAssignmentWrapper>;
mutable TypedAssignment typedAssignment;
std::tuple<Variable, Expr> t;
};
@@ -2053,7 +2054,7 @@ struct PointerAssignmentStmt {
std::variant<std::list<BoundsRemapping>, std::list<BoundsSpec>> u;
};
TUPLE_CLASS_BOILERPLATE(PointerAssignmentStmt);
- mutable AssignmentStmt::TypedAssignment typedAssignment;
+ mutable TypedAssignment typedAssignment;
std::tuple<DataRef, Bounds, Expr> t;
};
@@ -3298,8 +3299,7 @@ struct CallStmt {
Call call;
std::optional<Chevrons> chevrons;
CharBlock source;
- mutable common::ForwardOwningPointer<evaluate::ProcedureRef>
- typedCall; // filled by semantics
+ mutable TypedCall typedCall; // filled by semantics
};
// R1529 function-subprogram ->
@@ -5339,7 +5339,7 @@ struct OpenMPAtomicConstruct : public OmpBlockConstruct {
struct Op {
int what;
- AssignmentStmt::TypedAssignment assign;
+ TypedAssignment assign;
};
TypedExpr atom, cond;
Op op0, op1;
diff --git a/flang/include/flang/Parser/tools.h b/flang/include/flang/Parser/tools.h
index d105f03dd31d3..5e705a8b47276 100644
--- a/flang/include/flang/Parser/tools.h
+++ b/flang/include/flang/Parser/tools.h
@@ -136,11 +136,20 @@ template <typename A>
struct HasSource<A, decltype(static_cast<void>(A::source), 0)>
: std::true_type {};
-// Detects parse tree nodes with "typedExpr" members.
+// Detects parse tree nodes with "typedExpr", "typedCall", &c. members.
template <typename A, typename = int> struct HasTypedExpr : std::false_type {};
template <typename A>
struct HasTypedExpr<A, decltype(static_cast<void>(A::typedExpr), 0)>
: std::true_type {};
+template <typename A, typename = int> struct HasTypedCall : std::false_type {};
+template <typename A>
+struct HasTypedCall<A, decltype(static_cast<void>(A::typedCall), 0)>
+ : std::true_type {};
+template <typename A, typename = int>
+struct HasTypedAssignment : std::false_type {};
+template <typename A>
+struct HasTypedAssignment<A, decltype(static_cast<void>(A::typedAssignment), 0)>
+ : std::true_type {};
// GetSource()
diff --git a/flang/include/flang/Semantics/expression.h b/flang/include/flang/Semantics/expression.h
index 639ef99d2d936..e32158f3daa32 100644
--- a/flang/include/flang/Semantics/expression.h
+++ b/flang/include/flang/Semantics/expression.h
@@ -464,6 +464,13 @@ evaluate::Expr<evaluate::SubscriptInteger> AnalyzeKindSelector(
SemanticsContext &, common::TypeCategory,
const std::optional<parser::KindSelector> &);
+void NoteUsedSymbols(SemanticsContext &, const SomeExpr &);
+void NoteUsedSymbols(SemanticsContext &, const evaluate::ProcedureRef &);
+void NoteUsedSymbols(SemanticsContext &, const evaluate::Assignment &);
+void NoteUsedSymbols(SemanticsContext &, const parser::TypedExpr &);
+void NoteUsedSymbols(SemanticsContext &, const parser::TypedCall &);
+void NoteUsedSymbols(SemanticsContext &, const parser::TypedAssignment &);
+
// Semantic analysis of all expressions in a parse tree, which becomes
// decorated with typed representations for top-level expressions.
class ExprChecker {
@@ -475,11 +482,11 @@ class ExprChecker {
bool Walk(const parser::Program &);
bool Pre(const parser::Expr &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
bool Pre(const parser::Variable &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
bool Pre(const parser::Selector &x) {
@@ -491,11 +498,11 @@ class ExprChecker {
return false;
}
bool Pre(const parser::AllocateObject &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
bool Pre(const parser::PointerObject &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
bool Pre(const parser::DataStmtObject &);
@@ -503,15 +510,15 @@ class ExprChecker {
bool Pre(const parser::DataImpliedDo &);
bool Pre(const parser::CallStmt &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
bool Pre(const parser::AssignmentStmt &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
bool Pre(const parser::PointerAssignmentStmt &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
@@ -552,27 +559,37 @@ class ExprChecker {
}
template <typename A> bool Pre(const parser::Scalar<A> &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
template <typename A> bool Pre(const parser::Constant<A> &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
template <typename A> bool Pre(const parser::Integer<A> &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
template <typename A> bool Pre(const parser::Logical<A> &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
template <typename A> bool Pre(const parser::DefaultChar<A> &x) {
- exprAnalyzer_.Analyze(x);
+ AnalyzeAndNoteUses(x);
return false;
}
private:
+ template <typename A> void AnalyzeAndNoteUses(const A &x) {
+ exprAnalyzer_.Analyze(x);
+ if constexpr (parser::HasTypedExpr<A>::value) {
+ NoteUsedSymbols(context_, x.typedExpr);
+ } else if constexpr (parser::HasTypedCall<A>::value) {
+ NoteUsedSymbols(context_, x.typedCall);
+ } else if constexpr (parser::HasTypedAssignment<A>::value) {
+ NoteUsedSymbols(context_, x.typedAssignment);
+ }
+ }
bool InWhereBody() const { return whereDepth_ > 0; }
SemanticsContext &context_;
diff --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h
index c03d0a02d7faf..88bc23380b22d 100644
--- a/flang/include/flang/Semantics/semantics.h
+++ b/flang/include/flang/Semantics/semantics.h
@@ -331,6 +331,8 @@ class SemanticsContext {
void NoteDefinedSymbol(const Symbol &);
bool IsSymbolDefined(const Symbol &) const;
+ void NoteUsedSymbol(const Symbol &);
+ bool IsSymbolUsed(const Symbol &) const;
void DumpSymbols(llvm::raw_ostream &);
@@ -390,6 +392,7 @@ class SemanticsContext {
ModuleDependences moduleDependences_;
std::map<const Symbol *, SourceName> moduleFileOutputRenamings_;
UnorderedSymbolSet isDefined_;
+ UnorderedSymbolSet isUsed_;
std::list<ProgramTree> programTrees_;
};
diff --git a/flang/include/flang/Support/Fortran-features.h b/flang/include/flang/Support/Fortran-features.h
index ef5c1a84ba3d7..e81695c749784 100644
--- a/flang/include/flang/Support/Fortran-features.h
+++ b/flang/include/flang/Support/Fortran-features.h
@@ -79,7 +79,8 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
CompatibleDeclarationsFromDistinctModules, ConstantIsContiguous,
NullActualForDefaultIntentAllocatable, UseAssociationIntoSameNameSubprogram,
HostAssociatedIntentOutInSpecExpr, NonVolatilePointerToVolatile,
- RealConstantWidening, VolatileOrAsynchronousTemporary)
+ RealConstantWidening, VolatileOrAsynchronousTemporary, UnusedVariable,
+ UsedUndefinedVariable)
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index a0035ae330e35..37f718c98ebb5 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -1094,6 +1094,9 @@ template semantics::UnorderedSymbolSet CollectSymbols(
const Expr<SomeInteger> &);
template semantics::UnorderedSymbolSet CollectSymbols(
const Expr<SubscriptInteger> &);
+template semantics::UnorderedSymbolSet CollectSymbols(
+ const ProcedureDesignator &);
+template semantics::UnorderedSymbolSet CollectSymbols(const Assignment &);
struct CollectCudaSymbolsHelper : public SetTraverse<CollectCudaSymbolsHelper,
semantics::UnorderedSymbolSet> {
diff --git a/flang/lib/Lower/OpenMP/Atomic.cpp b/flang/lib/Lower/OpenMP/Atomic.cpp
index 3ab8a5891e8a6..f31de82fc2a5f 100644
--- a/flang/lib/Lower/OpenMP/Atomic.cpp
+++ b/flang/lib/Lower/OpenMP/Atomic.cpp
@@ -80,7 +80,7 @@ dumpAtomicAnalysis(const parser::OpenMPAtomicConstruct::Analysis &analysis) {
}
return "<null>"s;
};
- auto assignStr = [&](const parser::AssignmentStmt::TypedAssignment &assign) {
+ auto assignStr = [&](const parser::TypedAssignment &assign) {
if (auto *maybe = assign.get(); maybe && maybe->v) {
std::string str;
llvm::raw_string_ostream os(str);
diff --git a/flang/lib/Semantics/check-omp-atomic.cpp b/flang/lib/Semantics/check-omp-atomic.cpp
index 9a4cb84ad3c2c..2218acce95372 100644
--- a/flang/lib/Semantics/check-omp-atomic.cpp
+++ b/flang/lib/Semantics/check-omp-atomic.cpp
@@ -260,7 +260,7 @@ static std::optional<evaluate::Assignment> GetEvaluateAssignment(
using AssignmentStmt = common::Indirection<parser::AssignmentStmt>;
using PointerAssignmentStmt =
common::Indirection<parser::PointerAssignmentStmt>;
- using TypedAssignment = parser::AssignmentStmt::TypedAssignment;
+ using TypedAssignment = parser::TypedAssignment;
return common::visit(
[](auto &&s) -> std::optional<evaluate::Assignment> {
@@ -426,7 +426,7 @@ static void SetExpr(parser::TypedExpr &expr, MaybeExpr value) {
}
}
-static void SetAssignment(parser::AssignmentStmt::TypedAssignment &assign,
+static void SetAssignment(parser::TypedAssignment &assign,
std::optional<evaluate::Assignment> value) {
if (value) {
assign.Reset(new evaluate::GenericAssignmentWrapper(std::move(value)),
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 6f5d0bf9eb242..e301190629234 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -5345,6 +5345,96 @@ evaluate::Expr<evaluate::SubscriptInteger> AnalyzeKindSelector(
return analyzer.AnalyzeKindSelector(category, selector);
}
+// NoteUsedSymbols()
+
+static void NoteUsedSymbol(SemanticsContext &context, const Symbol &symbol) {
+ const Symbol &root{GetAssociationRoot(symbol)};
+ switch (root.owner().kind()) {
+ case semantics::Scope::Kind::Subprogram:
+ case semantics::Scope::Kind::MainProgram:
+ case semantics::Scope::Kind::BlockConstruct:
+ if ((root.has<semantics::ObjectEntityDetails>() ||
+ IsProcedurePointer(root))) {
+ context.NoteUsedSymbol(root);
+ if (root.test(Symbol::Flag::CrayPointee)) {
+ context.NoteUsedSymbol(GetCrayPointer(root));
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+template <typename A>
+void NoteUsedSymbolsHelper(SemanticsContext &context, const A &x) {
+ if (context.ShouldWarn(common::UsageWarning::UnusedVariable)) {
+ for (const Symbol &symbol : CollectSymbols(x)) {
+ NoteUsedSymbol(context, symbol);
+ }
+ }
+}
+
+void NoteUsedSymbols(SemanticsContext &context, const SomeExpr &expr) {
+ NoteUsedSymbolsHelper(context, expr);
+}
+
+static bool IsBindingUsedAsProcedure(const SomeExpr &expr) {
+ if (const auto *pd{std::get_if<evaluate::ProcedureDesignator>(&expr.u)}) {
+ if (const Symbol *symbol{pd->GetSymbol()}) {
+ return symbol->has<ProcBindingDetails>();
+ }
+ }
+ return false;
+}
+
+void NoteUsedSymbols(
+ SemanticsContext &context, const evaluate::ProcedureRef &call) {
+ NoteUsedSymbolsHelper(context, call.proc());
+ for (const auto &maybeArg : call.arguments()) {
+ if (maybeArg) {
+ if (const auto *expr{maybeArg->UnwrapExpr()}) {
+ if (!IsBindingUsedAsProcedure(*expr)) {
+ // Ignore procedure bindings being used as actual procedures
+ // (a local extension).
+ NoteUsedSymbolsHelper(context, *expr);
+ }
+ }
+ }
+ }
+}
+
+void NoteUsedSymbols(
+ SemanticsContext &context, const evaluate::Assignment &assignment) {
+ if (IsBindingUsedAsProcedure(assignment.rhs)) {
+ // Don't look at the RHS, we're just using its binding (extension).
+ NoteUsedSymbolsHelper(context, assignment.lhs);
+ } else {
+ NoteUsedSymbolsHelper(context, assignment);
+ }
+}
+
+void NoteUsedSymbols(
+ SemanticsContext &context, const parser::TypedExpr &typedExpr) {
+ if (typedExpr && typedExpr->v) {
+ NoteUsedSymbols(context, *typedExpr->v);
+ }
+}
+
+void NoteUsedSymbols(
+ SemanticsContext &context, const parser::TypedCall &typedCall) {
+ if (typedCall) {
+ NoteUsedSymbols(context, *typedCall);
+ }
+}
+
+void NoteUsedSymbols(
+ SemanticsContext &context, const parser::TypedAssignment &typedAssignment) {
+ if (typedAssignment && typedAssignment->v) {
+ NoteUsedSymbols(context, *typedAssignment->v);
+ }
+}
+
ExprChecker::ExprChecker(SemanticsContext &context) : context_{context} {}
bool ExprChecker::Pre(const parser::DataStmtObject &obj) {
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index 2606d997b1cd7..58069f3eff068 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -160,22 +160,23 @@ class MiscChecker : public virtual BaseChecker {
SemanticsContext &context_;
};
+static bool WasDefined(const SemanticsContext &context, const Symbol &symbol) {
+ return context.IsSymbolDefined(symbol) ||
+ IsInitialized(symbol, /*ignoreDataStatements=*/true,
+ /*ignoreAllocatable=*/true, /*ignorePointer=*/true);
+}
+
static void WarnUndefinedFunctionResult(
SemanticsContext &context, const Scope &scope) {
- auto WasDefined{[&context](const Symbol &symbol) {
- return context.IsSymbolDefined(symbol) ||
- IsInitialized(symbol, /*ignoreDataStatements=*/true,
- /*ignoreAllocatable=*/true, /*ignorePointer=*/true);
- }};
if (const Symbol * symbol{scope.symbol()}) {
if (const auto *subp{symbol->detailsIf<SubprogramDetails>()}) {
if (subp->isFunction() && !subp->isInterface() && !subp->stmtFunction()) {
- bool wasDefined{WasDefined(subp->result())};
+ bool wasDefined{WasDefined(context, subp->result())};
if (!wasDefined) {
// Definitions of ENTRY result variables also count.
for (const auto &pair : scope) {
const Symbol &local{*pair.second};
- if (IsFunctionResult(local) && WasDefined(local)) {
+ if (IsFunctionResult(local) && WasDefined(context, local)) {
wasDefined = true;
break;
}
@@ -195,6 +196,41 @@ static void WarnUndefinedFunctionResult(
}
}
+static void WarnUnusedOrUndefinedLocal(
+ SemanticsContext &context, const Scope &scope) {
+ if (scope.kind() == Scope::Kind::Subprogram ||
+ scope.kind() == Scope::Kind::MainProgram ||
+ scope.kind() == Scope::Kind::BlockConstruct) {
+ for (const auto &[_, symbolRef] : scope) {
+ const Symbol &symbol{*symbolRef};
+ if ((symbol.has<semantics::ObjectEntityDetails>() ||
+ IsProcedurePointer(symbol)) &&
+ !IsFunctionResult(symbol) && !IsNamedConstant(symbol) &&
+ !IsDummy(symbol) && !FindEquivalenceSet(symbol) &&
+ !FindCommonBlockContaining(symbol)) {
+ if (context.IsSymbolUsed(symbol)) {
+ if (!WasDefined(context, symbol)) {
+ context.Warn(common::UsageWarning::UsedUndefinedVariable,
+ symbol.name(),
+ "Value of uninitialized local variable '%s' is used but never defined"_warn_en_US,
+ symbol.name());
+ }
+ } else {
+ if (!context.IsSymbolDefined(symbol)) { // ignore initialization
+ context.Warn(common::UsageWarning::UnusedVariable, symbol.name(),
+ "Local variable '%s' is never used"_warn_en_US, symbol.name());
+ }
+ }
+ }
+ }
+ }
+ if (!scope.IsModuleFile()) {
+ for (const Scope &child : scope.children()) {
+ WarnUnusedOrUndefinedLocal(context, child);
+ }
+ }
+}
+
using StatementSemanticsPass1 = ExprChecker;
using StatementSemanticsPass2 = SemanticsVisitor<AllocateChecker,
ArithmeticIfStmtChecker, AssignmentChecker, CaseChecker, CoarrayChecker,
@@ -224,6 +260,9 @@ static bool PerformStatementSemantics(
if (!context.messages().AnyFatalError()) {
WarnUndefinedFunctionResult(context, context.globalScope());
}
+ if (!context.messages().AnyFatalError()) {
+ WarnUnusedOrUndefinedLocal(context, context.globalScope());
+ }
if (!context.AnyFatalError()) {
pass2.CompileDataInitializationsIntoInitializers();
}
@@ -779,4 +818,12 @@ bool SemanticsContext::IsSymbolDefined(const Symbol &symbol) const {
return isDefined_.find(symbol) != isDefined_.end();
}
+void SemanticsContext::NoteUsedSymbol(const Symbol &symbol) {
+ isUsed_.insert(symbol);
+}
+
+bool SemanticsContext::IsSymbolUsed(const Symbol &symbol) const {
+ return isUsed_.find(symbol) != isUsed_.end();
+}
+
} // namespace Fortran::semantics
diff --git a/flang/lib/Support/Fortran-features.cpp b/flang/lib/Support/Fortran-features.cpp
index 4a6fb8d75a135..e01ad1396c381 100644
--- a/flang/lib/Support/Fortran-features.cpp
+++ b/flang/lib/Support/Fortran-features.cpp
@@ -152,6 +152,7 @@ LanguageFeatureControl::LanguageFeatureControl() {
// New warnings, on by default
warnLanguage_.set(LanguageFeature::SavedLocalInSpecExpr);
warnLanguage_.set(LanguageFeature::NullActualForAllocatable);
+ warnUsage_.set(UsageWarning::UsedUndefinedVariable);
}
std::optional<LanguageControlFlag> LanguageFeatureControl::FindWarning(
diff --git a/flang/test/Driver/disable-diagnostic.f90 b/flang/test/Driver/disable-diagnostic.f90
index 849489377da12..f743b1ac13c73 100644
--- a/flang/test/Driver/disable-diagnostic.f90
+++ b/flang/test/Driver/disable-diagnostic.f90
@@ -7,13 +7,11 @@
! ERROR2: error: Unknown diagnostic option: -WKnown-Bad-Implicit-Interface
program disable_diagnostic
- REAL :: x
- INTEGER :: y
- ! CHECK-NOT: warning
+ ! CHECK-NOT: warning
! WARN: warning: If the procedure's interface were explicit, this reference would b...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/173504
More information about the flang-commits
mailing list