Commit 9da05986 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Merge the :nodoc warning and refactored warnings system.

Fixes #436
parents 31be5eb1 69ad2164
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ require 'jsduck/process/versions'
require 'jsduck/process/return_values'
require 'jsduck/process/fires'
require 'jsduck/process/lint'
require 'jsduck/process/no_doc'
require 'jsduck/process/circular_deps'

module JsDuck
@@ -71,6 +72,7 @@ module JsDuck
      Process::ReturnValues.new(relations).process_all!
      Process::Fires.new(relations).process_all!
      Process::Lint.new(relations).process_all!
      Process::NoDoc.new(relations).process_all!
      relations
    end

+51 −98
Original line number Diff line number Diff line
require 'jsduck/util/singleton'
require 'jsduck/util/os'
require 'jsduck/warning/registry'
require 'jsduck/warning/warn_exception'

module JsDuck

@@ -18,51 +20,7 @@ module JsDuck
      @verbose = false
      @colors = nil

      @warning_docs = [
        [:global, "Member doesn't belong to any class"],
        [:inheritdoc, "@inheritdoc referring to unknown class or member"],
        [:extend, "@extend/mixin/requires/uses referring to unknown class"],
        [:tag, "Use of unsupported @tag"],
        [:tag_repeated, "An @tag used multiple times, but only once allowed"],
        [:tag_syntax, "@tag syntax error"],
        [:link, "{@link} to unknown class or member"],
        [:link_ambiguous, "{@link} is ambiguous"],
        [:link_auto, "Auto-detected link to unknown class or member"],
        [:html, "Unclosed HTML tag."],

        [:alt_name, "Name used as both classname and alternate classname"],
        [:name_missing, "Member or parameter has no name"],
        [:no_doc, "Public class without documentation"],
        [:no_doc_member, "Public member without documentation"],
        [:no_doc_param, "Parameter of public member without documentation"],
        [:dup_param, "Method has two parameters with the same name"],
        [:dup_member, "Class has two members with the same name"],
        [:req_after_opt, "Required parameter comes after optional"],
        [:param_count, "Less parameters documented than detected from code"],
        [:subproperty, "@param foo.bar where foo param doesn't exist"],
        [:sing_static, "Singleton class member marked as @static"],
        [:type_syntax, "Syntax error in {type definition}"],
        [:type_name, "Unknown type referenced in {type definition}"],
        [:enum, "Enum with invalid values or no values at all"],
        [:fires, "@fires references unknown event"],

        [:image, "{@img} referring to missing file"],
        [:image_unused, "An image exists in --images dir that's not used"],
        [:cat_old_format, "Categories file uses old deprecated format"],
        [:cat_no_match, "Class pattern in categories file matches nothing"],
        [:cat_class_missing, "Class is missing from categories file"],
        [:guide, "Guide is missing from --guides dir"],

        [:aside, "Problem with @aside tag"],
        [:hide, "Problem with @hide tag"],
      ]
      # Turn off all warnings by default.
      # This is good for testing.
      # When running JSDuck app, the Options class enables most warnings.
      @warnings = {}
      @warning_docs.each do |w|
        @warnings[w[0]] = {:enabled => false, :patterns => []}
      end
      @warnings = Warning::Registry.new

      @shown_warnings = {}
    end
@@ -77,31 +35,17 @@ module JsDuck
    # Enables or disables a particular warning
    # or all warnings when type == :all.
    # Additionally a filename pattern can be specified.
    def set_warning(type, enabled, pattern=nil)
      if type == :all
        # When used with a pattern, only add the pattern to the rules
        # where it can have an effect - otherwise we get a warning.
        @warnings.each_key do |key|
          set_warning(key, enabled, pattern) unless pattern && @warnings[key][:enabled] == enabled
        end
      elsif @warnings.has_key?(type)
        if pattern
          if @warnings[type][:enabled] == enabled
            warn(nil, "Warning rule '#{enabled ? '+' : '-'}#{type}:#{pattern}' has no effect")
          else
            @warnings[type][:patterns] << Regexp.new(Regexp.escape(pattern))
          end
        else
          @warnings[type] = {:enabled => enabled, :patterns => []}
        end
      else
        warn(nil, "Warning of type '#{type}' doesn't exist")
    def set_warning(type, enabled, pattern=nil, params=[])
      begin
        @warnings.set(type, enabled, pattern, params)
      rescue Warning::WarnException => e
        warn(nil, e.message)
      end
    end

    # get documentation for all warnings
    def doc_warnings
      @warning_docs.map {|w| " #{@warnings[w[0]][:enabled] ? '+' : '-'}#{w[0]} - #{w[1]}" }
      @warnings.doc
    end

    # Prints warning message.
@@ -125,47 +69,25 @@ module JsDuck
        filename = filename[:filename]
      end

      msg = paint(:yellow, "Warning: ") + format(filename, line) + " " + msg

      if warning_enabled?(type, filename)
        if !@shown_warnings[msg]
          $stderr.puts msg
          @shown_warnings[msg] = true
        end
        print_warning(msg, filename, line)
      end

      return false
    end

    # True when the warning is enabled for the given type and filename
    # combination.
    def warning_enabled?(type, filename)
      if type == nil
        true
      elsif !@warnings.has_key?(type)
        warn(nil, "Unknown warning type #{type}")
        false
      else
        rule = @warnings[type]
        if rule[:patterns].any? {|re| filename =~ re }
          !rule[:enabled]
        else
          rule[:enabled]
        end
      end
    end
    # Prints :nodoc warning message.
    #
    # Because the :nodoc warning needs different parameters, for now
    # we're using a separate method specially for these.
    def warn_nodoc(type, visibility, msg, file)
      filename = file[:filename]
      line = file[:linenr]

    # Formats filename and line number for output
    def format(filename=nil, line=nil)
      out = ""
      if filename
        out = Util::OS.windows? ? filename.gsub('/', '\\') : filename
        if line
          out += ":#{line}:"
      if @warnings.enabled?(:nodoc, filename, [type, visibility])
        print_warning(msg, filename, line)
      end
    end
      paint(:magenta, out)
    end

    # Prints fatal error message with backtrace.
    # The error param should be $! from resque block.
@@ -202,6 +124,37 @@ module JsDuck

    CLEAR = "\e[0m"

    def warning_enabled?(type, filename)
      if type == nil
        true
      elsif !@warnings.has?(type)
        warn(nil, "Unknown warning type #{type}")
      else
        @warnings.enabled?(type, filename)
      end
    end

    def print_warning(msg, filename, line)
      warning = paint(:yellow, "Warning: ") + format(filename, line) + " " + msg

      if !@shown_warnings[warning]
        $stderr.puts warning
        @shown_warnings[warning] = true
      end
    end

    # Formats filename and line number for output
    def format(filename=nil, line=nil)
      out = ""
      if filename
        out = Util::OS.windows? ? filename.gsub('/', '\\') : filename
        if line
          out += ":#{line}:"
        end
      end
      paint(:magenta, out)
    end

    # Helper for doing colored output in UNIX terminal
    #
    # Only does color output when STDERR is attached to TTY
+8 −8
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ require 'jsduck/util/io'
require 'jsduck/util/parallel'
require 'jsduck/tag_registry'
require 'jsduck/js/ext_patterns'
require 'jsduck/warning/parser'

module JsDuck

@@ -148,7 +149,7 @@ module JsDuck
      Logger.set_warning(:link_auto, false)
      Logger.set_warning(:param_count, false)
      Logger.set_warning(:fires, false)
      Logger.set_warning(:no_doc_param, false)
      Logger.set_warning(:nodoc, false)

      @optparser = create_option_parser
    end
@@ -672,7 +673,7 @@ module JsDuck
          Logger.verbose = true
        end

        opts.on('--warnings=+A,-B,+C', Array,
        opts.on('--warnings=+A,-B,+C',
          "Turns warnings selectively on/off.",
          "",
          " +all - to turn on all warnings.",
@@ -696,13 +697,12 @@ module JsDuck
          "(Those with '+' in front of them default to on)",
          "",
          *Logger.doc_warnings) do |warnings|
          warnings.each do |op|
            if op =~ /^([-+]?)(\w+)(?::(.*))?$/
              enable = !($1 == "-")
              name = $2.to_sym
              path = $3
              Logger.set_warning(name, enable, path)
          begin
            Warning::Parser.new(warnings).parse.each do |w|
              Logger.set_warning(w[:type], w[:enabled], w[:path], w[:params])
            end
          rescue Warning::WarnException => e
            Logger.warn(nil, e.message)
          end
        end

+0 −28
Original line number Diff line number Diff line
require 'jsduck/logger'
require 'jsduck/class'

module JsDuck
  module Process
@@ -14,7 +13,6 @@ module JsDuck

      # Runs the linter
      def process_all!
        warn_no_doc
        warn_unnamed
        warn_optional_params
        warn_duplicate_params
@@ -37,32 +35,6 @@ module JsDuck
        end
      end

      # print warning for each class or public member with no name
      def warn_no_doc
        @relations.each do |cls|

          if cls[:doc] == "" && !cls[:private]
            warn(:no_doc, "No documentation for #{cls[:name]}", cls)
          end

          cls.all_local_members.each do |member|
            if !member[:private] && !member[:hide] && !JsDuck::Class.constructor?(member)
              if member[:doc] == ""
                warn(:no_doc_member, "No documentation for #{member[:owner]}##{member[:name]}", member)
              end

              (member[:params] || []).each do |p|
                if p[:doc] == ""
                  warn(:no_doc_param, "No documentation for parameter #{p[:name]} of #{member[:owner]}##{member[:name]}", member)
                end
              end

            end
          end

        end
      end

      # print warning for each non-optional parameter that follows an optional parameter
      def warn_optional_params
        each_member do |member|
+50 −0
Original line number Diff line number Diff line
require 'jsduck/logger'
require 'jsduck/class'

module JsDuck
  module Process

    # Reports missing documentation
    class NoDoc
      def initialize(relations)
        @relations = relations
      end

      # Prints warning for each class or public member with no name
      def process_all!
        @relations.each do |cls|

          if cls[:doc] == ""
            warn(:class, "No documentation for #{cls[:name]}", cls)
          end

          cls.all_local_members.each do |member|
            if !member[:hide] && !JsDuck::Class.constructor?(member)
              if member[:doc] == ""
                warn(:member, "No documentation for #{member[:owner]}##{member[:name]}", member)
              end

              (member[:params] || []).each do |p|
                if p[:doc] == ""
                  warn(:param, "No documentation for parameter #{p[:name]} of #{member[:owner]}##{member[:name]}", member)
                end
              end

            end
          end

        end
      end

      private

      def warn(type, msg, owner)
        visibility = owner[:private] ? :private : (owner[:protected] ? :protected : :public)

        Logger.warn_nodoc(type, visibility, msg, owner[:files][0])
      end

    end

  end
end
Loading