Showing with 359 additions and 225 deletions.
  1. +2 −3 .github/workflows/ci.yml
  2. +8 −0 .rustfmt.toml
  3. +1 −0 Cargo.toml
  4. +1 −1 LICENSE-MIT
  5. +11 −1 README.md
  6. +5 −5 gazebo_lint/Cargo.toml
  7. +64 −47 gazebo_lint/src/clippy.rs
  8. +237 −132 gazebo_lint/src/lib.rs
  9. +4 −3 linter_test/Cargo.toml
  10. +24 −28 linter_test/src/lib.rs
  11. +1 −1 rust-toolchain
  12. +0 −4 rustfmt.toml
  13. +1 −0 tools/rust/ossconfigs/clippy.toml
5 changes: 2 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ jobs:

steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly-2021-07-22
override: true
toolchain: nightly-2022-02-24
components: clippy, rustfmt, rust-src, rustc-dev, llvm-tools-preview
- run: cargo fmt -- --check
- run: cargo clippy ${{ matrix.flags }}
Expand Down
8 changes: 8 additions & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Get help on options with `rustfmt --help=config`
# Please keep these in alphabetical order.
edition = "2021"
group_imports = "StdExternalCrate"
imports_granularity = "Item"
merge_derives = false
use_field_init_shorthand = true
version = "Two"
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ members = [
# Uncomment to manually test linter_test
# "linter_test",
]
resolver = "2"
2 changes: 1 addition & 1 deletion LICENSE-MIT
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) Facebook, Inc. and its affiliates.
Copyright (c) Meta Platforms, Inc. and affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# This project is archived

gazebo-lint included lints aiming at:
* make writing code easier for Rust newcomers
* enforce certain code patterns in buck2 project
* enforce recommended patterns using gazebo library

This lint relies heavily on rust compiler internals, it is expensive to maintain,
so we decided to archive it.

# Gazebo Lint - a linter for patterns relating to the Gazebo Library

[![GitHub link](https://img.shields.io/badge/GitHub-facebookincubator%2Fgazebo_lint-blue.svg)](https://github.com/facebookincubator/gazebo_lint)
Expand Down Expand Up @@ -27,7 +37,7 @@ We will look to update the linter to use the proper alternatives if applicable w
1. Check the [GitHub Actions](https://github.com/facebookincubator/gazebo_lint/actions) are green.
2. Update `CHANGELOG.md` with the changes since the last release. [This link](https://github.com/facebookincubator/gazebo_lint/compare/v0.1.1...main) can help (update to compare against the last release).
3. Update the version numbers of the `Cargo.toml` file in `gazebo_lint`. Bump them by 0.0.1 if there are no incompatible changes, or 0.1.0 if there are.
4. Run `cargo publish --dry-run --allow-dirty`, then without the `--dry-run` in `gazebo_lint` directory.
4. Run `cargo publish --allow-dirty --dry-run`, then without the `--dry-run` in `gazebo_lint` directory.
5. Create a [GitHub release](https://github.com/facebookincubator/gazebo_lint/releases/new) with `v0.X.Y`, using the `gazebo_lint` version as the name.


Expand Down
10 changes: 5 additions & 5 deletions gazebo_lint/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[package]
name = "gazebo_lint"
version = "0.1.1"
edition = "2018"
license = "MIT OR Apache-2.0"
authors = ["Facebook"]
description = "A linter for the Gazebo Rust library"
edition = "2021"
license = "MIT OR Apache-2.0"
name = "gazebo_lint"
repository = "https://github.com/facebookincubator/gazebo_lint"
version = "0.1.1"

[lib]
plugin = true

[dependencies]
smallvec = "1.4"
rustversion = "1.0"
111 changes: 64 additions & 47 deletions gazebo_lint/src/clippy.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under both the MIT license found in the
* LICENSE-MIT file in the root directory of this source tree and the Apache
Expand All @@ -10,25 +10,30 @@
// Utilities copied from Clippy on 15 May 2020 with minimal modification
// https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs

use rustc_hir::{
self, def,
def::{DefKind, Res},
def_id::{DefId, CRATE_DEF_INDEX},
Expr, ExprKind, Impl, Item, ItemKind,
};
use std::mem;

use rustc_hir::def;
use rustc_hir::def::DefKind;
use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::CRATE_DEF_INDEX;
use rustc_hir::Expr;
use rustc_hir::ExprKind;
use rustc_hir::Impl;
use rustc_hir::Item;
use rustc_hir::ItemKind;
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
use rustc_middle::{
traits,
ty::{subst::GenericArg, Ty, TyKind, TyS},
};
use rustc_span::{self, symbol::Symbol, Span};
use rustc_trait_selection::traits::{
predicate_for_trait_def, query::evaluate_obligation::InferCtxtExt,
};
use rustc_typeck::hir_ty_to_ty;
use smallvec::SmallVec;
use std::mem;
use rustc_middle::traits;
use rustc_middle::ty::subst::GenericArg;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::TraitRef;
use rustc_middle::ty::Ty;
use rustc_middle::ty::TyKind;
use rustc_span::symbol::Symbol;
use rustc_span::Span;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;

macro_rules! sym {
($tt:tt) => {
Expand All @@ -54,11 +59,11 @@ pub fn method_calls<'tcx>(

let mut current = expr;
for _ in 0..max_depth {
if let ExprKind::MethodCall(path, span, args, _) = &current.kind {
if let ExprKind::MethodCall(path, receiver, args, _) = &current.kind {
method_names.push(path.ident.name);
arg_lists.push(&**args);
spans.push(*span);
current = &args[0];
spans.push(path.ident.span);
current = receiver;
if current.span.from_expansion() {
break;
}
Expand All @@ -71,8 +76,10 @@ pub fn method_calls<'tcx>(
(current, method_names, arg_lists, spans)
}

type PathToRes = Option<def::Res<!>>;

/// Gets the definition associated to a path.
pub fn path_to_res(cx: &LateContext, path: &[&str]) -> Option<def::Res> {
pub fn path_to_res(cx: &LateContext, path: &[&str]) -> PathToRes {
let crates = cx.tcx.crates(());
let krate = crates
.iter()
Expand All @@ -87,7 +94,7 @@ pub fn path_to_res(cx: &LateContext, path: &[&str]) -> Option<def::Res> {
return Some(Res::Def(DefKind::Mod, krate));
}

let mut items = cx.tcx.item_children(krate);
let mut items = cx.tcx.module_children(krate);
let mut path_it = path.iter().skip(1).peekable();

loop {
Expand All @@ -96,14 +103,13 @@ pub fn path_to_res(cx: &LateContext, path: &[&str]) -> Option<def::Res> {
None => return None,
};

let result = SmallVec::<[_; 8]>::new();
for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() {
for item in mem::take(&mut items).iter() {
if item.ident.name.as_str() == *segment {
if path_it.peek().is_none() {
return Some(item.res);
}

items = cx.tcx.item_children(item.res.def_id());
items = cx.tcx.module_children(item.res.def_id());
break;
}
}
Expand All @@ -117,23 +123,20 @@ pub fn path_to_res(cx: &LateContext, path: &[&str]) -> Option<def::Res> {
/// See also `get_trait_def_id`.
pub fn implements_trait<'tcx>(
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
trait_id: DefId,
ty_params: &[GenericArg<'tcx>],
ty_params: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
) -> bool {
let ty = cx.tcx.erase_regions(ty);
let obligation = predicate_for_trait_def(
cx.tcx,
cx.param_env,
traits::ObligationCause::dummy(),
trait_id,
0,
ty,
ty_params,
);
let trait_ref = TraitRef::new(cx.tcx, trait_id, ty_params);
let obligation = rustc_trait_selection::traits::Obligation {
cause: traits::ObligationCause::dummy(),
param_env: cx.param_env,
recursion_depth: 0,
predicate: trait_ref.without_const().to_predicate(cx.tcx),
};
cx.tcx
.infer_ctxt()
.enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
.build()
.predicate_must_hold_modulo_regions(&obligation)
}

/// Checks whether this is the implementation of a specific trait, if so, return the type the trait
Expand Down Expand Up @@ -185,18 +188,13 @@ pub fn match_def_path(cx: &LateContext, did: DefId, syms: &[&str]) -> bool {
}

/// Matches the given `ty` with generics to the given types and generic types
pub fn match_ty_path(
cx: &LateContext,
ty: &TyS,
ty_path: &[&str],
generic_tys: &[&[&str]],
) -> bool {
pub fn match_ty_path(cx: &LateContext, ty: Ty, ty_path: &[&str], generic_tys: &[&[&str]]) -> bool {
if let TyKind::Adt(ty, subst) = ty.kind() {
if match_def_path(cx, ty.did, ty_path) {
if match_def_path(cx, ty.did(), ty_path) {
let mut i = 0;
while i < generic_tys.len() {
if let Some(ty_param) = subst.type_at(i).ty_adt_def() {
if !match_def_path(cx, ty_param.did, generic_tys[i]) {
if !match_def_path(cx, ty_param.did(), generic_tys[i]) {
return false;
} else {
i += 1;
Expand All @@ -212,3 +210,22 @@ pub fn match_ty_path(

false
}

pub fn unpack_non_local<T>(res: def::Res<T>) -> Option<def::Res<!>> {
match res {
def::Res::Local(_) => None,
x => Some(x.expect_non_local()),
}
}

pub fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &rustc_hir::Ty<'_>) -> Ty<'tcx> {
cx.maybe_typeck_results()
.and_then(|results| {
if results.hir_owner == hir_ty.hir_id.owner {
results.node_type_opt(hir_ty.hir_id)
} else {
None
}
})
.unwrap_or_else(|| hir_ty_to_ty(cx.tcx, hir_ty))
}
Loading