Skip to content
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

Decouple warning detection from program instances #12293

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions spec/compiler/semantic/warnings_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,12 @@ describe "Semantic: warnings" do
CR

compiler = create_spec_compiler
compiler.warnings = Warnings::All
compiler.warnings_exclude << Crystal.normalize_path "lib"
compiler.warnings.level = :all
compiler.warnings.exclude_lib_path = true
compiler.prelude = "empty"
result = compiler.compile Compiler::Source.new(main_filename, File.read(main_filename)), output_filename

result.program.warning_failures.size.should eq(1)
compiler.warnings.infos.size.should eq(1)
end
end
end
Expand Down Expand Up @@ -396,12 +396,12 @@ describe "Semantic: warnings" do
)

compiler = create_spec_compiler
compiler.warnings = Warnings::All
compiler.warnings_exclude << Crystal.normalize_path "lib"
compiler.warnings.level = :all
compiler.warnings.exclude_lib_path = true
compiler.prelude = "empty"
result = compiler.compile Compiler::Source.new(main_filename, File.read(main_filename)), output_filename

result.program.warning_failures.size.should eq(1)
compiler.warnings.infos.size.should eq(1)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/spec_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def assert_no_errors(*args, **opts)
end

def warnings_result(code)
semantic(code).program.warning_failures
semantic(code).program.warnings.infos
end

def assert_warning(code, message, *, file = __FILE__, line = __LINE__)
Expand Down
34 changes: 15 additions & 19 deletions src/compiler/crystal/command.cr
Original file line number Diff line number Diff line change
Expand Up @@ -592,30 +592,26 @@ class Crystal::Command
end

private def setup_compiler_warning_options(opts, compiler)
compiler.warnings_exclude << Crystal.normalize_path "lib"
warnings_exclude_default = true
opts.on("--warnings all|none", "Which warnings detect. (default: all)") do |w|
compiler.warnings = case w
when "all"
Crystal::Warnings::All
when "none"
Crystal::Warnings::None
else
error "--warnings should be all, or none"
raise "unreachable"
end
opts.on("--warnings all|none", "Which warnings to detect. (default: all)") do |w|
compiler.warnings.level = case w
when "all"
Crystal::WarningLevel::All
when "none"
Crystal::WarningLevel::None
else
error "--warnings should be all, or none"
raise "unreachable"
end
end
opts.on("--error-on-warnings", "Treat warnings as errors.") do |w|
compiler.error_on_warnings = true
compiler.warnings.error_on_warnings = true
end
opts.on("--exclude-warnings <path>", "Exclude warnings from path (default: lib)") do |f|
if warnings_exclude_default
# if an --exclude-warnings is used explicitly, then we remove the default value
compiler.warnings_exclude.clear
warnings_exclude_default = false
end
compiler.warnings_exclude << Crystal.normalize_path f
compiler.warnings.exclude_lib_path = false
compiler.warnings.exclude_path(f)
end

compiler.warnings.exclude_lib_path = true
end

private def validate_emit_values(values)
Expand Down
17 changes: 2 additions & 15 deletions src/compiler/crystal/compiler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ module Crystal
Default = LineNumbers
end

enum Warnings
All
None
end

# Main interface to the compiler.
#
# A Compiler parses source code, type checks it and
Expand Down Expand Up @@ -109,14 +104,8 @@ module Crystal
# and can later be used to generate API docs.
property? wants_doc = false

# Which kind of warnings wants to be detected.
property warnings : Warnings = Warnings::All

# Paths to ignore for warnings detection.
property warnings_exclude : Array(String) = [] of String

# If `true` compiler will error if warnings are found.
property error_on_warnings : Bool = false
# Warning settings and all detected warnings.
property warnings = WarningCollection.new

@[Flags]
enum EmitTarget
Expand Down Expand Up @@ -218,8 +207,6 @@ module Crystal
program.show_error_trace = show_error_trace?
program.progress_tracker = @progress_tracker
program.warnings = @warnings
program.warnings_exclude = @warnings_exclude.map { |p| File.expand_path p }
program.error_on_warnings = @error_on_warnings
program
end

Expand Down
4 changes: 2 additions & 2 deletions src/compiler/crystal/macros/methods.cr
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ module Crystal
filename
end

delegate report_warning_at, to: @program
delegate warnings, to: @program

def interpret_top_level_call(node)
interpret_top_level_call?(node) ||
Expand Down Expand Up @@ -2199,7 +2199,7 @@ module Crystal
ArrayLiteral.map(@names) { |name| MacroId.new(name) }
end
when "global"
interpreter.report_warning_at(name_loc, "Deprecated Path#global. Use `#global?` instead")
interpreter.warnings.add_warning_at(name_loc, "Deprecated Path#global. Use `#global?` instead")
interpret_check_args { BoolLiteral.new(@global) }
when "global?"
interpret_check_args { BoolLiteral.new(@global) }
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/semantic/abstract_def_checker.cr
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ class Crystal::AbstractDefChecker
impl_param = impl_method.args[i]
base_param = base_method.args[i]
unless impl_param.external_name == base_param.external_name
@program.report_warning(impl_param, "positional parameter '#{impl_param.external_name}' corresponds to parameter '#{base_param.external_name}' of the overridden method #{Call.def_full_name(base_type, base_method)}, which has a different name and may affect named argument passing")
@program.warnings.try &.add_warning(impl_param, "positional parameter '#{impl_param.external_name}' corresponds to parameter '#{base_param.external_name}' of the overridden method #{Call.def_full_name(base_type, base_method)}, which has a different name and may affect named argument passing")
HertzDevil marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
Expand Down
9 changes: 0 additions & 9 deletions src/compiler/crystal/semantic/ast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,6 @@ module Crystal
::raise exception_type.for_node(self, message, inner)
end

def warning(message, inner = nil, exception_type = Crystal::TypeException)
# TODO extract message formatting from exceptions
String.build do |io|
exception = exception_type.for_node(self, message, inner)
exception.warning = true
exception.append_to_s(io, nil)
end
end

def simple_literal?
case self
when Nop, NilLiteral, BoolLiteral, NumberLiteral, CharLiteral,
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/crystal/semantic/top_level_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -507,11 +507,11 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
link_annotation = LinkAnnotation.from(ann)

if link_annotation.static?
@program.report_warning(ann, "specifying static linking for individual libraries is deprecated")
@program.warnings.try &.add_warning(ann, "specifying static linking for individual libraries is deprecated")
end

if ann.args.size > 1
@program.report_warning(ann, "using non-named arguments for Link annotations is deprecated")
@program.warnings.try &.add_warning(ann, "using non-named arguments for Link annotations is deprecated")
end

type.add_link_annotation(link_annotation)
Expand Down
83 changes: 9 additions & 74 deletions src/compiler/crystal/semantic/warnings.cr
Original file line number Diff line number Diff line change
@@ -1,68 +1,28 @@
require "../warnings"

module Crystal
class Program
# Which kind of warnings wants to be detected.
property warnings : Warnings = Warnings::All

# Paths to ignore for warnings detection.
property warnings_exclude : Array(String) = [] of String

# Detected warning failures.
property warning_failures = [] of String

# If `true` compiler will error if warnings are found.
property error_on_warnings : Bool = false
# Warning settings and all detected warnings.
property warnings = WarningCollection.new

@deprecated_constants_detected = Set(String).new
@deprecated_methods_detected = Set(String).new
@deprecated_macros_detected = Set(String).new

def report_warning(node : ASTNode, message : String)
return unless self.warnings.all?
return if self.ignore_warning_due_to_location?(node.location)

self.warning_failures << node.warning(message)
end

def report_warning_at(location : Location?, message : String)
return unless self.warnings.all?
return if self.ignore_warning_due_to_location?(location)

if location
message = String.build do |io|
exception = SyntaxException.new message, location.line_number, location.column_number, location.filename
exception.warning = true
exception.append_to_s(io, nil)
end
end

self.warning_failures << message
end

def ignore_warning_due_to_location?(location : Location?)
return false unless location

filename = location.original_filename
return false unless filename

@program.warnings_exclude.any? do |path|
filename.starts_with?(path)
end
end

def check_deprecated_constant(const : Const, node : Path)
return unless @warnings.all?
return unless @warnings.level.all?

check_deprecation(const, node, @deprecated_constants_detected)
end

def check_call_to_deprecated_macro(a_macro : Macro, call : Call)
return unless @warnings.all?
return unless @warnings.level.all?

check_deprecation(a_macro, call, @deprecated_macros_detected)
end

def check_call_to_deprecated_method(node : Call)
return unless @warnings.all?
return unless @warnings.level.all?
return if compiler_expanded_call(node)

node.target_defs.try &.each do |target_def|
Expand All @@ -74,7 +34,7 @@ module Crystal
if (ann = object.annotation(self.deprecated_annotation)) &&
(deprecated_annotation = DeprecatedAnnotation.from(ann))
use_location = use_site.location.try(&.macro_location) || use_site.location
return if !use_location || ignore_warning_due_to_location?(use_location)
return if !use_location || @warnings.ignore_warning_due_to_location?(use_location)

# skip warning if the use site was already informed
name = object.short_reference
Expand All @@ -89,7 +49,7 @@ module Crystal
end
end

@warning_failures << use_site.warning(full_message)
@warnings.infos << use_site.warning(full_message)
end
end

Expand Down Expand Up @@ -163,29 +123,4 @@ module Crystal
to_s
end
end

class Command
def report_warnings
compiler = @compiler
return unless compiler

program = compiler.program?
return unless program
return if program.warning_failures.empty?

program.warning_failures.each do |message|
STDERR.puts message
STDERR.puts "\n"
end
STDERR.puts "A total of #{program.warning_failures.size} warnings were found."
end

def warnings_fail_on_exit?
compiler = @compiler
return false unless compiler

program = compiler.program
program.error_on_warnings && program.warning_failures.size > 0
end
end
end
Loading