diff --git a/mk/tests.mk b/mk/tests.mk index 20736165b73b0..87cfed2426e9c 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -240,52 +240,16 @@ cleantestlibs: # Tidy ###################################################################### -ifdef CFG_NOTIDY .PHONY: tidy -tidy: -else - -# Run the tidy script in multiple parts to avoid huge 'echo' commands -.PHONY: tidy -tidy: tidy-basic tidy-binaries tidy-errors tidy-features - -endif - -.PHONY: tidy-basic -tidy-basic: - @$(call E, check: formatting) - $(Q) $(CFG_PYTHON) $(S)src/etc/tidy.py $(S)src/ - -.PHONY: tidy-binaries -tidy-binaries: - @$(call E, check: binaries) - $(Q)find $(S)src -type f \ - \( -perm -u+x -or -perm -g+x -or -perm -o+x \) \ - -not -name '*.rs' -and -not -name '*.py' \ - -and -not -name '*.sh' -and -not -name '*.pp' \ - | grep '^$(S)src/jemalloc' -v \ - | grep '^$(S)src/libuv' -v \ - | grep '^$(S)src/llvm' -v \ - | grep '^$(S)src/rt/hoedown' -v \ - | grep '^$(S)src/gyp' -v \ - | grep '^$(S)src/etc' -v \ - | grep '^$(S)src/doc' -v \ - | grep '^$(S)src/compiler-rt' -v \ - | grep '^$(S)src/libbacktrace' -v \ - | grep '^$(S)src/rust-installer' -v \ - | grep '^$(S)src/liblibc' -v \ - | xargs $(CFG_PYTHON) $(S)src/etc/check-binaries.py - -.PHONY: tidy-errors -tidy-errors: - @$(call E, check: extended errors) - $(Q) $(CFG_PYTHON) $(S)src/etc/errorck.py $(S)src/ - -.PHONY: tidy-features -tidy-features: - @$(call E, check: feature sanity) - $(Q) $(CFG_PYTHON) $(S)src/etc/featureck.py $(S)src/ - +tidy: $(HBIN0_H_$(CFG_BUILD))/tidy$(X_$(CFG_BUILD)) + $< $(S)src + +$(HBIN0_H_$(CFG_BUILD))/tidy$(X_$(CFG_BUILD)): \ + $(TSREQ0_T_$(CFG_BUILD)_H_$(CFG_BUILD)) \ + $(TLIB0_T_$(CFG_BUILD)_H_$(CFG_BUILD))/stamp.std \ + $(call rwildcard,$(S)src/tools/tidy/src,*.rs) + $(STAGE0_T_$(CFG_BUILD)_H_$(CFG_BUILD)) src/tools/tidy/src/main.rs \ + --out-dir $(@D) --crate-name tidy ###################################################################### # Sets of tests diff --git a/src/bootstrap/build/check.rs b/src/bootstrap/build/check.rs index a2445ae498a77..d4e344fc48247 100644 --- a/src/bootstrap/build/check.rs +++ b/src/bootstrap/build/check.rs @@ -33,3 +33,10 @@ pub fn cargotest(build: &Build, stage: u32, host: &str) { .env("PATH", newpath) .arg(&build.cargo)); } + +pub fn tidy(build: &Build, stage: u32, host: &str) { + println!("tidy check stage{} ({})", stage, host); + let compiler = Compiler::new(stage, host); + build.run(build.tool_cmd(&compiler, "tidy") + .arg(build.src.join("src"))); +} diff --git a/src/bootstrap/build/mod.rs b/src/bootstrap/build/mod.rs index 248bf6cb4ea15..4f0bfb84344e7 100644 --- a/src/bootstrap/build/mod.rs +++ b/src/bootstrap/build/mod.rs @@ -197,6 +197,9 @@ impl Build { ToolCargoTest { stage } => { compile::tool(self, stage, target.target, "cargotest"); } + ToolTidy { stage } => { + compile::tool(self, stage, target.target, "tidy"); + } DocBook { stage } => { doc::rustbook(self, stage, target.target, "book", &doc_out); } @@ -230,6 +233,9 @@ impl Build { CheckCargoTest { stage } => { check::cargotest(self, stage, target.target); } + CheckTidy { stage } => { + check::tidy(self, stage, target.target); + } DistDocs { stage } => dist::docs(self, stage, target.target), DistMingw { _dummy } => dist::mingw(self, target.target), diff --git a/src/bootstrap/build/step.rs b/src/bootstrap/build/step.rs index 80fcc32e5371d..626cf7586317f 100644 --- a/src/bootstrap/build/step.rs +++ b/src/bootstrap/build/step.rs @@ -51,6 +51,7 @@ macro_rules! targets { (tool_rustbook, ToolRustbook { stage: u32 }), (tool_error_index, ToolErrorIndex { stage: u32 }), (tool_cargotest, ToolCargoTest { stage: u32 }), + (tool_tidy, ToolTidy { stage: u32 }), // Steps for long-running native builds. Ideally these wouldn't // actually exist and would be part of build scripts, but for now @@ -79,6 +80,7 @@ macro_rules! targets { (check, Check { stage: u32, compiler: Compiler<'a> }), (check_linkcheck, CheckLinkcheck { stage: u32 }), (check_cargotest, CheckCargoTest { stage: u32 }), + (check_tidy, CheckTidy { stage: u32 }), // Distribution targets, creating tarballs (dist, Dist { stage: u32 }), @@ -316,8 +318,13 @@ impl<'a> Step<'a> { Source::CheckCargoTest { stage } => { vec![self.tool_cargotest(stage)] } + Source::CheckTidy { stage } => { + vec![self.tool_tidy(stage)] + } - Source::ToolLinkchecker { stage } => { + Source::ToolLinkchecker { stage } | + Source::ToolTidy { stage } | + Source::ToolCargoTest { stage } => { vec![self.libstd(self.compiler(stage))] } Source::ToolErrorIndex { stage } | diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 9199c755f603e..1f3ea8f19bb02 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -42,5 +42,7 @@ check-cargotest: $(Q)$(BOOTSTRAP) --step check-cargotest dist: $(Q)$(BOOTSTRAP) --step dist +tidy: + $(Q)$(BOOTSTRAP) --step check-tidy --stage 0 .PHONY: dist diff --git a/src/etc/check-binaries.py b/src/etc/check-binaries.py deleted file mode 100755 index 91c01b178075d..0000000000000 --- a/src/etc/check-binaries.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -import sys - -offenders = sys.argv[1:] -if len(offenders) > 0: - print("Binaries checked into src:") - for offender in offenders: - print(offender) - sys.exit(1) diff --git a/src/etc/errorck.py b/src/etc/errorck.py deleted file mode 100644 index 1f5f3784ac61d..0000000000000 --- a/src/etc/errorck.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright 2015 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -# Digs error codes out of files named 'diagnostics.rs' across -# the tree, and ensures thare are no duplicates. - -import sys -import os -import re - -if len(sys.argv) < 2: - print("usage: errorck.py ") - sys.exit(1) - -src_dir = sys.argv[1] -errcode_map = {} -errcode_checked = [] -errcode_not_found = [] -error_re = re.compile("(E\d\d\d\d)") - -def check_unused_error_codes(error_codes, check_error_codes, filenames, dirnames, dirpath): - for filename in filenames: - if filename == "diagnostics.rs" or not filename.endswith(".rs"): - continue - path = os.path.join(dirpath, filename) - - with open(path, 'r') as f: - for line in f: - match = error_re.search(line) - if match: - errcode = match.group(1) - if errcode in error_codes: - error_codes.remove(errcode) - if errcode not in check_error_codes: - check_error_codes.append(errcode) - for dirname in dirnames: - path = os.path.join(dirpath, dirname) - for (dirpath, dnames, fnames) in os.walk(path): - check_unused_error_codes(error_codes, check_error_codes, fnames, dnames, dirpath) - - -# In the register_long_diagnostics! macro, entries look like this: -# -# EXXXX: r##" -# -# "##, -# -# These two variables are for detecting the beginning and end of diagnostic -# messages so that duplicate error codes are not reported when a code occurs -# inside a diagnostic message -long_diag_begin = "r##\"" -long_diag_end = "\"##" - -errors = False -all_errors = [] - -for (dirpath, dirnames, filenames) in os.walk(src_dir): - if "src/test" in dirpath or "src/llvm" in dirpath: - # Short circuit for fast - continue - - errcode_to_check = [] - for filename in filenames: - if filename != "diagnostics.rs": - continue - path = os.path.join(dirpath, filename) - - with open(path, 'r') as f: - inside_long_diag = False - errcode_to_check = [] - for line_num, line in enumerate(f, start=1): - if inside_long_diag: - # Skip duplicate error code checking for this line - if long_diag_end in line: - inside_long_diag = False - continue - - match = error_re.search(line) - if match: - errcode = match.group(1) - new_record = [(errcode, path, line_num, line)] - existing = errcode_map.get(errcode) - if existing is not None: - # This is a dupe - errcode_map[errcode] = existing + new_record - else: - errcode_map[errcode] = new_record - # we don't check if this is a long error explanation - if (long_diag_begin not in line and not line.strip().startswith("//") - and errcode not in errcode_to_check and errcode not in errcode_checked - and errcode not in errcode_not_found): - errcode_to_check.append(errcode) - - if long_diag_begin in line: - inside_long_diag = True - break - check_unused_error_codes(errcode_to_check, errcode_checked, filenames, dirnames, dirpath) - if len(errcode_to_check) > 0: - for errcode in errcode_to_check: - if errcode in errcode_checked: - continue - errcode_not_found.append(errcode) - -if len(errcode_not_found) > 0: - errcode_not_found.sort() - for errcode in errcode_not_found: - if errcode in errcode_checked: - continue - all_errors.append(errcode) - print("error: unused error code: {0} ({1}:{2})".format(*errcode_map[errcode][0])) - errors = True - - -for errcode, entries in errcode_map.items(): - all_errors.append(entries[0][0]) - if len(entries) > 1: - entries.sort() - print("error: duplicate error code " + errcode) - for entry in entries: - print("{1}: {2}\n{3}".format(*entry)) - errors = True - -print -print("* {0} error codes".format(len(errcode_map))) -print("* highest error code: " + max(all_errors)) -print - -if errors: - sys.exit(1) diff --git a/src/etc/featureck.py b/src/etc/featureck.py deleted file mode 100644 index d6cc25177e4ae..0000000000000 --- a/src/etc/featureck.py +++ /dev/null @@ -1,251 +0,0 @@ -# Copyright 2015 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -# This script does a tree-wide sanity checks against stability -# attributes, currently: -# * For all feature_name/level pairs the 'since' field is the same -# * That no features are both stable and unstable. -# * That lib features don't have the same name as lang features -# unless they are on the 'joint_features' whitelist -# * That features that exist in both lang and lib and are stable -# since the same version -# * Prints information about features - -import sys -import os -import re -import codecs - -if len(sys.argv) < 2: - print("usage: featureck.py ") - sys.exit(1) - -src_dir = sys.argv[1] - -# Features that are allowed to exist in both the language and the library -joint_features = [ ] - -# Grab the list of language features from the compiler -language_gate_statuses = [ "Active", "Deprecated", "Removed", "Accepted" ] -feature_gate_source = os.path.join(src_dir, "libsyntax", "feature_gate.rs") -language_features = [] -language_feature_names = [] -with open(feature_gate_source, 'r') as f: - for line in f: - original_line = line - line = line.strip() - is_feature_line = False - for status in language_gate_statuses: - if status in line and line.startswith("("): - is_feature_line = True - - if is_feature_line: - # turn ` ("foo", "1.0.0", Some(10), Active)` into - # `"foo", "1.0.0", Some(10), Active` - line = line.strip(' ,()') - parts = line.split(",") - if len(parts) != 4: - print("error: unexpected number of components in line: " + original_line) - sys.exit(1) - feature_name = parts[0].strip().replace('"', "") - since = parts[1].strip().replace('"', "") - issue = parts[2].strip() - status = parts[3].strip() - assert len(feature_name) > 0 - assert len(since) > 0 - assert len(issue) > 0 - assert len(status) > 0 - - language_feature_names += [feature_name] - language_features += [(feature_name, since, issue, status)] - -assert len(language_features) > 0 - -errors = False - -lib_features = { } -lib_features_and_level = { } -for (dirpath, dirnames, filenames) in os.walk(src_dir): - # Don't look for feature names in tests - if "src/test" in dirpath: - continue - - # Takes a long time to traverse LLVM - if "src/llvm" in dirpath: - continue - - for filename in filenames: - if not filename.endswith(".rs"): - continue - - path = os.path.join(dirpath, filename) - with codecs.open(filename=path, mode='r', encoding="utf-8") as f: - line_num = 0 - for line in f: - line_num += 1 - level = None - if "[unstable(" in line: - level = "unstable" - elif "[stable(" in line: - level = "stable" - else: - continue - - # This is a stability attribute. For the purposes of this - # script we expect both the 'feature' and 'since' attributes on - # the same line, e.g. - # `#[unstable(feature = "foo", since = "1.0.0")]` - - p = re.compile('(unstable|stable).*feature *= *"(\w*)"') - m = p.search(line) - if not m is None: - feature_name = m.group(2) - since = None - if re.compile("\[ *stable").search(line) is not None: - pp = re.compile('since *= *"([\w\.]*)"') - mm = pp.search(line) - if not mm is None: - since = mm.group(1) - else: - print("error: misformed stability attribute") - print("line %d of %:" % (line_num, path)) - print(line) - errors = True - - lib_features[feature_name] = feature_name - if lib_features_and_level.get((feature_name, level)) is None: - # Add it to the observed features - lib_features_and_level[(feature_name, level)] = \ - (since, path, line_num, line) - else: - # Verify that for this combination of feature_name and level the 'since' - # attribute matches. - (expected_since, source_path, source_line_num, source_line) = \ - lib_features_and_level.get((feature_name, level)) - if since != expected_since: - print("error: mismatch in %s feature '%s'" % (level, feature_name)) - print("line %d of %s:" % (source_line_num, source_path)) - print(source_line) - print("line %d of %s:" % (line_num, path)) - print(line) - errors = True - - # Verify that this lib feature doesn't duplicate a lang feature - if feature_name in language_feature_names: - print("error: lib feature '%s' duplicates a lang feature" % (feature_name)) - print("line %d of %s:" % (line_num, path)) - print(line) - errors = True - - else: - print("error: misformed stability attribute") - print("line %d of %s:" % (line_num, path)) - print(line) - errors = True - -# Merge data about both lists -# name, lang, lib, status, stable since - -language_feature_stats = {} - -for f in language_features: - name = f[0] - lang = True - lib = False - status = "unstable" - stable_since = None - - if f[3] == "Accepted": - status = "stable" - if status == "stable": - stable_since = f[1] - - language_feature_stats[name] = (name, lang, lib, status, stable_since) - -lib_feature_stats = {} - -for f in lib_features: - name = f - lang = False - lib = True - status = "unstable" - stable_since = None - - is_stable = lib_features_and_level.get((name, "stable")) is not None - is_unstable = lib_features_and_level.get((name, "unstable")) is not None - - if is_stable and is_unstable: - print("error: feature '%s' is both stable and unstable" % (name)) - errors = True - - if is_stable: - status = "stable" - stable_since = lib_features_and_level[(name, "stable")][0] - elif is_unstable: - status = "unstable" - - lib_feature_stats[name] = (name, lang, lib, status, stable_since) - -# Check for overlap in two sets -merged_stats = { } - -for name in lib_feature_stats: - if language_feature_stats.get(name) is not None: - if not name in joint_features: - print("error: feature '%s' is both a lang and lib feature but not whitelisted" % (name)) - errors = True - lang_status = language_feature_stats[name][3] - lib_status = lib_feature_stats[name][3] - lang_stable_since = language_feature_stats[name][4] - lib_stable_since = lib_feature_stats[name][4] - - if lang_status != lib_status and lib_status != "rustc_deprecated": - print("error: feature '%s' has lang status %s " + - "but lib status %s" % (name, lang_status, lib_status)) - errors = True - - if lang_stable_since != lib_stable_since: - print("error: feature '%s' has lang stable since %s " + - "but lib stable since %s" % (name, lang_stable_since, lib_stable_since)) - errors = True - - merged_stats[name] = (name, True, True, lang_status, lang_stable_since) - - del language_feature_stats[name] - del lib_feature_stats[name] - -if errors: - sys.exit(1) - -# Finally, display the stats -stats = {} -stats.update(language_feature_stats) -stats.update(lib_feature_stats) -stats.update(merged_stats) -lines = [] -for s in stats: - s = stats[s] - type_ = "lang" - if s[1] and s[2]: - type_ = "lang/lib" - elif s[2]: - type_ = "lib" - line = "{: <32}".format(s[0]) + \ - "{: <8}".format(type_) + \ - "{: <12}".format(s[3]) + \ - "{: <8}".format(str(s[4])) - lines += [line] - -lines.sort() - -print -for line in lines: - print("* " + line) -print diff --git a/src/etc/licenseck.py b/src/etc/licenseck.py deleted file mode 100644 index aa2a00beae583..0000000000000 --- a/src/etc/licenseck.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -import re -import os - -license_re = re.compile( -u"""(#|//) Copyright .* The Rust Project Developers. See the COPYRIGHT -\\1 file at the top-level directory of this distribution and at -\\1 http://rust-lang.org/COPYRIGHT. -\\1 -\\1 Licensed under the Apache License, Version 2.0 or the MIT license -\\1 , at your -\\1 option. This file may not be copied, modified, or distributed -\\1 except according to those terms.""") - -exceptions = [ - "libstd/sync/mpsc/mpsc_queue.rs", # BSD - "libstd/sync/mpsc/spsc_queue.rs", # BSD - "test/bench/shootout-binarytrees.rs", # BSD - "test/bench/shootout-chameneos-redux.rs", # BSD - "test/bench/shootout-fannkuch-redux.rs", # BSD - "test/bench/shootout-fasta.rs", # BSD - "test/bench/shootout-fasta-redux.rs", # BSD - "test/bench/shootout-k-nucleotide.rs", # BSD - "test/bench/shootout-mandelbrot.rs", # BSD - "test/bench/shootout-meteor.rs", # BSD - "test/bench/shootout-nbody.rs", # BSD - "test/bench/shootout-regex-dna.rs", # BSD - "test/bench/shootout-reverse-complement.rs", # BSD - "test/bench/shootout-spectralnorm.rs", # BSD - "test/bench/shootout-threadring.rs", # BSD -] - -def check_license(name, contents): - name = os.path.normpath(name) - # Whitelist check - if any(name.endswith(os.path.normpath(e)) for e in exceptions): - return True - - # Xfail check - firstlineish = contents[:100] - if "ignore-license" in firstlineish: - return True - - # License check - boilerplate = contents[:500] - return bool(license_re.search(boilerplate)) diff --git a/src/etc/tidy.py b/src/etc/tidy.py deleted file mode 100644 index 9264646673b5b..0000000000000 --- a/src/etc/tidy.py +++ /dev/null @@ -1,230 +0,0 @@ -# Copyright 2010-2014 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -import sys -import fileinput -import subprocess -import re -import os -from licenseck import check_license -import snapshot - -err = 0 -cols = 100 -cr_flag = "ignore-tidy-cr" -tab_flag = "ignore-tidy-tab" -linelength_flag = "ignore-tidy-linelength" - -interesting_files = ['.rs', '.py', '.js', '.sh', '.c', '.h'] -uninteresting_files = ['miniz.c', 'jquery', 'rust_android_dummy'] -stable_whitelist = { - 'src/bootstrap', - 'src/build_helper', - 'src/libcollectionstest', - 'src/libcore', - 'src/libstd', - 'src/rustc/std_shim', - 'src/rustc/test_shim', - 'src/test' -} - - -def report_error_name_no(name, no, s): - global err - print("%s:%d: %s" % (name, no, s)) - err = 1 - - -def report_err(s): - report_error_name_no(fileinput.filename(), fileinput.filelineno(), s) - - -def report_warn(s): - print("%s:%d: %s" % (fileinput.filename(), - fileinput.filelineno(), - s)) - - -def do_license_check(name, contents): - if not check_license(name, contents): - report_error_name_no(name, 1, "incorrect license") - - -def update_counts(current_name): - global file_counts - global count_other_linted_files - - _, ext = os.path.splitext(current_name) - - if ext in interesting_files: - file_counts[ext] += 1 - else: - count_other_linted_files += 1 - - -def interesting_file(f): - if any(x in f for x in uninteresting_files): - return False - - return any(os.path.splitext(f)[1] == ext for ext in interesting_files) - - -# Be careful to support Python 2.4, 2.6, and 3.x here! -config_proc = subprocess.Popen(["git", "config", "core.autocrlf"], - stdout=subprocess.PIPE) -result = config_proc.communicate()[0] - -true = "true".encode('utf8') -autocrlf = result.strip() == true if result is not None else False - -current_name = "" -current_contents = "" -check_tab = True -check_cr = True -check_linelength = True - -if len(sys.argv) < 2: - print("usage: tidy.py ") - sys.exit(1) - -src_dir = sys.argv[1] - -count_lines = 0 -count_non_blank_lines = 0 -count_other_linted_files = 0 - -file_counts = {ext: 0 for ext in interesting_files} - -all_paths = set() -needs_unstable_attr = set() - -try: - for (dirpath, dirnames, filenames) in os.walk(src_dir): - # Skip some third-party directories - skippable_dirs = { - 'src/jemalloc', - 'src/llvm', - 'src/gyp', - 'src/libbacktrace', - 'src/libuv', - 'src/compiler-rt', - 'src/rt/hoedown', - 'src/rustllvm', - 'src/rt/valgrind', - 'src/rt/msvc', - 'src/rust-installer', - 'src/liblibc', - } - - dirpath = os.path.normpath(dirpath) - if any(os.path.normpath(d) in dirpath for d in skippable_dirs): - continue - - file_names = [os.path.join(dirpath, f) for f in filenames - if interesting_file(f) - and not f.endswith("_gen.rs") - and not ".#" is f] - - if not file_names: - continue - - for line in fileinput.input(file_names, - openhook=fileinput.hook_encoded("utf-8")): - - filename = fileinput.filename() - - if "tidy.py" not in filename: - if "TODO" in line: - report_err("TODO is deprecated; use FIXME") - match = re.match(r'^.*/(\*|/!?)\s*XXX', line) - if match: - report_err("XXX is no longer necessary, use FIXME") - match = re.match(r'^.*//\s*(NOTE.*)$', line) - if match and "TRAVIS" not in os.environ: - m = match.group(1) - if "snap" in m.lower(): - report_warn(match.group(1)) - match = re.match(r'^.*//\s*SNAP\s+(\w+)', line) - if match: - hsh = match.group(1) - date, rev = snapshot.curr_snapshot_rev() - if not hsh.startswith(rev): - report_err("snapshot out of date (" + date - + "): " + line) - else: - if "SNAP " in line: - report_warn("unmatched SNAP line: " + line) - search = re.search(r'^#!\[unstable', line) - if search: - needs_unstable_attr.discard(filename) - - if cr_flag in line: - check_cr = False - if tab_flag in line: - check_tab = False - if linelength_flag in line: - check_linelength = False - - if check_tab and ('\t' in line and - "Makefile" not in filename): - report_err("tab character") - if check_cr and not autocrlf and '\r' in line: - report_err("CR character") - if line.endswith(" \n") or line.endswith("\t\n"): - report_err("trailing whitespace") - line_len = len(line)-2 if autocrlf else len(line)-1 - - if check_linelength and line_len > cols: - report_err("line longer than %d chars" % cols) - - if fileinput.isfirstline(): - # This happens at the end of each file except the last. - if current_name != "": - update_counts(current_name) - assert len(current_contents) > 0 - do_license_check(current_name, current_contents) - - current_name = filename - current_contents = "" - check_cr = True - check_tab = True - check_linelength = True - if all(f not in filename for f in stable_whitelist) and \ - re.search(r'src/.*/lib\.rs', filename): - needs_unstable_attr.add(filename) - - # Put a reasonable limit on the amount of header data we use for - # the licenseck - if len(current_contents) < 1000: - current_contents += line - - count_lines += 1 - if line.strip(): - count_non_blank_lines += 1 - - if current_name != "": - update_counts(current_name) - assert len(current_contents) > 0 - do_license_check(current_name, current_contents) - for f in needs_unstable_attr: - report_error_name_no(f, 1, "requires unstable attribute") - -except UnicodeDecodeError as e: - report_err("UTF-8 decoding error " + str(e)) - -print -for ext in sorted(file_counts, key=file_counts.get, reverse=True): - print("* linted {} {} files".format(file_counts[ext], ext)) -print("* linted {} other files".format(count_other_linted_files)) -print("* total lines of code: {}".format(count_lines)) -print("* total non-blank lines of code: {}".format(count_non_blank_lines)) -print() - -sys.exit(err) diff --git a/src/librustc_incremental/persist/serialize.rs b/src/librustc_incremental/persist/serialize.rs deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/src/test/auxiliary/specialization_cross_crate_defaults.rs b/src/test/auxiliary/specialization_cross_crate_defaults.rs old mode 100755 new mode 100644 diff --git a/src/test/compile-fail/regions-wf-trait-object.rs b/src/test/compile-fail/regions-wf-trait-object.rs index 40b715cf3b14d..39b8482cfa30e 100644 --- a/src/test/compile-fail/regions-wf-trait-object.rs +++ b/src/test/compile-fail/regions-wf-trait-object.rs @@ -6,7 +6,7 @@ // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // , at your // option. This file may not be copied, modified, or distributed -// except according to those terms.t +// except according to those terms. // Check that the explicit lifetime bound (`'b`, in this example) must // outlive all the superbound from the trait (`'a`, in this example). diff --git a/src/test/compile-fail/specialization/specialization-polarity.rs b/src/test/compile-fail/specialization/specialization-polarity.rs old mode 100755 new mode 100644 diff --git a/src/test/run-make/compiler-lookup-paths/native.c b/src/test/run-make/compiler-lookup-paths/native.c index e69de29bb2d1d..3066947052294 100644 --- a/src/test/run-make/compiler-lookup-paths/native.c +++ b/src/test/run-make/compiler-lookup-paths/native.c @@ -0,0 +1,9 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. diff --git a/src/test/run-pass/issue-11577.rs b/src/test/run-pass/issue-11577.rs index c1997fac74b78..a64fbb6afd61d 100644 --- a/src/test/run-pass/issue-11577.rs +++ b/src/test/run-pass/issue-11577.rs @@ -1,4 +1,4 @@ - // Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/test/run-pass/issue-9382.rs b/src/test/run-pass/issue-9382.rs index 2c84e202b26f1..fb7ffdcd515bb 100644 --- a/src/test/run-pass/issue-9382.rs +++ b/src/test/run-pass/issue-9382.rs @@ -1,6 +1,6 @@ // pretty-expanded FIXME #23616 - // Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/src/tools/tidy/Cargo.lock b/src/tools/tidy/Cargo.lock new file mode 100644 index 0000000000000..acaf9e5550fd3 --- /dev/null +++ b/src/tools/tidy/Cargo.lock @@ -0,0 +1,4 @@ +[root] +name = "tidy" +version = "0.1.0" + diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml new file mode 100644 index 0000000000000..e900bd47fb7bd --- /dev/null +++ b/src/tools/tidy/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "tidy" +version = "0.1.0" +authors = ["Alex Crichton "] + +[dependencies] diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs new file mode 100644 index 0000000000000..84d3ff2b238bb --- /dev/null +++ b/src/tools/tidy/src/bins.rs @@ -0,0 +1,45 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Tidy check to ensure that there are no binaries checked into the source tree +//! by accident. +//! +//! In the past we've accidentally checked in test binaries and such which add a +//! huge amount of bloat to the git history, so it's good to just ensure we +//! don't do that again :) + +use std::path::Path; + +// All files are executable on Windows, so just check on Unix +#[cfg(windows)] +pub fn check(_path: &Path, _bad: &mut bool) {} + +#[cfg(unix)] +pub fn check(path: &Path, bad: &mut bool) { + use std::fs; + use std::os::unix::prelude::*; + + super::walk(path, + &mut |path| super::filter_dirs(path) || path.ends_with("src/etc"), + &mut |file| { + let filename = file.file_name().unwrap().to_string_lossy(); + let extensions = [".py", ".sh"]; + if extensions.iter().any(|e| filename.ends_with(e)) { + return + } + + let metadata = t!(fs::metadata(&file)); + if metadata.mode() & 0o111 != 0 { + println!("binary checked into source: {}", file.display()); + *bad = true; + } + }) +} + diff --git a/src/tools/tidy/src/errors.rs b/src/tools/tidy/src/errors.rs new file mode 100644 index 0000000000000..21ec9270429e9 --- /dev/null +++ b/src/tools/tidy/src/errors.rs @@ -0,0 +1,90 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Tidy check to verify the validity of long error diagnostic codes. +//! +//! This ensures that error codes are used at most once and also prints out some +//! statistics about the error codes. + +use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; + +pub fn check(path: &Path, bad: &mut bool) { + let mut contents = String::new(); + let mut map = HashMap::new(); + super::walk(path, + &mut |path| super::filter_dirs(path) || path.ends_with("src/test"), + &mut |file| { + let filename = file.file_name().unwrap().to_string_lossy(); + if filename != "diagnostics.rs" { + return + } + + contents.truncate(0); + t!(t!(File::open(file)).read_to_string(&mut contents)); + + // In the register_long_diagnostics! macro, entries look like this: + // + // EXXXX: r##" + // + // "##, + // + // and these long messages often have error codes themselves inside + // them, but we don't want to report duplicates in these cases. This + // variable keeps track of whether we're currently inside one of these + // long diagnostic messages. + let mut inside_long_diag = false; + for (num, line) in contents.lines().enumerate() { + if inside_long_diag { + inside_long_diag = !line.contains("\"##"); + continue + } + + let mut search = line; + while let Some(i) = search.find("E") { + search = &search[i + 1..]; + let code = if search.len() > 4 { + search[..4].parse::() + } else { + continue + }; + let code = match code { + Ok(n) => n, + Err(..) => continue, + }; + map.entry(code).or_insert(Vec::new()) + .push((file.to_owned(), num + 1, line.to_owned())); + break + } + + inside_long_diag = line.contains("r##\""); + } + }); + + let mut max = 0; + println!("* {} error codes", map.len()); + for (code, entries) in map { + if code > max { + max = code; + } + if entries.len() == 1 { + continue + } + + println!("duplicate error code: {}", code); + for (file, line_num, line) in entries { + println!("{}:{}: {}", file.display(), line_num, line); + } + *bad = true; + } + println!("* highest error code: E{:04}", max); +} diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs new file mode 100644 index 0000000000000..c665b6e662f0f --- /dev/null +++ b/src/tools/tidy/src/features.rs @@ -0,0 +1,155 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Tidy check to ensure that unstable features are all in order +//! +//! This check will ensure properties like: +//! +//! * All stability attributes look reasonably well formed +//! * The set of library features is disjoint from the set of language features +//! * Library features have at most one stability level +//! * Library features have at most one `since` value + +use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; + +const STATUSES: &'static [&'static str] = &[ + "Active", "Deprecated", "Removed", "Accepted", +]; + +struct Feature { + name: String, + since: String, + status: String, +} + +struct LibFeature { + level: String, + since: String, +} + +pub fn check(path: &Path, bad: &mut bool) { + let features = collect_lang_features(&path.join("libsyntax/feature_gate.rs")); + let mut lib_features = HashMap::::new(); + + let mut contents = String::new(); + super::walk(path, + &mut |path| super::filter_dirs(path) || path.ends_with("src/test"), + &mut |file| { + let filename = file.file_name().unwrap().to_string_lossy(); + if !filename.ends_with(".rs") || filename == "features.rs" { + return + } + + contents.truncate(0); + t!(t!(File::open(file)).read_to_string(&mut contents)); + + for (i, line) in contents.lines().enumerate() { + let mut err = |msg: &str| { + println!("{}:{}: {}", file.display(), i + 1, msg); + *bad = true; + }; + let level = if line.contains("[unstable(") { + "unstable" + } else if line.contains("[stable(") { + "stable" + } else { + continue + }; + let feature_name = match find_attr_val(line, "feature") { + Some(name) => name, + None => { + err("malformed stability attribute"); + continue + } + }; + let since = match find_attr_val(line, "since") { + Some(name) => name, + None if level == "stable" => { + err("malformed stability attribute"); + continue + } + None => "None", + }; + + if features.iter().any(|f| f.name == feature_name) { + err("duplicating a lang feature"); + } + if let Some(ref s) = lib_features.get(feature_name) { + if s.level != level { + err("different stability level than before"); + } + if s.since != since { + err("different `since` than before"); + } + continue + } + lib_features.insert(feature_name.to_owned(), LibFeature { + level: level.to_owned(), + since: since.to_owned(), + }); + } + }); + + let mut lines = Vec::new(); + for feature in features { + lines.push(format!("{:<32} {:<8} {:<12} {:<8}", + feature.name, "lang", feature.status, feature.since)); + } + for (name, feature) in lib_features { + lines.push(format!("{:<32} {:<8} {:<12} {:<8}", + name, "lib", feature.level, feature.since)); + } + + lines.sort(); + for line in lines { + println!("* {}", line); + } +} + +fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> { + line.find(attr).and_then(|i| { + line[i..].find("\"").map(|j| i + j + 1) + }).and_then(|i| { + line[i..].find("\"").map(|j| (i, i + j)) + }).map(|(i, j)| { + &line[i..j] + }) +} + +fn collect_lang_features(path: &Path) -> Vec { + let mut contents = String::new(); + t!(t!(File::open(path)).read_to_string(&mut contents)); + + let mut features = Vec::new(); + for line in contents.lines().map(|l| l.trim()) { + if !STATUSES.iter().any(|s| line.contains(s) && line.starts_with("(")) { + continue + } + let mut parts = line.split(","); + let name = parts.next().unwrap().replace("\"", "").replace("(", ""); + let since = parts.next().unwrap().trim().replace("\"", ""); + let status = match parts.skip(1).next().unwrap() { + s if s.contains("Active") => "unstable", + s if s.contains("Removed") => "unstable", + s if s.contains("Accepted") => "stable", + s => panic!("unknown status: {}", s), + }; + + features.push(Feature { + name: name, + since: since, + status: status.to_owned(), + }); + } + return features +} diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs new file mode 100644 index 0000000000000..d8acf3250dff9 --- /dev/null +++ b/src/tools/tidy/src/main.rs @@ -0,0 +1,76 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Tidy checks for source code in this repository +//! +//! This program runs all of the various tidy checks for style, cleanliness, +//! etc. This is run by default on `make check` and as part of the auto +//! builders. + +use std::fs; +use std::path::{PathBuf, Path}; +use std::env; + +macro_rules! t { + ($e:expr) => (match $e { + Ok(e) => e, + Err(e) => panic!("{} failed with {}", stringify!($e), e), + }) +} + +mod bins; +mod style; +mod errors; +mod features; + +fn main() { + let path = env::args_os().skip(1).next().expect("need an argument"); + let path = PathBuf::from(path); + + let mut bad = false; + bins::check(&path, &mut bad); + style::check(&path, &mut bad); + errors::check(&path, &mut bad); + features::check(&path, &mut bad); + + if bad { + panic!("some tidy checks failed"); + } +} + +fn filter_dirs(path: &Path) -> bool { + let skip = [ + "src/jemalloc", + "src/llvm", + "src/libbacktrace", + "src/compiler-rt", + "src/rt/hoedown", + "src/rustllvm", + "src/rust-installer", + "src/liblibc", + ]; + skip.iter().any(|p| path.ends_with(p)) +} + + +fn walk(path: &Path, skip: &mut FnMut(&Path) -> bool, f: &mut FnMut(&Path)) { + for entry in t!(fs::read_dir(path)) { + let entry = t!(entry); + let kind = t!(entry.file_type()); + let path = entry.path(); + if kind.is_dir() { + if !skip(&path) { + walk(&path, skip, f); + } + } else { + f(&path); + } + } +} diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs new file mode 100644 index 0000000000000..4c5f72c1e7960 --- /dev/null +++ b/src/tools/tidy/src/style.rs @@ -0,0 +1,127 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Tidy check to enforce various stylistic guidelines on the Rust codebase. +//! +//! Example checks are: +//! +//! * No lines over 100 characters +//! * No tabs +//! * No trailing whitespace +//! * No CR characters +//! * No `TODO` or `XXX` directives +//! * A valid license header is at the top +//! +//! A number of these checks can be opted-out of with various directives like +//! `// ignore-tidy-linelength`. + +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; + +const COLS: usize = 100; +const LICENSE: &'static str = "\ +Copyright The Rust Project Developers. See the COPYRIGHT +file at the top-level directory of this distribution and at +http://rust-lang.org/COPYRIGHT. + +Licensed under the Apache License, Version 2.0 or the MIT license +, at your +option. This file may not be copied, modified, or distributed +except according to those terms."; + +pub fn check(path: &Path, bad: &mut bool) { + let mut contents = String::new(); + super::walk(path, &mut super::filter_dirs, &mut |file| { + let filename = file.file_name().unwrap().to_string_lossy(); + let extensions = [".rs", ".py", ".js", ".sh", ".c", ".h"]; + if extensions.iter().all(|e| !filename.ends_with(e)) || + filename.starts_with(".#") { + return + } + if filename == "miniz.c" || filename.contains("jquery") { + return + } + + contents.truncate(0); + t!(t!(File::open(file)).read_to_string(&mut contents)); + let skip_cr = contents.contains("ignore-tidy-cr"); + let skip_tab = contents.contains("ignore-tidy-tab"); + let skip_length = contents.contains("ignore-tidy-linelength"); + for (i, line) in contents.split("\n").enumerate() { + let mut err = |msg: &str| { + println!("{}:{}: {}", file.display(), i + 1, msg); + *bad = true; + }; + if line.chars().count() > COLS && !skip_length { + err(&format!("line longer than {} chars", COLS)); + } + if line.contains("\t") && !skip_tab { + err("tab character"); + } + if line.ends_with(" ") || line.ends_with("\t") { + err("trailing whitespace"); + } + if line.contains("\r") && !skip_cr { + err("CR character"); + } + if filename != "style.rs" && filename != "tidy.py" { + if line.contains("TODO") { + err("TODO is deprecated; use FIXME") + } + if line.contains("//") && line.contains(" XXX") { + err("XXX is deprecated; use FIXME") + } + } + } + if !licenseck(file, &contents) { + println!("{}: incorrect license", file.display()); + *bad = true; + } + }) +} + +fn licenseck(file: &Path, contents: &str) -> bool { + if contents.contains("ignore-license") { + return true + } + let exceptions = [ + "libstd/sync/mpsc/mpsc_queue.rs", + "libstd/sync/mpsc/spsc_queue.rs", + ]; + if exceptions.iter().any(|f| file.ends_with(f)) { + return true + } + + // Skip the BOM if it's there + let bom = "\u{feff}"; + let contents = if contents.starts_with(bom) {&contents[3..]} else {contents}; + + // See if the license shows up in the first 100 lines + let lines = contents.lines().take(100).collect::>(); + lines.windows(LICENSE.lines().count()).any(|window| { + let offset = if window.iter().all(|w| w.starts_with("//")) { + 2 + } else if window.iter().all(|w| w.starts_with("#")) { + 1 + } else { + return false + }; + window.iter().map(|a| a[offset..].trim()) + .zip(LICENSE.lines()).all(|(a, b)| { + a == b || match b.find("") { + Some(i) => a.starts_with(&b[..i]) && a.ends_with(&b[i+6..]), + None => false, + } + }) + }) + +}